22 base::KeyEqualityMatcher<Tagged<Object>>,
23 base::DefaultAllocationPolicy> {
29 uint32_t old_count = entry->
value;
30 if (UINT32_MAX -
count < old_count) {
31 entry->
value = UINT32_MAX;
39 if (entry ==
nullptr)
return 0;
45 return static_cast<uint32_t
>(
key.ptr());
53 int start = info->function_token_position();
58bool CompareCoverageBlock(
const CoverageBlock& a,
const CoverageBlock& b) {
61 if (a.start == b.start)
return a.end > b.end;
62 return a.start < b.start;
65void SortBlockData(std::vector<CoverageBlock>& v) {
67 std::sort(v.begin(), v.end(), CompareCoverageBlock);
70std::vector<CoverageBlock> GetSortedBlockData(
72 DCHECK(shared->HasCoverageInfo(isolate));
77 std::vector<CoverageBlock>
result;
78 if (coverage_info->slot_count() == 0)
return result;
80 for (
int i = 0;
i < coverage_info->slot_count();
i++) {
81 const int start_pos = coverage_info->slots_start_source_position(
i);
82 const int until_pos = coverage_info->slots_end_source_position(
i);
83 const int count = coverage_info->slots_block_count(
i);
99class CoverageBlockIterator final {
101 explicit CoverageBlockIterator(CoverageFunction* function)
104 CompareCoverageBlock));
107 ~CoverageBlockIterator() {
110 CompareCoverageBlock));
113 bool HasNext()
const {
119 if (!ended_) MaybeWriteCurrent();
128 if (read_index_ == -1) {
132 }
else if (!delete_current_) {
141 CoverageBlock& block = GetBlock();
155 CoverageBlock& GetBlock() {
160 CoverageBlock& GetNextBlock() {
166 CoverageBlock& GetPreviousBlock() {
172 CoverageBlock& GetParent() {
177 bool HasSiblingOrChild() {
179 return HasNext() && GetNextBlock().start < GetParent().end;
182 CoverageBlock& GetSiblingOrChild() {
183 DCHECK(HasSiblingOrChild());
185 return GetNextBlock();
199 void MaybeWriteCurrent() {
200 if (delete_current_)
return;
201 if (read_index_ >= 0 && write_index_ != read_index_) {
224bool HaveSameSourceRange(
const CoverageBlock& lhs,
const CoverageBlock& rhs) {
225 return lhs.start == rhs.start && lhs.end == rhs.end;
228void MergeDuplicateRanges(CoverageFunction* function) {
229 CoverageBlockIterator iter(function);
231 while (iter.Next() && iter.HasNext()) {
232 CoverageBlock& block = iter.GetBlock();
233 CoverageBlock& next_block = iter.GetNextBlock();
235 if (!HaveSameSourceRange(block, next_block))
continue;
238 next_block.count = std::max(block.count, next_block.count);
247void RewritePositionSingletonsToRanges(CoverageFunction* function) {
248 CoverageBlockIterator iter(function);
250 while (iter.Next()) {
251 CoverageBlock& block = iter.GetBlock();
252 CoverageBlock& parent = iter.GetParent();
254 if (block.start >= function->end) {
260 if (iter.HasSiblingOrChild()) {
261 block.end = iter.GetSiblingOrChild().start;
262 }
else if (iter.IsTopLevel()) {
266 block.end = parent.end - 1;
268 block.end = parent.end;
274void MergeConsecutiveRanges(CoverageFunction* function) {
275 CoverageBlockIterator iter(function);
277 while (iter.Next()) {
278 CoverageBlock& block = iter.GetBlock();
280 if (iter.HasSiblingOrChild()) {
281 CoverageBlock& sibling = iter.GetSiblingOrChild();
282 if (sibling.start == block.end && sibling.count == block.count) {
285 sibling.start = block.start;
292void MergeNestedRanges(CoverageFunction* function) {
293 CoverageBlockIterator iter(function);
295 while (iter.Next()) {
296 CoverageBlock& block = iter.GetBlock();
297 CoverageBlock& parent = iter.GetParent();
299 if (parent.count == block.count) {
307void RewriteFunctionScopeCounter(CoverageFunction* function) {
309 DCHECK(!function->blocks.empty());
311 CoverageBlockIterator iter(function);
313 DCHECK(iter.IsTopLevel());
315 CoverageBlock& block = iter.GetBlock();
321 function->count = block.count;
331void FilterAliasedSingletons(CoverageFunction* function) {
332 CoverageBlockIterator iter(function);
336 while (iter.Next()) {
337 CoverageBlock& previous_block = iter.GetPreviousBlock();
338 CoverageBlock& block = iter.GetBlock();
341 bool aliases_start = block.start == previous_block.start;
343 if (is_singleton && aliases_start) {
349 DCHECK_IMPLIES(iter.HasNext(), iter.GetNextBlock().start != block.start);
355void FilterUncoveredRanges(CoverageFunction* function) {
356 CoverageBlockIterator iter(function);
358 while (iter.Next()) {
359 CoverageBlock& block = iter.GetBlock();
360 CoverageBlock& parent = iter.GetParent();
361 if (block.count == 0 && parent.count == 0) iter.DeleteBlock();
365void FilterEmptyRanges(CoverageFunction* function) {
366 CoverageBlockIterator iter(function);
368 while (iter.Next()) {
369 CoverageBlock& block = iter.GetBlock();
370 if (block.start == block.end) iter.DeleteBlock();
374void ClampToBinary(CoverageFunction* function) {
375 CoverageBlockIterator iter(function);
377 while (iter.Next()) {
378 CoverageBlock& block = iter.GetBlock();
379 if (block.count > 0) block.count = 1;
384 DCHECK(shared->HasCoverageInfo(isolate));
389 for (
int i = 0;
i < coverage_info->slot_count();
i++) {
390 coverage_info->ResetBlockCount(
i);
414void CollectBlockCoverageInternal(Isolate* isolate, CoverageFunction* function,
417 DCHECK(IsBlockMode(mode));
421 if (!function->HasNonEmptySourceRange())
return;
423 function->has_block_coverage =
true;
424 function->blocks = GetSortedBlockData(isolate, info);
435 RewriteFunctionScopeCounter(function);
438 if (!function->HasBlocks())
return;
447 FilterAliasedSingletons(function);
451 RewritePositionSingletonsToRanges(function);
456 MergeConsecutiveRanges(function);
458 SortBlockData(function->blocks);
459 MergeDuplicateRanges(function);
460 MergeNestedRanges(function);
462 MergeConsecutiveRanges(function);
466 FilterUncoveredRanges(function);
469 FilterEmptyRanges(function);
472void CollectBlockCoverage(Isolate* isolate, CoverageFunction* function,
475 CollectBlockCoverageInternal(isolate, function, info, mode);
478 ResetAllBlockCounts(isolate, info);
481void PrintBlockCoverage(
const CoverageFunction* function,
483 bool has_nonempty_source_range,
484 bool function_is_relevant) {
486 std::unique_ptr<char[]> function_name = function->name->ToCString();
488 "Coverage for function='%s', SFI=%p, has_nonempty_source_range=%d, "
489 "function_is_relevant=%d\n",
490 function_name.get(),
reinterpret_cast<void*
>(info.ptr()),
491 has_nonempty_source_range, function_is_relevant);
492 i::PrintF(
"{start: %d, end: %d, count: %d}\n", function->start, function->end,
494 for (
const auto& block : function->blocks) {
495 i::PrintF(
"{start: %d, end: %d, count: %d}\n", block.start, block.end,
500void CollectAndMaybeResetCounts(Isolate* isolate,
501 SharedToCounterMap* counter_map,
503 const bool reset_count =
506 switch (isolate->code_coverage_mode()) {
513 *isolate->factory()->feedback_vectors_for_profiling_tools()));
515 isolate->factory()->feedback_vectors_for_profiling_tools());
516 for (
int i = 0;
i < list->
length();
i++) {
519 DCHECK(shared->IsSubjectToDebugging());
520 uint32_t count =
static_cast<uint32_t
>(vector->invocation_count());
521 if (reset_count) vector->clear_invocation_count(
kRelaxedStore);
522 counter_map->Add(shared, count);
528 *isolate->factory()->feedback_vectors_for_profiling_tools()));
531 HeapObjectIterator heap_iterator(isolate->heap());
533 !current_obj.is_null(); current_obj = heap_iterator.Next()) {
534 if (!IsJSFunction(current_obj))
continue;
537 if (!shared->IsSubjectToDebugging())
continue;
538 if (!(func->has_feedback_vector() ||
539 func->has_closure_feedback_cell_array())) {
543 if (func->has_feedback_vector()) {
544 count =
static_cast<uint32_t
>(
545 func->feedback_vector()->invocation_count());
546 }
else if (func->shared()->HasBytecodeArray() &&
547 func->raw_feedback_cell()->interrupt_budget() <
553 counter_map->Add(shared, count);
560 for (JavaScriptStackFrameIterator it(isolate); !it.done(); it.Advance()) {
562 if (counter_map->Get(shared) != 0)
continue;
563 counter_map->Add(shared, 1);
572struct SharedFunctionInfoAndCount {
576 start(StartPosition(*info)),
577 end(info->EndPosition()) {}
584 bool operator<(
const SharedFunctionInfoAndCount& that)
const {
585 if (this->start != that.start)
return this->start < that.start;
586 if (this->end != that.end)
return this->
end > that.end;
587 if (this->info->is_toplevel() != that.info->is_toplevel()) {
588 return this->info->is_toplevel();
590 return this->count > that.count;
602 DCHECK(!isolate->is_best_effort_code_coverage());
603 std::unique_ptr<Coverage>
result =
604 Collect(isolate, isolate->code_coverage_mode());
605 if (isolate->is_precise_binary_code_coverage() ||
606 isolate->is_block_binary_code_coverage()) {
609 isolate->SetFeedbackVectorsForProfilingTools(
627 CollectAndMaybeResetCounts(isolate, &counter_map, collectionMode);
633 std::vector<Handle<Script>> scripts;
636 script = scriptIt.
Next()) {
637 if (script->IsUserJavaScript()) scripts.push_back(
handle(script, isolate));
642 result->emplace_back(script);
643 std::vector<CoverageFunction>* functions = &
result->back().functions;
645 std::vector<SharedFunctionInfoAndCount> sorted;
651 info = infos.
Next()) {
652 sorted.emplace_back(
handle(info, isolate), counter_map.
Get(info));
654 std::sort(sorted.begin(), sorted.end());
658 std::vector<size_t> nesting;
661 for (
const SharedFunctionInfoAndCount& v : sorted) {
665 uint32_t
count = v.count;
693 while (!nesting.empty() && functions->at(nesting.back()).end <=
start) {
698 switch (collectionMode) {
704 count = info->has_reported_binary_coverage() ? 0 : 1;
705 info->set_has_reported_binary_coverage(
true);
716 if (IsBlockMode(collectionMode) && info->HasCoverageInfo(isolate)) {
717 CollectBlockCoverage(isolate, &function, *info, collectionMode);
722 bool is_covered = (
count != 0);
723 bool parent_is_covered =
724 (!nesting.empty() && functions->at(nesting.back()).count != 0);
725 bool has_block_coverage = !function.blocks.empty();
726 bool function_is_relevant =
727 (is_covered || parent_is_covered || has_block_coverage);
731 bool has_nonempty_source_range = function.HasNonEmptySourceRange();
733 if (has_nonempty_source_range && function_is_relevant) {
734 nesting.push_back(functions->size());
735 functions->emplace_back(function);
738 if (
v8_flags.trace_block_coverage) {
739 PrintBlockCoverage(&function, *info, has_nonempty_source_range,
740 function_is_relevant);
745 if (functions->empty())
result->pop_back();
751 if (mode != isolate->code_coverage_mode()) {
755 isolate->CollectSourcePositionsForAllBytecodeArrays();
759 isolate->set_disable_bytecode_flushing(
true);
768 isolate->debug()->RemoveAllCoverageInfos();
769 isolate->SetFeedbackVectorsForProfilingTools(
782 std::vector<Handle<JSFunction>> funcs_needing_feedback_vector;
786 o = heap_iterator.
Next()) {
787 if (IsJSFunction(o)) {
789 if (func->has_closure_feedback_cell_array()) {
790 funcs_needing_feedback_vector.push_back(
793 }
else if (IsBinaryMode(mode) && IsSharedFunctionInfo(o)) {
798 shared->set_has_reported_binary_coverage(
false);
799 }
else if (IsFeedbackVector(o)) {
808 func->shared()->is_compiled_scope(isolate));
814 isolate->MaybeInitializeVectorListFromHeap();
819 isolate->set_code_coverage_mode(mode);
#define DISALLOW_GARBAGE_COLLECTION(name)
Entry * LookupOrInsert(const Tagged< SharedFunctionInfo > &key, uint32_t hash)
Entry * Lookup(const Tagged< SharedFunctionInfo > &key, uint32_t hash) const
static std::unique_ptr< Coverage > CollectPrecise(Isolate *isolate)
static V8_EXPORT_PRIVATE void SelectMode(Isolate *isolate, debug::CoverageMode mode)
static std::unique_ptr< Coverage > Collect(Isolate *isolate, v8::debug::CoverageMode collectionMode)
static std::unique_ptr< Coverage > CollectBestEffort(Isolate *isolate)
static V8_EXPORT_PRIVATE void DeoptimizeAll(Isolate *isolate)
Tagged< HeapObject > Next()
static V8_EXPORT_PRIVATE void EnsureFeedbackVector(Isolate *isolate, DirectHandle< JSFunction > function, IsCompiledScope *compiled_scope)
FeedbackVector eventually. Generally this shouldn't be used to get the.
V8_EXPORT_PRIVATE Tagged< SharedFunctionInfo > Next()
static Handle< String > DebugName(Isolate *isolate, DirectHandle< SharedFunctionInfo > shared)
static uint32_t Hash(Tagged< SharedFunctionInfo > key)
void Add(Tagged< SharedFunctionInfo > key, uint32_t count)
uint32_t Get(Tagged< SharedFunctionInfo > key)
V8_INLINE constexpr bool is_null() const
static int InterruptBudgetFor(Isolate *isolate, Tagged< JSFunction > function, std::optional< CodeKind > override_active_tier={})
T & emplace_back(Args &&... args)
const JSFunctionRef function_
std::vector< CoverageBlock > nesting_stack_
ZoneVector< RpoNumber > & result
V8_INLINE IndirectHandle< T > handle(Tagged< T > object, Isolate *isolate)
constexpr int kNoSourcePosition
void PrintF(const char *format,...)
Tagged(T object) -> Tagged< T >
kInterpreterTrampolineOffset Tagged< HeapObject >
PerThreadAssertScopeDebugOnly< true, SAFEPOINTS_ASSERT, HEAP_ALLOCATION_ASSERT > AllowGarbageCollection
V8_INLINE constexpr bool operator<(Builtin a, Builtin b)
V8_EXPORT_PRIVATE FlagValues v8_flags
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
static constexpr RelaxedStoreTag kRelaxedStore
#define DCHECK_LE(v1, v2)
#define DCHECK_IMPLIES(v1, v2)
#define DCHECK_NE(v1, v2)
#define DCHECK(condition)
#define DCHECK_EQ(v1, v2)
#define DCHECK_GT(v1, v2)
static constexpr int kFunctionLiteralSourcePosition