Closed Bug 1405567 Opened 3 years ago Closed 2 years ago

LCOV data from js::GetCodeCoverageSummary() ignores line 1 and not-taken branches

Categories

(Core :: JavaScript Engine, defect, P3)

52 Branch
defect

Tracking

()

RESOLVED FIXED
mozilla59
Tracking Status
firefox59 --- fixed

People

(Reporter: ptomato, Assigned: ptomato)

References

Details

(Whiteboard: [js:tech-debt][js:testability])

Attachments

(3 files, 4 obsolete files)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36

Steps to reproduce:

There are a couple of tweaks that should be made in order to get the LCOV output similar to that produced by GCC or Clang:

- Line 1 is never marked as executable or executed
- Branches not taken are marked with hit count "-" instead of 0 (a dash means the whole branch instruction was never executed, 0 should mean the branch instruction was executed but the other branch was taken every time)

See the attachment for a minimal C++ program that demonstrates both problems.


Actual results:

The JS program being instrumented is:

1 let x = function () { return 0; };
2 let y = 0;
3 if (x() > 0)
4     y++;
5 else
6    y++;

The LCOV output is:

TN:Compartment_5f7fbb0700a800
SF:cov.js
FN:1,top-level
FN:1,x
FNDA:1,top-level
FNDA:1,x
FNF:2
FNH:2
BRDA:3,0,0,1
BRDA:3,0,1,-
BRF:2
BRH:1
DA:2,1
DA:3,1
DA:4,0
DA:6,1
LF:4
LH:3
end_of_record

Note that line 1 is not counted as executable and the totals of executable and executed lines are wrong. (Adding a \n to the beginning of the program does yield the correct totals and another DA: entry for line 2.)


Expected results:

The LCOV output should instead be this:

TN:Compartment_5f7fbb0700a800
SF:cov.js
FN:1,top-level
FN:1,x
FNDA:1,top-level
FNDA:1,x
FNF:2
FNH:2
BRDA:3,0,0,1
BRDA:3,0,1,0
BRF:2
BRH:1
DA:1,1
DA:2,1
DA:3,1
DA:4,0
DA:6,1
LF:5
LH:4
end_of_record
Thanks again for making these minimal test cases :)

