llvm.org GIT mirror llvm / 2ce33a5
[llvm-cov] Move LineCoverageIterator to libCoverage. NFC. LineCoverageIterator makes it easy for clients of coverage data to determine line execution counts for a file or function. The coverage iteration logic is tricky enough that it really pays not to have multiple copies of it. Hopefully having just one implementation in LLVM will make the iteration logic easier to test, reuse, and update. This commit is NFC but I've added a unit test to go along with it just because it's easy to do now. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@316141 91177308-0d34-0410-b5e6-96231b3b80d8 Vedant Kumar 1 year, 11 months ago
5 changed file(s) with 193 addition(s) and 142 deletion(s). Raw diff Collapse all Expand all
593593 getInstantiationGroups(StringRef Filename) const;
594594 };
595595
596 /// Coverage statistics for a single line.
597 class LineCoverageStats {
598 uint64_t ExecutionCount;
599 bool HasMultipleRegions;
600 bool Mapped;
601 unsigned Line;
602 ArrayRef LineSegments;
603 const CoverageSegment *WrappedSegment;
604
605 friend class LineCoverageIterator;
606 LineCoverageStats() = default;
607
608 public:
609 LineCoverageStats(ArrayRef LineSegments,
610 const CoverageSegment *WrappedSegment, unsigned Line);
611
612 uint64_t getExecutionCount() const { return ExecutionCount; }
613
614 bool hasMultipleRegions() const { return HasMultipleRegions; }
615
616 bool isMapped() const { return Mapped; }
617
618 unsigned getLine() const { return Line; }
619
620 ArrayRef getLineSegments() const {
621 return LineSegments;
622 }
623
624 const CoverageSegment *getWrappedSegment() const { return WrappedSegment; }
625 };
626
627 /// An iterator over the \c LineCoverageStats objects for lines described by
628 /// a \c CoverageData instance.
629 class LineCoverageIterator
630 : public iterator_facade_base<
631 LineCoverageIterator, std::forward_iterator_tag, LineCoverageStats> {
632 public:
633 LineCoverageIterator(const CoverageData &CD)
634 : LineCoverageIterator(CD, CD.begin()->Line) {}
635
636 LineCoverageIterator(const CoverageData &CD, unsigned Line)
637 : CD(CD), WrappedSegment(nullptr), Next(CD.begin()), Ended(false),
638 Line(Line), Segments(), Stats() {
639 this->operator++();
640 }
641
642 LineCoverageIterator &operator=(const LineCoverageIterator &R) = default;
643
644 bool operator==(const LineCoverageIterator &R) const {
645 return &CD == &R.CD && Next == R.Next && Ended == R.Ended;
646 }
647
648 const LineCoverageStats &operator*() const { return Stats; }
649
650 LineCoverageStats &operator*() { return Stats; }
651
652 LineCoverageIterator &operator++();
653
654 LineCoverageIterator getEnd() const {
655 auto EndIt = *this;
656 EndIt.Next = CD.end();
657 EndIt.Ended = true;
658 return EndIt;
659 }
660
661 private:
662 const CoverageData &CD;
663 const CoverageSegment *WrappedSegment;
664 std::vector::const_iterator Next;
665 bool Ended;
666 unsigned Line;
667 SmallVector Segments;
668 LineCoverageStats Stats;
669 };
670
671 /// Get a \c LineCoverageIterator range for the lines described by \p CD.
672 static inline iterator_range
673 getLineCoverageStats(const coverage::CoverageData &CD) {
674 auto Begin = LineCoverageIterator(CD);
675 auto End = Begin.getEnd();
676 return make_range(Begin, End);
677 }
678
596679 // Profile coverage map has the following layout:
597680 // [CoverageMapFileHeader]
598681 // [ArrayStart]
670670 return ExpansionCoverage;
671671 }
672672
673 LineCoverageStats::LineCoverageStats(
674 ArrayRef LineSegments,
675 const coverage::CoverageSegment *WrappedSegment, unsigned Line)
676 : ExecutionCount(0), HasMultipleRegions(false), Mapped(false), Line(Line),
677 LineSegments(LineSegments), WrappedSegment(WrappedSegment) {
678 // Find the minimum number of regions which start in this line.
679 unsigned MinRegionCount = 0;
680 auto isStartOfRegion = [](const coverage::CoverageSegment *S) {
681 return !S->IsGapRegion && S->HasCount && S->IsRegionEntry;
682 };
683 for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I)
684 if (isStartOfRegion(LineSegments[I]))
685 ++MinRegionCount;
686
687 bool StartOfSkippedRegion = !LineSegments.empty() &&
688 !LineSegments.front()->HasCount &&
689 LineSegments.front()->IsRegionEntry;
690
691 HasMultipleRegions = MinRegionCount > 1;
692 Mapped =
693 !StartOfSkippedRegion &&
694 ((WrappedSegment && WrappedSegment->HasCount) || (MinRegionCount > 0));
695
696 if (!Mapped)
697 return;
698
699 // Pick the max count from the non-gap, region entry segments. If there
700 // aren't any, use the wrapped count.
701 if (!MinRegionCount) {
702 ExecutionCount = WrappedSegment->Count;
703 return;
704 }
705 for (const auto *LS : LineSegments)
706 if (isStartOfRegion(LS))
707 ExecutionCount = std::max(ExecutionCount, LS->Count);
708 }
709
710 LineCoverageIterator &LineCoverageIterator::operator++() {
711 if (Next == CD.end()) {
712 Stats = LineCoverageStats();
713 Ended = true;
714 return *this;
715 }
716 if (Segments.size())
717 WrappedSegment = Segments.back();
718 Segments.clear();
719 while (Next != CD.end() && Next->Line == Line)
720 Segments.push_back(&*Next++);
721 Stats = LineCoverageStats(Segments, WrappedSegment, Line);
722 ++Line;
723 return *this;
724 }
725
673726 static std::string getCoverageMapErrString(coveragemap_error Err) {
674727 switch (Err) {
675728 case coveragemap_error::success:
1515
1616 using namespace llvm;
1717 using namespace coverage;
18
19 LineCoverageStats::LineCoverageStats(
20 ArrayRef LineSegments,
21 const coverage::CoverageSegment *WrappedSegment, unsigned Line)
22 : ExecutionCount(0), HasMultipleRegions(false), Mapped(false), Line(Line),
23 LineSegments(LineSegments), WrappedSegment(WrappedSegment) {
24 // Find the minimum number of regions which start in this line.
25 unsigned MinRegionCount = 0;
26 auto isStartOfRegion = [](const coverage::CoverageSegment *S) {
27 return !S->IsGapRegion && S->HasCount && S->IsRegionEntry;
28 };
29 for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I)
30 if (isStartOfRegion(LineSegments[I]))
31 ++MinRegionCount;
32
33 bool StartOfSkippedRegion = !LineSegments.empty() &&
34 !LineSegments.front()->HasCount &&
35 LineSegments.front()->IsRegionEntry;
36
37 HasMultipleRegions = MinRegionCount > 1;
38 Mapped =
39 !StartOfSkippedRegion &&
40 ((WrappedSegment && WrappedSegment->HasCount) || (MinRegionCount > 0));
41
42 if (!Mapped)
43 return;
44
45 // Pick the max count from the non-gap, region entry segments. If there
46 // aren't any, use the wrapped count.
47 if (!MinRegionCount) {
48 ExecutionCount = WrappedSegment->Count;
49 return;
50 }
51 for (const auto *LS : LineSegments)
52 if (isStartOfRegion(LS))
53 ExecutionCount = std::max(ExecutionCount, LS->Count);
54 }
55
56 LineCoverageIterator &LineCoverageIterator::operator++() {
57 if (Next == CD.end()) {
58 Stats = LineCoverageStats();
59 Ended = true;
60 return *this;
61 }
62 if (Segments.size())
63 WrappedSegment = Segments.back();
64 Segments.clear();
65 while (Next != CD.end() && Next->Line == Line)
66 Segments.push_back(&*Next++);
67 Stats = LineCoverageStats(Segments, WrappedSegment, Line);
68 ++Line;
69 return *this;
70 }
7118
7219 FunctionCoverageSummary
7320 FunctionCoverageSummary::get(const CoverageMapping &CM,
1414 #ifndef LLVM_COV_COVERAGESUMMARYINFO_H
1515 #define LLVM_COV_COVERAGESUMMARYINFO_H
1616
17 #include "llvm/ADT/iterator.h"
18 #include "llvm/ADT/iterator_range.h"
1917 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
2018 #include "llvm/Support/raw_ostream.h"
2119
136134 return double(Executed) / double(NumFunctions) * 100.0;
137135 }
138136 };
139
140 /// \brief Coverage statistics for a single line.
141 class LineCoverageStats {
142 uint64_t ExecutionCount;
143 bool HasMultipleRegions;
144 bool Mapped;
145 unsigned Line;
146 ArrayRef LineSegments;
147 const coverage::CoverageSegment *WrappedSegment;
148
149 friend class LineCoverageIterator;
150 LineCoverageStats() = default;
151
152 public:
153 LineCoverageStats(ArrayRef LineSegments,
154 const coverage::CoverageSegment *WrappedSegment,
155 unsigned Line);
156
157 uint64_t getExecutionCount() const { return ExecutionCount; }
158
159 bool hasMultipleRegions() const { return HasMultipleRegions; }
160
161 bool isMapped() const { return Mapped; }
162
163 unsigned getLine() const { return Line; }
164
165 ArrayRef getLineSegments() const {
166 return LineSegments;
167 }
168
169 const coverage::CoverageSegment *getWrappedSegment() const {
170 return WrappedSegment;
171 }
172 };
173
174 /// Iterates over LineCoverageStats for each line described by a CoverageData
175 /// object.
176 class LineCoverageIterator
177 : public iterator_facade_base<
178 LineCoverageIterator, std::forward_iterator_tag, LineCoverageStats> {
179 public:
180 LineCoverageIterator(const coverage::CoverageData &CD)
181 : LineCoverageIterator(CD, CD.begin()->Line) {}
182
183 LineCoverageIterator(const coverage::CoverageData &CD, unsigned Line)
184 : CD(CD), WrappedSegment(nullptr), Next(CD.begin()), Ended(false),
185 Line(Line), Segments(), Stats() {
186 this->operator++();
187 }
188
189 LineCoverageIterator &operator=(const LineCoverageIterator &R) = default;
190
191 bool operator==(const LineCoverageIterator &R) const {
192 return &CD == &R.CD && Next == R.Next && Ended == R.Ended;
193 }
194
195 const LineCoverageStats &operator*() const { return Stats; }
196
197 LineCoverageStats &operator*() { return Stats; }
198
199 LineCoverageIterator &operator++();
200
201 LineCoverageIterator getEnd() const {
202 auto EndIt = *this;
203 EndIt.Next = CD.end();
204 EndIt.Ended = true;
205 return EndIt;
206 }
207
208 private:
209 const coverage::CoverageData &CD;
210 const coverage::CoverageSegment *WrappedSegment;
211 std::vector::const_iterator Next;
212 bool Ended;
213 unsigned Line;
214 SmallVector Segments;
215 LineCoverageStats Stats;
216 };
217
218 /// Get a range of LineCoverageStats for each line described by a CoverageData
219 /// object.
220 static inline iterator_range
221 getLineCoverageStats(const coverage::CoverageData &CD) {
222 auto Begin = LineCoverageIterator(CD);
223 auto End = Begin.getEnd();
224 return make_range(Begin, End);
225 }
226137
227138 /// \brief A summary of function's code coverage.
228139 struct FunctionCoverageSummary {
634634 ASSERT_EQ(CoverageSegment(11, 11, false), Segments[6]);
635635 }
636636
637 TEST_P(CoverageMappingTest, test_line_coverage_iterator) {
638 ProfileWriter.addRecord({"func", 0x1234, {30, 20, 10, 0}}, Err);
639
640 startFunction("func", 0x1234);
641 addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
642 addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);
643 addCMR(Counter::getCounter(2), "file1", 5, 8, 9, 1);
644 addCMR(Counter::getCounter(3), "file1", 10, 10, 11, 11);
645 EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
646
647 CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
648
649 unsigned NumLineStats = 0;
650 for (const auto &LCS : getLineCoverageStats(Data)) {
651 ++NumLineStats;
652 (void)LCS;
653 }
654 ASSERT_EQ(11U, NumLineStats);
655
656 LineCoverageIterator LCI{Data};
657
658 ASSERT_EQ(1U, LCI->getLine());
659 ASSERT_EQ(20ULL, LCI->getExecutionCount());
660 ++LCI;
661 ASSERT_EQ(2U, LCI->getLine());
662 ASSERT_EQ(20ULL, LCI->getExecutionCount());
663 ++LCI;
664 ASSERT_EQ(3U, LCI->getLine());
665 ASSERT_EQ(20ULL, LCI->getExecutionCount());
666 ++LCI;
667 ASSERT_EQ(4U, LCI->getLine());
668 ASSERT_EQ(20ULL, LCI->getExecutionCount());
669 ++LCI;
670 ASSERT_EQ(5U, LCI->getLine());
671 ASSERT_EQ(10ULL, LCI->getExecutionCount());
672 ++LCI;
673 ASSERT_EQ(6U, LCI->getLine());
674 ASSERT_EQ(10ULL, LCI->getExecutionCount());
675 ++LCI;
676 ASSERT_EQ(7U, LCI->getLine());
677 ASSERT_EQ(10ULL, LCI->getExecutionCount());
678 ++LCI;
679 ASSERT_EQ(8U, LCI->getLine());
680 ASSERT_EQ(10ULL, LCI->getExecutionCount());
681 ++LCI;
682 ASSERT_EQ(9U, LCI->getLine());
683 ASSERT_EQ(10ULL, LCI->getExecutionCount());
684 ++LCI;
685 ASSERT_EQ(10U, LCI->getLine());
686 ASSERT_EQ(0ULL, LCI->getExecutionCount());
687 ++LCI;
688 ASSERT_EQ(11U, LCI->getLine());
689 ASSERT_EQ(0ULL, LCI->getExecutionCount());
690 ++LCI;
691 ASSERT_EQ(LCI, LCI.getEnd());
692 }
693
637694 TEST_P(CoverageMappingTest, uncovered_function) {
638695 startFunction("func", 0x1234);
639696 addCMR(Counter::getZero(), "file1", 1, 2, 3, 4);