5 #include "src/debug/debug-coverage.h" 7 #include "src/ast/ast.h" 8 #include "src/base/hashmap.h" 9 #include "src/debug/debug.h" 10 #include "src/deoptimizer.h" 11 #include "src/frames-inl.h" 12 #include "src/isolate.h" 13 #include "src/objects.h" 14 #include "src/objects/debug-objects-inl.h" 21 base::KeyEqualityMatcher<void*>,
22 base::DefaultAllocationPolicy> {
26 Entry* entry = LookupOrInsert(key, Hash(key), []() {
return 0; });
28 if (UINT32_MAX - count < old_count) {
29 entry->value = UINT32_MAX;
31 entry->value = old_count + count;
36 Entry* entry = Lookup(key, Hash(key));
37 if (entry ==
nullptr)
return 0;
43 return static_cast<uint32_t>(
reinterpret_cast<intptr_t
>(key));
51 int start = info->function_token_position();
52 if (start == kNoSourcePosition) start = info->StartPosition();
56 bool CompareSharedFunctionInfo(SharedFunctionInfo* a, SharedFunctionInfo* b) {
57 int a_start = StartPosition(a);
58 int b_start = StartPosition(b);
59 if (a_start == b_start)
return a->EndPosition() > b->EndPosition();
60 return a_start < b_start;
63 bool CompareCoverageBlock(
const CoverageBlock& a,
const CoverageBlock& b) {
64 DCHECK_NE(kNoSourcePosition, a.start);
65 DCHECK_NE(kNoSourcePosition, b.start);
66 if (a.start == b.start)
return a.end > b.end;
67 return a.start < b.start;
70 void SortBlockData(std::vector<CoverageBlock>& v) {
72 std::sort(v.begin(), v.end(), CompareCoverageBlock);
75 std::vector<CoverageBlock> GetSortedBlockData(SharedFunctionInfo* shared) {
76 DCHECK(shared->HasCoverageInfo());
78 CoverageInfo coverage_info =
79 CoverageInfo::cast(shared->GetDebugInfo()->coverage_info());
81 std::vector<CoverageBlock> result;
82 if (coverage_info->SlotCount() == 0)
return result;
84 for (
int i = 0;
i < coverage_info->SlotCount();
i++) {
85 const int start_pos = coverage_info->StartSourcePosition(
i);
86 const int until_pos = coverage_info->EndSourcePosition(
i);
87 const int count = coverage_info->BlockCount(
i);
89 DCHECK_NE(kNoSourcePosition, start_pos);
90 result.emplace_back(start_pos, until_pos, count);
93 SortBlockData(result);
103 class CoverageBlockIterator final {
105 explicit CoverageBlockIterator(CoverageFunction*
function)
106 : function_(function),
108 delete_current_(false),
111 DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(),
112 CompareCoverageBlock));
115 ~CoverageBlockIterator() {
117 DCHECK(std::is_sorted(function_->blocks.begin(), function_->blocks.end(),
118 CompareCoverageBlock));
121 bool HasNext()
const {
122 return read_index_ + 1 <
static_cast<int>(function_->blocks.size());
127 if (!ended_) MaybeWriteCurrent();
136 if (read_index_ == -1) {
138 nesting_stack_.emplace_back(function_->start, function_->end,
140 }
else if (!delete_current_) {
141 nesting_stack_.emplace_back(GetBlock());
144 delete_current_ =
false;
149 CoverageBlock& block = GetBlock();
150 while (nesting_stack_.size() > 1 &&
151 nesting_stack_.back().end <= block.start) {
152 nesting_stack_.pop_back();
155 DCHECK_IMPLIES(block.start >= function_->end,
156 block.end == kNoSourcePosition);
157 DCHECK_NE(block.start, kNoSourcePosition);
158 DCHECK_LE(block.end, GetParent().end);
163 CoverageBlock& GetBlock() {
165 return function_->blocks[read_index_];
168 CoverageBlock& GetNextBlock() {
171 return function_->blocks[read_index_ + 1];
174 CoverageBlock& GetPreviousBlock() {
176 DCHECK_GT(read_index_, 0);
177 return function_->blocks[read_index_ - 1];
180 CoverageBlock& GetParent() {
182 return nesting_stack_.back();
185 bool HasSiblingOrChild() {
187 return HasNext() && GetNextBlock().start < GetParent().end;
190 CoverageBlock& GetSiblingOrChild() {
191 DCHECK(HasSiblingOrChild());
193 return GetNextBlock();
198 bool IsTopLevel()
const {
return nesting_stack_.size() == 1; }
201 DCHECK(!delete_current_);
203 delete_current_ =
true;
207 void MaybeWriteCurrent() {
208 if (delete_current_)
return;
209 if (read_index_ >= 0 && write_index_ != read_index_) {
210 function_->blocks[write_index_] = function_->blocks[read_index_];
219 function_->blocks.resize(write_index_);
222 bool IsActive()
const {
return read_index_ >= 0 && !ended_; }
224 CoverageFunction* function_;
225 std::vector<CoverageBlock> nesting_stack_;
227 bool delete_current_;
232 bool HaveSameSourceRange(
const CoverageBlock& lhs,
const CoverageBlock& rhs) {
233 return lhs.start == rhs.start && lhs.end == rhs.end;
236 void MergeDuplicateSingletons(CoverageFunction*
function) {
237 CoverageBlockIterator iter(
function);
239 while (iter.Next() && iter.HasNext()) {
240 CoverageBlock& block = iter.GetBlock();
241 CoverageBlock& next_block = iter.GetNextBlock();
247 if (!HaveSameSourceRange(block, next_block))
continue;
249 DCHECK_EQ(kNoSourcePosition, block.end);
250 next_block.count = std::max(block.count, next_block.count);
255 void MergeDuplicateRanges(CoverageFunction*
function) {
256 CoverageBlockIterator iter(
function);
258 while (iter.Next() && iter.HasNext()) {
259 CoverageBlock& block = iter.GetBlock();
260 CoverageBlock& next_block = iter.GetNextBlock();
262 if (!HaveSameSourceRange(block, next_block))
continue;
264 DCHECK_NE(kNoSourcePosition, block.end);
265 next_block.count = std::max(block.count, next_block.count);
274 void RewritePositionSingletonsToRanges(CoverageFunction*
function) {
275 CoverageBlockIterator iter(
function);
277 while (iter.Next()) {
278 CoverageBlock& block = iter.GetBlock();
279 CoverageBlock& parent = iter.GetParent();
281 if (block.start >= function->end) {
282 DCHECK_EQ(block.end, kNoSourcePosition);
284 }
else if (block.end == kNoSourcePosition) {
287 if (iter.HasSiblingOrChild()) {
288 block.end = iter.GetSiblingOrChild().start;
289 }
else if (iter.IsTopLevel()) {
293 block.end = parent.end - 1;
295 block.end = parent.end;
301 void MergeConsecutiveRanges(CoverageFunction*
function) {
302 CoverageBlockIterator iter(
function);
304 while (iter.Next()) {
305 CoverageBlock& block = iter.GetBlock();
307 if (iter.HasSiblingOrChild()) {
308 CoverageBlock& sibling = iter.GetSiblingOrChild();
309 if (sibling.start == block.end && sibling.count == block.count) {
312 sibling.start = block.start;
319 void MergeNestedRanges(CoverageFunction*
function) {
320 CoverageBlockIterator iter(
function);
322 while (iter.Next()) {
323 CoverageBlock& block = iter.GetBlock();
324 CoverageBlock& parent = iter.GetParent();
326 if (parent.count == block.count) {
334 void FilterAliasedSingletons(CoverageFunction*
function) {
335 CoverageBlockIterator iter(
function);
339 while (iter.Next()) {
340 CoverageBlock& previous_block = iter.GetPreviousBlock();
341 CoverageBlock& block = iter.GetBlock();
343 bool is_singleton = block.end == kNoSourcePosition;
344 bool aliases_start = block.start == previous_block.start;
346 if (is_singleton && aliases_start) {
349 DCHECK_NE(previous_block.end, kNoSourcePosition);
352 DCHECK_IMPLIES(iter.HasNext(), iter.GetNextBlock().start != block.start);
358 void FilterUncoveredRanges(CoverageFunction*
function) {
359 CoverageBlockIterator iter(
function);
361 while (iter.Next()) {
362 CoverageBlock& block = iter.GetBlock();
363 CoverageBlock& parent = iter.GetParent();
364 if (block.count == 0 && parent.count == 0) iter.DeleteBlock();
368 void FilterEmptyRanges(CoverageFunction*
function) {
369 CoverageBlockIterator iter(
function);
371 while (iter.Next()) {
372 CoverageBlock& block = iter.GetBlock();
373 if (block.start == block.end) iter.DeleteBlock();
377 void ClampToBinary(CoverageFunction*
function) {
378 CoverageBlockIterator iter(
function);
380 while (iter.Next()) {
381 CoverageBlock& block = iter.GetBlock();
382 if (block.count > 0) block.count = 1;
386 void ResetAllBlockCounts(SharedFunctionInfo* shared) {
387 DCHECK(shared->HasCoverageInfo());
389 CoverageInfo coverage_info =
390 CoverageInfo::cast(shared->GetDebugInfo()->coverage_info());
392 for (
int i = 0;
i < coverage_info->SlotCount();
i++) {
393 coverage_info->ResetBlockCount(
i);
397 bool IsBlockMode(debug::Coverage::Mode mode) {
399 case debug::Coverage::kBlockBinary:
400 case debug::Coverage::kBlockCount:
407 bool IsBinaryMode(debug::Coverage::Mode mode) {
409 case debug::Coverage::kBlockBinary:
410 case debug::Coverage::kPreciseBinary:
417 void CollectBlockCoverage(CoverageFunction*
function, SharedFunctionInfo* info,
418 debug::Coverage::Mode mode) {
419 DCHECK(IsBlockMode(mode));
421 function->has_block_coverage =
true;
422 function->blocks = GetSortedBlockData(info);
425 if (mode == debug::Coverage::kBlockBinary) ClampToBinary(
function);
428 MergeDuplicateSingletons(
function);
437 FilterAliasedSingletons(
function);
441 RewritePositionSingletonsToRanges(
function);
446 MergeConsecutiveRanges(
function);
448 SortBlockData(function->blocks);
449 MergeDuplicateRanges(
function);
450 MergeNestedRanges(
function);
452 MergeConsecutiveRanges(
function);
456 FilterUncoveredRanges(
function);
459 FilterEmptyRanges(
function);
462 ResetAllBlockCounts(info);
466 std::unique_ptr<Coverage> Coverage::CollectPrecise(Isolate* isolate) {
467 DCHECK(!isolate->is_best_effort_code_coverage());
468 std::unique_ptr<Coverage> result =
469 Collect(isolate, isolate->code_coverage_mode());
470 if (!isolate->is_collecting_type_profile() &&
471 (isolate->is_precise_binary_code_coverage() ||
472 isolate->is_block_binary_code_coverage())) {
475 isolate->SetFeedbackVectorsForProfilingTools(*ArrayList::New(isolate, 0));
480 std::unique_ptr<Coverage> Coverage::CollectBestEffort(Isolate* isolate) {
481 return Collect(isolate, v8::debug::Coverage::kBestEffort);
484 std::unique_ptr<Coverage> Coverage::Collect(
485 Isolate* isolate, v8::debug::Coverage::Mode collectionMode) {
486 SharedToCounterMap counter_map;
488 const bool reset_count = collectionMode != v8::debug::Coverage::kBestEffort;
490 switch (isolate->code_coverage_mode()) {
491 case v8::debug::Coverage::kBlockBinary:
492 case v8::debug::Coverage::kBlockCount:
493 case v8::debug::Coverage::kPreciseBinary:
494 case v8::debug::Coverage::kPreciseCount: {
496 DCHECK(isolate->factory()
497 ->feedback_vectors_for_profiling_tools()
499 Handle<ArrayList> list = Handle<ArrayList>::cast(
500 isolate->factory()->feedback_vectors_for_profiling_tools());
501 for (
int i = 0;
i < list->Length();
i++) {
502 FeedbackVector* vector = FeedbackVector::cast(list->Get(
i));
503 SharedFunctionInfo* shared = vector->shared_function_info();
504 DCHECK(shared->IsSubjectToDebugging());
506 if (reset_count) vector->clear_invocation_count();
507 counter_map.Add(shared, count);
511 case v8::debug::Coverage::kBestEffort: {
512 DCHECK(!isolate->factory()
513 ->feedback_vectors_for_profiling_tools()
515 DCHECK_EQ(v8::debug::Coverage::kBestEffort, collectionMode);
516 HeapIterator heap_iterator(isolate->heap());
517 while (HeapObject* current_obj = heap_iterator.next()) {
518 if (!current_obj->IsFeedbackVector())
continue;
519 FeedbackVector* vector = FeedbackVector::cast(current_obj);
520 SharedFunctionInfo* shared = vector->shared_function_info();
521 if (!shared->IsSubjectToDebugging())
continue;
523 counter_map.Add(shared, count);
531 std::unique_ptr<Coverage> result(
new Coverage());
532 Script::Iterator scripts(isolate);
533 while (Script* script = scripts.Next()) {
534 if (!script->IsUserJavaScript())
continue;
537 Handle<Script> script_handle(script, isolate);
538 result->emplace_back(script_handle);
539 std::vector<CoverageFunction>* functions = &result->back().functions;
541 std::vector<SharedFunctionInfo*> sorted;
545 SharedFunctionInfo::ScriptIterator infos(isolate, *script_handle);
546 while (SharedFunctionInfo* info = infos.Next()) {
547 sorted.push_back(info);
549 std::sort(sorted.begin(), sorted.end(), CompareSharedFunctionInfo);
553 std::vector<size_t> nesting;
556 for (SharedFunctionInfo* info : sorted) {
557 int start = StartPosition(info);
558 int end = info->EndPosition();
559 uint32_t count = counter_map.Get(info);
561 while (!nesting.empty() && functions->at(nesting.back()).end <= start) {
565 switch (collectionMode) {
566 case v8::debug::Coverage::kBlockCount:
567 case v8::debug::Coverage::kPreciseCount:
569 case v8::debug::Coverage::kBlockBinary:
570 case v8::debug::Coverage::kPreciseBinary:
571 count = info->has_reported_binary_coverage() ? 0 : 1;
572 info->set_has_reported_binary_coverage(
true);
574 case v8::debug::Coverage::kBestEffort:
580 Handle<String> name(info->DebugName(), isolate);
581 CoverageFunction
function(start, end, count, name);
583 if (IsBlockMode(collectionMode) && info->HasCoverageInfo()) {
584 CollectBlockCoverage(&
function, info, collectionMode);
589 bool is_covered = (count != 0);
590 bool parent_is_covered =
591 (!nesting.empty() && functions->at(nesting.back()).count != 0);
592 bool has_block_coverage = !
function.blocks.empty();
593 if (is_covered || parent_is_covered || has_block_coverage) {
594 nesting.push_back(functions->size());
595 functions->emplace_back(
function);
600 if (functions->empty()) result->pop_back();
605 void Coverage::SelectMode(Isolate* isolate, debug::Coverage::Mode mode) {
607 case debug::Coverage::kBestEffort:
612 isolate->debug()->RemoveAllCoverageInfos();
613 if (!isolate->is_collecting_type_profile()) {
614 isolate->SetFeedbackVectorsForProfilingTools(
615 ReadOnlyRoots(isolate).undefined_value());
618 case debug::Coverage::kBlockBinary:
619 case debug::Coverage::kBlockCount:
620 case debug::Coverage::kPreciseBinary:
621 case debug::Coverage::kPreciseCount: {
622 HandleScope scope(isolate);
626 Deoptimizer::DeoptimizeAll(isolate);
629 isolate->MaybeInitializeVectorListFromHeap();
631 HeapIterator heap_iterator(isolate->heap());
632 while (HeapObject* o = heap_iterator.next()) {
633 if (IsBinaryMode(mode) && o->IsSharedFunctionInfo()) {
637 SharedFunctionInfo* shared = SharedFunctionInfo::cast(o);
638 shared->set_has_reported_binary_coverage(
false);
639 }
else if (o->IsFeedbackVector()) {
641 FeedbackVector* vector = FeedbackVector::cast(o);
642 vector->clear_invocation_count();
649 isolate->set_code_coverage_mode(mode);