[vm,dynamic_modules] Add RecordCoverage instruction.

The RecordCoverage instruction has an A/E encoding. The A argument
is the type of coverage being recorded, whereas the E argument is
the logical index into the coverage array for updating whether that
source position has been hit.

Also adds new metadata to the bytecode component for the coverage
arrays associated with bytecode containing RecordCoverage instructions
and a new runtime entry for lazily allocate the coverage array for
an interpreted function when needed.

The type of coverage is encoded in the RecordCoverage instruction,
despite being redundant with the information in the coverage array, so that checking whether that type of coverage is currently enabled at
runtime doesn't require either accessing the coverage array (which may
be lazily allocated), forcing allocation of the coverage array just to
discover that type of coverage is currently disabled, or reading the
serialized bytecode component to avoid that forced allocation.

------

Other changes:

Source reporting now treats unexecuted interpreted functions when
not forcing compilation as if they were uncompiled native functions,
so that the source report from running the same code gives the same
result whether using the interpreter or the native compiler.

Bytecode closures are no longer skipped in source reports. Previously
any closure without a context scope was skipped, but bytecode closures
don't have those.

TEST=vm/cc/SourceReport_Coverage

Cq-Include-Trybots: luci.dart.try:vm-dyn-linux-debug-x64-try,vm-aot-dyn-linux-debug-x64-try,vm-aot-dyn-linux-product-x64-try
Change-Id: I7557e5dd4c98331c7ca2f5c867dd5f6d03e9d756
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501520
Reviewed-by: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Tess Strickland
2026-05-19 04:27:39 -07:00
parent d2b5c1a81e
commit 52cfd29cbb
21 changed files with 1915 additions and 1437 deletions
+49 -3
View File
@@ -47,7 +47,7 @@ which reside in different sections such as libraries, classes, members, code, et
```
type BytecodeFile {
UInt32 magic = 0x44424333; // 'DBC3'
UInt32 formatVersion = 2;
UInt32 formatVersion = 3;
// Descriptors of the sections below.
// Each section has a fixed index in the descriptors array.
@@ -69,6 +69,7 @@ type BytecodeFile {
SourceFile[] sourceFiles;
LineStarts[] lineStarts;
LocalVariables[] localVariables;
RecordedCoverageArray[] recordedCoverage;
PackedObject[] annotations;
}
@@ -604,7 +605,8 @@ Code section contains bodies of members (including field initializers).
type Code {
UInt flags = (hasExceptionsTable, hasSourcePositions, hasNullableFields,
hasClosures, hasParameterFlags, hasForwardingStubTarget,
hasDefaultFunctionTypeArgs, hasLocalVariables)
hasDefaultFunctionTypeArgs, hasLocalVariables,
hasRecordedCoverage)
if hasParameterFlags
// For all parameters: (isCovariant, isCovariantByClass)
@@ -635,6 +637,11 @@ type Code {
// Offset of LocalVariables in localVariables section of BytecodeFile.
UInt localVariablesOffset;
if hasRecordedCoverage
// Offset of RecordedCoverageArray in recordedCoverage section of
// BytecodeFile.
Uint recordedCoverageOffset;
if hasNullableFields
List<PackedObject> nullableFields;
@@ -681,7 +688,8 @@ type ClosureDeclaration {
type ClosureCode {
UInt flags = (hasExceptionsTable, hasSourcePositions, hasLocalVariables,
capturesOnlyFinalNotLateVars, hasLocalFunctionId)
capturesOnlyFinalNotLateVars, hasLocalFunctionId,
hasRecordedCoverage)
if hasLocalFunctionId
UInt localFunctionId;
@@ -699,6 +707,11 @@ type ClosureCode {
if hasLocalVariables
// Offset of LocalVariables in localVariables section of BytecodeFile.
UInt localVariablesOffset;
if hasRecordedCoverage
// Offset of RecordedCoverageArray in recordedCoverage section of
// BytecodeFile.
UInt recordedCoverageOffset;
}
```
@@ -968,6 +981,28 @@ type ContextVariable extends LocalVariableEntry {
}
```
### Recorded coverage
```
type RecordedCoverageArray {
UInt numEntries;
// Unordered encoded list of entries.
RecordedCoverageEntry[numEntries] entries;
}
type RecordedCoverageEntry = {
// The index of the entry's type in the RecordedEntryType enum.
UInt type;
// Delta-encoded file offset.
SLEB128 fileOffset;
}
enum RecordedCoverageType = {
regular = 0,
branchTarget = 1;
}
```
## Bytecode instructions
### Execution state
@@ -1445,3 +1480,14 @@ Store object SP[0] into the element [D] of closure SP[-1].
No-op. Provides a unique PC offset to ensure an emitted source position is not
overwritten by a different source position emitted by the next instruction.
#### RecordCoverage A, E
Records coverage. [A] is the index in the RecordedCoverageType enum for the type
of coverage being recorded, and [E] is the index of the entry in
the RecordedCoverageArray.
The information in [A] is redundant, but allows the interpreter to check to see
if the isolate group is currently recorded that type of coverage without needing
either to iterate over the serialized RecordedCoverageArray or to index into the
bytecode's coverage array, which may be lazily allocated.