@@ -54,13 +54,6 @@ int StartPosition(SharedFunctionInfo info) {
5454 return start;
5555}
5656
57- bool CompareSharedFunctionInfo (SharedFunctionInfo a, SharedFunctionInfo b) {
58- int a_start = StartPosition (a);
59- int b_start = StartPosition (b);
60- if (a_start == b_start) return a.EndPosition () > b.EndPosition ();
61- return a_start < b_start;
62- }
63-
6457bool CompareCoverageBlock (const CoverageBlock& a, const CoverageBlock& b) {
6558 DCHECK_NE (kNoSourcePosition , a.start );
6659 DCHECK_NE (kNoSourcePosition , b.start );
@@ -482,32 +475,12 @@ void CollectBlockCoverage(CoverageFunction* function, SharedFunctionInfo info,
482475 // Reset all counters on the DebugInfo to zero.
483476 ResetAllBlockCounts (info);
484477}
485- } // anonymous namespace
486-
487- std::unique_ptr<Coverage> Coverage::CollectPrecise (Isolate* isolate) {
488- DCHECK (!isolate->is_best_effort_code_coverage ());
489- std::unique_ptr<Coverage> result =
490- Collect (isolate, isolate->code_coverage_mode ());
491- if (!isolate->is_collecting_type_profile () &&
492- (isolate->is_precise_binary_code_coverage () ||
493- isolate->is_block_binary_code_coverage ())) {
494- // We do not have to hold onto feedback vectors for invocations we already
495- // reported. So we can reset the list.
496- isolate->SetFeedbackVectorsForProfilingTools (*ArrayList::New (isolate, 0 ));
497- }
498- return result;
499- }
500-
501- std::unique_ptr<Coverage> Coverage::CollectBestEffort (Isolate* isolate) {
502- return Collect (isolate, v8::debug::CoverageMode::kBestEffort );
503- }
504-
505- std::unique_ptr<Coverage> Coverage::Collect (
506- Isolate* isolate, v8::debug::CoverageMode collectionMode) {
507- SharedToCounterMap counter_map;
508478
479+ void CollectAndMaybeResetCounts (Isolate* isolate,
480+ SharedToCounterMap* counter_map,
481+ v8::debug::CoverageMode coverage_mode) {
509482 const bool reset_count =
510- collectionMode != v8::debug::CoverageMode::kBestEffort ;
483+ coverage_mode != v8::debug::CoverageMode::kBestEffort ;
511484
512485 switch (isolate->code_coverage_mode ()) {
513486 case v8::debug::CoverageMode::kBlockBinary :
@@ -526,15 +499,15 @@ std::unique_ptr<Coverage> Coverage::Collect(
526499 DCHECK (shared.IsSubjectToDebugging ());
527500 uint32_t count = static_cast <uint32_t >(vector.invocation_count ());
528501 if (reset_count) vector.clear_invocation_count ();
529- counter_map. Add (shared, count);
502+ counter_map-> Add (shared, count);
530503 }
531504 break ;
532505 }
533506 case v8::debug::CoverageMode::kBestEffort : {
534507 DCHECK (!isolate->factory ()
535508 ->feedback_vectors_for_profiling_tools ()
536509 ->IsArrayList ());
537- DCHECK_EQ (v8::debug::CoverageMode::kBestEffort , collectionMode );
510+ DCHECK_EQ (v8::debug::CoverageMode::kBestEffort , coverage_mode );
538511 HeapObjectIterator heap_iterator (isolate->heap ());
539512 for (HeapObject current_obj = heap_iterator.Next ();
540513 !current_obj.is_null (); current_obj = heap_iterator.Next ()) {
@@ -543,8 +516,9 @@ std::unique_ptr<Coverage> Coverage::Collect(
543516 SharedFunctionInfo shared = func.shared ();
544517 if (!shared.IsSubjectToDebugging ()) continue ;
545518 if (!(func.has_feedback_vector () ||
546- func.has_closure_feedback_cell_array ()))
519+ func.has_closure_feedback_cell_array ())) {
547520 continue ;
521+ }
548522 uint32_t count = 0 ;
549523 if (func.has_feedback_vector ()) {
550524 count =
@@ -555,7 +529,7 @@ std::unique_ptr<Coverage> Coverage::Collect(
555529 // atleast once. We don't have precise invocation count here.
556530 count = 1 ;
557531 }
558- counter_map. Add (shared, count);
532+ counter_map-> Add (shared, count);
559533 }
560534
561535 // Also check functions on the stack to collect the count map. With lazy
@@ -564,12 +538,64 @@ std::unique_ptr<Coverage> Coverage::Collect(
564538 // updated (i.e. it didn't execute return / jump).
565539 for (JavaScriptFrameIterator it (isolate); !it.done (); it.Advance ()) {
566540 SharedFunctionInfo shared = it.frame ()->function ().shared ();
567- if (counter_map. Get (shared) != 0 ) continue ;
568- counter_map. Add (shared, 1 );
541+ if (counter_map-> Get (shared) != 0 ) continue ;
542+ counter_map-> Add (shared, 1 );
569543 }
570544 break ;
571545 }
572546 }
547+ }
548+
549+ // A {SFI, count} tuple is used to sort by source range (stored on
550+ // the SFI) and call count (in the counter map).
551+ struct SharedFunctionInfoAndCount {
552+ SharedFunctionInfoAndCount (SharedFunctionInfo info, uint32_t count)
553+ : info(info),
554+ count (count),
555+ start(StartPosition(info)),
556+ end(info.EndPosition()) {}
557+
558+ // Sort by:
559+ // - start, ascending.
560+ // - end, descending.
561+ // - count, ascending.
562+ bool operator <(const SharedFunctionInfoAndCount& that) const {
563+ if (this ->start != that.start ) return this ->start < that.start ;
564+ if (this ->end != that.end ) return this ->end > that.end ;
565+ return this ->count < that.count ;
566+ }
567+
568+ SharedFunctionInfo info;
569+ uint32_t count;
570+ int start;
571+ int end;
572+ };
573+
574+ } // anonymous namespace
575+
576+ std::unique_ptr<Coverage> Coverage::CollectPrecise (Isolate* isolate) {
577+ DCHECK (!isolate->is_best_effort_code_coverage ());
578+ std::unique_ptr<Coverage> result =
579+ Collect (isolate, isolate->code_coverage_mode ());
580+ if (!isolate->is_collecting_type_profile () &&
581+ (isolate->is_precise_binary_code_coverage () ||
582+ isolate->is_block_binary_code_coverage ())) {
583+ // We do not have to hold onto feedback vectors for invocations we already
584+ // reported. So we can reset the list.
585+ isolate->SetFeedbackVectorsForProfilingTools (*ArrayList::New (isolate, 0 ));
586+ }
587+ return result;
588+ }
589+
590+ std::unique_ptr<Coverage> Coverage::CollectBestEffort (Isolate* isolate) {
591+ return Collect (isolate, v8::debug::CoverageMode::kBestEffort );
592+ }
593+
594+ std::unique_ptr<Coverage> Coverage::Collect (
595+ Isolate* isolate, v8::debug::CoverageMode collectionMode) {
596+ // Collect call counts for all functions.
597+ SharedToCounterMap counter_map;
598+ CollectAndMaybeResetCounts (isolate, &counter_map, collectionMode);
573599
574600 // Iterate shared function infos of every script and build a mapping
575601 // between source ranges and invocation counts.
@@ -584,30 +610,40 @@ std::unique_ptr<Coverage> Coverage::Collect(
584610 result->emplace_back (script_handle);
585611 std::vector<CoverageFunction>* functions = &result->back ().functions ;
586612
587- std::vector<SharedFunctionInfo > sorted;
613+ std::vector<SharedFunctionInfoAndCount > sorted;
588614
589615 {
590616 // Sort functions by start position, from outer to inner functions.
591617 SharedFunctionInfo::ScriptIterator infos (isolate, *script_handle);
592618 for (SharedFunctionInfo info = infos.Next (); !info.is_null ();
593619 info = infos.Next ()) {
594- sorted.push_back (info);
620+ sorted.emplace_back (info, counter_map. Get (info) );
595621 }
596- std::sort (sorted.begin (), sorted.end (), CompareSharedFunctionInfo );
622+ std::sort (sorted.begin (), sorted.end ());
597623 }
598624
599625 // Stack to track nested functions, referring function by index.
600626 std::vector<size_t > nesting;
601627
602628 // Use sorted list to reconstruct function nesting.
603- for (SharedFunctionInfo info : sorted) {
604- int start = StartPosition (info);
605- int end = info.EndPosition ();
606- uint32_t count = counter_map.Get (info);
629+ for (const SharedFunctionInfoAndCount& v : sorted) {
630+ SharedFunctionInfo info = v.info ;
631+ int start = v.start ;
632+ int end = v.end ;
633+ uint32_t count = v.count ;
634+
607635 // Find the correct outer function based on start position.
636+ //
637+ // This is not robust when considering two functions with identical source
638+ // ranges. In this case, it is unclear which function is the inner / outer
639+ // function. Above, we ensure that such functions are sorted in ascending
640+ // `count` order, so at least our `parent_is_covered` optimization below
641+ // should be fine.
642+ // TODO(jgruber): Consider removing the optimization.
608643 while (!nesting.empty () && functions->at (nesting.back ()).end <= start) {
609644 nesting.pop_back ();
610645 }
646+
611647 if (count != 0 ) {
612648 switch (collectionMode) {
613649 case v8::debug::CoverageMode::kBlockCount :
0 commit comments