llvm-cov: Disentangle the coverage data logic from the display (NFC)

This splits the logic for actually looking up coverage information
from the logic that displays it. These were tangled rather thoroughly
so this change is a bit large, but it mostly consists of moving things
around. The coverage lookup logic itself now lives in the library,
rather than being spread between the library and the tool.

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@218184 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Justin Bogner 2014-09-20 15:31:56 +00:00
parent 73edcffc8f
commit 9eb38163a5
14 changed files with 542 additions and 479 deletions

View File

@ -21,8 +21,12 @@
#include <system_error> #include <system_error>
namespace llvm { namespace llvm {
class IndexedInstrProfReader;
namespace coverage { namespace coverage {
class ObjectFileCoverageMappingReader;
class CoverageMapping;
struct CounterExpressions; struct CounterExpressions;
enum CoverageMappingVersion { CoverageMappingVersion1 }; enum CoverageMappingVersion { CoverageMappingVersion1 };
@ -217,7 +221,7 @@ public:
}; };
/// \brief Code coverage information for a single function. /// \brief Code coverage information for a single function.
struct FunctionCoverageMapping { struct FunctionRecord {
/// \brief Raw function name. /// \brief Raw function name.
std::string Name; std::string Name;
/// \brief Associated files. /// \brief Associated files.
@ -225,10 +229,134 @@ struct FunctionCoverageMapping {
/// \brief Regions in the function along with their counts. /// \brief Regions in the function along with their counts.
std::vector<CountedRegion> CountedRegions; std::vector<CountedRegion> CountedRegions;
FunctionCoverageMapping(StringRef Name, ArrayRef<StringRef> Filenames) FunctionRecord(StringRef Name, ArrayRef<StringRef> Filenames)
: Name(Name), Filenames(Filenames.begin(), Filenames.end()) {} : Name(Name), Filenames(Filenames.begin(), Filenames.end()) {}
}; };
/// \brief Coverage information for a macro expansion or #included file.
///
/// When covered code has pieces that can be expanded for more detail, such as a
/// preprocessor macro use and its definition, these are represented as
/// expansions whose coverage can be looked up independently.
struct ExpansionRecord {
/// \brief The abstract file this expansion covers.
unsigned FileID;
/// \brief The region that expands to this record.
const CountedRegion &Region;
/// \brief Coverage for the expansion.
const FunctionRecord &Function;
ExpansionRecord(const CountedRegion &Region,
const FunctionRecord &Function)
: FileID(Region.ExpandedFileID), Region(Region), Function(Function) {}
};
/// \brief The execution count information starting at a point in a file.
///
/// A sequence of CoverageSegments gives execution counts for a file in format
/// that's simple to iterate through for processing.
struct CoverageSegment {
/// \brief The line where this segment begins.
unsigned Line;
/// \brief The column where this segment begins.
unsigned Col;
/// \brief The execution count, or zero if no count was recorded.
uint64_t Count;
/// \brief When false, the segment was uninstrumented or skipped.
bool HasCount;
/// \brief Whether this enters a new region or returns to a previous count.
bool IsRegionEntry;
CoverageSegment(unsigned Line, unsigned Col, bool IsRegionEntry)
: Line(Line), Col(Col), Count(0), HasCount(false),
IsRegionEntry(IsRegionEntry) {}
void setCount(uint64_t NewCount) {
Count = NewCount;
HasCount = true;
}
};
/// \brief Coverage information to be processed or displayed.
///
/// This represents the coverage of an entire file, expansion, or function. It
/// provides a sequence of CoverageSegments to iterate through, as well as the
/// list of expansions that can be further processed.
class CoverageData {
std::string Filename;
std::vector<CoverageSegment> Segments;
std::vector<ExpansionRecord> Expansions;
friend class CoverageMapping;
public:
CoverageData() {}
CoverageData(StringRef Filename) : Filename(Filename) {}
CoverageData(CoverageData &&RHS)
: Filename(std::move(RHS.Filename)), Segments(std::move(RHS.Segments)),
Expansions(std::move(RHS.Expansions)) {}
/// \brief Get the name of the file this data covers.
StringRef getFilename() { return Filename; }
std::vector<CoverageSegment>::iterator begin() { return Segments.begin(); }
std::vector<CoverageSegment>::iterator end() { return Segments.end(); }
bool empty() { return Segments.empty(); }
/// \brief Expansions that can be further processed.
std::vector<ExpansionRecord> getExpansions() { return Expansions; }
};
/// \brief The mapping of profile information to coverage data.
///
/// This is the main interface to get coverage information, using a profile to
/// fill out execution counts.
class CoverageMapping {
std::vector<FunctionRecord> Functions;
unsigned MismatchedFunctionCount;
CoverageMapping() : MismatchedFunctionCount(0) {}
public:
/// Load the coverage mapping using the given readers.
static ErrorOr<std::unique_ptr<CoverageMapping>>
load(ObjectFileCoverageMappingReader &CoverageReader,
IndexedInstrProfReader &ProfileReader);
/// \brief The number of functions that couldn't have their profiles mapped.
///
/// This is a count of functions whose profile is out of date or otherwise
/// can't be associated with any coverage information.
unsigned getMismatchedCount() { return MismatchedFunctionCount; }
/// \brief Returns the list of files that are covered.
std::vector<StringRef> getUniqueSourceFiles();
/// \brief Get the coverage for a particular file.
///
/// The given filename must be the name as recorded in the coverage
/// information. That is, only names returned from getUniqueSourceFiles will
/// yield a result.
CoverageData getCoverageForFile(StringRef Filename);
/// \brief Gets all of the functions covered by this profile.
ArrayRef<FunctionRecord> getCoveredFunctions() {
return ArrayRef<FunctionRecord>(Functions.data(), Functions.size());
}
/// \brief Get the list of function instantiations in the file.
///
/// Fucntions that are instantiated more than once, such as C++ template
/// specializations, have distinct coverage records for each instantiation.
std::vector<const FunctionRecord *> getInstantiations(StringRef Filename);
/// \brief Get the coverage for a particular function.
CoverageData getCoverageForFunction(const FunctionRecord &Function);
/// \brief Get the coverage for an expansion within a coverage set.
CoverageData getCoverageForExpansion(const ExpansionRecord &Expansion);
};
} // end namespace coverage } // end namespace coverage
} // end namespace llvm } // end namespace llvm

View File

@ -13,6 +13,12 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "llvm/ProfileData/CoverageMapping.h" #include "llvm/ProfileData/CoverageMapping.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ProfileData/CoverageMappingReader.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorHandling.h"
using namespace llvm; using namespace llvm;
@ -140,3 +146,272 @@ ErrorOr<int64_t> CounterMappingContext::evaluate(const Counter &C) const {
} }
llvm_unreachable("Unhandled CounterKind"); llvm_unreachable("Unhandled CounterKind");
} }
ErrorOr<std::unique_ptr<CoverageMapping>>
CoverageMapping::load(ObjectFileCoverageMappingReader &CoverageReader,
IndexedInstrProfReader &ProfileReader) {
auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());
std::vector<uint64_t> Counts;
for (const auto &Record : CoverageReader) {
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)
return EC;
Coverage->MismatchedFunctionCount++;
continue;
}
FunctionRecord Function(Record.FunctionName, Record.Filenames);
CounterMappingContext Ctx(Record.Expressions, Counts);
for (const auto &Region : Record.MappingRegions) {
ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(Region.Count);
if (!ExecutionCount)
break;
Function.CountedRegions.push_back(CountedRegion(Region, *ExecutionCount));
}
if (Function.CountedRegions.size() != Record.MappingRegions.size()) {
Coverage->MismatchedFunctionCount++;
continue;
}
Coverage->Functions.push_back(Function);
}
return std::move(Coverage);
}
namespace {
/// \brief Distributes functions into instantiation sets.
///
/// An instantiation set is a collection of functions that have the same source
/// code, ie, template functions specializations.
class FunctionInstantiationSetCollector {
typedef DenseMap<std::pair<unsigned, unsigned>,
std::vector<const FunctionRecord *>> MapT;
MapT InstantiatedFunctions;
public:
void insert(const FunctionRecord &Function, unsigned FileID) {
auto I = Function.CountedRegions.begin(), E = Function.CountedRegions.end();
while (I != E && I->FileID != FileID)
++I;
assert(I != E && "function does not cover the given file");
auto &Functions = InstantiatedFunctions[I->startLoc()];
Functions.push_back(&Function);
}
MapT::iterator begin() { return InstantiatedFunctions.begin(); }
MapT::iterator end() { return InstantiatedFunctions.end(); }
};
class SegmentBuilder {
std::vector<CoverageSegment> Segments;
SmallVector<const CountedRegion *, 8> ActiveRegions;
/// Start a segment with no count specified.
void startSegment(unsigned Line, unsigned Col) {
Segments.emplace_back(Line, Col, /*IsRegionEntry=*/false);
}
/// Start a segment with the given Region's count.
void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry,
const CountedRegion &Region) {
if (Segments.empty())
Segments.emplace_back(Line, Col, IsRegionEntry);
CoverageSegment S = Segments.back();
// Avoid creating empty regions.
if (S.Line != Line || S.Col != Col) {
Segments.emplace_back(Line, Col, IsRegionEntry);
S = Segments.back();
}
// Set this region's count.
if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion)
Segments.back().setCount(Region.ExecutionCount);
}
/// 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);
else
startSegment(Line, Col, false, *ActiveRegions.back());
}
public:
/// Build a list of CoverageSegments from a sorted list of Regions.
std::vector<CoverageSegment> buildSegments(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);
}
// Pop any regions that are left in the stack.
while (!ActiveRegions.empty())
popRegion();
return Segments;
}
};
}
std::vector<StringRef> CoverageMapping::getUniqueSourceFiles() {
std::vector<StringRef> Filenames;
for (const auto &Function : getCoveredFunctions())
for (const auto &Filename : Function.Filenames)
Filenames.push_back(Filename);
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);
for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I)
if (SourceFile == Function.Filenames[I])
FilenameEquivalence[I] = true;
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;
}
static Optional<unsigned> findMainViewFileID(const FunctionRecord &Function) {
llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
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;
}
/// Sort a nested sequence of regions from a single file.
template <class It> static void sortNestedRegions(It First, It Last) {
std::sort(First, Last,
[](const CountedRegion &LHS, const CountedRegion &RHS) {
if (LHS.startLoc() == RHS.startLoc())
// When LHS completely contains RHS, we sort LHS first.
return RHS.endLoc() < LHS.endLoc();
return LHS.startLoc() < RHS.startLoc();
});
}
static bool isExpansion(const CountedRegion &R, unsigned FileID) {
return R.Kind == CounterMappingRegion::ExpansionRegion && R.FileID == FileID;
}
CoverageData CoverageMapping::getCoverageForFile(StringRef Filename) {
CoverageData FileCoverage(Filename);
std::vector<coverage::CountedRegion> Regions;
for (const auto &Function : Functions) {
auto MainFileID = findMainViewFileID(Filename, Function);
if (!MainFileID)
continue;
auto FileIDs = gatherFileIDs(Filename, Function);
for (const auto &CR : Function.CountedRegions)
if (FileIDs.count(CR.FileID)) {
Regions.push_back(CR);
if (isExpansion(CR, *MainFileID))
FileCoverage.Expansions.emplace_back(CR, Function);
}
}
sortNestedRegions(Regions.begin(), Regions.end());
FileCoverage.Segments = SegmentBuilder().buildSegments(Regions);
return FileCoverage;
}
std::vector<const FunctionRecord *>
CoverageMapping::getInstantiations(StringRef Filename) {
FunctionInstantiationSetCollector InstantiationSetCollector;
for (const auto &Function : Functions) {
auto MainFileID = findMainViewFileID(Filename, Function);
if (!MainFileID)
continue;
InstantiationSetCollector.insert(Function, *MainFileID);
}
std::vector<const FunctionRecord *> Result;
for (const auto &InstantiationSet : InstantiationSetCollector) {
if (InstantiationSet.second.size() < 2)
continue;
for (auto Function : InstantiationSet.second)
Result.push_back(Function);
}
return Result;
}
CoverageData
CoverageMapping::getCoverageForFunction(const FunctionRecord &Function) {
auto MainFileID = findMainViewFileID(Function);
if (!MainFileID)
return CoverageData();
CoverageData FunctionCoverage(Function.Filenames[*MainFileID]);
std::vector<coverage::CountedRegion> Regions;
for (const auto &CR : Function.CountedRegions)
if (CR.FileID == *MainFileID) {
Regions.push_back(CR);
if (isExpansion(CR, *MainFileID))
FunctionCoverage.Expansions.emplace_back(CR, Function);
}
sortNestedRegions(Regions.begin(), Regions.end());
FunctionCoverage.Segments = SegmentBuilder().buildSegments(Regions);
return FunctionCoverage;
}
CoverageData
CoverageMapping::getCoverageForExpansion(const ExpansionRecord &Expansion) {
CoverageData ExpansionCoverage(
Expansion.Function.Filenames[Expansion.FileID]);
std::vector<coverage::CountedRegion> Regions;
for (const auto &CR : Expansion.Function.CountedRegions)
if (CR.FileID == Expansion.FileID) {
Regions.push_back(CR);
if (isExpansion(CR, Expansion.FileID))
ExpansionCoverage.Expansions.emplace_back(CR, Expansion.Function);
}
sortNestedRegions(Regions.begin(), Regions.end());
ExpansionCoverage.Segments = SegmentBuilder().buildSegments(Regions);
return ExpansionCoverage;
}

