i32 2, ; The number of function records
i32 20, ; The length of the string that contains the encoded translation unit filenames
i32 20, ; The length of the string that contains the encoded coverage mapping data
- i32 1, ; Coverage mapping format version
+ i32 2, ; Coverage mapping format version
},
[2 x { i64, i32, i64 }] [ ; Function records
{ i64, i32, i64 } {
[40 x i8] c"..." ; Encoded data (dissected later)
}, section "__llvm_covmap", align 8
+The current version of the format is version 3. The only difference from version 2 is that a special encoding for column end locations was introduced to indicate gap regions.
+
The function record layout has evolved since version 1. In version 1, the function record for *foo* is defined as follows:
.. code-block:: llvm
* The length of the string in the third field of *__llvm_coverage_mapping* that contains the encoded coverage mapping data.
-* The format version. The current version is 2 (encoded as a 1).
+* The format version. The current version is 3 (encoded as a 2).
.. _function records:
* *numLines*: The difference between the ending line and the starting line
of the current mapping region.
-* *columnEnd*: The ending column of the mapping region.
+* *columnEnd*: The ending column of the mapping region. If the high bit is set,
+ the current mapping region is a gap area. A count for a gap area is only used
+ as the line execution count if there are no other regions on a line.
/// A SkippedRegion represents a source range with code that was skipped
/// by a preprocessor or similar means.
- SkippedRegion
+ SkippedRegion,
+
+ /// A GapRegion is like a CodeRegion, but its count is only set as the
+ /// line execution count when its the only region in the line.
+ GapRegion
};
Counter Count;
LineEnd, ColumnEnd, SkippedRegion);
}
+ static CounterMappingRegion
+ makeGapRegion(Counter Count, unsigned FileID, unsigned LineStart,
+ unsigned ColumnStart, unsigned LineEnd, unsigned ColumnEnd) {
+ return CounterMappingRegion(Count, FileID, 0, LineStart, ColumnStart,
+ LineEnd, (1U << 31) | ColumnEnd, GapRegion);
+ }
+
inline LineColPair startLoc() const {
return LineColPair(LineStart, ColumnStart);
}
bool HasCount;
/// Whether this enters a new region or returns to a previous count.
bool IsRegionEntry;
+ /// Whether this enters a gap region.
+ bool IsGapRegion;
CoverageSegment(unsigned Line, unsigned Col, bool IsRegionEntry)
: Line(Line), Col(Col), Count(0), HasCount(false),
- IsRegionEntry(IsRegionEntry) {}
+ IsRegionEntry(IsRegionEntry), IsGapRegion(false) {}
CoverageSegment(unsigned Line, unsigned Col, uint64_t Count,
- bool IsRegionEntry)
+ bool IsRegionEntry, bool IsGapRegion = false)
: Line(Line), Col(Col), Count(Count), HasCount(true),
- IsRegionEntry(IsRegionEntry) {}
+ IsRegionEntry(IsRegionEntry), IsGapRegion(IsGapRegion) {}
friend bool operator==(const CoverageSegment &L, const CoverageSegment &R) {
- return std::tie(L.Line, L.Col, L.Count, L.HasCount, L.IsRegionEntry) ==
- std::tie(R.Line, R.Col, R.Count, R.HasCount, R.IsRegionEntry);
+ return std::tie(L.Line, L.Col, L.Count, L.HasCount, L.IsRegionEntry,
+ L.IsGapRegion) == std::tie(R.Line, R.Col, R.Count,
+ R.HasCount, R.IsRegionEntry,
+ R.IsGapRegion);
}
};
// name string pointer to MD5 to support name section compression. Name
// section is also compressed.
Version2 = 1,
- // The current version is Version2
+ // A new interpretation of the columnEnd field is added in order to mark
+ // regions as gap areas.
+ Version3 = 2,
+ // The current version is Version3
CurrentVersion = INSTR_PROF_COVMAP_VERSION
};
/* Indexed profile format version (start from 1). */
#define INSTR_PROF_INDEX_VERSION 4
/* Coverage mapping format vresion (start from 0). */
-#define INSTR_PROF_COVMAP_VERSION 1
+#define INSTR_PROF_COVMAP_VERSION 2
/* Profile version is always of type uint64_t. Reserve the upper 8 bits in the
* version for other variants of profile. We set the lowest bit of the upper 8
/// Emit a segment with the count from \p Region starting at \p StartLoc.
//
- /// \p IsRegionEntry: The segment is at the start of a new region.
+ /// \p IsRegionEntry: The segment is at the start of a new non-gap region.
/// \p EmitSkippedRegion: The segment must be emitted as a skipped region.
void startSegment(const CountedRegion &Region, LineColPair StartLoc,
bool IsRegionEntry, bool EmitSkippedRegion = false) {
if (HasCount)
Segments.emplace_back(StartLoc.first, StartLoc.second,
- Region.ExecutionCount, IsRegionEntry);
+ Region.ExecutionCount, IsRegionEntry,
+ Region.Kind == CounterMappingRegion::GapRegion);
else
Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry);
dbgs() << "Segment at " << Last.Line << ":" << Last.Col
<< " (count = " << Last.Count << ")"
<< (Last.IsRegionEntry ? ", RegionEntry" : "")
- << (!Last.HasCount ? ", Skipped" : "") << "\n";
+ << (!Last.HasCount ? ", Skipped" : "")
+ << (Last.IsGapRegion ? ", Gap" : "") << "\n";
});
}
completeRegionsUntil(CurStartLoc, FirstCompletedRegion);
}
+ bool GapRegion = CR.value().Kind == CounterMappingRegion::GapRegion;
+
// Try to emit a segment for the current region.
if (CurStartLoc == CR.value().endLoc()) {
// Avoid making zero-length regions active. If it's the last region,
// emit a skipped segment. Otherwise use its predecessor's count.
const bool Skipped = (CR.index() + 1) == Regions.size();
startSegment(ActiveRegions.empty() ? CR.value() : *ActiveRegions.back(),
- CurStartLoc, true, Skipped);
+ CurStartLoc, !GapRegion, Skipped);
continue;
}
if (CR.index() + 1 == Regions.size() ||
CurStartLoc != Regions[CR.index() + 1].startLoc()) {
// Emit a segment if the next region doesn't start at the same location
// as this one.
- startSegment(CR.value(), CurStartLoc, true);
+ startSegment(CR.value(), CurStartLoc, !GapRegion);
}
// This region is active (i.e not completed).
if (auto Err = readIntMax(ColumnEnd, std::numeric_limits<unsigned>::max()))
return Err;
LineStart += LineStartDelta;
+
+ // If the high bit of ColumnEnd is set, this is a gap region.
+ if (ColumnEnd & (1U << 31)) {
+ Kind = CounterMappingRegion::GapRegion;
+ ColumnEnd &= ~(1U << 31);
+ }
+
// Adjust the column locations for the empty regions that are supposed to
// cover whole lines. Those regions should be encoded with the
// column range (1 -> std::numeric_limits<unsigned>::max()), but because
return llvm::make_unique<VersionedCovMapFuncRecordReader<
CovMapVersion::Version1, IntPtrT, Endian>>(P, R, F);
case CovMapVersion::Version2:
+ case CovMapVersion::Version3:
// Decompress the name data.
if (Error E = P.create(P.getNameData()))
return std::move(E);
- return llvm::make_unique<VersionedCovMapFuncRecordReader<
- CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F);
+ if (Version == CovMapVersion::Version2)
+ return llvm::make_unique<VersionedCovMapFuncRecordReader<
+ CovMapVersion::Version2, IntPtrT, Endian>>(P, R, F);
+ else
+ return llvm::make_unique<VersionedCovMapFuncRecordReader<
+ CovMapVersion::Version3, IntPtrT, Endian>>(P, R, F);
}
llvm_unreachable("Unsupported version");
}
Counter Count = Minimizer.adjust(I->Count);
switch (I->Kind) {
case CounterMappingRegion::CodeRegion:
+ case CounterMappingRegion::GapRegion:
writeCounter(MinExpressions, Count, OS);
break;
case CounterMappingRegion::ExpansionRegion: {
// RUN: llvm-cov show %S/Inputs/deferred-regions.covmapping -instr-profile %S/Inputs/deferred-regions.profdata -show-line-counts-or-regions -dump -path-equivalence=/Users/vk/src/llvm.org-coverage-braces/llvm/test/tools,%S/.. %s 2>%t.markers > %t.out && FileCheck %s -input-file %t.out && FileCheck %s -input-file %t.markers -check-prefix=MARKER
void foo(int x) {
- if (x == 0) {
+ if (x == 0) { // CHECK: [[@LINE]]|{{ +}}2|
return; // CHECK: [[@LINE]]|{{ +}}1|
}
return; // CHECK: [[@LINE]]|{{ +}}0|
for (int i = 0; i < 10; ++i) { // CHECK: [[@LINE]]|{{ +}}2|
- if (i % 2 == 0)
+ if (i % 2 == 0) // CHECK: [[@LINE]]|{{ +}}2|
continue; // CHECK: [[@LINE]]|{{ +}}1|
- if (i % 5 == 0)
+ if (i % 5 == 0) // CHECK: [[@LINE]]|{{ +}}1|
break; // CHECK: [[@LINE]]|{{ +}}0|
int x = i;
int x = 0;
while (++x < 10) { // CHECK: [[@LINE]]|{{ +}}3|
- if (x == 1)
+ if (x == 1) // CHECK: [[@LINE]]|{{ +}}2|
continue; // CHECK: [[@LINE]]|{{ +}}1|
while (++x < 4) { // CHECK: [[@LINE]]|{{ +}}1|
- if (x == 3)
+ if (x == 3) // CHECK: [[@LINE]]|{{ +}}1|
break; // CHECK: [[@LINE]]|{{ +}}1|
// CHECK: [[@LINE]]|{{ +}}0|
while (++x < 5) {} // CHECK: [[@LINE]]|{{ +}}0|
} // CHECK: [[@LINE]]|{{ +}}1|
- if (x == 0)
+ if (x == 0) // CHECK: [[@LINE]]|{{ +}}1|
throw Error(); // CHECK: [[@LINE]]|{{ +}}0|
-
+ // CHECK: [[@LINE]]|{{ +}}1|
while (++x < 9) { // CHECK: [[@LINE]]|{{ +}}6|
if (x == 0) // CHECK: [[@LINE]]|{{ +}}5|
break; // CHECK: [[@LINE]]|{{ +}}0|
}
void gotos() {
- if (false)
+ if (false) // CHECK: [[@LINE]]|{{ +}}1|
goto out; // CHECK: [[@LINE]]|{{ +}}0|
+ // CHECK: [[@LINE]]|{{ +}}1|
+ return; // CHECK: [[@LINE]]|{{ +}}1|
- return;
-
-out: // CHECK: [[@LINE]]|{{ +}}1|
+out: // CHECK: [[@LINE]]|{{ +}}0|
return;
}
// MARKER: Marker at 4:7 = 2
// MARKER-NEXT: Highlighted line 17, 5 -> 11
-// MARKER-NEXT: Marker at 17:5 = 0
// MARKER-NEXT: Marker at 19:3 = 1
// MARKER-NEXT: Marker at 19:19 = 2
// MARKER-NEXT: Marker at 19:27 = 1
-// MARKER-NEXT: Marker at 21:7 = 1
// MARKER-NEXT: Marker at 23:5 = 1
// MARKER-NEXT: Marker at 23:9 = 1
// MARKER-NEXT: Highlighted line 24, 7 -> 12
-// MARKER-NEXT: Marker at 24:7 = 0
// MARKER-NEXT: Highlighted line 36, 5 -> 11
-// MARKER-NEXT: Marker at 36:5 = 0
// MARKER-NEXT: Marker at 39:10 = 3
-// MARKER-NEXT: Marker at 41:7 = 1
// MARKER-NEXT: Marker at 43:5 = 1
// MARKER-NEXT: Marker at 43:12 = 1
-// MARKER-NEXT: Highlighted line 45, 14 -> ?
-// MARKER-NEXT: Marker at 45:9 = 1
// MARKER-NEXT: Highlighted line 46, 1 -> ?
// MARKER-NEXT: Highlighted line 47, 1 -> 7
// MARKER-NEXT: Highlighted line 47, 7 -> 14
// MARKER-NEXT: Marker at 47:14 = 0
// MARKER-NEXT: Marker at 47:23 = 0
// MARKER-NEXT: Highlighted line 51, 7 -> 20
-// MARKER-NEXT: Marker at 51:7 = 0
// MARKER-NEXT: Marker at 53:5 = 1
// MARKER-NEXT: Marker at 53:12 = 6
// MARKER-NEXT: Highlighted line 55, 9 -> 14
// MARKER-NEXT: Highlighted line 63, 5 -> 13
-// MARKER-NEXT: Marker at 63:5 = 0
// MARKER-NEXT: Highlighted line 67, 1 -> ?
// MARKER-NEXT: Highlighted line 68, 1 -> 8
-// MARKER-NEXT: Highlighted line 68, 8 -> ?
// MARKER-NEXT: Highlighted line 69, 1 -> 2
// Find the minimum number of regions which start in this line.
unsigned MinRegionCount = 0;
auto isStartOfRegion = [](const coverage::CoverageSegment *S) {
- return S->HasCount && S->IsRegionEntry;
+ return !S->IsGapRegion && S->HasCount && S->IsRegionEntry;
};
for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I)
if (isStartOfRegion(LineSegments[I]))
// avoid erroneously using the wrapped count, and to avoid picking region
// counts which come from deferred regions.
if (LineSegments.size() > 1) {
- for (unsigned I = 0; I < LineSegments.size() - 1; ++I)
- ExecutionCount = std::max(ExecutionCount, LineSegments[I]->Count);
+ for (unsigned I = 0; I < LineSegments.size() - 1; ++I) {
+ if (!LineSegments[I]->IsGapRegion)
+ ExecutionCount = std::max(ExecutionCount, LineSegments[I]->Count);
+ }
return;
}
- // Just pick the maximum count.
- if (WrappedSegment && WrappedSegment->HasCount)
+ // If a non-gap region starts here, use its count. Otherwise use the wrapped
+ // count.
+ if (MinRegionCount == 1)
+ ExecutionCount = LineSegments[0]->Count;
+ else
ExecutionCount = WrappedSegment->Count;
- if (!LineSegments.empty())
- ExecutionCount = std::max(ExecutionCount, LineSegments[0]->Count);
}
unsigned SourceCoverageView::getFirstUncoveredLineNo() {
const auto *CurSeg = Segments[I];
if (CurSeg->Col == ExpansionCol)
Color = "cyan";
- else if (CheckIfUncovered(CurSeg))
+ else if (!CurSeg->IsGapRegion && CheckIfUncovered(CurSeg))
Color = "red";
else
Color = None;
Col = End;
if (Col == ExpansionCol)
Highlight = raw_ostream::CYAN;
- else if (S->HasCount && S->Count == 0)
+ else if (!S->IsGapRegion && S->HasCount && S->Count == 0)
Highlight = raw_ostream::RED;
else
Highlight = None;