llvm-6502/tools/llvm-cov/CodeCoverage.cpp
Alex Lorenz 6c7a6a1ba2 llvm-cov: add code coverage tool that's based on coverage mapping format and clang's pgo.
This commit expands llvm-cov's functionality by adding support for a new code coverage
tool that uses LLVM's coverage mapping format and clang's instrumentation based profiling.
The gcov compatible tool can be invoked by supplying the 'gcov' command as the first argument,
or by modifying the tool's name to end with 'gcov'.

Differential Revision: http://reviews.llvm.org/D4445


git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216300 91177308-0d34-0410-b5e6-96231b3b80d8
2014-08-22 22:56:03 +00:00

710 lines
25 KiB
C++

//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// The 'CodeCoverageTool' class implements a command line tool to analyze and
// report coverage information using the profiling instrumentation and code
// coverage mapping.
//
//===----------------------------------------------------------------------===//
#include "FunctionCoverageMapping.h"
#include "RenderingSupport.h"
#include "CoverageViewOptions.h"
#include "CoverageFilters.h"
#include "SourceCoverageDataManager.h"
#include "SourceCoverageView.h"
#include "CoverageSummary.h"
#include "CoverageReport.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/ProfileData/CoverageMapping.h"
#include "llvm/ProfileData/CoverageMappingReader.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/Path.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/PrettyStackTrace.h"
#include <system_error>
#include <functional>
using namespace llvm;
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, e.g.
/// template functions specializations.
class FunctionInstantiationSetCollector {
ArrayRef<FunctionCoverageMapping> FunctionMappings;
typedef uint64_t KeyType;
typedef std::vector<const FunctionCoverageMapping *> SetType;
std::unordered_map<uint64_t, SetType> InstantiatedFunctions;
static KeyType getKey(const MappingRegion &R) {
return uint64_t(R.LineStart) | uint64_t(R.ColumnStart) << 32;
}
public:
void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
KeyType Key = 0;
for (const auto &R : Function.MappingRegions) {
if (R.FileID == FileID) {
Key = getKey(R);
break;
}
}
auto I = InstantiatedFunctions.find(Key);
if (I == InstantiatedFunctions.end()) {
SetType Set;
Set.push_back(&Function);
InstantiatedFunctions.insert(std::make_pair(Key, Set));
} else
I->second.push_back(&Function);
}
std::unordered_map<KeyType, SetType>::iterator begin() {
return InstantiatedFunctions.begin();
}
std::unordered_map<KeyType, SetType>::iterator end() {
return InstantiatedFunctions.end();
}
};
/// \brief The implementation of the coverage tool.
class CodeCoverageTool {
public:
enum Command {
/// \brief The show command.
Show,
/// \brief The report command.
Report
};
/// \brief Print the error message to the error output stream.
void error(const Twine &Message, StringRef Whence = "");
/// \brief Return a memory buffer for the given source file.
ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
/// \brief Return true if two filepaths refer to the same file.
bool equivalentFiles(StringRef A, StringRef B);
/// \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 a source view which shows coverage for an expansion
/// of a file.
void createExpansionSubView(const MappingRegion &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 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);
/// \brief Load the coverage mapping data. Return true if an error occured.
bool load();
int run(Command Cmd, int argc, const char **argv);
typedef std::function<int(int, const char **)> CommandLineParserType;
int show(int argc, const char **argv,
CommandLineParserType commandLineParser);
int report(int argc, const char **argv,
CommandLineParserType commandLineParser);
StringRef ObjectFilename;
CoverageViewOptions ViewOpts;
std::unique_ptr<IndexedInstrProfReader> PGOReader;
CoverageFiltersMatchAll Filters;
std::vector<std::string> SourceFiles;
std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
LoadedSourceFiles;
std::vector<FunctionCoverageMapping> FunctionMappingRecords;
bool CompareFilenamesOnly;
};
}
void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
errs() << "error: ";
if (!Whence.empty())
errs() << Whence << ": ";
errs() << Message << "\n";
}
ErrorOr<const MemoryBuffer &>
CodeCoverageTool::getSourceFile(StringRef SourceFile) {
SmallString<256> Path(SourceFile);
sys::fs::make_absolute(Path);
for (const auto &Files : LoadedSourceFiles) {
if (sys::fs::equivalent(Path.str(), Files.first)) {
return *Files.second;
}
}
auto Buffer = MemoryBuffer::getFile(SourceFile);
if (auto EC = Buffer.getError()) {
error(EC.message(), SourceFile);
return EC;
}
LoadedSourceFiles.push_back(std::make_pair(
std::string(Path.begin(), Path.end()), std::move(Buffer.get())));
return *LoadedSourceFiles.back().second;
}
/// \brief Return a line start - line end range which contains
/// all the mapping regions of a given function with a particular file id.
std::pair<unsigned, unsigned>
findExpandedFileInterestingLineRange(unsigned FileID,
const FunctionCoverageMapping &Function) {
unsigned LineStart = std::numeric_limits<unsigned>::max();
unsigned LineEnd = 0;
for (const auto &Region : Function.MappingRegions) {
if (Region.FileID != FileID)
continue;
LineStart = std::min(Region.LineStart, LineStart);
LineEnd = std::max(Region.LineEnd, LineEnd);
}
return std::make_pair(LineStart, LineEnd);
}
bool CodeCoverageTool::equivalentFiles(StringRef A, StringRef B) {
if (CompareFilenamesOnly)
return sys::path::filename(A).equals_lower(sys::path::filename(B));
return sys::fs::equivalent(A, B);
}
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 (equivalentFiles(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 (equivalentFiles(SourceFile, Function.Filenames[I]))
FilenameEquivalence[I] = true;
}
for (const auto &Region : Function.MappingRegions) {
if (Region.Kind == MappingRegion::ExpansionRegion &&
FilenameEquivalence[Region.FileID])
IsExpandedFile[Region.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 &Region : Function.MappingRegions) {
if (Region.Kind == MappingRegion::ExpansionRegion)
IsExpandedFile[Region.ExpandedFileID] = true;
}
for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
if (IsExpandedFile[I])
continue;
MainViewFileID = I;
return false;
}
return true;
}
void CodeCoverageTool::createExpansionSubView(
const MappingRegion &ExpandedRegion,
const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {
auto ExpandedLines = findExpandedFileInterestingLineRange(
ExpandedRegion.ExpandedFileID, Function);
if (ViewOpts.Debug)
llvm::outs() << "Expansion of " << ExpandedRegion.ExpandedFileID << ":"
<< ExpandedLines.first << " -> " << ExpandedLines.second
<< " @ " << ExpandedRegion.FileID << ", "
<< ExpandedRegion.LineStart << ":"
<< ExpandedRegion.ColumnStart << "\n";
auto SourceBuffer =
getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
if (!SourceBuffer)
return;
auto SubView = llvm::make_unique<SourceCoverageView>(
SourceBuffer.get(), Parent.getOptions(), ExpandedLines.first,
ExpandedLines.second, ExpandedRegion);
SourceCoverageDataManager RegionManager;
for (const auto &Region : Function.MappingRegions) {
if (Region.FileID == ExpandedRegion.ExpandedFileID)
RegionManager.insert(Region);
}
SubView->load(RegionManager);
createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
Parent.addChild(std::move(SubView));
}
void CodeCoverageTool::createExpansionSubViews(
SourceCoverageView &View, unsigned ViewFileID,
const FunctionCoverageMapping &Function) {
if (!ViewOpts.ShowExpandedRegions)
return;
for (const auto &Region : Function.MappingRegions) {
if (Region.Kind != CounterMappingRegion::ExpansionRegion)
continue;
if (Region.FileID != ViewFileID)
continue;
createExpansionSubView(Region, Function, View);
}
}
void CodeCoverageTool::createInstantiationSubView(
StringRef SourceFile, const FunctionCoverageMapping &Function,
SourceCoverageView &View) {
SourceCoverageDataManager RegionManager;
SmallSet<unsigned, 8> InterestingFileIDs;
if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
return;
// Get the interesting regions
for (const auto &Region : Function.MappingRegions) {
if (InterestingFileIDs.count(Region.FileID))
RegionManager.insert(Region);
}
View.load(RegionManager);
unsigned MainFileID;
if (findMainViewFileID(SourceFile, Function, MainFileID))
return;
createExpansionSubViews(View, MainFileID, Function);
}
bool CodeCoverageTool::createSourceFileView(
StringRef SourceFile, SourceCoverageView &View,
ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
bool UseOnlyRegionsInMainFile) {
SourceCoverageDataManager RegionManager;
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 &Region : Function.MappingRegions) {
if (InterestingFileIDs.count(Region.FileID))
RegionManager.insert(Region);
}
InstantiationSetCollector.insert(Function, MainFileID);
createExpansionSubViews(View, MainFileID, Function);
}
if (RegionManager.getSourceRegions().empty())
return true;
View.load(RegionManager);
// Show instantiations
if (!ViewOpts.ShowFunctionInstantiations)
return false;
for (const auto &InstantiationSet : InstantiationSetCollector) {
if (InstantiationSet.second.size() < 2)
continue;
auto InterestingRange = findExpandedFileInterestingLineRange(
InstantiationSet.second.front()->MappingRegions.front().FileID,
*InstantiationSet.second.front());
for (auto Function : InstantiationSet.second) {
auto SubView = llvm::make_unique<SourceCoverageView>(
View, InterestingRange.first, InterestingRange.second,
Function->PrettyName);
createInstantiationSubView(SourceFile, *Function, *SubView);
View.addChild(std::move(SubView));
}
}
return false;
}
bool CodeCoverageTool::load() {
auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
if (auto EC = CounterMappingBuff.getError()) {
error(EC.message(), ObjectFilename);
return true;
}
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) {
outs() << "File " << R.FileID << "| " << R.LineStart << ":"
<< R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd
<< " = ";
Ctx.dump(R.Count);
if (R.Kind == CounterMappingRegion::ExpansionRegion) {
outs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
}
outs() << "\n";
}
std::error_code Error;
Function.MappingRegions.push_back(
MappingRegion(R, Ctx.evaluate(R.Count, Error)));
if (Error && !RegionError) {
colored_ostream(errs(), raw_ostream::RED)
<< "error: Regions and counters don't match in a function '"
<< Function.PrettyName << "' (re-run the instrumented binary).";
errs() << "\n";
RegionError = true;
}
}
if (RegionError || !Filters.matches(Function))
continue;
FunctionMappingRecords.push_back(Function);
}
return false;
}
int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
// Print a stack trace if we signal out.
sys::PrintStackTraceOnErrorSignal();
PrettyStackTraceProgram X(argc, argv);
llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::list<std::string> InputSourceFiles(
cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
cl::opt<std::string> PGOFilename(
"instr-profile", cl::Required,
cl::desc(
"File with the profile data obtained after an instrumented run"));
cl::opt<bool> DebugDump("dump", cl::Optional,
cl::desc("Show internal debug dump"));
cl::opt<bool> FilenameEquivalence(
"filename-equivalence", cl::Optional,
cl::desc("Compare the filenames instead of full filepaths"));
cl::OptionCategory FilteringCategory("Function filtering options");
cl::list<std::string> NameFilters(
"name", cl::Optional,
cl::desc("Show code coverage only for functions with the given name"),
cl::ZeroOrMore, cl::cat(FilteringCategory));
cl::list<std::string> NameRegexFilters(
"name-regex", cl::Optional,
cl::desc("Show code coverage only for functions that match the given "
"regular expression"),
cl::ZeroOrMore, cl::cat(FilteringCategory));
cl::opt<double> RegionCoverageLtFilter(
"region-coverage-lt", cl::Optional,
cl::desc("Show code coverage only for functions with region coverage "
"less than the given threshold"),
cl::cat(FilteringCategory));
cl::opt<double> RegionCoverageGtFilter(
"region-coverage-gt", cl::Optional,
cl::desc("Show code coverage only for functions with region coverage "
"greater than the given threshold"),
cl::cat(FilteringCategory));
cl::opt<double> LineCoverageLtFilter(
"line-coverage-lt", cl::Optional,
cl::desc("Show code coverage only for functions with line coverage less "
"than the given threshold"),
cl::cat(FilteringCategory));
cl::opt<double> LineCoverageGtFilter(
"line-coverage-gt", cl::Optional,
cl::desc("Show code coverage only for functions with line coverage "
"greater than the given threshold"),
cl::cat(FilteringCategory));
auto commandLineParser = [&, this](int argc, const char **argv) -> int {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
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 &Name : NameFilters)
NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
for (const auto &Regex : NameRegexFilters)
NameFilterer->push_back(
llvm::make_unique<NameRegexCoverageFilter>(Regex));
Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
}
if (RegionCoverageLtFilter.getNumOccurrences() ||
RegionCoverageGtFilter.getNumOccurrences() ||
LineCoverageLtFilter.getNumOccurrences() ||
LineCoverageGtFilter.getNumOccurrences()) {
auto StatFilterer = new CoverageFilters;
if (RegionCoverageLtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
if (RegionCoverageGtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
if (LineCoverageLtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
LineCoverageFilter::LessThan, LineCoverageLtFilter));
if (LineCoverageGtFilter.getNumOccurrences())
StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
}
SourceFiles = InputSourceFiles;
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);
case Report:
return report(argc, argv, commandLineParser);
}
return 0;
}
int CodeCoverageTool::show(int argc, const char **argv,
CommandLineParserType commandLineParser) {
cl::OptionCategory ViewCategory("Viewing options");
cl::opt<bool> ShowLineExecutionCounts(
"show-line-counts", cl::Optional,
cl::desc("Show the execution counts for each line"), cl::init(true),
cl::cat(ViewCategory));
cl::opt<bool> ShowRegions(
"show-regions", cl::Optional,
cl::desc("Show the execution counts for each region"),
cl::cat(ViewCategory));
cl::opt<bool> ShowBestLineRegionsCounts(
"show-line-counts-or-regions", cl::Optional,
cl::desc("Show the execution counts for each line, or the execution "
"counts for each region on lines that have multiple regions"),
cl::cat(ViewCategory));
cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
cl::desc("Show expanded source regions"),
cl::cat(ViewCategory));
cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
cl::desc("Show function instantiations"),
cl::cat(ViewCategory));
cl::opt<bool> NoColors("no-colors", cl::Optional,
cl::desc("Don't show text colors"), cl::init(false),
cl::cat(ViewCategory));
auto Err = commandLineParser(argc, argv);
if (Err)
return Err;
ViewOpts.Colors = !NoColors;
ViewOpts.ShowLineNumbers = true;
ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
!ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
ViewOpts.ShowExpandedRegions = ShowExpansions;
ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
if (load())
return 1;
if (!Filters.empty()) {
// Show functions
for (const auto &Function : FunctionMappingRecords) {
unsigned MainFileID;
if (findMainViewFileID(Function, MainFileID))
continue;
StringRef SourceFile = Function.Filenames[MainFileID];
std::unique_ptr<SourceCoverageView> mainView;
auto SourceBuffer = getSourceFile(SourceFile);
if (!SourceBuffer)
return 1;
auto Range = findExpandedFileInterestingLineRange(MainFileID, Function);
mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts,
Range.first, Range.second));
createSourceFileView(SourceFile, *mainView, Function, true);
ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
<< Function.PrettyName << " from " << SourceFile << ":";
outs() << "\n";
mainView->render(outs());
if (FunctionMappingRecords.size() > 1)
outs() << "\n";
}
return 0;
}
// Show files
bool ShowFilenames = SourceFiles.size() != 1;
if (SourceFiles.empty()) {
// Get the source files from the function coverage mapping
std::set<StringRef> UniqueFilenames;
for (const auto &Function : FunctionMappingRecords) {
for (const auto &Filename : Function.Filenames)
UniqueFilenames.insert(Filename);
}
for (const auto &Filename : UniqueFilenames)
SourceFiles.push_back(Filename);
}
for (const auto &SourceFile : SourceFiles) {
std::unique_ptr<SourceCoverageView> mainView;
auto SourceBuffer = getSourceFile(SourceFile);
if (!SourceBuffer)
return 1;
mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts));
if (createSourceFileView(SourceFile, *mainView, FunctionMappingRecords)) {
ViewOpts.colored_ostream(outs(), raw_ostream::RED)
<< "warning: The file '" << SourceFile << "' isn't covered.";
outs() << "\n";
continue;
}
if (ShowFilenames) {
ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
outs() << "\n";
}
mainView->render(outs());
if (SourceFiles.size() > 1)
outs() << "\n";
}
return 0;
}
int CodeCoverageTool::report(int argc, const char **argv,
CommandLineParserType commandLineParser) {
cl::opt<bool> NoColors("no-colors", cl::Optional,
cl::desc("Don't show text colors"), cl::init(false));
auto Err = commandLineParser(argc, argv);
if (Err)
return Err;
ViewOpts.Colors = !NoColors;
if (load())
return 1;
CoverageSummary Summarizer;
Summarizer.createSummaries(FunctionMappingRecords);
CoverageReport Report(ViewOpts, Summarizer);
if (SourceFiles.empty() && Filters.empty()) {
Report.renderFileReports(llvm::outs());
return 0;
}
Report.renderFunctionReports(llvm::outs());
return 0;
}
int show_main(int argc, const char **argv) {
CodeCoverageTool Tool;
return Tool.run(CodeCoverageTool::Show, argc, argv);
}
int report_main(int argc, const char **argv) {
CodeCoverageTool Tool;
return Tool.run(CodeCoverageTool::Report, argc, argv);
}