]> granicus.if.org Git - llvm/commitdiff
[llvm-cov] Move LineCoverageIterator to libCoverage. NFC.
authorVedant Kumar <vsk@apple.com>
Wed, 18 Oct 2017 23:58:28 +0000 (23:58 +0000)
committerVedant Kumar <vsk@apple.com>
Wed, 18 Oct 2017 23:58:28 +0000 (23:58 +0000)
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

include/llvm/ProfileData/Coverage/CoverageMapping.h
lib/ProfileData/Coverage/CoverageMapping.cpp
tools/llvm-cov/CoverageSummaryInfo.cpp
tools/llvm-cov/CoverageSummaryInfo.h
unittests/ProfileData/CoverageMappingTest.cpp

index 22286f6d4688222cd0cb2eb626d2e31a459aa9b9..5a4098cf666c44cd5847b6f2354ff7c2af8c641c 100644 (file)
@@ -594,6 +594,89 @@ public:
   getInstantiationGroups(StringRef Filename) const;
 };
 
+/// Coverage statistics for a single line.
+class LineCoverageStats {
+  uint64_t ExecutionCount;
+  bool HasMultipleRegions;
+  bool Mapped;
+  unsigned Line;
+  ArrayRef<const CoverageSegment *> LineSegments;
+  const CoverageSegment *WrappedSegment;
+
+  friend class LineCoverageIterator;
+  LineCoverageStats() = default;
+
+public:
+  LineCoverageStats(ArrayRef<const CoverageSegment *> LineSegments,
+                    const CoverageSegment *WrappedSegment, unsigned Line);
+
+  uint64_t getExecutionCount() const { return ExecutionCount; }
+
+  bool hasMultipleRegions() const { return HasMultipleRegions; }
+
+  bool isMapped() const { return Mapped; }
+
+  unsigned getLine() const { return Line; }
+
+  ArrayRef<const CoverageSegment *> getLineSegments() const {
+    return LineSegments;
+  }
+
+  const CoverageSegment *getWrappedSegment() const { return WrappedSegment; }
+};
+
+/// An iterator over the \c LineCoverageStats objects for lines described by
+/// a \c CoverageData instance.
+class LineCoverageIterator
+    : public iterator_facade_base<
+          LineCoverageIterator, std::forward_iterator_tag, LineCoverageStats> {
+public:
+  LineCoverageIterator(const CoverageData &CD)
+      : LineCoverageIterator(CD, CD.begin()->Line) {}
+
+  LineCoverageIterator(const CoverageData &CD, unsigned Line)
+      : CD(CD), WrappedSegment(nullptr), Next(CD.begin()), Ended(false),
+        Line(Line), Segments(), Stats() {
+    this->operator++();
+  }
+
+  LineCoverageIterator &operator=(const LineCoverageIterator &R) = default;
+
+  bool operator==(const LineCoverageIterator &R) const {
+    return &CD == &R.CD && Next == R.Next && Ended == R.Ended;
+  }
+
+  const LineCoverageStats &operator*() const { return Stats; }
+
+  LineCoverageStats &operator*() { return Stats; }
+
+  LineCoverageIterator &operator++();
+
+  LineCoverageIterator getEnd() const {
+    auto EndIt = *this;
+    EndIt.Next = CD.end();
+    EndIt.Ended = true;
+    return EndIt;
+  }
+
+private:
+  const CoverageData &CD;
+  const CoverageSegment *WrappedSegment;
+  std::vector<CoverageSegment>::const_iterator Next;
+  bool Ended;
+  unsigned Line;
+  SmallVector<const CoverageSegment *, 4> Segments;
+  LineCoverageStats Stats;
+};
+
+/// Get a \c LineCoverageIterator range for the lines described by \p CD.
+static inline iterator_range<LineCoverageIterator>
+getLineCoverageStats(const coverage::CoverageData &CD) {
+  auto Begin = LineCoverageIterator(CD);
+  auto End = Begin.getEnd();
+  return make_range(Begin, End);
+}
+
 // Profile coverage map has the following layout:
 // [CoverageMapFileHeader]
 // [ArrayStart]
