mirror of
https://github.com/RPCS3/llvm.git
synced 2024-11-27 13:40:43 +00:00
[Coverage] Build sorted and unique segments
A coverage segment contains a starting line and column, an execution count, and some other metadata. Clients of the coverage library use segments to prepare line-oriented reports. Users of the coverage library depend on segments being unique and sorted in source order. Currently this is not guaranteed (this is why the clang change which introduced deferred regions was reverted). This commit documents the "unique and sorted" condition and asserts that it holds. It also fixes the SegmentBuilder so that it produces correct output in some edge cases. Testing: I've added unit tests for some edge cases. I've also checked that the new SegmentBuilder implementation is fully covered. Apart from running check-profile and the llvm-cov tests, I've successfully used a stage1 llvm-cov to prepare a coverage report for an instrumented clang binary. Differential Revision: https://reviews.llvm.org/D36813 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@312817 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
31b24fc041
commit
33671ba388
@ -470,6 +470,8 @@ public:
|
||||
/// Get the name of the file this data covers.
|
||||
StringRef getFilename() const { return Filename; }
|
||||
|
||||
/// Get an iterator over the coverage segments for this object. The segments
|
||||
/// are guaranteed to be uniqued and sorted by location.
|
||||
std::vector<CoverageSegment>::const_iterator begin() const {
|
||||
return Segments.begin();
|
||||
}
|
||||
|
@ -318,59 +318,130 @@ class SegmentBuilder {
|
||||
|
||||
SegmentBuilder(std::vector<CoverageSegment> &Segments) : Segments(Segments) {}
|
||||
|
||||
/// Start a segment with no count specified.
|
||||
void startSegment(unsigned Line, unsigned Col) {
|
||||
DEBUG(dbgs() << "Top level segment at " << Line << ":" << Col << "\n");
|
||||
Segments.emplace_back(Line, Col, /*IsRegionEntry=*/false);
|
||||
}
|
||||
/// 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 EmitSkippedRegion: The segment must be emitted as a skipped region.
|
||||
void startSegment(const CountedRegion &Region, LineColPair StartLoc,
|
||||
bool IsRegionEntry, bool EmitSkippedRegion = false) {
|
||||
bool HasCount = !EmitSkippedRegion &&
|
||||
(Region.Kind != CounterMappingRegion::SkippedRegion);
|
||||
|
||||
/// Start a segment with the given Region's count.
|
||||
void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry,
|
||||
const CountedRegion &Region) {
|
||||
// Avoid creating empty regions.
|
||||
if (!Segments.empty() && Segments.back().Line == Line &&
|
||||
Segments.back().Col == Col)
|
||||
Segments.pop_back();
|
||||
DEBUG(dbgs() << "Segment at " << Line << ":" << Col);
|
||||
// Set this region's count.
|
||||
if (Region.Kind != CounterMappingRegion::SkippedRegion) {
|
||||
DEBUG(dbgs() << " with count " << Region.ExecutionCount);
|
||||
Segments.emplace_back(Line, Col, Region.ExecutionCount, IsRegionEntry);
|
||||
} else
|
||||
Segments.emplace_back(Line, Col, IsRegionEntry);
|
||||
DEBUG(dbgs() << "\n");
|
||||
}
|
||||
// If the new segment wouldn't affect coverage rendering, skip it.
|
||||
if (!Segments.empty() && !IsRegionEntry && !EmitSkippedRegion) {
|
||||
const auto &Last = Segments.back();
|
||||
if (Last.HasCount == HasCount && Last.Count == Region.ExecutionCount &&
|
||||
!Last.IsRegionEntry)
|
||||
return;
|
||||
}
|
||||
|
||||
/// Start a segment for the given region.
|
||||
void startSegment(const CountedRegion &Region) {
|
||||
startSegment(Region.LineStart, Region.ColumnStart, true, Region);
|
||||
}
|
||||
|
||||
/// Pop the top region off of the active stack, starting a new segment with
|
||||
/// the containing Region's count.
|
||||
void popRegion() {
|
||||
const CountedRegion *Active = ActiveRegions.back();
|
||||
unsigned Line = Active->LineEnd, Col = Active->ColumnEnd;
|
||||
ActiveRegions.pop_back();
|
||||
if (ActiveRegions.empty())
|
||||
startSegment(Line, Col);
|
||||
if (HasCount)
|
||||
Segments.emplace_back(StartLoc.first, StartLoc.second,
|
||||
Region.ExecutionCount, IsRegionEntry);
|
||||
else
|
||||
startSegment(Line, Col, false, *ActiveRegions.back());
|
||||
Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry);
|
||||
|
||||
DEBUG({
|
||||
const auto &Last = Segments.back();
|
||||
dbgs() << "Segment at " << Last.Line << ":" << Last.Col
|
||||
<< " (count = " << Last.Count << ")"
|
||||
<< (Last.IsRegionEntry ? ", RegionEntry" : "")
|
||||
<< (!Last.HasCount ? ", Skipped" : "") << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
/// Emit segments for active regions which end before \p Loc.
|
||||
///
|
||||
/// \p Loc: The start location of the next region. If None, all active
|
||||
/// regions are completed.
|
||||
/// \p FirstCompletedRegion: Index of the first completed region.
|
||||
void completeRegionsUntil(Optional<LineColPair> Loc,
|
||||
unsigned FirstCompletedRegion) {
|
||||
// Sort the completed regions by end location. This makes it simple to
|
||||
// emit closing segments in sorted order.
|
||||
auto CompletedRegionsIt = ActiveRegions.begin() + FirstCompletedRegion;
|
||||
std::stable_sort(CompletedRegionsIt, ActiveRegions.end(),
|
||||
[](const CountedRegion *L, const CountedRegion *R) {
|
||||
return L->endLoc() < R->endLoc();
|
||||
});
|
||||
|
||||
// Emit segments for all completed regions.
|
||||
for (unsigned I = FirstCompletedRegion + 1, E = ActiveRegions.size(); I < E;
|
||||
++I) {
|
||||
const auto *CompletedRegion = ActiveRegions[I];
|
||||
assert((!Loc || CompletedRegion->endLoc() <= *Loc) &&
|
||||
"Completed region ends after start of new region");
|
||||
|
||||
const auto *PrevCompletedRegion = ActiveRegions[I - 1];
|
||||
auto CompletedSegmentLoc = PrevCompletedRegion->endLoc();
|
||||
|
||||
// Don't emit any more segments if they start where the new region begins.
|
||||
if (Loc && CompletedSegmentLoc == *Loc)
|
||||
break;
|
||||
|
||||
// Don't emit a segment if the next completed region ends at the same
|
||||
// location as this one.
|
||||
if (CompletedSegmentLoc == CompletedRegion->endLoc())
|
||||
continue;
|
||||
|
||||
startSegment(*CompletedRegion, CompletedSegmentLoc, false);
|
||||
}
|
||||
|
||||
auto Last = ActiveRegions.back();
|
||||
if (FirstCompletedRegion && Last->endLoc() != *Loc) {
|
||||
// If there's a gap after the end of the last completed region and the
|
||||
// start of the new region, use the last active region to fill the gap.
|
||||
startSegment(*ActiveRegions[FirstCompletedRegion - 1], Last->endLoc(),
|
||||
false);
|
||||
} else if (!FirstCompletedRegion && (!Loc || *Loc != Last->endLoc())) {
|
||||
// Emit a skipped segment if there are no more active regions. This
|
||||
// ensures that gaps between functions are marked correctly.
|
||||
startSegment(*Last, Last->endLoc(), false, true);
|
||||
}
|
||||
|
||||
// Pop the completed regions.
|
||||
ActiveRegions.erase(CompletedRegionsIt, ActiveRegions.end());
|
||||
}
|
||||
|
||||
void buildSegmentsImpl(ArrayRef<CountedRegion> Regions) {
|
||||
for (const auto &Region : Regions) {
|
||||
// Pop any regions that end before this one starts.
|
||||
while (!ActiveRegions.empty() &&
|
||||
ActiveRegions.back()->endLoc() <= Region.startLoc())
|
||||
popRegion();
|
||||
// Add this region to the stack.
|
||||
ActiveRegions.push_back(&Region);
|
||||
startSegment(Region);
|
||||
for (const auto &CR : enumerate(Regions)) {
|
||||
auto CurStartLoc = CR.value().startLoc();
|
||||
|
||||
// Active regions which end before the current region need to be popped.
|
||||
auto CompletedRegions =
|
||||
std::stable_partition(ActiveRegions.begin(), ActiveRegions.end(),
|
||||
[&](const CountedRegion *Region) {
|
||||
return !(Region->endLoc() <= CurStartLoc);
|
||||
});
|
||||
if (CompletedRegions != ActiveRegions.end()) {
|
||||
unsigned FirstCompletedRegion =
|
||||
std::distance(ActiveRegions.begin(), CompletedRegions);
|
||||
completeRegionsUntil(CurStartLoc, FirstCompletedRegion);
|
||||
}
|
||||
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
|
||||
// This region is active (i.e not completed).
|
||||
ActiveRegions.push_back(&CR.value());
|
||||
}
|
||||
// Pop any regions that are left in the stack.
|
||||
while (!ActiveRegions.empty())
|
||||
popRegion();
|
||||
|
||||
// Complete any remaining active regions.
|
||||
if (!ActiveRegions.empty())
|
||||
completeRegionsUntil(None, 0);
|
||||
}
|
||||
|
||||
/// Sort a nested sequence of regions from a single file.
|
||||
@ -431,7 +502,7 @@ class SegmentBuilder {
|
||||
}
|
||||
|
||||
public:
|
||||
/// Build a list of CoverageSegments from a list of Regions.
|
||||
/// Build a sorted list of CoverageSegments from a list of Regions.
|
||||
static std::vector<CoverageSegment>
|
||||
buildSegments(MutableArrayRef<CountedRegion> Regions) {
|
||||
std::vector<CoverageSegment> Segments;
|
||||
@ -440,7 +511,28 @@ public:
|
||||
sortNestedRegions(Regions);
|
||||
ArrayRef<CountedRegion> CombinedRegions = combineRegions(Regions);
|
||||
|
||||
DEBUG({
|
||||
dbgs() << "Combined regions:\n";
|
||||
for (const auto &CR : CombinedRegions)
|
||||
dbgs() << " " << CR.LineStart << ":" << CR.ColumnStart << " -> "
|
||||
<< CR.LineEnd << ":" << CR.ColumnEnd
|
||||
<< " (count=" << CR.ExecutionCount << ")\n";
|
||||
});
|
||||
|
||||
Builder.buildSegmentsImpl(CombinedRegions);
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
|
||||
const auto &L = Segments[I - 1];
|
||||
const auto &R = Segments[I];
|
||||
if (!(L.Line < R.Line) && !(L.Line == R.Line && L.Col < R.Col)) {
|
||||
DEBUG(dbgs() << " ! Segment " << L.Line << ":" << L.Col
|
||||
<< " followed by " << R.Line << ":" << R.Col << "\n");
|
||||
assert(false && "Coverage segments not unique or sorted");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return Segments;
|
||||
}
|
||||
};
|
||||
|
@ -5,7 +5,7 @@ void foo(int x) {
|
||||
return; // CHECK: [[@LINE]]|{{ +}}1|
|
||||
}
|
||||
|
||||
} // CHECK: [[@LINE]]|{{ +}}2|
|
||||
} // CHECK: [[@LINE]]|{{ +}}1|
|
||||
|
||||
void bar() {
|
||||
return;
|
||||
@ -103,11 +103,9 @@ int main() {
|
||||
// MARKER-NEXT: Highlighted line 47, 14 -> 21
|
||||
// MARKER-NEXT: Highlighted line 47, 21 -> 23
|
||||
// MARKER-NEXT: Highlighted line 47, 23 -> 25
|
||||
// MARKER-NEXT: Highlighted line 47, 25 -> ?
|
||||
// MARKER-NEXT: Marker at 47:7 = 0
|
||||
// MARKER-NEXT: Marker at 47:14 = 0
|
||||
// MARKER-NEXT: Marker at 47:23 = 0
|
||||
// MARKER-NEXT: Highlighted line 48, 1 -> 6
|
||||
// MARKER-NEXT: Highlighted line 51, 7 -> 20
|
||||
// MARKER-NEXT: Marker at 51:7 = 0
|
||||
// MARKER-NEXT: Marker at 53:5 = 1
|
||||
|
@ -10,7 +10,7 @@ void func2(int x) {
|
||||
while(x >= 9) {
|
||||
return;
|
||||
--x; // SHARED: Highlighted line [[@LINE]], 7 ->
|
||||
} // SHARED: Highlighted line [[@LINE]], 1 -> 6
|
||||
}
|
||||
int i = 0; // SHARED: Highlighted line [[@LINE]], 5 ->
|
||||
} // SHARED: Highlighted line [[@LINE]], 1 -> 4
|
||||
}
|
||||
|
@ -174,9 +174,12 @@ struct CoverageMappingTest : ::testing::TestWithParam<std::pair<bool, bool>> {
|
||||
}
|
||||
|
||||
void addCMR(Counter C, StringRef File, unsigned LS, unsigned CS, unsigned LE,
|
||||
unsigned CE) {
|
||||
InputFunctions.back().Regions.push_back(CounterMappingRegion::makeRegion(
|
||||
C, getFileIndexForFunction(File), LS, CS, LE, CE));
|
||||
unsigned CE, bool Skipped = false) {
|
||||
auto &Regions = InputFunctions.back().Regions;
|
||||
unsigned FileID = getFileIndexForFunction(File);
|
||||
Regions.push_back(
|
||||
Skipped ? CounterMappingRegion::makeSkipped(FileID, LS, CS, LE, CE)
|
||||
: CounterMappingRegion::makeRegion(C, FileID, LS, CS, LE, CE));
|
||||
}
|
||||
|
||||
void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS,
|
||||
@ -362,6 +365,238 @@ TEST_P(CoverageMappingTest, load_coverage_for_several_functions) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, create_combined_regions) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {1, 2, 3}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
// Given regions which start at the same location, emit a segment for the
|
||||
// last region.
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 2, 2);
|
||||
addCMR(Counter::getCounter(1), "file1", 1, 1, 2, 2);
|
||||
addCMR(Counter::getCounter(2), "file1", 1, 1, 2, 2);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
ASSERT_EQ(2U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 1, 6, true), Segments[0]);
|
||||
EXPECT_EQ(CoverageSegment(2, 2, false), Segments[1]);
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, skipped_segments_have_no_count) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {1}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 5, 5);
|
||||
addCMR(Counter::getCounter(0), "file1", 5, 1, 5, 5, /*Skipped=*/true);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
ASSERT_EQ(3U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
|
||||
EXPECT_EQ(CoverageSegment(5, 1, true), Segments[1]);
|
||||
EXPECT_EQ(CoverageSegment(5, 5, false), Segments[2]);
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, multiple_regions_end_after_parent_ends) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {1, 0}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
// 1| F{ a{
|
||||
// 2|
|
||||
// 3| a} b{ c{
|
||||
// 4|
|
||||
// 5| b}
|
||||
// 6|
|
||||
// 7| c} d{ e{
|
||||
// 8|
|
||||
// 9| d} e} F}
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); //< F
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 3, 5); //< a
|
||||
addCMR(Counter::getCounter(0), "file1", 3, 5, 5, 4); //< b
|
||||
addCMR(Counter::getCounter(1), "file1", 3, 5, 7, 3); //< c
|
||||
addCMR(Counter::getCounter(1), "file1", 7, 3, 9, 2); //< d
|
||||
addCMR(Counter::getCounter(1), "file1", 7, 7, 9, 7); //< e
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
// Old output (not sorted or unique):
|
||||
// Segment at 1:1 with count 1
|
||||
// Segment at 1:1 with count 1
|
||||
// Segment at 3:5 with count 1
|
||||
// Segment at 3:5 with count 0
|
||||
// Segment at 3:5 with count 1
|
||||
// Segment at 5:4 with count 0
|
||||
// Segment at 7:3 with count 1
|
||||
// Segment at 7:3 with count 0
|
||||
// Segment at 7:7 with count 0
|
||||
// Segment at 9:7 with count 0
|
||||
// Segment at 9:2 with count 1
|
||||
// Top level segment at 9:9
|
||||
|
||||
// New output (sorted and unique):
|
||||
// Segment at 1:1 (count = 1), RegionEntry
|
||||
// Segment at 3:5 (count = 1), RegionEntry
|
||||
// Segment at 5:4 (count = 0)
|
||||
// Segment at 7:3 (count = 0), RegionEntry
|
||||
// Segment at 7:7 (count = 0), RegionEntry
|
||||
// Segment at 9:2 (count = 0)
|
||||
// Segment at 9:7 (count = 1)
|
||||
// Segment at 9:9 (count = 0), Skipped
|
||||
|
||||
ASSERT_EQ(8U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
|
||||
EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[1]);
|
||||
EXPECT_EQ(CoverageSegment(5, 4, 0, false), Segments[2]);
|
||||
EXPECT_EQ(CoverageSegment(7, 3, 0, true), Segments[3]);
|
||||
EXPECT_EQ(CoverageSegment(7, 7, 0, true), Segments[4]);
|
||||
EXPECT_EQ(CoverageSegment(9, 2, 0, false), Segments[5]);
|
||||
EXPECT_EQ(CoverageSegment(9, 7, 1, false), Segments[6]);
|
||||
EXPECT_EQ(CoverageSegment(9, 9, false), Segments[7]);
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, dont_emit_redundant_segments) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {1, 1}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 4, 4);
|
||||
addCMR(Counter::getCounter(1), "file1", 2, 2, 5, 5);
|
||||
addCMR(Counter::getCounter(0), "file1", 3, 3, 6, 6);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
ASSERT_EQ(5U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
|
||||
EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]);
|
||||
EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]);
|
||||
EXPECT_EQ(CoverageSegment(4, 4, 1, false), Segments[3]);
|
||||
// A closing segment starting at 5:5 would be redundant: it would have the
|
||||
// same count as the segment starting at 4:4, and has all the same metadata.
|
||||
EXPECT_EQ(CoverageSegment(6, 6, false), Segments[4]);
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, dont_emit_closing_segment_at_new_region_start) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {1}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 6, 5);
|
||||
addCMR(Counter::getCounter(0), "file1", 2, 2, 6, 5);
|
||||
addCMR(Counter::getCounter(0), "file1", 3, 3, 6, 5);
|
||||
addCMR(Counter::getCounter(0), "file1", 6, 5, 7, 7);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
ASSERT_EQ(5U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
|
||||
EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]);
|
||||
EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]);
|
||||
EXPECT_EQ(CoverageSegment(6, 5, 1, true), Segments[3]);
|
||||
// The old segment builder would get this wrong by emitting multiple segments
|
||||
// which start at 6:5 (a few of which were skipped segments). We should just
|
||||
// get a segment for the region entry.
|
||||
EXPECT_EQ(CoverageSegment(7, 7, false), Segments[4]);
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, handle_consecutive_regions_with_zero_length) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {1, 2}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);
|
||||
addCMR(Counter::getCounter(1), "file1", 1, 1, 1, 1);
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);
|
||||
addCMR(Counter::getCounter(1), "file1", 1, 1, 1, 1);
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
ASSERT_EQ(1U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 1, true), Segments[0]);
|
||||
// We need to get a skipped segment starting at 1:1. In this case there is
|
||||
// also a region entry at 1:1.
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, handle_sandwiched_zero_length_region) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {2, 1}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 5, 4, 4);
|
||||
addCMR(Counter::getCounter(1), "file1", 1, 9, 1, 50);
|
||||
addCMR(Counter::getCounter(1), "file1", 2, 7, 2, 34);
|
||||
addCMR(Counter::getCounter(1), "file1", 3, 5, 3, 21);
|
||||
addCMR(Counter::getCounter(1), "file1", 3, 21, 3, 21);
|
||||
addCMR(Counter::getCounter(1), "file1", 4, 12, 4, 17);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
ASSERT_EQ(10U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 5, 2, true), Segments[0]);
|
||||
EXPECT_EQ(CoverageSegment(1, 9, 1, true), Segments[1]);
|
||||
EXPECT_EQ(CoverageSegment(1, 50, 2, false), Segments[2]);
|
||||
EXPECT_EQ(CoverageSegment(2, 7, 1, true), Segments[3]);
|
||||
EXPECT_EQ(CoverageSegment(2, 34, 2, false), Segments[4]);
|
||||
EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[5]);
|
||||
EXPECT_EQ(CoverageSegment(3, 21, 2, true), Segments[6]);
|
||||
// Handle the zero-length region by creating a segment with its predecessor's
|
||||
// count (i.e the count from 1:5 -> 4:4).
|
||||
EXPECT_EQ(CoverageSegment(4, 4, false), Segments[7]);
|
||||
// The area between 4:4 and 4:12 is skipped.
|
||||
EXPECT_EQ(CoverageSegment(4, 12, 1, true), Segments[8]);
|
||||
EXPECT_EQ(CoverageSegment(4, 17, false), Segments[9]);
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, handle_last_completed_region) {
|
||||
ProfileWriter.addRecord({"func1", 0x1234, {1, 2, 3, 4}}, Err);
|
||||
startFunction("func1", 0x1234);
|
||||
|
||||
addCMR(Counter::getCounter(0), "file1", 1, 1, 8, 8);
|
||||
addCMR(Counter::getCounter(1), "file1", 2, 2, 5, 5);
|
||||
addCMR(Counter::getCounter(2), "file1", 3, 3, 4, 4);
|
||||
addCMR(Counter::getCounter(3), "file1", 6, 6, 7, 7);
|
||||
|
||||
EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
|
||||
const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
|
||||
const auto &FunctionRecord = *FunctionRecords.begin();
|
||||
CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
|
||||
std::vector<CoverageSegment> Segments(Data.begin(), Data.end());
|
||||
|
||||
ASSERT_EQ(8U, Segments.size());
|
||||
EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
|
||||
EXPECT_EQ(CoverageSegment(2, 2, 2, true), Segments[1]);
|
||||
EXPECT_EQ(CoverageSegment(3, 3, 3, true), Segments[2]);
|
||||
EXPECT_EQ(CoverageSegment(4, 4, 2, false), Segments[3]);
|
||||
EXPECT_EQ(CoverageSegment(5, 5, 1, false), Segments[4]);
|
||||
EXPECT_EQ(CoverageSegment(6, 6, 4, true), Segments[5]);
|
||||
EXPECT_EQ(CoverageSegment(7, 7, 1, false), Segments[6]);
|
||||
EXPECT_EQ(CoverageSegment(8, 8, false), Segments[7]);
|
||||
}
|
||||
|
||||
TEST_P(CoverageMappingTest, expansion_gets_first_counter) {
|
||||
startFunction("func", 0x1234);
|
||||
addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2);
|
||||
|
Loading…
Reference in New Issue
Block a user