Mercurial > hg > CbC > CbC_llvm
diff tools/llvm-cov/CodeCoverage.cpp @ 77:54457678186b LLVM3.6
LLVM 3.6
author | Kaito Tokumori <e105711@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 08 Sep 2014 22:06:00 +0900 |
parents | |
children | 60c9769439b8 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tools/llvm-cov/CodeCoverage.cpp Mon Sep 08 22:06:00 2014 +0900 @@ -0,0 +1,707 @@ +//===- 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]; + auto SourceBuffer = getSourceFile(SourceFile); + if (!SourceBuffer) + return 1; + auto Range = findExpandedFileInterestingLineRange(MainFileID, Function); + SourceCoverageView mainView(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) { + auto SourceBuffer = getSourceFile(SourceFile); + if (!SourceBuffer) + return 1; + SourceCoverageView mainView(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); +}