(In reply to Philip Chimento [:ptomato] from comment #0)
> - Branches not taken are marked with hit count "-" instead of 0 (a dash
> means the whole branch instruction was never executed, 0 should mean the
> branch instruction was executed but the other branch was taken every time)

You are right!
I miss-interpreted the documentation from http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php when I implemented it.
Glad to help make it easier for you. I wonder if I can help out with this one - it seems like it should be a straightforward patch.
(In reply to Philip Chimento [:ptomato] from comment #2)
> Glad to help make it easier for you. I wonder if I can help out with this
> one - it seems like it should be a straightforward patch.

That would be the fastest way to get this fixed ;)

The LCov output produced by SpiderMonkey is all located in:
  http://searchfox.org/mozilla-central/source/js/src/vm/CodeCoverage.cpp

The dash symbols are manually checked and added here:
  http://searchfox.org/mozilla-central/rev/7ba03cebf1dcf3b4c40c85a4efad42a5022cf1ec/js/src/vm/CodeCoverage.cpp#238,244,342,407

there are several test cases that you might have to update, which are checking for the LCov output, such as:
  http://searchfox.org/mozilla-central/source/js/src/jit-test/tests/coverage

You can run these tests with:
  python ./jit-test/jit_test.py  ./path/to/js  coverage
Priority: -- → P3
Whiteboard: [js:tech-debt][js:testability]
Attachment #8926195 - Attachment is obsolete: true
Attachment #8926195 - Flags: review?(nicolas.b.pierron)
The -/0 issue was indeed quite straightforward.

The off-by-one issue not so much, because leaving out the first line sidestepped another problem: a JSScript's first line can also be part of another JSScript. I'm not sure if the solution that I chose to ensure that each line's hit count was only written out once is the correct one, but so far it works for my purposes.
Attachment #8926196 - Attachment is obsolete: true
Attachment #8926196 - Flags: review?(nicolas.b.pierron)
Attachment #8926197 - Flags: review?(nicolas.b.pierron) → review+
Comment on attachment 8926624 [details] [diff] [review]
Get LCOV stats for first line of script, and handle lines that belong to more than one script

Review of attachment 8926624 [details] [diff] [review]:
-----------------------------------------------------------------

::: js/src/vm/CodeCoverage.cpp
@@ +207,5 @@
>              }
>  
> +            if ((oldLine != lineno || lineno == script->lineno()) &&
> +                fallsthrough &&
> +                !linesWritten_.has(lineno)) {

nit: put the { on a new line, when the condition span on multiple lines.

::: js/src/vm/CodeCoverage.h
@@ +77,5 @@
>      size_t numLinesInstrumented_;
>      size_t numLinesHit_;
> +    // Which lines have had their statistics printed, since a line may be part
> +    // of more than one JSScript.
> +    HashSet<size_t, DefaultHasher<size_t>, SystemAllocPolicy> linesWritten_;

I don't think we want this behaviour.  For example if we have the following code:

  function f() { return function g() { if (arguments.length) return;
    … some code …
  } }

The reports that we might get will show the first line displayed, which does not correspond to the sum of all the lines.

This issues sounds like a post-processing issue to me than a lcov report generation issue.
Attachment #8926624 - Flags: review?(nicolas.b.pierron)
I buy that, it indeed depends on what the expected behaviour is. Just to be clear, you want the sum of all the execution counts from each script for a particular line displayed?
(In reply to Philip Chimento [:ptomato] from comment #10)
> I buy that, it indeed depends on what the expected behaviour is. Just to be
> clear, you want the sum of all the execution counts from each script for a
> particular line displayed?

Yes, because you have multiple sources of information. Otherwise, the alternative of picking the first one depends on the creation & clean-up order which is not desirable, and potentially confusing.
Attachment #8926624 - Attachment is obsolete: true
Comment on attachment 8928053 [details] [diff] [review]
Get LCOV stats for first line of script, and handle lines that belong to more than one script

Review of attachment 8928053 [details] [diff] [review]:
-----------------------------------------------------------------

2 remarks:
 - If this is only per JSScript, then why should it be shared across all functions?
 - It seems you are trying to solve the fact that lines are not being visited in an increasing line number, is that right?

::: js/src/vm/CodeCoverage.h
@@ +72,5 @@
>      size_t numBranchesFound_;
>      size_t numBranchesHit_;
>  
> +    // Holds lines statistics. A line may be part of more than one JSScript,
> +    // so this is shared between all scripts in the source file.

nit: add a comment saying that line values are added, if the same line appears in multiple function, across multiple JSScript or multiple times within the same function.
Attachment #8928053 - Flags: review?(nicolas.b.pierron) → review+
(In reply to Nicolas B. Pierron [:nbp] from comment #13)
>  - If this is only per JSScript, then why should it be shared across all
> functions?

My understanding was that there is one JSScript per function plus one for the toplevel in the source file. I may be using the wrong term? The intention is one coverage hash map per source file.

>  - It seems you are trying to solve the fact that lines are not being
> visited in an increasing line number, is that right?

They seem to be visited in an increasing line number within each function, but not across functions. The problem I'm really trying to solve with the hash map is  So if you have this source

let x = function () { return 0; };
let y = 0;
if (x() > 0)
    y++;
else
    y++;

Then without the hashmap you get

DA:1,1
DA:2,1
DA:3,1
DA:4,0
DA:6,1
DA:1,1

Line 1 is repeated because it's covered both by the toplevel JSScript and the JSScript for function x, and added to outDA_ by two subsequent invocations of LCovSource::writeScript(). So it's not just a question of reordering the lines.

> ::: js/src/vm/CodeCoverage.h
> @@ +72,5 @@
> >      size_t numBranchesFound_;
> >      size_t numBranchesHit_;
> >  
> > +    // Holds lines statistics. A line may be part of more than one JSScript,
> > +    // so this is shared between all scripts in the source file.
> 
> nit: add a comment saying that line values are added, if the same line
> appears in multiple function, across multiple JSScript or multiple times
> within the same function.

Will do.
Attachment #8928053 - Attachment is obsolete: true
Attachment #8928388 - Flags: review+
Keywords: checkin-needed
Pushed by shindli@mozilla.com:
https://hg.mozilla.org/integration/mozilla-inbound/rev/6341e29e55f5
Get LCOV stats for first line of script, and handle lines that belong to more than one script. r=nbp
https://hg.mozilla.org/integration/mozilla-inbound/rev/994d32bb01a3
Mark LCOV branches not taken with 0, and not executed with -. r=nbp
Keywords: checkin-needed
https://hg.mozilla.org/mozilla-central/rev/6341e29e55f5
https://hg.mozilla.org/mozilla-central/rev/994d32bb01a3
Status: UNCONFIRMED → RESOLVED
Closed: 2 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla59
Duplicate of this bug: 1398835
Assignee: nobody → philip.chimento
You need to log in before you can comment on or make changes to this bug.