index 52f9447aa3e7b38cea9e4ae7029a7886336e8f23..bda61768e7b1a14fe00ebb654964510157e4c87b 100644 (file)
@@ -671,6 +671,59 @@ CoverageData CoverageMapping::getCoverageForExpansion(
   return ExpansionCoverage;
 }
 
+LineCoverageStats::LineCoverageStats(
+    ArrayRef<const coverage::CoverageSegment *> LineSegments,
+    const coverage::CoverageSegment *WrappedSegment, unsigned Line)
+    : ExecutionCount(0), HasMultipleRegions(false), Mapped(false), Line(Line),
+      LineSegments(LineSegments), WrappedSegment(WrappedSegment) {
+  // Find the minimum number of regions which start in this line.
+  unsigned MinRegionCount = 0;
+  auto isStartOfRegion = [](const coverage::CoverageSegment *S) {
+    return !S->IsGapRegion && S->HasCount && S->IsRegionEntry;
+  };
+  for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I)
+    if (isStartOfRegion(LineSegments[I]))
+      ++MinRegionCount;
+
+  bool StartOfSkippedRegion = !LineSegments.empty() &&
+                              !LineSegments.front()->HasCount &&
+                              LineSegments.front()->IsRegionEntry;
+
+  HasMultipleRegions = MinRegionCount > 1;
+  Mapped =
+      !StartOfSkippedRegion &&
+      ((WrappedSegment && WrappedSegment->HasCount) || (MinRegionCount > 0));
+
+  if (!Mapped)
+    return;
+
+  // Pick the max count from the non-gap, region entry segments. If there
+  // aren't any, use the wrapped count.
+  if (!MinRegionCount) {
+    ExecutionCount = WrappedSegment->Count;
+    return;
+  }
+  for (const auto *LS : LineSegments)
+    if (isStartOfRegion(LS))
+      ExecutionCount = std::max(ExecutionCount, LS->Count);
+}
+
+LineCoverageIterator &LineCoverageIterator::operator++() {
+  if (Next == CD.end()) {
+    Stats = LineCoverageStats();
+    Ended = true;
+    return *this;
+  }
+  if (Segments.size())
+    WrappedSegment = Segments.back();
+  Segments.clear();
+  while (Next != CD.end() && Next->Line == Line)
+    Segments.push_back(&*Next++);
+  Stats = LineCoverageStats(Segments, WrappedSegment, Line);
+  ++Line;
+  return *this;
+}
+
 static std::string getCoverageMapErrString(coveragemap_error Err) {
   switch (Err) {
   case coveragemap_error::success:
index d1fcef4299d3eadb05cda4eff455e60e21426839..7847a2abf48c73ccded1307ce592521892dd0645 100644 (file)
 using namespace llvm;
 using namespace coverage;
 
-LineCoverageStats::LineCoverageStats(
-    ArrayRef<const coverage::CoverageSegment *> LineSegments,
-    const coverage::CoverageSegment *WrappedSegment, unsigned Line)
-    : ExecutionCount(0), HasMultipleRegions(false), Mapped(false), Line(Line),
-      LineSegments(LineSegments), WrappedSegment(WrappedSegment) {
-  // Find the minimum number of regions which start in this line.
-  unsigned MinRegionCount = 0;
-  auto isStartOfRegion = [](const coverage::CoverageSegment *S) {
-    return !S->IsGapRegion && S->HasCount && S->IsRegionEntry;
-  };
-  for (unsigned I = 0; I < LineSegments.size() && MinRegionCount < 2; ++I)
-    if (isStartOfRegion(LineSegments[I]))
-      ++MinRegionCount;
-
-  bool StartOfSkippedRegion = !LineSegments.empty() &&
-                              !LineSegments.front()->HasCount &&
-                              LineSegments.front()->IsRegionEntry;
-
-  HasMultipleRegions = MinRegionCount > 1;
-  Mapped =
-      !StartOfSkippedRegion &&
-      ((WrappedSegment && WrappedSegment->HasCount) || (MinRegionCount > 0));
-
-  if (!Mapped)
-    return;
-
-  // Pick the max count from the non-gap, region entry segments. If there
-  // aren't any, use the wrapped count.
-  if (!MinRegionCount) {
-    ExecutionCount = WrappedSegment->Count;
-    return;
-  }
-  for (const auto *LS : LineSegments)
-    if (isStartOfRegion(LS))
-      ExecutionCount = std::max(ExecutionCount, LS->Count);
-}
-
-LineCoverageIterator &LineCoverageIterator::operator++() {
-  if (Next == CD.end()) {
-    Stats = LineCoverageStats();
-    Ended = true;
-    return *this;
-  }
-  if (Segments.size())
-    WrappedSegment = Segments.back();
-  Segments.clear();
-  while (Next != CD.end() && Next->Line == Line)
-    Segments.push_back(&*Next++);
-  Stats = LineCoverageStats(Segments, WrappedSegment, Line);
-  ++Line;
-  return *this;
-}
-
 FunctionCoverageSummary
 FunctionCoverageSummary::get(const CoverageMapping &CM,
                              const coverage::FunctionRecord &Function) {
index d3f43d19104ab1492fc68532cc364c93767de795..8eae0b7fec97d55dca24a12b06a39d6248ade924 100644 (file)
@@ -15,8 +15,6 @@
 #ifndef LLVM_COV_COVERAGESUMMARYINFO_H
 #define LLVM_COV_COVERAGESUMMARYINFO_H
 
-#include "llvm/ADT/iterator.h"
-#include "llvm/ADT/iterator_range.h"
 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
 #include "llvm/Support/raw_ostream.h"
 
@@ -138,93 +136,6 @@ public:
   }
 };
 
-/// \brief Coverage statistics for a single line.
-class LineCoverageStats {
-  uint64_t ExecutionCount;
-  bool HasMultipleRegions;
-  bool Mapped;
-  unsigned Line;
-  ArrayRef<const coverage::CoverageSegment *> LineSegments;
-  const coverage::CoverageSegment *WrappedSegment;
-
-  friend class LineCoverageIterator;
-  LineCoverageStats() = default;
-
-public:
-  LineCoverageStats(ArrayRef<const coverage::CoverageSegment *> LineSegments,
-                    const coverage::CoverageSegment *WrappedSegment,
-                    unsigned Line);
-
-  uint64_t getExecutionCount() const { return ExecutionCount; }
-
-  bool hasMultipleRegions() const { return HasMultipleRegions; }
-
-  bool isMapped() const { return Mapped; }
-
-  unsigned getLine() const { return Line; }
-
-  ArrayRef<const coverage::CoverageSegment *> getLineSegments() const {
-    return LineSegments;
-  }
-
-  const coverage::CoverageSegment *getWrappedSegment() const {
-    return WrappedSegment;
-  }
-};
-
-/// Iterates over LineCoverageStats for each line described by a CoverageData
-/// object.
-class LineCoverageIterator
-    : public iterator_facade_base<
-          LineCoverageIterator, std::forward_iterator_tag, LineCoverageStats> {
-public:
-  LineCoverageIterator(const coverage::CoverageData &CD)
-      : LineCoverageIterator(CD, CD.begin()->Line) {}
-
-  LineCoverageIterator(const coverage::CoverageData &CD, unsigned Line)
-      : CD(CD), WrappedSegment(nullptr), Next(CD.begin()), Ended(false),
-        Line(Line), Segments(), Stats() {
-    this->operator++();
-  }
-
-  LineCoverageIterator &operator=(const LineCoverageIterator &R) = default;
-
-  bool operator==(const LineCoverageIterator &R) const {
-    return &CD == &R.CD && Next == R.Next && Ended == R.Ended;
-  }
-
-  const LineCoverageStats &operator*() const { return Stats; }
-
-  LineCoverageStats &operator*() { return Stats; }
-
-  LineCoverageIterator &operator++();
-
-  LineCoverageIterator getEnd() const {
-    auto EndIt = *this;
-    EndIt.Next = CD.end();
-    EndIt.Ended = true;
-    return EndIt;
-  }
-
-private:
-  const coverage::CoverageData &CD;
-  const coverage::CoverageSegment *WrappedSegment;
-  std::vector<coverage::CoverageSegment>::const_iterator Next;
-  bool Ended;
-  unsigned Line;
-  SmallVector<const coverage::CoverageSegment *, 4> Segments;
-  LineCoverageStats Stats;
-};
-
-/// Get a range of LineCoverageStats for each line described by a CoverageData
-/// object.
-static inline iterator_range<LineCoverageIterator>
-getLineCoverageStats(const coverage::CoverageData &CD) {
-  auto Begin = LineCoverageIterator(CD);
-  auto End = Begin.getEnd();
-  return make_range(Begin, End);
-}
-
 /// \brief A summary of function's code coverage.
 struct FunctionCoverageSummary {
   std::string Name;
index 8c9c8c482349d0f0c9d040d5de9d9e0c993d7b31..ad0a0cfb87377300ef6bb6b206397a569729fc5d 100644 (file)
@@ -635,6 +635,63 @@ TEST_P(CoverageMappingTest, basic_coverage_iteration) {
   ASSERT_EQ(CoverageSegment(11, 11, false),   Segments[6]);
 }
 
+TEST_P(CoverageMappingTest, test_line_coverage_iterator) {
+  ProfileWriter.addRecord({"func", 0x1234, {30, 20, 10, 0}}, Err);
+
+  startFunction("func", 0x1234);
+  addCMR(Counter::getCounter(0), "file1", 1, 1, 9, 9);
+  addCMR(Counter::getCounter(1), "file1", 1, 1, 4, 7);
+  addCMR(Counter::getCounter(2), "file1", 5, 8, 9, 1);
+  addCMR(Counter::getCounter(3), "file1", 10, 10, 11, 11);
+  EXPECT_THAT_ERROR(loadCoverageMapping(), Succeeded());
+
+  CoverageData Data = LoadedCoverage->getCoverageForFile("file1");
+
+  unsigned NumLineStats = 0;
+  for (const auto &LCS : getLineCoverageStats(Data)) {
+    ++NumLineStats;
+    (void)LCS;
+  }
+  ASSERT_EQ(11U, NumLineStats);
+
+  LineCoverageIterator LCI{Data};
+
+  ASSERT_EQ(1U, LCI->getLine());
+  ASSERT_EQ(20ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(2U, LCI->getLine());
+  ASSERT_EQ(20ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(3U, LCI->getLine());
+  ASSERT_EQ(20ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(4U, LCI->getLine());
+  ASSERT_EQ(20ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(5U, LCI->getLine());
+  ASSERT_EQ(10ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(6U, LCI->getLine());
+  ASSERT_EQ(10ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(7U, LCI->getLine());
+  ASSERT_EQ(10ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(8U, LCI->getLine());
+  ASSERT_EQ(10ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(9U, LCI->getLine());
+  ASSERT_EQ(10ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(10U, LCI->getLine());
+  ASSERT_EQ(0ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(11U, LCI->getLine());
+  ASSERT_EQ(0ULL, LCI->getExecutionCount());
+  ++LCI;
+  ASSERT_EQ(LCI, LCI.getEnd());
+}
+
 TEST_P(CoverageMappingTest, uncovered_function) {
   startFunction("func", 0x1234);
   addCMR(Counter::getZero(), "file1", 1, 2, 3, 4);