//===----------------------------------------------------------------------===//
#include "RenderingSupport.h"
-#include "CoverageViewOptions.h"
#include "CoverageFilters.h"
-#include "SourceCoverageDataManager.h"
-#include "SourceCoverageView.h"
-#include "CoverageSummary.h"
#include "CoverageReport.h"
-#include "llvm/ADT/DenseMap.h"
-#include "llvm/ADT/StringRef.h"
+#include "CoverageViewOptions.h"
+#include "SourceCoverageView.h"
#include "llvm/ADT/SmallString.h"
-#include "llvm/ADT/SmallSet.h"
-#include "llvm/ADT/DenseSet.h"
-#include "llvm/ProfileData/InstrProfReader.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/ProfileData/CoverageMapping.h"
-#include "llvm/ProfileData/CoverageMappingReader.h"
+#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/MemoryObject.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
#include <functional>
#include <system_error>
using namespace coverage;
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.
class CodeCoverageTool {
public:
/// \brief Return a memory buffer for the given source file.
ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
- /// \brief Collect a set of function's file ids which correspond to the
- /// given source file. Return false if the set is empty.
- bool gatherInterestingFileIDs(StringRef SourceFile,
- const FunctionCoverageMapping &Function,
- SmallSet<unsigned, 8> &InterestingFileIDs);
-
- /// \brief Find the file id which is not an expanded file id.
- bool findMainViewFileID(StringRef SourceFile,
- const FunctionCoverageMapping &Function,
- unsigned &MainViewFileID);
-
- bool findMainViewFileID(const FunctionCoverageMapping &Function,
- unsigned &MainViewFileID);
+ /// \brief Create source views for the expansions of the view.
+ void attachExpansionSubViews(SourceCoverageView &View,
+ ArrayRef<ExpansionRecord> Expansions,
+ CoverageMapping &Coverage);
- /// \brief Create a source view which shows coverage for an expansion
- /// of a file.
- void createExpansionSubView(const CountedRegion &ExpandedRegion,
- const FunctionCoverageMapping &Function,
- SourceCoverageView &Parent);
-
- void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,
- const FunctionCoverageMapping &Function);
-
- /// \brief Create a source view which shows coverage for an instantiation
- /// of a funciton.
- void createInstantiationSubView(StringRef SourceFile,
- const FunctionCoverageMapping &Function,
- SourceCoverageView &View);
+ /// \brief Create the source view of a particular function.
+ std::unique_ptr<SourceCoverageView>
+ createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage);
/// \brief Create the main source view of a particular source file.
- /// Return true if this particular source file is not covered.
- bool
- createSourceFileView(StringRef SourceFile, SourceCoverageView &View,
- ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
- bool UseOnlyRegionsInMainFile = false);
+ std::unique_ptr<SourceCoverageView>
+ createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage);
/// \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 report(int argc, const char **argv,
CommandLineParserType commandLineParser);
- StringRef ObjectFilename;
+ std::string ObjectFilename;
CoverageViewOptions ViewOpts;
- std::unique_ptr<IndexedInstrProfReader> PGOReader;
+ std::string PGOFilename;
CoverageFiltersMatchAll Filters;
std::vector<std::string> SourceFiles;
std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
LoadedSourceFiles;
- std::vector<FunctionCoverageMapping> FunctionMappingRecords;
bool CompareFilenamesOnly;
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) {
errs() << "error: ";
if (!Whence.empty())
return *LoadedSourceFiles.back().second;
}
-bool CodeCoverageTool::gatherInterestingFileIDs(
- StringRef SourceFile, const FunctionCoverageMapping &Function,
- SmallSet<unsigned, 8> &InterestingFileIDs) {
- bool Interesting = false;
- 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])
+void
+CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
+ ArrayRef<ExpansionRecord> Expansions,
+ CoverageMapping &Coverage) {
+ if (!ViewOpts.ShowExpandedRegions)
+ return;
+ for (const auto &Expansion : Expansions) {
+ auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
+ if (ExpansionCoverage.empty())
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])
+ auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
+ if (!SourceBuffer)
continue;
- MainViewFileID = I;
- return false;
+
+ auto SubViewExpansions = ExpansionCoverage.getExpansions();
+ auto SubView = llvm::make_unique<SourceCoverageView>(
+ SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage));
+ attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
+ View.addExpansion(Expansion.Region, std::move(SubView));
}
- return true;
}
-void CodeCoverageTool::createExpansionSubView(
- const CountedRegion &ExpandedRegion,
- const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {
- auto SourceBuffer =
- getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
+std::unique_ptr<SourceCoverageView>
+CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
+ CoverageMapping &Coverage) {
+ auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
+ if (FunctionCoverage.empty())
+ return nullptr;
+ auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
if (!SourceBuffer)
- return;
- auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
- Parent.getOptions());
- auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
- for (const auto &CR : Function.CountedRegions) {
- if (CR.FileID == ExpandedRegion.ExpandedFileID)
- RegionManager->insert(CR);
- }
- SubView->load(std::move(RegionManager));
- createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
- Parent.addExpansion(ExpandedRegion, std::move(SubView));
-}
+ return nullptr;
-void CodeCoverageTool::createExpansionSubViews(
- SourceCoverageView &View, unsigned ViewFileID,
- const FunctionCoverageMapping &Function) {
- if (!ViewOpts.ShowExpandedRegions)
- return;
- for (const auto &CR : Function.CountedRegions) {
- if (CR.Kind != CounterMappingRegion::ExpansionRegion)
- continue;
- if (CR.FileID != ViewFileID)
- continue;
- createExpansionSubView(CR, Function, View);
- }
-}
+ auto Expansions = FunctionCoverage.getExpansions();
+ auto View = llvm::make_unique<SourceCoverageView>(
+ SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage));
+ attachExpansionSubViews(*View, Expansions, Coverage);
-void CodeCoverageTool::createInstantiationSubView(
- StringRef SourceFile, const FunctionCoverageMapping &Function,
- SourceCoverageView &View) {
- auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
- SmallSet<unsigned, 8> InterestingFileIDs;
- if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
- return;
- // Get the interesting regions
- for (const auto &CR : Function.CountedRegions) {
- if (InterestingFileIDs.count(CR.FileID))
- RegionManager->insert(CR);
- }
- View.load(std::move(RegionManager));
- unsigned MainFileID;
- if (findMainViewFileID(SourceFile, Function, MainFileID))
- return;
- createExpansionSubViews(View, MainFileID, Function);
+ return View;
}
-bool CodeCoverageTool::createSourceFileView(
- StringRef SourceFile, SourceCoverageView &View,
- ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
- bool UseOnlyRegionsInMainFile) {
- auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
- FunctionInstantiationSetCollector InstantiationSetCollector;
-
- 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);
- createExpansionSubViews(View, MainFileID, Function);
- }
- if (RegionManager->getCoverageSegments().empty())
- return true;
- View.load(std::move(RegionManager));
- // Show instantiations
- if (!ViewOpts.ShowFunctionInstantiations)
- return false;
- for (const auto &InstantiationSet : InstantiationSetCollector) {
- if (InstantiationSet.second.size() < 2)
- continue;
- for (auto Function : InstantiationSet.second) {
+std::unique_ptr<SourceCoverageView>
+CodeCoverageTool::createSourceFileView(StringRef SourceFile,
+ CoverageMapping &Coverage) {
+ auto SourceBuffer = getSourceFile(SourceFile);
+ if (!SourceBuffer)
+ return nullptr;
+ auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
+ if (FileCoverage.empty())
+ return nullptr;
+
+ auto Expansions = FileCoverage.getExpansions();
+ auto View = llvm::make_unique<SourceCoverageView>(
+ SourceBuffer.get(), ViewOpts, std::move(FileCoverage));
+ attachExpansionSubViews(*View, Expansions, Coverage);
+
+ for (auto Function : Coverage.getInstantiations(SourceFile)) {
+ 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 Line = 0;
for (const auto &CR : Function->CountedRegions)
if (CR.FileID == FileID)
Line = std::max(CR.LineEnd, Line);
- auto SourceBuffer = getSourceFile(Function->Filenames[FileID]);
- if (!SourceBuffer)
- continue;
- auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
- View.getOptions());
- createInstantiationSubView(SourceFile, *Function, *SubView);
- View.addInstantiation(Function->Name, Line, std::move(SubView));
+ View->addInstantiation(Function->Name, Line, std::move(SubView));
}
}
- return false;
+ return View;
}
-bool CodeCoverageTool::load() {
- auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
- if (auto EC = CounterMappingBuff.getError()) {
- error(EC.message(), ObjectFilename);
- return true;
+std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
+ auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename);
+ if (std::error_code EC = CoverageOrErr.getError()) {
+ colored_ostream(errs(), raw_ostream::RED)
+ << "error: Failed to load coverage: " << EC.message();
+ errs() << "\n";
+ return nullptr;
}
- ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());
- if (auto EC = MappingReader.readHeader()) {
- error(EC.message(), ObjectFilename);
- return true;
- }
-
- std::vector<uint64_t> Counts;
- for (const auto &I : MappingReader) {
- FunctionCoverageMapping Function(I.FunctionName, I.Filenames);
-
- // Create the mapping regions with evaluated execution counts
- Counts.clear();
- PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts);
-
- // Get the biggest referenced counters
- bool RegionError = false;
- CounterMappingContext Ctx(I.Expressions, Counts);
- for (const auto &R : I.MappingRegions) {
- // Compute the values of mapped regions
- if (ViewOpts.Debug) {
- errs() << "File " << R.FileID << "| " << R.LineStart << ":"
- << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd
- << " = ";
- Ctx.dump(R.Count);
- if (R.Kind == CounterMappingRegion::ExpansionRegion) {
- errs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
- }
- errs() << "\n";
- }
- ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count);
- if (ExecutionCount) {
- Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount));
- } else if (!RegionError) {
- colored_ostream(errs(), raw_ostream::RED)
- << "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);
+ auto Coverage = std::move(CoverageOrErr.get());
+ unsigned Mismatched = Coverage->getMismatchedCount();
+ if (Mismatched) {
+ colored_ostream(errs(), raw_ostream::RED)
+ << "warning: " << Mismatched << " functions have mismatched data. ";
+ errs() << "\n";
}
if (CompareFilenamesOnly) {
- auto CoveredFiles = getUniqueFilenames(FunctionMappingRecords);
+ auto CoveredFiles = Coverage.get()->getUniqueSourceFiles();
for (auto &SF : SourceFiles) {
StringRef SFBase = sys::path::filename(SF);
for (const auto &CF : CoveredFiles)
}
}
- return false;
+ return Coverage;
}
int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ cl::opt<std::string, true> ObjectFilename(
+ cl::Positional, cl::Required, cl::location(this->ObjectFilename),
+ cl::desc("Covered executable or object file."));
+
cl::list<std::string> InputSourceFiles(
cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
- cl::opt<std::string> PGOFilename(
- "instr-profile", cl::Required,
+ cl::opt<std::string, true> PGOFilename(
+ "instr-profile", cl::Required, cl::location(this->PGOFilename),
cl::desc(
"File with the profile data obtained after an instrumented run"));
ViewOpts.Debug = DebugDump;
CompareFilenamesOnly = FilenameEquivalence;
- if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
- error(EC.message(), PGOFilename);
- return 1;
- }
-
// Create the function filters
if (!NameFilters.empty() || !NameRegexFilters.empty()) {
auto NameFilterer = new CoverageFilters;
for (const auto &File : InputSourceFiles) {
SmallString<128> Path(File);
- if (std::error_code EC = sys::fs::make_absolute(Path)) {
- errs() << "error: " << File << ": " << EC.message();
- return 1;
- }
+ if (!CompareFilenamesOnly)
+ if (std::error_code EC = sys::fs::make_absolute(Path)) {
+ errs() << "error: " << File << ": " << EC.message();
+ return 1;
+ }
SourceFiles.push_back(Path.str());
}
return 0;
};
- // Parse the object filename
- if (argc > 1) {
- StringRef Arg(argv[1]);
- if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {
- cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");
- return 0;
- }
- ObjectFilename = Arg;
-
- argv[1] = argv[0];
- --argc;
- ++argv;
- } else {
- errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";
- return 1;
- }
-
switch (Cmd) {
case Show:
return show(argc, argv, commandLineParser);
ViewOpts.ShowExpandedRegions = ShowExpansions;
ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
- if (load())
+ auto Coverage = load();
+ if (!Coverage)
return 1;
if (!Filters.empty()) {
// Show functions
- for (const auto &Function : FunctionMappingRecords) {
- unsigned MainFileID;
- if (findMainViewFileID(Function, MainFileID))
+ for (const auto &Function : Coverage->getCoveredFunctions()) {
+ if (!Filters.matches(Function))
continue;
- StringRef SourceFile = Function.Filenames[MainFileID];
- auto SourceBuffer = getSourceFile(SourceFile);
- if (!SourceBuffer)
- return 1;
- SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
- createSourceFileView(SourceFile, mainView, Function, true);
- ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
- << Function.Name << " from " << SourceFile << ":";
- outs() << "\n";
- mainView.render(outs(), /*WholeFile=*/false);
- if (FunctionMappingRecords.size() > 1)
+
+ auto mainView = createFunctionView(Function, *Coverage);
+ if (!mainView) {
+ ViewOpts.colored_ostream(outs(), raw_ostream::RED)
+ << "warning: Could not read coverage for '" << Function.Name;
outs() << "\n";
+ continue;
+ }
+ ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name
+ << ":";
+ outs() << "\n";
+ mainView->render(outs(), /*WholeFile=*/false);
+ outs() << "\n";
}
return 0;
}
if (SourceFiles.empty())
// Get the source files from the function coverage mapping
- for (StringRef Filename : getUniqueFilenames(FunctionMappingRecords))
+ for (StringRef Filename : Coverage->getUniqueSourceFiles())
SourceFiles.push_back(Filename);
for (const auto &SourceFile : SourceFiles) {
- auto SourceBuffer = getSourceFile(SourceFile);
- if (!SourceBuffer)
- return 1;
- SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
- if (createSourceFileView(SourceFile, mainView, FunctionMappingRecords)) {
+ auto mainView = createSourceFileView(SourceFile, *Coverage);
+ if (!mainView) {
ViewOpts.colored_ostream(outs(), raw_ostream::RED)
<< "warning: The file '" << SourceFile << "' isn't covered.";
outs() << "\n";
ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
outs() << "\n";
}
- mainView.render(outs(), /*Wholefile=*/true);
+ mainView->render(outs(), /*Wholefile=*/true);
if (SourceFiles.size() > 1)
outs() << "\n";
}
ViewOpts.Colors = !NoColors;
- if (load())
+ auto Coverage = load();
+ if (!Coverage)
return 1;
- CoverageSummary Summarizer;
- Summarizer.createSummaries(FunctionMappingRecords);
- CoverageReport Report(ViewOpts, Summarizer);
- if (SourceFiles.empty() && Filters.empty()) {
+ CoverageReport Report(ViewOpts, std::move(Coverage));
+ if (SourceFiles.empty())
Report.renderFileReports(llvm::outs());
- return 0;
- }
-
- Report.renderFunctionReports(llvm::outs());
+ else
+ Report.renderFunctionReports(SourceFiles, llvm::outs());
return 0;
}
-int show_main(int argc, const char **argv) {
+int showMain(int argc, const char *argv[]) {
CodeCoverageTool Tool;
return Tool.run(CodeCoverageTool::Show, argc, argv);
}
-int report_main(int argc, const char **argv) {
+int reportMain(int argc, const char *argv[]) {
CodeCoverageTool Tool;
return Tool.run(CodeCoverageTool::Report, argc, argv);
}