llvm.org GIT mirror llvm / 33671ba
[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 Vedant Kumar 2 years ago
5 changed file(s) with 384 addition(s) and 57 deletion(s). Raw diff Collapse all Expand all
469469 /// Get the name of the file this data covers.
470470 StringRef getFilename() const { return Filename; }
471471
472 /// Get an iterator over the coverage segments for this object. The segments
473 /// are guaranteed to be uniqued and sorted by location.
472474 std::vector::const_iterator begin() const {
473475 return Segments.begin();
474476 }
317317
318318 SegmentBuilder(std::vector &Segments) : Segments(Segments) {}
319319
320 /// Start a segment with no count specified.
321 void startSegment(unsigned Line, unsigned Col) {
322 DEBUG(dbgs() << "Top level segment at " << Line << ":" << Col << "\n");
323 Segments.emplace_back(Line, Col, /*IsRegionEntry=*/false);
324 }
325
326 /// Start a segment with the given Region's count.
327 void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry,
328 const CountedRegion &Region) {
329 // Avoid creating empty regions.
330 if (!Segments.empty() && Segments.back().Line == Line &&
331 Segments.back().Col == Col)
332 Segments.pop_back();
333 DEBUG(dbgs() << "Segment at " << Line << ":" << Col);
334 // Set this region's count.
335 if (Region.Kind != CounterMappingRegion::SkippedRegion) {
336 DEBUG(dbgs() << " with count " << Region.ExecutionCount);
337 Segments.emplace_back(Line, Col, Region.ExecutionCount, IsRegionEntry);
338 } else
339 Segments.emplace_back(Line, Col, IsRegionEntry);
340 DEBUG(dbgs() << "\n");
341 }
342
343 /// Start a segment for the given region.
344 void startSegment(const CountedRegion &Region) {
345 startSegment(Region.LineStart, Region.ColumnStart, true, Region);
346 }
347
348 /// Pop the top region off of the active stack, starting a new segment with
349 /// the containing Region's count.
350 void popRegion() {
351 const CountedRegion *Active = ActiveRegions.back();
352 unsigned Line = Active->LineEnd, Col = Active->ColumnEnd;
353 ActiveRegions.pop_back();
354 if (ActiveRegions.empty())
355 startSegment(Line, Col);
320 /// Emit a segment with the count from \p Region starting at \p StartLoc.
321 //
322 /// \p IsRegionEntry: The segment is at the start of a new region.
323 /// \p EmitSkippedRegion: The segment must be emitted as a skipped region.
324 void startSegment(const CountedRegion &Region, LineColPair StartLoc,
325 bool IsRegionEntry, bool EmitSkippedRegion = false) {
326 bool HasCount = !EmitSkippedRegion &&
327 (Region.Kind != CounterMappingRegion::SkippedRegion);
328
329 // If the new segment wouldn't affect coverage rendering, skip it.
330 if (!Segments.empty() && !IsRegionEntry && !EmitSkippedRegion) {
331 const auto &Last = Segments.back();
332 if (Last.HasCount == HasCount && Last.Count == Region.ExecutionCount &&
333 !Last.IsRegionEntry)
334 return;
335 }
336
337 if (HasCount)
338 Segments.emplace_back(StartLoc.first, StartLoc.second,
339 Region.ExecutionCount, IsRegionEntry);
356340 else
357 startSegment(Line, Col, false, *ActiveRegions.back());
341 Segments.emplace_back(StartLoc.first, StartLoc.second, IsRegionEntry);
342
343 DEBUG({
344 const auto &Last = Segments.back();
345 dbgs() << "Segment at " << Last.Line << ":" << Last.Col
346 << " (count = " << Last.Count << ")"
347 << (Last.IsRegionEntry ? ", RegionEntry" : "")
348 << (!Last.HasCount ? ", Skipped" : "") << "\n";
349 });
350 }
351
352 /// Emit segments for active regions which end before \p Loc.
353 ///
354 /// \p Loc: The start location of the next region. If None, all active
355 /// regions are completed.
356 /// \p FirstCompletedRegion: Index of the first completed region.
357 void completeRegionsUntil(Optional Loc,
358 unsigned FirstCompletedRegion) {
359 // Sort the completed regions by end location. This makes it simple to
360 // emit closing segments in sorted order.
361 auto CompletedRegionsIt = ActiveRegions.begin() + FirstCompletedRegion;
362 std::stable_sort(CompletedRegionsIt, ActiveRegions.end(),
363 [](const CountedRegion *L, const CountedRegion *R) {
364 return L->endLoc() < R->endLoc();
365 });
366
367 // Emit segments for all completed regions.
368 for (unsigned I = FirstCompletedRegion + 1, E = ActiveRegions.size(); I < E;
369 ++I) {
370 const auto *CompletedRegion = ActiveRegions[I];
371 assert((!Loc || CompletedRegion->endLoc() <= *Loc) &&
372 "Completed region ends after start of new region");
373
374 const auto *PrevCompletedRegion = ActiveRegions[I - 1];
375 auto CompletedSegmentLoc = PrevCompletedRegion->endLoc();
376
377 // Don't emit any more segments if they start where the new region begins.
378 if (Loc && CompletedSegmentLoc == *Loc)
379 break;
380
381 // Don't emit a segment if the next completed region ends at the same
382 // location as this one.
383 if (CompletedSegmentLoc == CompletedRegion->endLoc())
384 continue;
385
386 startSegment(*CompletedRegion, CompletedSegmentLoc, false);
387 }
388
389 auto Last = ActiveRegions.back();
390 if (FirstCompletedRegion && Last->endLoc() != *Loc) {
391 // If there's a gap after the end of the last completed region and the
392 // start of the new region, use the last active region to fill the gap.
393 startSegment(*ActiveRegions[FirstCompletedRegion - 1], Last->endLoc(),
394 false);
395 } else if (!FirstCompletedRegion && (!Loc || *Loc != Last->endLoc())) {
396 // Emit a skipped segment if there are no more active regions. This
397 // ensures that gaps between functions are marked correctly.
398 startSegment(*Last, Last->endLoc(), false, true);
399 }
400
401 // Pop the completed regions.
402 ActiveRegions.erase(CompletedRegionsIt, ActiveRegions.end());
358403 }
359404
360405 void buildSegmentsImpl(ArrayRef Regions) {
361 for (const auto &Region : Regions) {
362 // Pop any regions that end before this one starts.
363 while (!ActiveRegions.empty() &&
364 ActiveRegions.back()->endLoc() <= Region.startLoc())
365 popRegion();
366 // Add this region to the stack.
367 ActiveRegions.push_back(&Region);
368 startSegment(Region);
369 }
370 // Pop any regions that are left in the stack.
371 while (!ActiveRegions.empty())
372 popRegion();
406 for (const auto &CR : enumerate(Regions)) {
407 auto CurStartLoc = CR.value().startLoc();
408
409 // Active regions which end before the current region need to be popped.
410 auto CompletedRegions =
411 std::stable_partition(ActiveRegions.begin(), ActiveRegions.end(),
412 [&](const CountedRegion *Region) {
413 return !(Region->endLoc() <= CurStartLoc);
414 });
415 if (CompletedRegions != ActiveRegions.end()) {
416 unsigned FirstCompletedRegion =
417 std::distance(ActiveRegions.begin(), CompletedRegions);
418 completeRegionsUntil(CurStartLoc, FirstCompletedRegion);
419 }
420
421 // Try to emit a segment for the current region.
422 if (CurStartLoc == CR.value().endLoc()) {
423 // Avoid making zero-length regions active. If it's the last region,
424 // emit a skipped segment. Otherwise use its predecessor's count.
425 const bool Skipped = (CR.index() + 1) == Regions.size();
426 startSegment(ActiveRegions.empty() ? CR.value() : *ActiveRegions.back(),
427 CurStartLoc, true, Skipped);
428 continue;
429 }
430 if (CR.index() + 1 == Regions.size() ||
431 CurStartLoc != Regions[CR.index() + 1].startLoc()) {
432 // Emit a segment if the next region doesn't start at the same location
433 // as this one.
434 startSegment(CR.value(), CurStartLoc, true);
435 }
436
437 // This region is active (i.e not completed).
438 ActiveRegions.push_back(&CR.value());
439 }
440
441 // Complete any remaining active regions.
442 if (!ActiveRegions.empty())
443 completeRegionsUntil(None, 0);
373444 }
374445
375446 /// Sort a nested sequence of regions from a single file.
430501 }
431502
432503 public:
433 /// Build a list of CoverageSegments from a list of Regions.
504 /// Build a sorted list of CoverageSegments from a list of Regions.
434505 static std::vector
435506 buildSegments(MutableArrayRef Regions) {
436507 std::vector Segments;
439510 sortNestedRegions(Regions);
440511 ArrayRef CombinedRegions = combineRegions(Regions);
441512
513 DEBUG({
514 dbgs() << "Combined regions:\n";
515 for (const auto &CR : CombinedRegions)
516 dbgs() << " " << CR.LineStart << ":" << CR.ColumnStart << " -> "
517 << CR.LineEnd << ":" << CR.ColumnEnd
518 << " (count=" << CR.ExecutionCount << ")\n";
519 });
520
442521 Builder.buildSegmentsImpl(CombinedRegions);
522
523 #ifndef NDEBUG
524 for (unsigned I = 1, E = Segments.size(); I < E; ++I) {
525 const auto &L = Segments[I - 1];
526 const auto &R = Segments[I];
527 if (!(L.Line < R.Line) && !(L.Line == R.Line && L.Col < R.Col)) {
528 DEBUG(dbgs() << " ! Segment " << L.Line << ":" << L.Col
529 << " followed by " << R.Line << ":" << R.Col << "\n");
530 assert(false && "Coverage segments not unique or sorted");
531 }
532 }
533 #endif
534
443535 return Segments;
444536 }
445537 };
44 return; // CHECK: [[@LINE]]|{{ +}}1|
55 }
66
7 } // CHECK: [[@LINE]]|{{ +}}2|
7 } // CHECK: [[@LINE]]|{{ +}}1|
88
99 void bar() {
1010 return;
102102 // MARKER-NEXT: Highlighted line 47, 14 -> 21
103103 // MARKER-NEXT: Highlighted line 47, 21 -> 23
104104 // MARKER-NEXT: Highlighted line 47, 23 -> 25
105 // MARKER-NEXT: Highlighted line 47, 25 -> ?
106105 // MARKER-NEXT: Marker at 47:7 = 0
107106 // MARKER-NEXT: Marker at 47:14 = 0
108107 // MARKER-NEXT: Marker at 47:23 = 0
109 // MARKER-NEXT: Highlighted line 48, 1 -> 6
110108 // MARKER-NEXT: Highlighted line 51, 7 -> 20
111109 // MARKER-NEXT: Marker at 51:7 = 0
112110 // MARKER-NEXT: Marker at 53:5 = 1
99 while(x >= 9) {
1010 return;
1111 --x; // SHARED: Highlighted line [[@LINE]], 7 ->
12 } // SHARED: Highlighted line [[@LINE]], 1 -> 6
12 }
1313 int i = 0; // SHARED: Highlighted line [[@LINE]], 5 ->
1414 } // SHARED: Highlighted line [[@LINE]], 1 -> 4
1515 }
173173 }
174174
175175 void addCMR(Counter C, StringRef File, unsigned LS, unsigned CS, unsigned LE,
176 unsigned CE) {
177 InputFunctions.back().Regions.push_back(CounterMappingRegion::makeRegion(
178 C, getFileIndexForFunction(File), LS, CS, LE, CE));
176 unsigned CE, bool Skipped = false) {
177 auto &Regions = InputFunctions.back().Regions;
178 unsigned FileID = getFileIndexForFunction(File);
179 Regions.push_back(
180 Skipped ? CounterMappingRegion::makeSkipped(FileID, LS, CS, LE, CE)
181 : CounterMappingRegion::makeRegion(C, FileID, LS, CS, LE, CE));
179182 }
180183
181184 void addExpansionCMR(StringRef File, StringRef ExpandedFile, unsigned LS,
361364 }
362365 }
363366
367 TEST_P(CoverageMappingTest, create_combined_regions) {
368 ProfileWriter.addRecord({"func1", 0x1234, {1, 2, 3}}, Err);
369 startFunction("func1", 0x1234);
370
371 // Given regions which start at the same location, emit a segment for the
372 // last region.
373 addCMR(Counter::getCounter(0), "file1", 1, 1, 2, 2);
374 addCMR(Counter::getCounter(1), "file1", 1, 1, 2, 2);
375 addCMR(Counter::getCounter(2), "file1", 1, 1, 2, 2);
376
377 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
378 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
379 const auto &FunctionRecord = *FunctionRecords.begin();
380 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
381 std::vector Segments(Data.begin(), Data.end());
382
383 ASSERT_EQ(2U, Segments.size());
384 EXPECT_EQ(CoverageSegment(1, 1, 6, true), Segments[0]);
385 EXPECT_EQ(CoverageSegment(2, 2, false), Segments[1]);
386 }
387
388 TEST_P(CoverageMappingTest, skipped_segments_have_no_count) {
389 ProfileWriter.addRecord({"func1", 0x1234, {1}}, Err);
390 startFunction("func1", 0x1234);
391
392 addCMR(Counter::getCounter(0), "file1", 1, 1, 5, 5);
393 addCMR(Counter::getCounter(0), "file1", 5, 1, 5, 5, /*Skipped=*/true);
394
395 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
396 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
397 const auto &FunctionRecord = *FunctionRecords.begin();
398 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
399 std::vector Segments(Data.begin(), Data.end());
400
401 ASSERT_EQ(3U, Segments.size());
402 EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
403 EXPECT_EQ(CoverageSegment(5, 1, true), Segments[1]);
404 EXPECT_EQ(CoverageSegment(5, 5, false), Segments[2]);
405 }
406
407 TEST_P(CoverageMappingTest, multiple_regions_end_after_parent_ends) {
408 ProfileWriter.addRecord({"func1", 0x1234, {1, 0}}, Err);
409 startFunction("func1", 0x1234);
410
411 // 1| F{ a{
412 // 2|
413 // 3| a} b{ c{
414 // 4|
415 // 5| b}
416 // 6|
417 // 7| c} d{ e{
418 // 8|
419 // 9| d} e} F}
420 addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9); //< F
421 addCMR(Counter::getCounter(0), "file1", 1, 1, 3, 5); //< a
422 addCMR(Counter::getCounter(0), "file1", 3, 5, 5, 4); //< b
423 addCMR(Counter::getCounter(1), "file1", 3, 5, 7, 3); //< c
424 addCMR(Counter::getCounter(1), "file1", 7, 3, 9, 2); //< d
425 addCMR(Counter::getCounter(1), "file1", 7, 7, 9, 7); //< e
426
427 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
428 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
429 const auto &FunctionRecord = *FunctionRecords.begin();
430 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
431 std::vector Segments(Data.begin(), Data.end());
432
433 // Old output (not sorted or unique):
434 // Segment at 1:1 with count 1
435 // Segment at 1:1 with count 1
436 // Segment at 3:5 with count 1
437 // Segment at 3:5 with count 0
438 // Segment at 3:5 with count 1
439 // Segment at 5:4 with count 0
440 // Segment at 7:3 with count 1
441 // Segment at 7:3 with count 0
442 // Segment at 7:7 with count 0
443 // Segment at 9:7 with count 0
444 // Segment at 9:2 with count 1
445 // Top level segment at 9:9
446
447 // New output (sorted and unique):
448 // Segment at 1:1 (count = 1), RegionEntry
449 // Segment at 3:5 (count = 1), RegionEntry
450 // Segment at 5:4 (count = 0)
451 // Segment at 7:3 (count = 0), RegionEntry
452 // Segment at 7:7 (count = 0), RegionEntry
453 // Segment at 9:2 (count = 0)
454 // Segment at 9:7 (count = 1)
455 // Segment at 9:9 (count = 0), Skipped
456
457 ASSERT_EQ(8U, Segments.size());
458 EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
459 EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[1]);
460 EXPECT_EQ(CoverageSegment(5, 4, 0, false), Segments[2]);
461 EXPECT_EQ(CoverageSegment(7, 3, 0, true), Segments[3]);
462 EXPECT_EQ(CoverageSegment(7, 7, 0, true), Segments[4]);
463 EXPECT_EQ(CoverageSegment(9, 2, 0, false), Segments[5]);
464 EXPECT_EQ(CoverageSegment(9, 7, 1, false), Segments[6]);
465 EXPECT_EQ(CoverageSegment(9, 9, false), Segments[7]);
466 }
467
468 TEST_P(CoverageMappingTest, dont_emit_redundant_segments) {
469 ProfileWriter.addRecord({"func1", 0x1234, {1, 1}}, Err);
470 startFunction("func1", 0x1234);
471
472 addCMR(Counter::getCounter(0), "file1", 1, 1, 4, 4);
473 addCMR(Counter::getCounter(1), "file1", 2, 2, 5, 5);
474 addCMR(Counter::getCounter(0), "file1", 3, 3, 6, 6);
475
476 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
477 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
478 const auto &FunctionRecord = *FunctionRecords.begin();
479 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
480 std::vector Segments(Data.begin(), Data.end());
481
482 ASSERT_EQ(5U, Segments.size());
483 EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
484 EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]);
485 EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]);
486 EXPECT_EQ(CoverageSegment(4, 4, 1, false), Segments[3]);
487 // A closing segment starting at 5:5 would be redundant: it would have the
488 // same count as the segment starting at 4:4, and has all the same metadata.
489 EXPECT_EQ(CoverageSegment(6, 6, false), Segments[4]);
490 }
491
492 TEST_P(CoverageMappingTest, dont_emit_closing_segment_at_new_region_start) {
493 ProfileWriter.addRecord({"func1", 0x1234, {1}}, Err);
494 startFunction("func1", 0x1234);
495
496 addCMR(Counter::getCounter(0), "file1", 1, 1, 6, 5);
497 addCMR(Counter::getCounter(0), "file1", 2, 2, 6, 5);
498 addCMR(Counter::getCounter(0), "file1", 3, 3, 6, 5);
499 addCMR(Counter::getCounter(0), "file1", 6, 5, 7, 7);
500
501 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
502 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
503 const auto &FunctionRecord = *FunctionRecords.begin();
504 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
505 std::vector Segments(Data.begin(), Data.end());
506
507 ASSERT_EQ(5U, Segments.size());
508 EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
509 EXPECT_EQ(CoverageSegment(2, 2, 1, true), Segments[1]);
510 EXPECT_EQ(CoverageSegment(3, 3, 1, true), Segments[2]);
511 EXPECT_EQ(CoverageSegment(6, 5, 1, true), Segments[3]);
512 // The old segment builder would get this wrong by emitting multiple segments
513 // which start at 6:5 (a few of which were skipped segments). We should just
514 // get a segment for the region entry.
515 EXPECT_EQ(CoverageSegment(7, 7, false), Segments[4]);
516 }
517
518 TEST_P(CoverageMappingTest, handle_consecutive_regions_with_zero_length) {
519 ProfileWriter.addRecord({"func1", 0x1234, {1, 2}}, Err);
520 startFunction("func1", 0x1234);
521
522 addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);
523 addCMR(Counter::getCounter(1), "file1", 1, 1, 1, 1);
524 addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);
525 addCMR(Counter::getCounter(1), "file1", 1, 1, 1, 1);
526 addCMR(Counter::getCounter(0), "file1", 1, 1, 1, 1);
527
528 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
529 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
530 const auto &FunctionRecord = *FunctionRecords.begin();
531 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
532 std::vector Segments(Data.begin(), Data.end());
533
534 ASSERT_EQ(1U, Segments.size());
535 EXPECT_EQ(CoverageSegment(1, 1, true), Segments[0]);
536 // We need to get a skipped segment starting at 1:1. In this case there is
537 // also a region entry at 1:1.
538 }
539
540 TEST_P(CoverageMappingTest, handle_sandwiched_zero_length_region) {
541 ProfileWriter.addRecord({"func1", 0x1234, {2, 1}}, Err);
542 startFunction("func1", 0x1234);
543
544 addCMR(Counter::getCounter(0), "file1", 1, 5, 4, 4);
545 addCMR(Counter::getCounter(1), "file1", 1, 9, 1, 50);
546 addCMR(Counter::getCounter(1), "file1", 2, 7, 2, 34);
547 addCMR(Counter::getCounter(1), "file1", 3, 5, 3, 21);
548 addCMR(Counter::getCounter(1), "file1", 3, 21, 3, 21);
549 addCMR(Counter::getCounter(1), "file1", 4, 12, 4, 17);
550
551 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
552 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
553 const auto &FunctionRecord = *FunctionRecords.begin();
554 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
555 std::vector Segments(Data.begin(), Data.end());
556
557 ASSERT_EQ(10U, Segments.size());
558 EXPECT_EQ(CoverageSegment(1, 5, 2, true), Segments[0]);
559 EXPECT_EQ(CoverageSegment(1, 9, 1, true), Segments[1]);
560 EXPECT_EQ(CoverageSegment(1, 50, 2, false), Segments[2]);
561 EXPECT_EQ(CoverageSegment(2, 7, 1, true), Segments[3]);
562 EXPECT_EQ(CoverageSegment(2, 34, 2, false), Segments[4]);
563 EXPECT_EQ(CoverageSegment(3, 5, 1, true), Segments[5]);
564 EXPECT_EQ(CoverageSegment(3, 21, 2, true), Segments[6]);
565 // Handle the zero-length region by creating a segment with its predecessor's
566 // count (i.e the count from 1:5 -> 4:4).
567 EXPECT_EQ(CoverageSegment(4, 4, false), Segments[7]);
568 // The area between 4:4 and 4:12 is skipped.
569 EXPECT_EQ(CoverageSegment(4, 12, 1, true), Segments[8]);
570 EXPECT_EQ(CoverageSegment(4, 17, false), Segments[9]);
571 }
572
573 TEST_P(CoverageMappingTest, handle_last_completed_region) {
574 ProfileWriter.addRecord({"func1", 0x1234, {1, 2, 3, 4}}, Err);
575 startFunction("func1", 0x1234);
576
577 addCMR(Counter::getCounter(0), "file1", 1, 1, 8, 8);
578 addCMR(Counter::getCounter(1), "file1", 2, 2, 5, 5);
579 addCMR(Counter::getCounter(2), "file1", 3, 3, 4, 4);
580 addCMR(Counter::getCounter(3), "file1", 6, 6, 7, 7);
581
582 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
583 const auto FunctionRecords = LoadedCoverage->getCoveredFunctions();
584 const auto &FunctionRecord = *FunctionRecords.begin();
585 CoverageData Data = LoadedCoverage->getCoverageForFunction(FunctionRecord);
586 std::vector Segments(Data.begin(), Data.end());
587
588 ASSERT_EQ(8U, Segments.size());
589 EXPECT_EQ(CoverageSegment(1, 1, 1, true), Segments[0]);
590 EXPECT_EQ(CoverageSegment(2, 2, 2, true), Segments[1]);
591 EXPECT_EQ(CoverageSegment(3, 3, 3, true), Segments[2]);
592 EXPECT_EQ(CoverageSegment(4, 4, 2, false), Segments[3]);
593 EXPECT_EQ(CoverageSegment(5, 5, 1, false), Segments[4]);
594 EXPECT_EQ(CoverageSegment(6, 6, 4, true), Segments[5]);
595 EXPECT_EQ(CoverageSegment(7, 7, 1, false), Segments[6]);
596 EXPECT_EQ(CoverageSegment(8, 8, false), Segments[7]);
597 }
598
364599 TEST_P(CoverageMappingTest, expansion_gets_first_counter) {
365600 startFunction("func", 0x1234);
366601 addCMR(Counter::getCounter(1), "foo", 10, 1, 10, 2);