Skip to content

Commit 0d7889d

Browse files
sigurdschneiderCommit Bot
authored andcommitted
[coverage] Correctly report coverage for inline scripts
This fixes a bug where coverage for the inline script <script>function foo() {}<script> started to get deterministically reported as covered after crrev.com/c/1771776, while before it, we most of the time reported it as uncovered (depending on heap order of SFIs). The correct result is to report `foo` as uncovered as it is never called. The problem arose from the fact that v8:9212 needed to handle extra-wrappers around scripts correctly. Those wrappers have the same source range as the wrapped script and a call count of zero even if the wrapped script is executed. To filter them out, we previously determined nesting for identical source ranges by ascending call count. However, in the script case above, the script has call count one, while `foo` (which has the same source range) has call count zero. In this case, nesting is decreasing order of call counts. This CL is a minimal change that sorts SFIs which are top-level to the front, only then considers call counts in descending order. This preserves the behavior that node's extra wrappers are sorted to the front (and then filtered out by existing logic), but also ensures that for the example above, we report the script's coverage before the coverage for `foo`. Bug: v8:9857, v9:9212 Change-Id: Id224b0d8f12028b1f586ee5039e126bb5b8d8d36 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1863197 Reviewed-by: Jakob Gruber <[email protected]> Commit-Queue: Sigurd Schneider <[email protected]> Cr-Commit-Position: refs/heads/master@{#64307}
1 parent ed40ab1 commit 0d7889d

2 files changed

Lines changed: 45 additions & 8 deletions

File tree

src/debug/debug-coverage.cc

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -577,11 +577,15 @@ struct SharedFunctionInfoAndCount {
577577
// Sort by:
578578
// - start, ascending.
579579
// - end, descending.
580-
// - count, ascending.
580+
// - info.is_toplevel() first
581+
// - count, descending.
581582
bool operator<(const SharedFunctionInfoAndCount& that) const {
582583
if (this->start != that.start) return this->start < that.start;
583584
if (this->end != that.end) return this->end > that.end;
584-
return this->count < that.count;
585+
if (this->info.is_toplevel() != that.info.is_toplevel()) {
586+
return this->info.is_toplevel();
587+
}
588+
return this->count > that.count;
585589
}
586590

587591
SharedFunctionInfo info;
@@ -653,12 +657,30 @@ std::unique_ptr<Coverage> Coverage::Collect(
653657

654658
// Find the correct outer function based on start position.
655659
//
656-
// This is not robust when considering two functions with identical source
657-
// ranges. In this case, it is unclear which function is the inner / outer
658-
// function. Above, we ensure that such functions are sorted in ascending
659-
// `count` order, so at least our `parent_is_covered` optimization below
660-
// should be fine.
661-
// TODO(jgruber): Consider removing the optimization.
660+
// This is, in general, not robust when considering two functions with
661+
// identical source ranges; then the notion of inner and outer is unclear.
662+
// Identical source ranges arise when the source range of top-most entity
663+
// (e.g. function) in the script is identical to the whole script, e.g.
664+
// <script>function foo() {}<script>. The script has its own shared
665+
// function info, which has the same source range as the SFI for `foo`.
666+
// Node.js creates an additional wrapper for scripts (again with identical
667+
// source range) and those wrappers will have a call count of zero even if
668+
// the wrapped script was executed (see v8:9212). We mitigate this issue
669+
// by sorting top-level SFIs first among SFIs with the same source range:
670+
// This ensures top-level SFIs are processed first. If a top-level SFI has
671+
// a non-zero call count, it gets recorded due to `function_is_relevant`
672+
// below (e.g. script wrappers), while top-level SFIs with zero call count
673+
// do not get reported (this ensures node's extra wrappers do not get
674+
// reported). If two SFIs with identical source ranges get reported, we
675+
// report them in decreasing order of call count, as in all known cases
676+
// this corresponds to the nesting order. In the case of the script tag
677+
// example above, we report the zero call count of `foo` last. As it turns
678+
// out, embedders started to rely on functions being reported in nesting
679+
// order.
680+
// TODO(jgruber): Investigate whether it is possible to remove node's
681+
// extra top-level wrapper script, or change its source range, or ensure
682+
// that it follows the invariant that nesting order is descending count
683+
// order for SFIs with identical source ranges.
662684
while (!nesting.empty() && functions->at(nesting.back()).end <= start) {
663685
nesting.pop_back();
664686
}

test/mjsunit/code-coverage-block.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,4 +1068,19 @@ f(43); // 0450
10681068
{"start":204,"end":226,"count":1}]
10691069
);
10701070

1071+
TestCoverage(
1072+
"https://crbug.com/v8/9857",
1073+
`function foo() {}`,
1074+
[{"start":0,"end":17,"count":1},
1075+
{"start":0,"end":17,"count":0}]
1076+
);
1077+
1078+
TestCoverage(
1079+
"https://crbug.com/v8/9857",
1080+
`function foo() {function bar() {}}; foo()`,
1081+
[{"start":0,"end":41,"count":1},
1082+
{"start":0,"end":34,"count":1},
1083+
{"start":16,"end":33,"count":0}]
1084+
);
1085+
10711086
%DebugToggleBlockCoverage(false);

0 commit comments

Comments
 (0)