I wondered how it’s implemented… these are my findings from bumbling through the source code with Chrome code search. It’s C++, but don’t worry, I don’t know C++ either. :)
A high level look at the code coverage feature
DevTools uses the Chrome Debugging Protocol to communicate with V8. Before looking at the Chrome code itself we can check the protocol documentation to get an idea what’s going on.
startPreciseCoverage endpoint that takes a
callCount parameter. If that parameter is true V8 will count the number of times the code has run, rather than just giving a binary value.
To generate the coverage report that’s shown at the top of this post we don’t need to count the exact number of calls, we just care whether or not the code has run.
There’s also a
takePreciseCoverage endpoint, but unfortunately there’s no example of what a coverage result looks like. There are two ways we can learn more.
First, we can debug the debugger and set a breakpoint after searching for
takePreciseCoverage. Stepping into the call we find a
dispatchResponse method where the results of requests that DevTools makes to V8/Chrome become available.
This is the an excerpt from the data that gets sent to Chrome DevTools:
As you can see it lists ranges of code and says how often they’ve been executed.
Finding the code inside V8
We now have a decent idea of what the API that V8 provides looks like. Next we’ll take a look at the actual API implementation.
By searching for
startPreciseCoverage we can find the relevant C++ code.
I don’t really understand this. But what matters is that we can click on some of the types in Chrome code search and find more code.
For example, if you click on
kPreciseCount we’ll find this:
Even if you don’t know the language you can always read the comments!
Looking back at the
startPreciseCoverage method we can see that
kPreciseCount is used if
callCount is passed in, otherwise
By clicking through the Chrome Code search UI you can discover more. I’ll highlight some of the things I found interesting below.
How precise is the coverage tracking
One of the things I was curious about was how it knows exactly what code range has run. It turns out that tracking isn’t actually that exact and it happens at the function level.
Where is the number of invocations stored?
Functions have a
FeedbackVector which can store the number of invocations.
Where is the invocation count incremented?
Here. The name
Generate_InterpreterEntryTrampoline suggests it’s to do with V8’s new Ignition interpreter. But the code also appears to depend on the platform (there are different implementations for
Anyway, I doesn’t matter all that much. This is the code that matters:
We add 1 to the invocation count.
FieldOperand points to the address of the invocation count in memory.
rcx at this time points to the location of the feedback vector in memory. Then V8 does some calculations to find out where the invocation count is in relation to the feedback vector object.
A Smi is a small integer. It’s a pretty ordinary integer, except V8 uses one bit to remind itself that this value isn’t a pointer.
When enabling coverage all optimizations are disabled:
Future inlining is disabled - I don’t know exactly inlining means in this context.
However, if we only care whether or not a function has been called - rather than the exact invation count - V8 does enable inlining once the invocation has been reported.
Pretty cool, right?
The coverage report works on a per function level by incrementing a counter when a function is called.
And Chrome code search is a truly helpful tool to figure out what’s going on!