View File

@ -8,7 +8,6 @@ add_llvm_tool(llvm-cov
CoverageReport.cpp CoverageReport.cpp
CoverageSummary.cpp CoverageSummary.cpp
CoverageSummaryInfo.cpp CoverageSummaryInfo.cpp
SourceCoverageDataManager.cpp
SourceCoverageView.cpp SourceCoverageView.cpp
TestingSupport.cpp TestingSupport.cpp
) )

View File

@ -16,15 +16,11 @@
#include "RenderingSupport.h" #include "RenderingSupport.h"
#include "CoverageViewOptions.h" #include "CoverageViewOptions.h"
#include "CoverageFilters.h" #include "CoverageFilters.h"
#include "SourceCoverageDataManager.h"
#include "SourceCoverageView.h" #include "SourceCoverageView.h"
#include "CoverageSummary.h" #include "CoverageSummary.h"
#include "CoverageReport.h" #include "CoverageReport.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ProfileData/InstrProfReader.h" #include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/CoverageMapping.h" #include "llvm/ProfileData/CoverageMapping.h"
#include "llvm/ProfileData/CoverageMappingReader.h" #include "llvm/ProfileData/CoverageMappingReader.h"
@ -43,34 +39,6 @@ using namespace llvm;
using namespace coverage; using namespace coverage;
namespace { namespace {
/// \brief Distribute the functions into instantiation sets.
///
/// An instantiation set is a collection of functions that have the same source
/// code, ie, template functions specializations.
class FunctionInstantiationSetCollector {
typedef DenseMap<std::pair<unsigned, unsigned>,
std::vector<const FunctionCoverageMapping *>> MapT;
MapT InstantiatedFunctions;
public:
void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
auto I = Function.CountedRegions.begin(), E = Function.CountedRegions.end();
while (I != E && I->FileID != FileID)
++I;
assert(I != E && "function does not cover the given file");
auto &Functions = InstantiatedFunctions[I->startLoc()];
Functions.push_back(&Function);
}
MapT::iterator begin() {
return InstantiatedFunctions.begin();
}
MapT::iterator end() {
return InstantiatedFunctions.end();
}
};
/// \brief The implementation of the coverage tool. /// \brief The implementation of the coverage tool.
class CodeCoverageTool { class CodeCoverageTool {
public: public:
@ -87,43 +55,21 @@ public:
/// \brief Return a memory buffer for the given source file. /// \brief Return a memory buffer for the given source file.
ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
/// \brief Collect a set of function's file ids which correspond to the /// \brief Create source views for the expansions of the view.
/// given source file. Return false if the set is empty. void attachExpansionSubViews(SourceCoverageView &View,
bool gatherInterestingFileIDs(StringRef SourceFile, ArrayRef<ExpansionRecord> Expansions,
const FunctionCoverageMapping &Function, CoverageMapping &Coverage);
SmallSet<unsigned, 8> &InterestingFileIDs);
/// \brief Find the file id which is not an expanded file id. /// \brief Create the source view of a particular function.
bool findMainViewFileID(StringRef SourceFile,
const FunctionCoverageMapping &Function,
unsigned &MainViewFileID);
bool findMainViewFileID(const FunctionCoverageMapping &Function,
unsigned &MainViewFileID);
/// \brief Create a source view which shows coverage for an expansion
/// of a file.
std::unique_ptr<SourceCoverageView> std::unique_ptr<SourceCoverageView>
createExpansionSubView(const CountedRegion &ExpandedRegion, createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage);
const FunctionCoverageMapping &Function);
void attachExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,
const FunctionCoverageMapping &Function);
/// \brief Create a source view which shows coverage for an instantiation
/// of a funciton.
std::unique_ptr<SourceCoverageView>
createInstantiationSubView(StringRef SourceFile,
const FunctionCoverageMapping &Function);
/// \brief Create the main source view of a particular source file. /// \brief Create the main source view of a particular source file.
std::unique_ptr<SourceCoverageView> std::unique_ptr<SourceCoverageView>
createSourceFileView(StringRef SourceFile, createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage);
ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
bool UseOnlyRegionsInMainFile = false);
/// \brief Load the coverage mapping data. Return true if an error occured. /// \brief Load the coverage mapping data. Return true if an error occured.
bool load(); std::unique_ptr<CoverageMapping> load();
int run(Command Cmd, int argc, const char **argv); int run(Command Cmd, int argc, const char **argv);
@ -137,29 +83,16 @@ public:
StringRef ObjectFilename; StringRef ObjectFilename;
CoverageViewOptions ViewOpts; CoverageViewOptions ViewOpts;
std::unique_ptr<IndexedInstrProfReader> PGOReader; std::string PGOFilename;
CoverageFiltersMatchAll Filters; CoverageFiltersMatchAll Filters;
std::vector<std::string> SourceFiles; std::vector<std::string> SourceFiles;
std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
LoadedSourceFiles; LoadedSourceFiles;
std::vector<FunctionCoverageMapping> FunctionMappingRecords;
bool CompareFilenamesOnly; bool CompareFilenamesOnly;
StringMap<std::string> RemappedFilenames; StringMap<std::string> RemappedFilenames;
}; };
} }
static std::vector<StringRef>
getUniqueFilenames(ArrayRef<FunctionCoverageMapping> FunctionMappingRecords) {
std::vector<StringRef> Filenames;
for (const auto &Function : FunctionMappingRecords)
for (const auto &Filename : Function.Filenames)
Filenames.push_back(Filename);
std::sort(Filenames.begin(), Filenames.end());
auto Last = std::unique(Filenames.begin(), Filenames.end());
Filenames.erase(Last, Filenames.end());
return Filenames;
}
void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
errs() << "error: "; errs() << "error: ";
if (!Whence.empty()) if (!Whence.empty())
@ -188,219 +121,115 @@ CodeCoverageTool::getSourceFile(StringRef SourceFile) {
return *LoadedSourceFiles.back().second; return *LoadedSourceFiles.back().second;
} }
bool CodeCoverageTool::gatherInterestingFileIDs( void
StringRef SourceFile, const FunctionCoverageMapping &Function, CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
SmallSet<unsigned, 8> &InterestingFileIDs) { ArrayRef<ExpansionRecord> Expansions,
bool Interesting = false; CoverageMapping &Coverage) {
for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
if (SourceFile == Function.Filenames[I]) {
InterestingFileIDs.insert(I);
Interesting = true;
}
}
return Interesting;
}
bool
CodeCoverageTool::findMainViewFileID(StringRef SourceFile,
const FunctionCoverageMapping &Function,
unsigned &MainViewFileID) {
llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(),
false);
for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
if (SourceFile == Function.Filenames[I])
FilenameEquivalence[I] = true;
}
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])
continue;
MainViewFileID = I;
return false;
}
return true;
}
bool
CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,
unsigned &MainViewFileID) {
llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
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])
continue;
MainViewFileID = I;
return false;
}
return true;
}
std::unique_ptr<SourceCoverageView> CodeCoverageTool::createExpansionSubView(
const CountedRegion &ExpandedRegion,
const FunctionCoverageMapping &Function) {
auto SourceBuffer =
getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
if (!SourceBuffer)
return nullptr;
auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
for (const auto &CR : Function.CountedRegions) {
if (CR.FileID == ExpandedRegion.ExpandedFileID)
RegionManager->insert(CR);
}
auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
ViewOpts);
SubView->load(std::move(RegionManager));
attachExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
return SubView;
}
void CodeCoverageTool::attachExpansionSubViews(
SourceCoverageView &View, unsigned ViewFileID,
const FunctionCoverageMapping &Function) {
if (!ViewOpts.ShowExpandedRegions) if (!ViewOpts.ShowExpandedRegions)
return; return;
for (const auto &CR : Function.CountedRegions) { for (const auto &Expansion : Expansions) {
if (CR.Kind != CounterMappingRegion::ExpansionRegion) auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
if (ExpansionCoverage.empty())
continue; continue;
if (CR.FileID != ViewFileID) auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
if (!SourceBuffer)
continue; continue;
auto SubView = createExpansionSubView(CR, Function);
if (SubView) auto SubViewExpansions = ExpansionCoverage.getExpansions();
View.addExpansion(CR, std::move(SubView)); auto SubView = llvm::make_unique<SourceCoverageView>(
SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
View.addExpansion(Expansion.Region, std::move(SubView));
} }
} }
std::unique_ptr<SourceCoverageView> std::unique_ptr<SourceCoverageView>
CodeCoverageTool::createInstantiationSubView( CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
StringRef SourceFile, const FunctionCoverageMapping &Function) { CoverageMapping &Coverage) {
auto RegionManager = llvm::make_unique<SourceCoverageDataManager>(); auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
SmallSet<unsigned, 8> InterestingFileIDs; if (FunctionCoverage.empty())
if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
return nullptr; return nullptr;
// Get the interesting regions auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
for (const auto &CR : Function.CountedRegions) {
if (InterestingFileIDs.count(CR.FileID))
RegionManager->insert(CR);
}
auto SourceBuffer = getSourceFile(SourceFile);
if (!SourceBuffer) if (!SourceBuffer)
return nullptr; return nullptr;
auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
ViewOpts); auto Expansions = FunctionCoverage.getExpansions();
SubView->load(std::move(RegionManager)); auto View = llvm::make_unique<SourceCoverageView>(
unsigned MainFileID; SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage));
if (!findMainViewFileID(SourceFile, Function, MainFileID)) attachExpansionSubViews(*View, Expansions, Coverage);
attachExpansionSubViews(*SubView, MainFileID, Function);
return SubView; return View;
} }
std::unique_ptr<SourceCoverageView> CodeCoverageTool::createSourceFileView( std::unique_ptr<SourceCoverageView>
StringRef SourceFile, CodeCoverageTool::createSourceFileView(StringRef SourceFile,
ArrayRef<FunctionCoverageMapping> FunctionMappingRecords, CoverageMapping &Coverage) {
bool UseOnlyRegionsInMainFile) {
auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
FunctionInstantiationSetCollector InstantiationSetCollector;
auto SourceBuffer = getSourceFile(SourceFile); auto SourceBuffer = getSourceFile(SourceFile);
if (!SourceBuffer) if (!SourceBuffer)
return nullptr; return nullptr;
auto View = auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
llvm::make_unique<SourceCoverageView>(SourceBuffer.get(), ViewOpts); if (FileCoverage.empty())
for (const auto &Function : FunctionMappingRecords) {
unsigned MainFileID;
if (findMainViewFileID(SourceFile, Function, MainFileID))
continue;
SmallSet<unsigned, 8> InterestingFileIDs;
if (UseOnlyRegionsInMainFile) {
InterestingFileIDs.insert(MainFileID);
} else if (!gatherInterestingFileIDs(SourceFile, Function,
InterestingFileIDs))
continue;
// Get the interesting regions
for (const auto &CR : Function.CountedRegions) {
if (InterestingFileIDs.count(CR.FileID))
RegionManager->insert(CR);
}
InstantiationSetCollector.insert(Function, MainFileID);
attachExpansionSubViews(*View, MainFileID, Function);
}
if (RegionManager->getCoverageSegments().empty())
return nullptr; return nullptr;
View->load(std::move(RegionManager));
// Show instantiations auto Expansions = FileCoverage.getExpansions();
if (!ViewOpts.ShowFunctionInstantiations) auto View = llvm::make_unique<SourceCoverageView>(
return View; SourceBuffer.get(), ViewOpts, std::move(FileCoverage));
for (const auto &InstantiationSet : InstantiationSetCollector) { attachExpansionSubViews(*View, Expansions, Coverage);
if (InstantiationSet.second.size() < 2)
continue; for (auto Function : Coverage.getInstantiations(SourceFile)) {
for (auto Function : InstantiationSet.second) { auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
auto SubViewExpansions = SubViewCoverage.getExpansions();
auto SubView = llvm::make_unique<SourceCoverageView>(
SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
if (SubView) {
unsigned FileID = Function->CountedRegions.front().FileID; unsigned FileID = Function->CountedRegions.front().FileID;
unsigned Line = 0; unsigned Line = 0;
for (const auto &CR : Function->CountedRegions) for (const auto &CR : Function->CountedRegions)
if (CR.FileID == FileID) if (CR.FileID == FileID)
Line = std::max(CR.LineEnd, Line); Line = std::max(CR.LineEnd, Line);
auto SubView = createInstantiationSubView(SourceFile, *Function); View->addInstantiation(Function->Name, Line, std::move(SubView));
if (SubView)
View->addInstantiation(Function->Name, Line, std::move(SubView));
} }
} }
return View; return View;
} }
bool CodeCoverageTool::load() { std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename); auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
if (auto EC = CounterMappingBuff.getError()) { if (auto EC = CounterMappingBuff.getError()) {
error(EC.message(), ObjectFilename); error(EC.message(), ObjectFilename);
return true; return nullptr;
} }
ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get()); ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());
if (auto EC = MappingReader.readHeader()) { if (auto EC = MappingReader.readHeader()) {
error(EC.message(), ObjectFilename); error(EC.message(), ObjectFilename);
return true; return nullptr;
} }
std::vector<uint64_t> Counts; std::unique_ptr<IndexedInstrProfReader> PGOReader;
for (const auto &I : MappingReader) { if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
FunctionCoverageMapping Function(I.FunctionName, I.Filenames); error(EC.message(), PGOFilename);
return nullptr;
}
// Create the mapping regions with evaluated execution counts auto CoverageOrErr = CoverageMapping::load(MappingReader, *PGOReader);
Counts.clear(); if (std::error_code EC = CoverageOrErr.getError()) {
PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts); colored_ostream(errs(), raw_ostream::RED)
<< "error: Failed to load coverage: " << EC.message();
// Get the biggest referenced counters errs() << "\n";
bool RegionError = false; return nullptr;
CounterMappingContext Ctx(I.Expressions, Counts); }
for (const auto &R : I.MappingRegions) { auto Coverage = std::move(CoverageOrErr.get());
ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count); unsigned Mismatched = Coverage->getMismatchedCount();
if (ExecutionCount) { if (Mismatched) {
Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount)); colored_ostream(errs(), raw_ostream::RED)
} else if (!RegionError) { << "warning: " << Mismatched << " functions have mismatched data. ";
colored_ostream(errs(), raw_ostream::RED) errs() << "\n";
<< "error: Regions and counters don't match in a function '"
<< Function.Name << "' (re-run the instrumented binary).";
errs() << "\n";
RegionError = true;
}
}
if (RegionError || !Filters.matches(Function))
continue;
FunctionMappingRecords.push_back(Function);
} }
if (CompareFilenamesOnly) { if (CompareFilenamesOnly) {
auto CoveredFiles = getUniqueFilenames(FunctionMappingRecords); auto CoveredFiles = Coverage.get()->getUniqueSourceFiles();
for (auto &SF : SourceFiles) { for (auto &SF : SourceFiles) {
StringRef SFBase = sys::path::filename(SF); StringRef SFBase = sys::path::filename(SF);
for (const auto &CF : CoveredFiles) for (const auto &CF : CoveredFiles)
@ -412,7 +241,7 @@ bool CodeCoverageTool::load() {
} }
} }
return false; return Coverage;
} }
int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
@ -424,8 +253,8 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::list<std::string> InputSourceFiles( cl::list<std::string> InputSourceFiles(
cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
cl::opt<std::string> PGOFilename( cl::opt<std::string, true> PGOFilename(
"instr-profile", cl::Required, "instr-profile", cl::Required, cl::location(this->PGOFilename),
cl::desc( cl::desc(
"File with the profile data obtained after an instrumented run")); "File with the profile data obtained after an instrumented run"));
@ -479,11 +308,6 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
ViewOpts.Debug = DebugDump; ViewOpts.Debug = DebugDump;
CompareFilenamesOnly = FilenameEquivalence; CompareFilenamesOnly = FilenameEquivalence;
if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
error(EC.message(), PGOFilename);
return 1;
}
// Create the function filters // Create the function filters
if (!NameFilters.empty() || !NameRegexFilters.empty()) { if (!NameFilters.empty() || !NameRegexFilters.empty()) {
auto NameFilterer = new CoverageFilters; auto NameFilterer = new CoverageFilters;
@ -597,30 +421,28 @@ int CodeCoverageTool::show(int argc, const char **argv,
ViewOpts.ShowExpandedRegions = ShowExpansions; ViewOpts.ShowExpandedRegions = ShowExpansions;
ViewOpts.ShowFunctionInstantiations = ShowInstantiations; ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
if (load()) auto Coverage = load();
if (!Coverage)
return 1; return 1;
if (!Filters.empty()) { if (!Filters.empty()) {
// Show functions // Show functions
for (const auto &Function : FunctionMappingRecords) { for (const auto &Function : Coverage->getCoveredFunctions()) {
unsigned MainFileID; if (!Filters.matches(Function))
if (findMainViewFileID(Function, MainFileID))
continue; continue;
StringRef SourceFile = Function.Filenames[MainFileID];
auto mainView = createSourceFileView(SourceFile, Function, true); auto mainView = createFunctionView(Function, *Coverage);
if (!mainView) { if (!mainView) {
ViewOpts.colored_ostream(outs(), raw_ostream::RED) ViewOpts.colored_ostream(outs(), raw_ostream::RED)
<< "warning: Could not read coverage for '" << Function.Name << "warning: Could not read coverage for '" << Function.Name;
<< " from " << SourceFile;
outs() << "\n"; outs() << "\n";
continue; continue;
} }
ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name
<< Function.Name << " from " << SourceFile << ":"; << ":";
outs() << "\n"; outs() << "\n";
mainView->render(outs(), /*WholeFile=*/false); mainView->render(outs(), /*WholeFile=*/false);
if (FunctionMappingRecords.size() > 1) outs() << "\n";
outs() << "\n";
} }
return 0; return 0;
} }
@ -630,11 +452,11 @@ int CodeCoverageTool::show(int argc, const char **argv,
if (SourceFiles.empty()) if (SourceFiles.empty())
// Get the source files from the function coverage mapping // Get the source files from the function coverage mapping
for (StringRef Filename : getUniqueFilenames(FunctionMappingRecords)) for (StringRef Filename : Coverage->getUniqueSourceFiles())
SourceFiles.push_back(Filename); SourceFiles.push_back(Filename);
for (const auto &SourceFile : SourceFiles) { for (const auto &SourceFile : SourceFiles) {
auto mainView = createSourceFileView(SourceFile, FunctionMappingRecords); auto mainView = createSourceFileView(SourceFile, *Coverage);
if (!mainView) { if (!mainView) {
ViewOpts.colored_ostream(outs(), raw_ostream::RED) ViewOpts.colored_ostream(outs(), raw_ostream::RED)
<< "warning: The file '" << SourceFile << "' isn't covered."; << "warning: The file '" << SourceFile << "' isn't covered.";
@ -665,11 +487,12 @@ int CodeCoverageTool::report(int argc, const char **argv,
ViewOpts.Colors = !NoColors; ViewOpts.Colors = !NoColors;
if (load()) auto Coverage = load();
if (!Coverage)
return 1; return 1;
CoverageSummary Summarizer; CoverageSummary Summarizer;
Summarizer.createSummaries(FunctionMappingRecords); Summarizer.createSummaries(Coverage->getCoveredFunctions());
CoverageReport Report(ViewOpts, Summarizer); CoverageReport Report(ViewOpts, Summarizer);
if (SourceFiles.empty() && Filters.empty()) { if (SourceFiles.empty() && Filters.empty()) {
Report.renderFileReports(llvm::outs()); Report.renderFileReports(llvm::outs());

View File

@ -17,21 +17,22 @@
using namespace llvm; using namespace llvm;
bool NameCoverageFilter::matches(const FunctionCoverageMapping &Function) { bool NameCoverageFilter::matches(const coverage::FunctionRecord &Function) {
StringRef FuncName = Function.Name; StringRef FuncName = Function.Name;
return FuncName.find(Name) != StringRef::npos; return FuncName.find(Name) != StringRef::npos;
} }
bool NameRegexCoverageFilter::matches(const FunctionCoverageMapping &Function) { bool
NameRegexCoverageFilter::matches(const coverage::FunctionRecord &Function) {
return llvm::Regex(Regex).match(Function.Name); return llvm::Regex(Regex).match(Function.Name);
} }
bool RegionCoverageFilter::matches(const FunctionCoverageMapping &Function) { bool RegionCoverageFilter::matches(const coverage::FunctionRecord &Function) {
return PassesThreshold(FunctionCoverageSummary::get(Function) return PassesThreshold(FunctionCoverageSummary::get(Function)
.RegionCoverage.getPercentCovered()); .RegionCoverage.getPercentCovered());
} }
bool LineCoverageFilter::matches(const FunctionCoverageMapping &Function) { bool LineCoverageFilter::matches(const coverage::FunctionRecord &Function) {
return PassesThreshold( return PassesThreshold(
FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered()); FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered());
} }
@ -40,7 +41,7 @@ void CoverageFilters::push_back(std::unique_ptr<CoverageFilter> Filter) {
Filters.push_back(std::move(Filter)); Filters.push_back(std::move(Filter));
} }
bool CoverageFilters::matches(const FunctionCoverageMapping &Function) { bool CoverageFilters::matches(const coverage::FunctionRecord &Function) {
for (const auto &Filter : Filters) { for (const auto &Filter : Filters) {
if (Filter->matches(Function)) if (Filter->matches(Function))
return true; return true;
@ -48,7 +49,8 @@ bool CoverageFilters::matches(const FunctionCoverageMapping &Function) {
return false; return false;
} }
bool CoverageFiltersMatchAll::matches(const FunctionCoverageMapping &Function) { bool
CoverageFiltersMatchAll::matches(const coverage::FunctionRecord &Function) {
for (const auto &Filter : Filters) { for (const auto &Filter : Filters) {
if (!Filter->matches(Function)) if (!Filter->matches(Function))
return false; return false;

View File

@ -20,15 +20,15 @@
namespace llvm { namespace llvm {
using coverage::FunctionCoverageMapping;
/// \brief Matches specific functions that pass the requirement of this filter. /// \brief Matches specific functions that pass the requirement of this filter.
class CoverageFilter { class CoverageFilter {
public: public:
virtual ~CoverageFilter() {} virtual ~CoverageFilter() {}
/// \brief Return true if the function passes the requirements of this filter. /// \brief Return true if the function passes the requirements of this filter.
virtual bool matches(const FunctionCoverageMapping &Function) { return true; } virtual bool matches(const coverage::FunctionRecord &Function) {
return true;
}
}; };
/// \brief Matches functions that contain a specific string in their name. /// \brief Matches functions that contain a specific string in their name.
@ -38,7 +38,7 @@ class NameCoverageFilter : public CoverageFilter {
public: public:
NameCoverageFilter(StringRef Name) : Name(Name) {} NameCoverageFilter(StringRef Name) : Name(Name) {}
bool matches(const FunctionCoverageMapping &Function) override; bool matches(const coverage::FunctionRecord &Function) override;
}; };
/// \brief Matches functions whose name matches a certain regular expression. /// \brief Matches functions whose name matches a certain regular expression.
@ -48,7 +48,7 @@ class NameRegexCoverageFilter : public CoverageFilter {
public: public:
NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {} NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}
bool matches(const FunctionCoverageMapping &Function) override; bool matches(const coverage::FunctionRecord &Function) override;
}; };
/// \brief Matches numbers that pass a certain threshold. /// \brief Matches numbers that pass a certain threshold.
@ -84,7 +84,7 @@ public:
RegionCoverageFilter(Operation Op, double Threshold) RegionCoverageFilter(Operation Op, double Threshold)
: StatisticThresholdFilter(Op, Threshold) {} : StatisticThresholdFilter(Op, Threshold) {}
bool matches(const FunctionCoverageMapping &Function) override; bool matches(const coverage::FunctionRecord &Function) override;
}; };
/// \brief Matches functions whose line coverage percentage /// \brief Matches functions whose line coverage percentage
@ -95,7 +95,7 @@ public:
LineCoverageFilter(Operation Op, double Threshold) LineCoverageFilter(Operation Op, double Threshold)
: StatisticThresholdFilter(Op, Threshold) {} : StatisticThresholdFilter(Op, Threshold) {}
bool matches(const FunctionCoverageMapping &Function) override; bool matches(const coverage::FunctionRecord &Function) override;
}; };
/// \brief A collection of filters. /// \brief A collection of filters.
@ -111,7 +111,7 @@ public:
bool empty() const { return Filters.empty(); } bool empty() const { return Filters.empty(); }
bool matches(const FunctionCoverageMapping &Function) override; bool matches(const coverage::FunctionRecord &Function) override;
}; };
/// \brief A collection of filters. /// \brief A collection of filters.
@ -119,7 +119,7 @@ public:
/// in an instance of this class. /// in an instance of this class.
class CoverageFiltersMatchAll : public CoverageFilters { class CoverageFiltersMatchAll : public CoverageFilters {
public: public:
bool matches(const FunctionCoverageMapping &Function) override; bool matches(const coverage::FunctionRecord &Function) override;
}; };
} // namespace llvm } // namespace llvm

View File

@ -27,8 +27,8 @@ unsigned CoverageSummary::getFileID(StringRef Filename) {
return Filenames.size() - 1; return Filenames.size() - 1;
} }
void CoverageSummary::createSummaries( void
ArrayRef<coverage::FunctionCoverageMapping> Functions) { CoverageSummary::createSummaries(ArrayRef<coverage::FunctionRecord> Functions) {
std::vector<std::pair<unsigned, size_t>> FunctionFileIDs; std::vector<std::pair<unsigned, size_t>> FunctionFileIDs;
FunctionFileIDs.resize(Functions.size()); FunctionFileIDs.resize(Functions.size());

View File

@ -30,7 +30,7 @@ class CoverageSummary {
unsigned getFileID(StringRef Filename); unsigned getFileID(StringRef Filename);
public: public:
void createSummaries(ArrayRef<coverage::FunctionCoverageMapping> Functions); void createSummaries(ArrayRef<coverage::FunctionRecord> Functions);
ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; } ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; }

View File

@ -18,7 +18,7 @@ using namespace llvm;
using namespace coverage; using namespace coverage;
FunctionCoverageSummary FunctionCoverageSummary
FunctionCoverageSummary::get(const FunctionCoverageMapping &Function) { FunctionCoverageSummary::get(const coverage::FunctionRecord &Function) {
// Compute the region coverage // Compute the region coverage
size_t NumCodeRegions = 0, CoveredRegions = 0; size_t NumCodeRegions = 0, CoveredRegions = 0;
for (auto &CR : Function.CountedRegions) { for (auto &CR : Function.CountedRegions) {

View File

@ -101,7 +101,7 @@ struct FunctionCoverageSummary {
/// \brief Compute the code coverage summary for the given function coverage /// \brief Compute the code coverage summary for the given function coverage
/// mapping record. /// mapping record.
static FunctionCoverageSummary static FunctionCoverageSummary
get(const coverage::FunctionCoverageMapping &Function); get(const coverage::FunctionRecord &Function);
}; };
/// \brief A summary of file's code coverage. /// \brief A summary of file's code coverage.

View File

@ -1,104 +0,0 @@
//===- SourceCoverageDataManager.cpp - Manager for source file coverage
// data-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class separates and merges mapping regions for a specific source file.
//
//===----------------------------------------------------------------------===//
#include "SourceCoverageDataManager.h"
using namespace llvm;
using namespace coverage;
void SourceCoverageDataManager::insert(const CountedRegion &CR) {
Regions.push_back(CR);
Segments.clear();
}
namespace {
class SegmentBuilder {
std::vector<CoverageSegment> Segments;
SmallVector<const CountedRegion *, 8> ActiveRegions;
/// Start a segment with no count specified.
void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry) {
Segments.emplace_back(Line, Col, IsRegionEntry);
}
/// Start a segment with the given Region's count.
void startSegment(unsigned Line, unsigned Col, bool IsRegionEntry,
const CountedRegion &Region) {
if (Segments.empty())
Segments.emplace_back(Line, Col, IsRegionEntry);
CoverageSegment S = Segments.back();
// Avoid creating empty regions.
if (S.Line != Line || S.Col != Col) {
Segments.emplace_back(Line, Col, IsRegionEntry);
S = Segments.back();
}
// Set this region's count.
if (Region.Kind != coverage::CounterMappingRegion::SkippedRegion)
Segments.back().setCount(Region.ExecutionCount);
}
/// 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, /*IsRegionEntry=*/false);
else
startSegment(Line, Col, /*IsRegionEntry=*/false, *ActiveRegions.back());
}
public:
/// Build a list of CoverageSegments from a sorted list of Regions.
std::vector<CoverageSegment>
buildSegments(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);
}
// Pop any regions that are left in the stack.
while (!ActiveRegions.empty())
popRegion();
return Segments;
}
};
}
ArrayRef<CoverageSegment> SourceCoverageDataManager::getCoverageSegments() {
if (Segments.empty()) {
// Sort the regions given that they're all in the same file at this point.
std::sort(Regions.begin(), Regions.end(),
[](const CountedRegion &LHS, const CountedRegion &RHS) {
if (LHS.startLoc() == RHS.startLoc())
// When LHS completely contains RHS, we sort LHS first.
return RHS.endLoc() < LHS.endLoc();
return LHS.startLoc() < RHS.startLoc();
});
Segments = SegmentBuilder().buildSegments(Regions);
}
return Segments;
}

View File

@ -1,53 +0,0 @@
//===- SourceCoverageDataManager.h - Manager for source file coverage data-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This class separates and merges mapping regions for a specific source file.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
#define LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
#include "llvm/ProfileData/CoverageMapping.h"
#include <vector>
namespace llvm {
struct CoverageSegment {
unsigned Line;
unsigned Col;
bool IsRegionEntry;
uint64_t Count;
bool HasCount;
CoverageSegment(unsigned Line, unsigned Col, bool IsRegionEntry)
: Line(Line), Col(Col), IsRegionEntry(IsRegionEntry),
Count(0), HasCount(false) {}
void setCount(uint64_t NewCount) {
Count = NewCount;
HasCount = true;
}
};
/// \brief Partions mapping regions by their kind and sums
/// the execution counts of the regions that start at the same location.
class SourceCoverageDataManager {
std::vector<coverage::CountedRegion> Regions;
std::vector<CoverageSegment> Segments;
public:
void insert(const coverage::CountedRegion &CR);
/// \brief Return a sequence of non-overlapping coverage segments.
ArrayRef<CoverageSegment> getCoverageSegments();
};
} // namespace llvm
#endif // LLVM_COV_SOURCECOVERAGEDATAMANAGER_H

View File

@ -18,11 +18,11 @@
using namespace llvm; using namespace llvm;
void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line, void SourceCoverageView::renderLine(
int64_t LineNumber, raw_ostream &OS, StringRef Line, int64_t LineNumber,
const CoverageSegment *WrappedSegment, const coverage::CoverageSegment *WrappedSegment,
ArrayRef<const CoverageSegment *> Segments, ArrayRef<const coverage::CoverageSegment *> Segments,
unsigned ExpansionCol) { unsigned ExpansionCol) {
Optional<raw_ostream::Colors> Highlight; Optional<raw_ostream::Colors> Highlight;
SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges; SmallVector<std::pair<unsigned, unsigned>, 2> HighlightedRanges;
@ -110,7 +110,7 @@ void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
} }
void SourceCoverageView::renderRegionMarkers( void SourceCoverageView::renderRegionMarkers(
raw_ostream &OS, ArrayRef<const CoverageSegment *> Segments) { raw_ostream &OS, ArrayRef<const coverage::CoverageSegment *> Segments) {
SmallString<32> Buffer; SmallString<32> Buffer;
raw_svector_ostream BufferOS(Buffer); raw_svector_ostream BufferOS(Buffer);
@ -158,14 +158,12 @@ void SourceCoverageView::render(raw_ostream &OS, bool WholeFile,
auto EndISV = InstantiationSubViews.end(); auto EndISV = InstantiationSubViews.end();
// Get the coverage information for the file. // Get the coverage information for the file.
auto CoverageSegments = RegionManager->getCoverageSegments(); auto NextSegment = CoverageInfo.begin();
assert(CoverageSegments.size() && "View with no coverage?"); auto EndSegment = CoverageInfo.end();
auto NextSegment = CoverageSegments.begin();
auto EndSegment = CoverageSegments.end();
unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0; unsigned FirstLine = NextSegment != EndSegment ? NextSegment->Line : 0;
const CoverageSegment *WrappedSegment = nullptr; const coverage::CoverageSegment *WrappedSegment = nullptr;
SmallVector<const CoverageSegment *, 8> LineSegments; SmallVector<const coverage::CoverageSegment *, 8> LineSegments;
for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) { for (line_iterator LI(File, /*SkipBlanks=*/false); !LI.is_at_eof(); ++LI) {
// If we aren't rendering the whole file, we need to filter out the prologue // If we aren't rendering the whole file, we need to filter out the prologue
// and epilogue. // and epilogue.

View File

@ -15,7 +15,6 @@
#define LLVM_COV_SOURCECOVERAGEVIEW_H #define LLVM_COV_SOURCECOVERAGEVIEW_H
#include "CoverageViewOptions.h" #include "CoverageViewOptions.h"
#include "SourceCoverageDataManager.h"
#include "llvm/ProfileData/CoverageMapping.h" #include "llvm/ProfileData/CoverageMapping.h"
#include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/MemoryBuffer.h"
#include <vector> #include <vector>
@ -105,14 +104,14 @@ private:
const MemoryBuffer &File; const MemoryBuffer &File;
const CoverageViewOptions &Options; const CoverageViewOptions &Options;
std::unique_ptr<SourceCoverageDataManager> RegionManager; coverage::CoverageData CoverageInfo;
std::vector<ExpansionView> ExpansionSubViews; std::vector<ExpansionView> ExpansionSubViews;
std::vector<InstantiationView> InstantiationSubViews; std::vector<InstantiationView> InstantiationSubViews;
/// \brief Render a source line with highlighting. /// \brief Render a source line with highlighting.
void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber, void renderLine(raw_ostream &OS, StringRef Line, int64_t LineNumber,
const CoverageSegment *WrappedSegment, const coverage::CoverageSegment *WrappedSegment,
ArrayRef<const CoverageSegment *> Segments, ArrayRef<const coverage::CoverageSegment *> Segments,
unsigned ExpansionCol); unsigned ExpansionCol);
void renderIndent(raw_ostream &OS, unsigned Level); void renderIndent(raw_ostream &OS, unsigned Level);
@ -126,16 +125,18 @@ private:
void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo); void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);
/// \brief Render all the region's execution counts on a line. /// \brief Render all the region's execution counts on a line.
void renderRegionMarkers(raw_ostream &OS, void
ArrayRef<const CoverageSegment *> Segments); renderRegionMarkers(raw_ostream &OS,
ArrayRef<const coverage::CoverageSegment *> Segments);
static const unsigned LineCoverageColumnWidth = 7; static const unsigned LineCoverageColumnWidth = 7;
static const unsigned LineNumberColumnWidth = 5; static const unsigned LineNumberColumnWidth = 5;
public: public:
SourceCoverageView(const MemoryBuffer &File, SourceCoverageView(const MemoryBuffer &File,
const CoverageViewOptions &Options) const CoverageViewOptions &Options,
: File(File), Options(Options) {} coverage::CoverageData &&CoverageInfo)
: File(File), Options(Options), CoverageInfo(std::move(CoverageInfo)) {}
const CoverageViewOptions &getOptions() const { return Options; } const CoverageViewOptions &getOptions() const { return Options; }
@ -154,12 +155,6 @@ public:
/// \brief Print the code coverage information for a specific /// \brief Print the code coverage information for a specific
/// portion of a source file to the output stream. /// portion of a source file to the output stream.
void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0); void render(raw_ostream &OS, bool WholeFile, unsigned IndentLevel = 0);
/// \brief Load the coverage information required for rendering
/// from the mapping regions in the data manager.
void load(std::unique_ptr<SourceCoverageDataManager> Data) {
RegionManager = std::move(Data);
}
}; };
} // namespace llvm } // namespace llvm