//===----------------------------------------------------------------------===//
#include "llvm/ProfileData/CoverageMapping.h"
-
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ProfileData/CoverageMappingReader.h"
#include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
using namespace llvm;
using namespace coverage;
-CounterExpressionBuilder::CounterExpressionBuilder(unsigned NumCounterValues) {
- Terms.resize(NumCounterValues);
-}
+#define DEBUG_TYPE "coverage-mapping"
Counter CounterExpressionBuilder::get(const CounterExpression &E) {
- for (unsigned I = 0, S = Expressions.size(); I < S; ++I) {
- if (Expressions[I] == E)
- return Counter::getExpression(I);
- }
+ auto It = ExpressionIndices.find(E);
+ if (It != ExpressionIndices.end())
+ return Counter::getExpression(It->second);
+ unsigned I = Expressions.size();
Expressions.push_back(E);
- return Counter::getExpression(Expressions.size() - 1);
+ ExpressionIndices[E] = I;
+ return Counter::getExpression(I);
}
-void CounterExpressionBuilder::extractTerms(Counter C, int Sign) {
+void CounterExpressionBuilder::extractTerms(
+ Counter C, int Sign, SmallVectorImpl<std::pair<unsigned, int>> &Terms) {
switch (C.getKind()) {
case Counter::Zero:
break;
case Counter::CounterValueReference:
- Terms[C.getCounterID()] += Sign;
+ Terms.push_back(std::make_pair(C.getCounterID(), Sign));
break;
case Counter::Expression:
const auto &E = Expressions[C.getExpressionID()];
- extractTerms(E.LHS, Sign);
- extractTerms(E.RHS, E.Kind == CounterExpression::Subtract ? -Sign : Sign);
+ extractTerms(E.LHS, Sign, Terms);
+ extractTerms(E.RHS, E.Kind == CounterExpression::Subtract ? -Sign : Sign,
+ Terms);
break;
}
}
Counter CounterExpressionBuilder::simplify(Counter ExpressionTree) {
// Gather constant terms.
- for (auto &I : Terms)
- I = 0;
- extractTerms(ExpressionTree);
+ llvm::SmallVector<std::pair<unsigned, int>, 32> Terms;
+ extractTerms(ExpressionTree, +1, Terms);
+
+ // If there are no terms, this is just a zero. The algorithm below assumes at
+ // least one term.
+ if (Terms.size() == 0)
+ return Counter::getZero();
+
+ // Group the terms by counter ID.
+ std::sort(Terms.begin(), Terms.end(),
+ [](const std::pair<unsigned, int> &LHS,
+ const std::pair<unsigned, int> &RHS) {
+ return LHS.first < RHS.first;
+ });
+
+ // Combine terms by counter ID to eliminate counters that sum to zero.
+ auto Prev = Terms.begin();
+ for (auto I = Prev + 1, E = Terms.end(); I != E; ++I) {
+ if (I->first == Prev->first) {
+ Prev->second += I->second;
+ continue;
+ }
+ ++Prev;
+ *Prev = *I;
+ }
+ Terms.erase(++Prev, Terms.end());
Counter C;
- // Create additions.
- // Note: the additions are created first
- // to avoid creation of a tree like ((0 - X) + Y) instead of (Y - X).
- for (unsigned I = 0, S = Terms.size(); I < S; ++I) {
- if (Terms[I] <= 0)
+ // Create additions. We do this before subtractions to avoid constructs like
+ // ((0 - X) + Y), as opposed to (Y - X).
+ for (auto Term : Terms) {
+ if (Term.second <= 0)
continue;
- for (int J = 0; J < Terms[I]; ++J) {
+ for (int I = 0; I < Term.second; ++I)
if (C.isZero())
- C = Counter::getCounter(I);
+ C = Counter::getCounter(Term.first);
else
C = get(CounterExpression(CounterExpression::Add, C,
- Counter::getCounter(I)));
- }
+ Counter::getCounter(Term.first)));
}
// Create subtractions.
- for (unsigned I = 0, S = Terms.size(); I < S; ++I) {
- if (Terms[I] >= 0)
+ for (auto Term : Terms) {
+ if (Term.second >= 0)
continue;
- for (int J = 0; J < (-Terms[I]); ++J)
+ for (int I = 0; I < -Term.second; ++I)
C = get(CounterExpression(CounterExpression::Subtract, C,
- Counter::getCounter(I)));
+ Counter::getCounter(Term.first)));
}
return C;
}
llvm_unreachable("Unhandled CounterKind");
}
+void FunctionRecordIterator::skipOtherFiles() {
+ while (Current != Records.end() && !Filename.empty() &&
+ Filename != Current->Filenames[0])
+ ++Current;
+ if (Current == Records.end())
+ *this = FunctionRecordIterator();
+}
+
+/// Get the function name from the record, removing the filename prefix if
+/// necessary.
+static StringRef getFuncNameWithoutPrefix(const CoverageMappingRecord &Record) {
+ StringRef FunctionName = Record.FunctionName;
+ if (Record.Filenames.empty())
+ return FunctionName;
+ StringRef Filename = sys::path::filename(Record.Filenames[0]);
+ if (FunctionName.startswith(Filename))
+ FunctionName = FunctionName.drop_front(Filename.size() + 1);
+ return FunctionName;
+}
+
ErrorOr<std::unique_ptr<CoverageMapping>>
-CoverageMapping::load(ObjectFileCoverageMappingReader &CoverageReader,
+CoverageMapping::load(CoverageMappingReader &CoverageReader,
IndexedInstrProfReader &ProfileReader) {
auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());
std::vector<uint64_t> Counts;
for (const auto &Record : CoverageReader) {
+ CounterMappingContext Ctx(Record.Expressions);
+
Counts.clear();
if (std::error_code EC = ProfileReader.getFunctionCounts(
Record.FunctionName, Record.FunctionHash, Counts)) {
- if (EC != instrprof_error::hash_mismatch &&
- EC != instrprof_error::unknown_function)
+ if (EC == instrprof_error::hash_mismatch) {
+ Coverage->MismatchedFunctionCount++;
+ continue;
+ } else if (EC != instrprof_error::unknown_function)
return EC;
- Coverage->MismatchedFunctionCount++;
- continue;
- }
+ } else
+ Ctx.setCounts(Counts);
+
+ assert(!Record.MappingRegions.empty() && "Function has no regions");
- FunctionRecord Function(Record.FunctionName, Record.Filenames);
- CounterMappingContext Ctx(Record.Expressions, Counts);
+ FunctionRecord Function(getFuncNameWithoutPrefix(Record), Record.Filenames);
for (const auto &Region : Record.MappingRegions) {
ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
if (!ExecutionCount)
break;
- Function.CountedRegions.push_back(CountedRegion(Region, *ExecutionCount));
+ Function.pushRegion(Region, *ExecutionCount);
}
if (Function.CountedRegions.size() != Record.MappingRegions.size()) {
Coverage->MismatchedFunctionCount++;
continue;
}
- Coverage->Functions.push_back(Function);
+ Coverage->Functions.push_back(std::move(Function));
}
return std::move(Coverage);
}
ErrorOr<std::unique_ptr<CoverageMapping>>
-CoverageMapping::load(StringRef ObjectFilename, StringRef ProfileFilename) {
+CoverageMapping::load(StringRef ObjectFilename, StringRef ProfileFilename,
+ Triple::ArchType Arch) {
auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
- if (auto EC = CounterMappingBuff.getError())
+ if (std::error_code EC = CounterMappingBuff.getError())
return EC;
- ObjectFileCoverageMappingReader CoverageReader(CounterMappingBuff.get());
- if (auto EC = CoverageReader.readHeader())
+ auto CoverageReaderOrErr =
+ BinaryCoverageReader::create(CounterMappingBuff.get(), Arch);
+ if (std::error_code EC = CoverageReaderOrErr.getError())
return EC;
- std::unique_ptr<IndexedInstrProfReader> ProfileReader;
- if (auto EC = IndexedInstrProfReader::create(ProfileFilename, ProfileReader))
+ auto CoverageReader = std::move(CoverageReaderOrErr.get());
+ auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename);
+ if (auto EC = ProfileReaderOrErr.getError())
return EC;
- return load(CoverageReader, *ProfileReader);
+ auto ProfileReader = std::move(ProfileReaderOrErr.get());
+ return load(*CoverageReader, *ProfileReader);
}
namespace {
/// 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);
}
Segments.emplace_back(Line, Col, IsRegionEntry);
S = Segments.back();
}
+ DEBUG(dbgs() << "Segment at " << Line << ":" << Col);
// Set this region's count.
- if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion)
+ if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion) {
+ DEBUG(dbgs() << " with count " << Region.ExecutionCount);
Segments.back().setCount(Region.ExecutionCount);
+ }
+ DEBUG(dbgs() << "\n");
}
/// Start a segment for the given region.
public:
/// Build a list of CoverageSegments from a sorted list of Regions.
std::vector<CoverageSegment> buildSegments(ArrayRef<CountedRegion> Regions) {
+ const CountedRegion *PrevRegion = nullptr;
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);
+ if (PrevRegion && PrevRegion->startLoc() == Region.startLoc() &&
+ PrevRegion->endLoc() == Region.endLoc()) {
+ if (Region.Kind == coverage::CounterMappingRegion::CodeRegion)
+ Segments.back().addCount(Region.ExecutionCount);
+ } else {
+ // Add this region to the stack.
+ ActiveRegions.push_back(&Region);
+ startSegment(Region);
+ }
+ PrevRegion = &Region;
}
// Pop any regions that are left in the stack.
while (!ActiveRegions.empty())
};
}
-std::vector<StringRef> CoverageMapping::getUniqueSourceFiles() {
+std::vector<StringRef> CoverageMapping::getUniqueSourceFiles() const {
std::vector<StringRef> Filenames;
for (const auto &Function : getCoveredFunctions())
- for (const auto &Filename : Function.Filenames)
- Filenames.push_back(Filename);
+ Filenames.insert(Filenames.end(), Function.Filenames.begin(),
+ Function.Filenames.end());
std::sort(Filenames.begin(), Filenames.end());
auto Last = std::unique(Filenames.begin(), Filenames.end());
Filenames.erase(Last, Filenames.end());
return Filenames;
}
-static Optional<unsigned> findMainViewFileID(StringRef SourceFile,
- const FunctionRecord &Function) {
- llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
- llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(),
- false);
+static SmallBitVector gatherFileIDs(StringRef SourceFile,
+ const FunctionRecord &Function) {
+ SmallBitVector FilenameEquivalence(Function.Filenames.size(), false);
for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I)
if (SourceFile == Function.Filenames[I])
FilenameEquivalence[I] = true;
+ return FilenameEquivalence;
+}
+
+static Optional<unsigned> findMainViewFileID(StringRef SourceFile,
+ const FunctionRecord &Function) {
+ SmallBitVector IsNotExpandedFile(Function.Filenames.size(), true);
+ SmallBitVector FilenameEquivalence = gatherFileIDs(SourceFile, Function);
for (const auto &CR : Function.CountedRegions)
if (CR.Kind == CounterMappingRegion::ExpansionRegion &&
FilenameEquivalence[CR.FileID])
- IsExpandedFile[CR.ExpandedFileID] = true;
- for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I)
- if (FilenameEquivalence[I] && !IsExpandedFile[I])
- return I;
- return None;
+ IsNotExpandedFile[CR.ExpandedFileID] = false;
+ IsNotExpandedFile &= FilenameEquivalence;
+ int I = IsNotExpandedFile.find_first();
+ if (I == -1)
+ return None;
+ return I;
}
static Optional<unsigned> findMainViewFileID(const FunctionRecord &Function) {
- llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
+ SmallBitVector IsNotExpandedFile(Function.Filenames.size(), true);
for (const auto &CR : Function.CountedRegions)
if (CR.Kind == CounterMappingRegion::ExpansionRegion)
- IsExpandedFile[CR.ExpandedFileID] = true;
- for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I)
- if (!IsExpandedFile[I])
- return I;
- return None;
-}
-
-static SmallSet<unsigned, 8> gatherFileIDs(StringRef SourceFile,
- const FunctionRecord &Function) {
- SmallSet<unsigned, 8> IDs;
- for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I)
- if (SourceFile == Function.Filenames[I])
- IDs.insert(I);
- return IDs;
+ IsNotExpandedFile[CR.ExpandedFileID] = false;
+ int I = IsNotExpandedFile.find_first();
+ if (I == -1)
+ return None;
+ return I;
}
/// Sort a nested sequence of regions from a single file.
continue;
auto FileIDs = gatherFileIDs(Filename, Function);
for (const auto &CR : Function.CountedRegions)
- if (FileIDs.count(CR.FileID)) {
+ if (FileIDs.test(CR.FileID)) {
Regions.push_back(CR);
if (isExpansion(CR, *MainFileID))
FileCoverage.Expansions.emplace_back(CR, Function);
}
sortNestedRegions(Regions.begin(), Regions.end());
+ DEBUG(dbgs() << "Emitting segments for file: " << Filename << "\n");
FileCoverage.Segments = SegmentBuilder().buildSegments(Regions);
return FileCoverage;
for (const auto &InstantiationSet : InstantiationSetCollector) {
if (InstantiationSet.second.size() < 2)
continue;
- for (auto Function : InstantiationSet.second)
- Result.push_back(Function);
+ Result.insert(Result.end(), InstantiationSet.second.begin(),
+ InstantiationSet.second.end());
}
return Result;
}
}
sortNestedRegions(Regions.begin(), Regions.end());
+ DEBUG(dbgs() << "Emitting segments for function: " << Function.Name << "\n");
FunctionCoverage.Segments = SegmentBuilder().buildSegments(Regions);
return FunctionCoverage;
}
sortNestedRegions(Regions.begin(), Regions.end());
+ DEBUG(dbgs() << "Emitting segments for expansion of file " << Expansion.FileID
+ << "\n");
ExpansionCoverage.Segments = SegmentBuilder().buildSegments(Regions);
return ExpansionCoverage;
}
+
+namespace {
+class CoverageMappingErrorCategoryType : public std::error_category {
+ const char *name() const LLVM_NOEXCEPT override { return "llvm.coveragemap"; }
+ std::string message(int IE) const override {
+ auto E = static_cast<coveragemap_error>(IE);
+ switch (E) {
+ case coveragemap_error::success:
+ return "Success";
+ case coveragemap_error::eof:
+ return "End of File";
+ case coveragemap_error::no_data_found:
+ return "No coverage data found";
+ case coveragemap_error::unsupported_version:
+ return "Unsupported coverage format version";
+ case coveragemap_error::truncated:
+ return "Truncated coverage data";
+ case coveragemap_error::malformed:
+ return "Malformed coverage data";
+ }
+ llvm_unreachable("A value of coveragemap_error has no message.");
+ }
+};
+}
+
+static ManagedStatic<CoverageMappingErrorCategoryType> ErrorCategory;
+
+const std::error_category &llvm::coveragemap_category() {
+ return *ErrorCategory;
+}