109467b48Spatrick //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // The 'CodeCoverageTool' class implements a command line tool to analyze and
1009467b48Spatrick // report coverage information using the profiling instrumentation and code
1109467b48Spatrick // coverage mapping.
1209467b48Spatrick //
1309467b48Spatrick //===----------------------------------------------------------------------===//
1409467b48Spatrick
1509467b48Spatrick #include "CoverageExporterJson.h"
1609467b48Spatrick #include "CoverageExporterLcov.h"
1709467b48Spatrick #include "CoverageFilters.h"
1809467b48Spatrick #include "CoverageReport.h"
1909467b48Spatrick #include "CoverageSummaryInfo.h"
2009467b48Spatrick #include "CoverageViewOptions.h"
2109467b48Spatrick #include "RenderingSupport.h"
2209467b48Spatrick #include "SourceCoverageView.h"
2309467b48Spatrick #include "llvm/ADT/SmallString.h"
2409467b48Spatrick #include "llvm/ADT/StringRef.h"
2509467b48Spatrick #include "llvm/ADT/Triple.h"
26*d415bd75Srobert #include "llvm/Debuginfod/BuildIDFetcher.h"
27*d415bd75Srobert #include "llvm/Debuginfod/Debuginfod.h"
28*d415bd75Srobert #include "llvm/Debuginfod/HTTPClient.h"
29*d415bd75Srobert #include "llvm/Object/BuildID.h"
3009467b48Spatrick #include "llvm/ProfileData/Coverage/CoverageMapping.h"
3109467b48Spatrick #include "llvm/ProfileData/InstrProfReader.h"
3209467b48Spatrick #include "llvm/Support/CommandLine.h"
3309467b48Spatrick #include "llvm/Support/FileSystem.h"
3409467b48Spatrick #include "llvm/Support/Format.h"
3509467b48Spatrick #include "llvm/Support/MemoryBuffer.h"
3609467b48Spatrick #include "llvm/Support/Path.h"
3709467b48Spatrick #include "llvm/Support/Process.h"
3809467b48Spatrick #include "llvm/Support/Program.h"
3909467b48Spatrick #include "llvm/Support/ScopedPrinter.h"
40097a140dSpatrick #include "llvm/Support/SpecialCaseList.h"
4109467b48Spatrick #include "llvm/Support/ThreadPool.h"
4209467b48Spatrick #include "llvm/Support/Threading.h"
4309467b48Spatrick #include "llvm/Support/ToolOutputFile.h"
4409467b48Spatrick #include "llvm/Support/VirtualFileSystem.h"
4509467b48Spatrick
4609467b48Spatrick #include <functional>
4709467b48Spatrick #include <map>
48*d415bd75Srobert #include <optional>
4909467b48Spatrick #include <system_error>
5009467b48Spatrick
5109467b48Spatrick using namespace llvm;
5209467b48Spatrick using namespace coverage;
5309467b48Spatrick
5409467b48Spatrick void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping,
5509467b48Spatrick const CoverageViewOptions &Options,
5609467b48Spatrick raw_ostream &OS);
5709467b48Spatrick
5809467b48Spatrick namespace {
5909467b48Spatrick /// The implementation of the coverage tool.
6009467b48Spatrick class CodeCoverageTool {
6109467b48Spatrick public:
6209467b48Spatrick enum Command {
6309467b48Spatrick /// The show command.
6409467b48Spatrick Show,
6509467b48Spatrick /// The report command.
6609467b48Spatrick Report,
6709467b48Spatrick /// The export command.
6809467b48Spatrick Export
6909467b48Spatrick };
7009467b48Spatrick
7109467b48Spatrick int run(Command Cmd, int argc, const char **argv);
7209467b48Spatrick
7309467b48Spatrick private:
7409467b48Spatrick /// Print the error message to the error output stream.
7509467b48Spatrick void error(const Twine &Message, StringRef Whence = "");
7609467b48Spatrick
7709467b48Spatrick /// Print the warning message to the error output stream.
7809467b48Spatrick void warning(const Twine &Message, StringRef Whence = "");
7909467b48Spatrick
8009467b48Spatrick /// Convert \p Path into an absolute path and append it to the list
8109467b48Spatrick /// of collected paths.
8209467b48Spatrick void addCollectedPath(const std::string &Path);
8309467b48Spatrick
8409467b48Spatrick /// If \p Path is a regular file, collect the path. If it's a
8509467b48Spatrick /// directory, recursively collect all of the paths within the directory.
8609467b48Spatrick void collectPaths(const std::string &Path);
8709467b48Spatrick
8873471bf0Spatrick /// Check if the two given files are the same file.
8973471bf0Spatrick bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2);
9073471bf0Spatrick
9173471bf0Spatrick /// Retrieve a file status with a cache.
92*d415bd75Srobert std::optional<sys::fs::file_status> getFileStatus(StringRef FilePath);
9373471bf0Spatrick
9409467b48Spatrick /// Return a memory buffer for the given source file.
9509467b48Spatrick ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
9609467b48Spatrick
9709467b48Spatrick /// Create source views for the expansions of the view.
9809467b48Spatrick void attachExpansionSubViews(SourceCoverageView &View,
9909467b48Spatrick ArrayRef<ExpansionRecord> Expansions,
10009467b48Spatrick const CoverageMapping &Coverage);
10109467b48Spatrick
10273471bf0Spatrick /// Create source views for the branches of the view.
10373471bf0Spatrick void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
10473471bf0Spatrick ArrayRef<CountedRegion> Branches,
10573471bf0Spatrick const MemoryBuffer &File,
10673471bf0Spatrick CoverageData &CoverageInfo);
10773471bf0Spatrick
10809467b48Spatrick /// Create the source view of a particular function.
10909467b48Spatrick std::unique_ptr<SourceCoverageView>
11009467b48Spatrick createFunctionView(const FunctionRecord &Function,
11109467b48Spatrick const CoverageMapping &Coverage);
11209467b48Spatrick
11309467b48Spatrick /// Create the main source view of a particular source file.
11409467b48Spatrick std::unique_ptr<SourceCoverageView>
11509467b48Spatrick createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
11609467b48Spatrick
11709467b48Spatrick /// Load the coverage mapping data. Return nullptr if an error occurred.
11809467b48Spatrick std::unique_ptr<CoverageMapping> load();
11909467b48Spatrick
12009467b48Spatrick /// Create a mapping from files in the Coverage data to local copies
12109467b48Spatrick /// (path-equivalence).
12209467b48Spatrick void remapPathNames(const CoverageMapping &Coverage);
12309467b48Spatrick
12409467b48Spatrick /// Remove input source files which aren't mapped by \p Coverage.
12509467b48Spatrick void removeUnmappedInputs(const CoverageMapping &Coverage);
12609467b48Spatrick
12709467b48Spatrick /// If a demangler is available, demangle all symbol names.
12809467b48Spatrick void demangleSymbols(const CoverageMapping &Coverage);
12909467b48Spatrick
13009467b48Spatrick /// Write out a source file view to the filesystem.
13109467b48Spatrick void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
13209467b48Spatrick CoveragePrinter *Printer, bool ShowFilenames);
13309467b48Spatrick
13409467b48Spatrick typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
13509467b48Spatrick
13609467b48Spatrick int doShow(int argc, const char **argv,
13709467b48Spatrick CommandLineParserType commandLineParser);
13809467b48Spatrick
13909467b48Spatrick int doReport(int argc, const char **argv,
14009467b48Spatrick CommandLineParserType commandLineParser);
14109467b48Spatrick
14209467b48Spatrick int doExport(int argc, const char **argv,
14309467b48Spatrick CommandLineParserType commandLineParser);
14409467b48Spatrick
14509467b48Spatrick std::vector<StringRef> ObjectFilenames;
14609467b48Spatrick CoverageViewOptions ViewOpts;
14709467b48Spatrick CoverageFiltersMatchAll Filters;
14809467b48Spatrick CoverageFilters IgnoreFilenameFilters;
14909467b48Spatrick
15073471bf0Spatrick /// True if InputSourceFiles are provided.
15173471bf0Spatrick bool HadSourceFiles = false;
15273471bf0Spatrick
15309467b48Spatrick /// The path to the indexed profile.
15409467b48Spatrick std::string PGOFilename;
15509467b48Spatrick
15609467b48Spatrick /// A list of input source files.
15709467b48Spatrick std::vector<std::string> SourceFiles;
15809467b48Spatrick
15909467b48Spatrick /// In -path-equivalence mode, this maps the absolute paths from the coverage
16009467b48Spatrick /// mapping data to the input source files.
16109467b48Spatrick StringMap<std::string> RemappedFilenames;
16209467b48Spatrick
16309467b48Spatrick /// The coverage data path to be remapped from, and the source path to be
16409467b48Spatrick /// remapped to, when using -path-equivalence.
165*d415bd75Srobert std::optional<std::pair<std::string, std::string>> PathRemapping;
16609467b48Spatrick
16773471bf0Spatrick /// File status cache used when finding the same file.
168*d415bd75Srobert StringMap<std::optional<sys::fs::file_status>> FileStatusCache;
16973471bf0Spatrick
17009467b48Spatrick /// The architecture the coverage mapping data targets.
17109467b48Spatrick std::vector<StringRef> CoverageArches;
17209467b48Spatrick
17309467b48Spatrick /// A cache for demangled symbols.
17409467b48Spatrick DemangleCache DC;
17509467b48Spatrick
17609467b48Spatrick /// A lock which guards printing to stderr.
17709467b48Spatrick std::mutex ErrsLock;
17809467b48Spatrick
17909467b48Spatrick /// A container for input source file buffers.
18009467b48Spatrick std::mutex LoadedSourceFilesLock;
18109467b48Spatrick std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
18209467b48Spatrick LoadedSourceFiles;
18309467b48Spatrick
184*d415bd75Srobert /// Allowlist from -name-allowlist to be used for filtering.
185*d415bd75Srobert std::unique_ptr<SpecialCaseList> NameAllowlist;
186*d415bd75Srobert
187*d415bd75Srobert std::unique_ptr<object::BuildIDFetcher> BIDFetcher;
18809467b48Spatrick };
18909467b48Spatrick }
19009467b48Spatrick
getErrorString(const Twine & Message,StringRef Whence,bool Warning)19109467b48Spatrick static std::string getErrorString(const Twine &Message, StringRef Whence,
19209467b48Spatrick bool Warning) {
19309467b48Spatrick std::string Str = (Warning ? "warning" : "error");
19409467b48Spatrick Str += ": ";
19509467b48Spatrick if (!Whence.empty())
19609467b48Spatrick Str += Whence.str() + ": ";
19709467b48Spatrick Str += Message.str() + "\n";
19809467b48Spatrick return Str;
19909467b48Spatrick }
20009467b48Spatrick
error(const Twine & Message,StringRef Whence)20109467b48Spatrick void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
20209467b48Spatrick std::unique_lock<std::mutex> Guard{ErrsLock};
20309467b48Spatrick ViewOpts.colored_ostream(errs(), raw_ostream::RED)
20409467b48Spatrick << getErrorString(Message, Whence, false);
20509467b48Spatrick }
20609467b48Spatrick
warning(const Twine & Message,StringRef Whence)20709467b48Spatrick void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
20809467b48Spatrick std::unique_lock<std::mutex> Guard{ErrsLock};
20909467b48Spatrick ViewOpts.colored_ostream(errs(), raw_ostream::RED)
21009467b48Spatrick << getErrorString(Message, Whence, true);
21109467b48Spatrick }
21209467b48Spatrick
addCollectedPath(const std::string & Path)21309467b48Spatrick void CodeCoverageTool::addCollectedPath(const std::string &Path) {
21409467b48Spatrick SmallString<128> EffectivePath(Path);
21509467b48Spatrick if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
21609467b48Spatrick error(EC.message(), Path);
21709467b48Spatrick return;
21809467b48Spatrick }
219*d415bd75Srobert sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true);
22009467b48Spatrick if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
22109467b48Spatrick SourceFiles.emplace_back(EffectivePath.str());
22273471bf0Spatrick HadSourceFiles = !SourceFiles.empty();
22309467b48Spatrick }
22409467b48Spatrick
collectPaths(const std::string & Path)22509467b48Spatrick void CodeCoverageTool::collectPaths(const std::string &Path) {
22609467b48Spatrick llvm::sys::fs::file_status Status;
22709467b48Spatrick llvm::sys::fs::status(Path, Status);
22809467b48Spatrick if (!llvm::sys::fs::exists(Status)) {
22909467b48Spatrick if (PathRemapping)
23009467b48Spatrick addCollectedPath(Path);
23109467b48Spatrick else
23209467b48Spatrick warning("Source file doesn't exist, proceeded by ignoring it.", Path);
23309467b48Spatrick return;
23409467b48Spatrick }
23509467b48Spatrick
23609467b48Spatrick if (llvm::sys::fs::is_regular_file(Status)) {
23709467b48Spatrick addCollectedPath(Path);
23809467b48Spatrick return;
23909467b48Spatrick }
24009467b48Spatrick
24109467b48Spatrick if (llvm::sys::fs::is_directory(Status)) {
24209467b48Spatrick std::error_code EC;
24309467b48Spatrick for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
24409467b48Spatrick F != E; F.increment(EC)) {
24509467b48Spatrick
24609467b48Spatrick auto Status = F->status();
24709467b48Spatrick if (!Status) {
24809467b48Spatrick warning(Status.getError().message(), F->path());
24909467b48Spatrick continue;
25009467b48Spatrick }
25109467b48Spatrick
25209467b48Spatrick if (Status->type() == llvm::sys::fs::file_type::regular_file)
25309467b48Spatrick addCollectedPath(F->path());
25409467b48Spatrick }
25509467b48Spatrick }
25609467b48Spatrick }
25709467b48Spatrick
258*d415bd75Srobert std::optional<sys::fs::file_status>
getFileStatus(StringRef FilePath)25973471bf0Spatrick CodeCoverageTool::getFileStatus(StringRef FilePath) {
26073471bf0Spatrick auto It = FileStatusCache.try_emplace(FilePath);
26173471bf0Spatrick auto &CachedStatus = It.first->getValue();
26273471bf0Spatrick if (!It.second)
26373471bf0Spatrick return CachedStatus;
26473471bf0Spatrick
26573471bf0Spatrick sys::fs::file_status Status;
26673471bf0Spatrick if (!sys::fs::status(FilePath, Status))
26773471bf0Spatrick CachedStatus = Status;
26873471bf0Spatrick return CachedStatus;
26973471bf0Spatrick }
27073471bf0Spatrick
isEquivalentFile(StringRef FilePath1,StringRef FilePath2)27173471bf0Spatrick bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1,
27273471bf0Spatrick StringRef FilePath2) {
27373471bf0Spatrick auto Status1 = getFileStatus(FilePath1);
27473471bf0Spatrick auto Status2 = getFileStatus(FilePath2);
275*d415bd75Srobert return Status1 && Status2 && sys::fs::equivalent(*Status1, *Status2);
27673471bf0Spatrick }
27773471bf0Spatrick
27809467b48Spatrick ErrorOr<const MemoryBuffer &>
getSourceFile(StringRef SourceFile)27909467b48Spatrick CodeCoverageTool::getSourceFile(StringRef SourceFile) {
28009467b48Spatrick // If we've remapped filenames, look up the real location for this file.
28109467b48Spatrick std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
28209467b48Spatrick if (!RemappedFilenames.empty()) {
28309467b48Spatrick auto Loc = RemappedFilenames.find(SourceFile);
28409467b48Spatrick if (Loc != RemappedFilenames.end())
28509467b48Spatrick SourceFile = Loc->second;
28609467b48Spatrick }
28709467b48Spatrick for (const auto &Files : LoadedSourceFiles)
28873471bf0Spatrick if (isEquivalentFile(SourceFile, Files.first))
28909467b48Spatrick return *Files.second;
29009467b48Spatrick auto Buffer = MemoryBuffer::getFile(SourceFile);
29109467b48Spatrick if (auto EC = Buffer.getError()) {
29209467b48Spatrick error(EC.message(), SourceFile);
29309467b48Spatrick return EC;
29409467b48Spatrick }
295097a140dSpatrick LoadedSourceFiles.emplace_back(std::string(SourceFile),
296097a140dSpatrick std::move(Buffer.get()));
29709467b48Spatrick return *LoadedSourceFiles.back().second;
29809467b48Spatrick }
29909467b48Spatrick
attachExpansionSubViews(SourceCoverageView & View,ArrayRef<ExpansionRecord> Expansions,const CoverageMapping & Coverage)30009467b48Spatrick void CodeCoverageTool::attachExpansionSubViews(
30109467b48Spatrick SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
30209467b48Spatrick const CoverageMapping &Coverage) {
30309467b48Spatrick if (!ViewOpts.ShowExpandedRegions)
30409467b48Spatrick return;
30509467b48Spatrick for (const auto &Expansion : Expansions) {
30609467b48Spatrick auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
30709467b48Spatrick if (ExpansionCoverage.empty())
30809467b48Spatrick continue;
30909467b48Spatrick auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
31009467b48Spatrick if (!SourceBuffer)
31109467b48Spatrick continue;
31209467b48Spatrick
31373471bf0Spatrick auto SubViewBranches = ExpansionCoverage.getBranches();
31409467b48Spatrick auto SubViewExpansions = ExpansionCoverage.getExpansions();
31509467b48Spatrick auto SubView =
31609467b48Spatrick SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
31709467b48Spatrick ViewOpts, std::move(ExpansionCoverage));
31809467b48Spatrick attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
31973471bf0Spatrick attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
32073471bf0Spatrick SourceBuffer.get(), ExpansionCoverage);
32109467b48Spatrick View.addExpansion(Expansion.Region, std::move(SubView));
32209467b48Spatrick }
32309467b48Spatrick }
32409467b48Spatrick
attachBranchSubViews(SourceCoverageView & View,StringRef SourceName,ArrayRef<CountedRegion> Branches,const MemoryBuffer & File,CoverageData & CoverageInfo)32573471bf0Spatrick void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
32673471bf0Spatrick StringRef SourceName,
32773471bf0Spatrick ArrayRef<CountedRegion> Branches,
32873471bf0Spatrick const MemoryBuffer &File,
32973471bf0Spatrick CoverageData &CoverageInfo) {
33073471bf0Spatrick if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
33173471bf0Spatrick return;
33273471bf0Spatrick
33373471bf0Spatrick const auto *NextBranch = Branches.begin();
33473471bf0Spatrick const auto *EndBranch = Branches.end();
33573471bf0Spatrick
33673471bf0Spatrick // Group branches that have the same line number into the same subview.
33773471bf0Spatrick while (NextBranch != EndBranch) {
33873471bf0Spatrick std::vector<CountedRegion> ViewBranches;
33973471bf0Spatrick unsigned CurrentLine = NextBranch->LineStart;
34073471bf0Spatrick
34173471bf0Spatrick while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
34273471bf0Spatrick ViewBranches.push_back(*NextBranch++);
34373471bf0Spatrick
34473471bf0Spatrick if (!ViewBranches.empty()) {
34573471bf0Spatrick auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
34673471bf0Spatrick std::move(CoverageInfo));
34773471bf0Spatrick View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
34873471bf0Spatrick }
34973471bf0Spatrick }
35073471bf0Spatrick }
35173471bf0Spatrick
35209467b48Spatrick std::unique_ptr<SourceCoverageView>
createFunctionView(const FunctionRecord & Function,const CoverageMapping & Coverage)35309467b48Spatrick CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
35409467b48Spatrick const CoverageMapping &Coverage) {
35509467b48Spatrick auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
35609467b48Spatrick if (FunctionCoverage.empty())
35709467b48Spatrick return nullptr;
35809467b48Spatrick auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
35909467b48Spatrick if (!SourceBuffer)
36009467b48Spatrick return nullptr;
36109467b48Spatrick
36273471bf0Spatrick auto Branches = FunctionCoverage.getBranches();
36309467b48Spatrick auto Expansions = FunctionCoverage.getExpansions();
36409467b48Spatrick auto View = SourceCoverageView::create(DC.demangle(Function.Name),
36509467b48Spatrick SourceBuffer.get(), ViewOpts,
36609467b48Spatrick std::move(FunctionCoverage));
36709467b48Spatrick attachExpansionSubViews(*View, Expansions, Coverage);
36873471bf0Spatrick attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
36973471bf0Spatrick SourceBuffer.get(), FunctionCoverage);
37009467b48Spatrick
37109467b48Spatrick return View;
37209467b48Spatrick }
37309467b48Spatrick
37409467b48Spatrick std::unique_ptr<SourceCoverageView>
createSourceFileView(StringRef SourceFile,const CoverageMapping & Coverage)37509467b48Spatrick CodeCoverageTool::createSourceFileView(StringRef SourceFile,
37609467b48Spatrick const CoverageMapping &Coverage) {
37709467b48Spatrick auto SourceBuffer = getSourceFile(SourceFile);
37809467b48Spatrick if (!SourceBuffer)
37909467b48Spatrick return nullptr;
38009467b48Spatrick auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
38109467b48Spatrick if (FileCoverage.empty())
38209467b48Spatrick return nullptr;
38309467b48Spatrick
38473471bf0Spatrick auto Branches = FileCoverage.getBranches();
38509467b48Spatrick auto Expansions = FileCoverage.getExpansions();
38609467b48Spatrick auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
38709467b48Spatrick ViewOpts, std::move(FileCoverage));
38809467b48Spatrick attachExpansionSubViews(*View, Expansions, Coverage);
38973471bf0Spatrick attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
39073471bf0Spatrick FileCoverage);
39109467b48Spatrick if (!ViewOpts.ShowFunctionInstantiations)
39209467b48Spatrick return View;
39309467b48Spatrick
39409467b48Spatrick for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) {
39509467b48Spatrick // Skip functions which have a single instantiation.
39609467b48Spatrick if (Group.size() < 2)
39709467b48Spatrick continue;
39809467b48Spatrick
39909467b48Spatrick for (const FunctionRecord *Function : Group.getInstantiations()) {
40009467b48Spatrick std::unique_ptr<SourceCoverageView> SubView{nullptr};
40109467b48Spatrick
40209467b48Spatrick StringRef Funcname = DC.demangle(Function->Name);
40309467b48Spatrick
40409467b48Spatrick if (Function->ExecutionCount > 0) {
40509467b48Spatrick auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
40609467b48Spatrick auto SubViewExpansions = SubViewCoverage.getExpansions();
40773471bf0Spatrick auto SubViewBranches = SubViewCoverage.getBranches();
40809467b48Spatrick SubView = SourceCoverageView::create(
40909467b48Spatrick Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
41009467b48Spatrick attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
41173471bf0Spatrick attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
41273471bf0Spatrick SourceBuffer.get(), SubViewCoverage);
41309467b48Spatrick }
41409467b48Spatrick
41509467b48Spatrick unsigned FileID = Function->CountedRegions.front().FileID;
41609467b48Spatrick unsigned Line = 0;
41709467b48Spatrick for (const auto &CR : Function->CountedRegions)
41809467b48Spatrick if (CR.FileID == FileID)
41909467b48Spatrick Line = std::max(CR.LineEnd, Line);
42009467b48Spatrick View->addInstantiation(Funcname, Line, std::move(SubView));
42109467b48Spatrick }
42209467b48Spatrick }
42309467b48Spatrick return View;
42409467b48Spatrick }
42509467b48Spatrick
modifiedTimeGT(StringRef LHS,StringRef RHS)42609467b48Spatrick static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
42709467b48Spatrick sys::fs::file_status Status;
42809467b48Spatrick if (sys::fs::status(LHS, Status))
42909467b48Spatrick return false;
43009467b48Spatrick auto LHSTime = Status.getLastModificationTime();
43109467b48Spatrick if (sys::fs::status(RHS, Status))
43209467b48Spatrick return false;
43309467b48Spatrick auto RHSTime = Status.getLastModificationTime();
43409467b48Spatrick return LHSTime > RHSTime;
43509467b48Spatrick }
43609467b48Spatrick
load()43709467b48Spatrick std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
43809467b48Spatrick for (StringRef ObjectFilename : ObjectFilenames)
43909467b48Spatrick if (modifiedTimeGT(ObjectFilename, PGOFilename))
44009467b48Spatrick warning("profile data may be out of date - object is newer",
44109467b48Spatrick ObjectFilename);
44209467b48Spatrick auto CoverageOrErr =
44373471bf0Spatrick CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches,
444*d415bd75Srobert ViewOpts.CompilationDirectory, BIDFetcher.get());
44509467b48Spatrick if (Error E = CoverageOrErr.takeError()) {
446*d415bd75Srobert error("Failed to load coverage: " + toString(std::move(E)));
44709467b48Spatrick return nullptr;
44809467b48Spatrick }
44909467b48Spatrick auto Coverage = std::move(CoverageOrErr.get());
45009467b48Spatrick unsigned Mismatched = Coverage->getMismatchedCount();
45109467b48Spatrick if (Mismatched) {
45209467b48Spatrick warning(Twine(Mismatched) + " functions have mismatched data");
45309467b48Spatrick
45409467b48Spatrick if (ViewOpts.Debug) {
45509467b48Spatrick for (const auto &HashMismatch : Coverage->getHashMismatches())
45609467b48Spatrick errs() << "hash-mismatch: "
45709467b48Spatrick << "No profile record found for '" << HashMismatch.first << "'"
45809467b48Spatrick << " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
45909467b48Spatrick << '\n';
46009467b48Spatrick }
46109467b48Spatrick }
46209467b48Spatrick
46309467b48Spatrick remapPathNames(*Coverage);
46409467b48Spatrick
46509467b48Spatrick if (!SourceFiles.empty())
46609467b48Spatrick removeUnmappedInputs(*Coverage);
46709467b48Spatrick
46809467b48Spatrick demangleSymbols(*Coverage);
46909467b48Spatrick
47009467b48Spatrick return Coverage;
47109467b48Spatrick }
47209467b48Spatrick
remapPathNames(const CoverageMapping & Coverage)47309467b48Spatrick void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
47409467b48Spatrick if (!PathRemapping)
47509467b48Spatrick return;
47609467b48Spatrick
47709467b48Spatrick // Convert remapping paths to native paths with trailing seperators.
47809467b48Spatrick auto nativeWithTrailing = [](StringRef Path) -> std::string {
47909467b48Spatrick if (Path.empty())
48009467b48Spatrick return "";
48109467b48Spatrick SmallString<128> NativePath;
48209467b48Spatrick sys::path::native(Path, NativePath);
48373471bf0Spatrick sys::path::remove_dots(NativePath, true);
48473471bf0Spatrick if (!NativePath.empty() && !sys::path::is_separator(NativePath.back()))
48509467b48Spatrick NativePath += sys::path::get_separator();
48609467b48Spatrick return NativePath.c_str();
48709467b48Spatrick };
48809467b48Spatrick std::string RemapFrom = nativeWithTrailing(PathRemapping->first);
48909467b48Spatrick std::string RemapTo = nativeWithTrailing(PathRemapping->second);
49009467b48Spatrick
49109467b48Spatrick // Create a mapping from coverage data file paths to local paths.
49209467b48Spatrick for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
49309467b48Spatrick SmallString<128> NativeFilename;
49409467b48Spatrick sys::path::native(Filename, NativeFilename);
49573471bf0Spatrick sys::path::remove_dots(NativeFilename, true);
49609467b48Spatrick if (NativeFilename.startswith(RemapFrom)) {
49709467b48Spatrick RemappedFilenames[Filename] =
49809467b48Spatrick RemapTo + NativeFilename.substr(RemapFrom.size()).str();
49909467b48Spatrick }
50009467b48Spatrick }
50109467b48Spatrick
50209467b48Spatrick // Convert input files from local paths to coverage data file paths.
50309467b48Spatrick StringMap<std::string> InvRemappedFilenames;
50409467b48Spatrick for (const auto &RemappedFilename : RemappedFilenames)
505097a140dSpatrick InvRemappedFilenames[RemappedFilename.getValue()] =
506097a140dSpatrick std::string(RemappedFilename.getKey());
50709467b48Spatrick
50809467b48Spatrick for (std::string &Filename : SourceFiles) {
50909467b48Spatrick SmallString<128> NativeFilename;
51009467b48Spatrick sys::path::native(Filename, NativeFilename);
51109467b48Spatrick auto CovFileName = InvRemappedFilenames.find(NativeFilename);
51209467b48Spatrick if (CovFileName != InvRemappedFilenames.end())
51309467b48Spatrick Filename = CovFileName->second;
51409467b48Spatrick }
51509467b48Spatrick }
51609467b48Spatrick
removeUnmappedInputs(const CoverageMapping & Coverage)51709467b48Spatrick void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
51809467b48Spatrick std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
51909467b48Spatrick
52009467b48Spatrick // The user may have specified source files which aren't in the coverage
52109467b48Spatrick // mapping. Filter these files away.
52273471bf0Spatrick llvm::erase_if(SourceFiles, [&](const std::string &SF) {
52373471bf0Spatrick return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF);
52409467b48Spatrick });
52509467b48Spatrick }
52609467b48Spatrick
demangleSymbols(const CoverageMapping & Coverage)52709467b48Spatrick void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
52809467b48Spatrick if (!ViewOpts.hasDemangler())
52909467b48Spatrick return;
53009467b48Spatrick
53109467b48Spatrick // Pass function names to the demangler in a temporary file.
53209467b48Spatrick int InputFD;
53309467b48Spatrick SmallString<256> InputPath;
53409467b48Spatrick std::error_code EC =
53509467b48Spatrick sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
53609467b48Spatrick if (EC) {
53709467b48Spatrick error(InputPath, EC.message());
53809467b48Spatrick return;
53909467b48Spatrick }
54009467b48Spatrick ToolOutputFile InputTOF{InputPath, InputFD};
54109467b48Spatrick
54209467b48Spatrick unsigned NumSymbols = 0;
54309467b48Spatrick for (const auto &Function : Coverage.getCoveredFunctions()) {
54409467b48Spatrick InputTOF.os() << Function.Name << '\n';
54509467b48Spatrick ++NumSymbols;
54609467b48Spatrick }
54709467b48Spatrick InputTOF.os().close();
54809467b48Spatrick
54909467b48Spatrick // Use another temporary file to store the demangler's output.
55009467b48Spatrick int OutputFD;
55109467b48Spatrick SmallString<256> OutputPath;
55209467b48Spatrick EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
55309467b48Spatrick OutputPath);
55409467b48Spatrick if (EC) {
55509467b48Spatrick error(OutputPath, EC.message());
55609467b48Spatrick return;
55709467b48Spatrick }
55809467b48Spatrick ToolOutputFile OutputTOF{OutputPath, OutputFD};
55909467b48Spatrick OutputTOF.os().close();
56009467b48Spatrick
56109467b48Spatrick // Invoke the demangler.
56209467b48Spatrick std::vector<StringRef> ArgsV;
563*d415bd75Srobert ArgsV.reserve(ViewOpts.DemanglerOpts.size());
56409467b48Spatrick for (StringRef Arg : ViewOpts.DemanglerOpts)
56509467b48Spatrick ArgsV.push_back(Arg);
566*d415bd75Srobert std::optional<StringRef> Redirects[] = {
567*d415bd75Srobert InputPath.str(), OutputPath.str(), {""}};
56809467b48Spatrick std::string ErrMsg;
569*d415bd75Srobert int RC =
570*d415bd75Srobert sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
571*d415bd75Srobert /*env=*/std::nullopt, Redirects, /*secondsToWait=*/0,
57209467b48Spatrick /*memoryLimit=*/0, &ErrMsg);
57309467b48Spatrick if (RC) {
57409467b48Spatrick error(ErrMsg, ViewOpts.DemanglerOpts[0]);
57509467b48Spatrick return;
57609467b48Spatrick }
57709467b48Spatrick
57809467b48Spatrick // Parse the demangler's output.
57909467b48Spatrick auto BufOrError = MemoryBuffer::getFile(OutputPath);
58009467b48Spatrick if (!BufOrError) {
58109467b48Spatrick error(OutputPath, BufOrError.getError().message());
58209467b48Spatrick return;
58309467b48Spatrick }
58409467b48Spatrick
58509467b48Spatrick std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
58609467b48Spatrick
58709467b48Spatrick SmallVector<StringRef, 8> Symbols;
58809467b48Spatrick StringRef DemanglerData = DemanglerBuf->getBuffer();
58909467b48Spatrick DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
59009467b48Spatrick /*KeepEmpty=*/false);
59109467b48Spatrick if (Symbols.size() != NumSymbols) {
59209467b48Spatrick error("Demangler did not provide expected number of symbols");
59309467b48Spatrick return;
59409467b48Spatrick }
59509467b48Spatrick
59609467b48Spatrick // Cache the demangled names.
59709467b48Spatrick unsigned I = 0;
59809467b48Spatrick for (const auto &Function : Coverage.getCoveredFunctions())
59909467b48Spatrick // On Windows, lines in the demangler's output file end with "\r\n".
60009467b48Spatrick // Splitting by '\n' keeps '\r's, so cut them now.
601097a140dSpatrick DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
60209467b48Spatrick }
60309467b48Spatrick
writeSourceFileView(StringRef SourceFile,CoverageMapping * Coverage,CoveragePrinter * Printer,bool ShowFilenames)60409467b48Spatrick void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
60509467b48Spatrick CoverageMapping *Coverage,
60609467b48Spatrick CoveragePrinter *Printer,
60709467b48Spatrick bool ShowFilenames) {
60809467b48Spatrick auto View = createSourceFileView(SourceFile, *Coverage);
60909467b48Spatrick if (!View) {
61009467b48Spatrick warning("The file '" + SourceFile + "' isn't covered.");
61109467b48Spatrick return;
61209467b48Spatrick }
61309467b48Spatrick
61409467b48Spatrick auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
61509467b48Spatrick if (Error E = OSOrErr.takeError()) {
61609467b48Spatrick error("Could not create view file!", toString(std::move(E)));
61709467b48Spatrick return;
61809467b48Spatrick }
61909467b48Spatrick auto OS = std::move(OSOrErr.get());
62009467b48Spatrick
62109467b48Spatrick View->print(*OS.get(), /*Wholefile=*/true,
62209467b48Spatrick /*ShowSourceName=*/ShowFilenames,
62309467b48Spatrick /*ShowTitle=*/ViewOpts.hasOutputDirectory());
62409467b48Spatrick Printer->closeViewFile(std::move(OS));
62509467b48Spatrick }
62609467b48Spatrick
run(Command Cmd,int argc,const char ** argv)62709467b48Spatrick int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
62809467b48Spatrick cl::opt<std::string> CovFilename(
62909467b48Spatrick cl::Positional, cl::desc("Covered executable or object file."));
63009467b48Spatrick
63109467b48Spatrick cl::list<std::string> CovFilenames(
632*d415bd75Srobert "object", cl::desc("Coverage executable or object file"));
63373471bf0Spatrick
63473471bf0Spatrick cl::opt<bool> DebugDumpCollectedObjects(
63573471bf0Spatrick "dump-collected-objects", cl::Optional, cl::Hidden,
63673471bf0Spatrick cl::desc("Show the collected coverage object files"));
63709467b48Spatrick
638*d415bd75Srobert cl::list<std::string> InputSourceFiles("sources", cl::Positional,
639*d415bd75Srobert cl::desc("<Source files>"));
64009467b48Spatrick
64109467b48Spatrick cl::opt<bool> DebugDumpCollectedPaths(
64209467b48Spatrick "dump-collected-paths", cl::Optional, cl::Hidden,
64309467b48Spatrick cl::desc("Show the collected paths to source files"));
64409467b48Spatrick
64509467b48Spatrick cl::opt<std::string, true> PGOFilename(
64609467b48Spatrick "instr-profile", cl::Required, cl::location(this->PGOFilename),
64709467b48Spatrick cl::desc(
64809467b48Spatrick "File with the profile data obtained after an instrumented run"));
64909467b48Spatrick
65009467b48Spatrick cl::list<std::string> Arches(
65109467b48Spatrick "arch", cl::desc("architectures of the coverage mapping binaries"));
65209467b48Spatrick
65309467b48Spatrick cl::opt<bool> DebugDump("dump", cl::Optional,
65409467b48Spatrick cl::desc("Show internal debug dump"));
65509467b48Spatrick
656*d415bd75Srobert cl::list<std::string> DebugFileDirectory(
657*d415bd75Srobert "debug-file-directory",
658*d415bd75Srobert cl::desc("Directories to search for object files by build ID"));
659*d415bd75Srobert cl::opt<bool> Debuginfod(
660*d415bd75Srobert "debuginfod", cl::ZeroOrMore,
661*d415bd75Srobert cl::desc("Use debuginfod to look up object files from profile"),
662*d415bd75Srobert cl::init(canUseDebuginfod()));
663*d415bd75Srobert
66409467b48Spatrick cl::opt<CoverageViewOptions::OutputFormat> Format(
66509467b48Spatrick "format", cl::desc("Output format for line-based coverage reports"),
66609467b48Spatrick cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
66709467b48Spatrick "Text output"),
66809467b48Spatrick clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
66909467b48Spatrick "HTML output"),
67009467b48Spatrick clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
67109467b48Spatrick "lcov tracefile output")),
67209467b48Spatrick cl::init(CoverageViewOptions::OutputFormat::Text));
67309467b48Spatrick
67409467b48Spatrick cl::opt<std::string> PathRemap(
67509467b48Spatrick "path-equivalence", cl::Optional,
67609467b48Spatrick cl::desc("<from>,<to> Map coverage data paths to local source file "
67709467b48Spatrick "paths"));
67809467b48Spatrick
67909467b48Spatrick cl::OptionCategory FilteringCategory("Function filtering options");
68009467b48Spatrick
68109467b48Spatrick cl::list<std::string> NameFilters(
68209467b48Spatrick "name", cl::Optional,
68309467b48Spatrick cl::desc("Show code coverage only for functions with the given name"),
684*d415bd75Srobert cl::cat(FilteringCategory));
68509467b48Spatrick
68609467b48Spatrick cl::list<std::string> NameFilterFiles(
687*d415bd75Srobert "name-allowlist", cl::Optional,
68809467b48Spatrick cl::desc("Show code coverage only for functions listed in the given "
68909467b48Spatrick "file"),
690*d415bd75Srobert cl::cat(FilteringCategory));
69109467b48Spatrick
69209467b48Spatrick cl::list<std::string> NameRegexFilters(
69309467b48Spatrick "name-regex", cl::Optional,
69409467b48Spatrick cl::desc("Show code coverage only for functions that match the given "
69509467b48Spatrick "regular expression"),
696*d415bd75Srobert cl::cat(FilteringCategory));
69709467b48Spatrick
69809467b48Spatrick cl::list<std::string> IgnoreFilenameRegexFilters(
69909467b48Spatrick "ignore-filename-regex", cl::Optional,
70009467b48Spatrick cl::desc("Skip source code files with file paths that match the given "
70109467b48Spatrick "regular expression"),
702*d415bd75Srobert cl::cat(FilteringCategory));
70309467b48Spatrick
70409467b48Spatrick cl::opt<double> RegionCoverageLtFilter(
70509467b48Spatrick "region-coverage-lt", cl::Optional,
70609467b48Spatrick cl::desc("Show code coverage only for functions with region coverage "
70709467b48Spatrick "less than the given threshold"),
70809467b48Spatrick cl::cat(FilteringCategory));
70909467b48Spatrick
71009467b48Spatrick cl::opt<double> RegionCoverageGtFilter(
71109467b48Spatrick "region-coverage-gt", cl::Optional,
71209467b48Spatrick cl::desc("Show code coverage only for functions with region coverage "
71309467b48Spatrick "greater than the given threshold"),
71409467b48Spatrick cl::cat(FilteringCategory));
71509467b48Spatrick
71609467b48Spatrick cl::opt<double> LineCoverageLtFilter(
71709467b48Spatrick "line-coverage-lt", cl::Optional,
71809467b48Spatrick cl::desc("Show code coverage only for functions with line coverage less "
71909467b48Spatrick "than the given threshold"),
72009467b48Spatrick cl::cat(FilteringCategory));
72109467b48Spatrick
72209467b48Spatrick cl::opt<double> LineCoverageGtFilter(
72309467b48Spatrick "line-coverage-gt", cl::Optional,
72409467b48Spatrick cl::desc("Show code coverage only for functions with line coverage "
72509467b48Spatrick "greater than the given threshold"),
72609467b48Spatrick cl::cat(FilteringCategory));
72709467b48Spatrick
72809467b48Spatrick cl::opt<cl::boolOrDefault> UseColor(
72909467b48Spatrick "use-color", cl::desc("Emit colored output (default=autodetect)"),
73009467b48Spatrick cl::init(cl::BOU_UNSET));
73109467b48Spatrick
73209467b48Spatrick cl::list<std::string> DemanglerOpts(
73309467b48Spatrick "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
73409467b48Spatrick
73509467b48Spatrick cl::opt<bool> RegionSummary(
73609467b48Spatrick "show-region-summary", cl::Optional,
73709467b48Spatrick cl::desc("Show region statistics in summary table"),
73809467b48Spatrick cl::init(true));
73909467b48Spatrick
74073471bf0Spatrick cl::opt<bool> BranchSummary(
74173471bf0Spatrick "show-branch-summary", cl::Optional,
74273471bf0Spatrick cl::desc("Show branch condition statistics in summary table"),
74373471bf0Spatrick cl::init(true));
74473471bf0Spatrick
74509467b48Spatrick cl::opt<bool> InstantiationSummary(
74609467b48Spatrick "show-instantiation-summary", cl::Optional,
74709467b48Spatrick cl::desc("Show instantiation statistics in summary table"));
74809467b48Spatrick
74909467b48Spatrick cl::opt<bool> SummaryOnly(
75009467b48Spatrick "summary-only", cl::Optional,
75109467b48Spatrick cl::desc("Export only summary information for each source file"));
75209467b48Spatrick
75309467b48Spatrick cl::opt<unsigned> NumThreads(
75409467b48Spatrick "num-threads", cl::init(0),
75509467b48Spatrick cl::desc("Number of merge threads to use (default: autodetect)"));
75609467b48Spatrick cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
75709467b48Spatrick cl::aliasopt(NumThreads));
75809467b48Spatrick
75973471bf0Spatrick cl::opt<std::string> CompilationDirectory(
76073471bf0Spatrick "compilation-dir", cl::init(""),
76173471bf0Spatrick cl::desc("Directory used as a base for relative coverage mapping paths"));
76273471bf0Spatrick
76309467b48Spatrick auto commandLineParser = [&, this](int argc, const char **argv) -> int {
76409467b48Spatrick cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
76509467b48Spatrick ViewOpts.Debug = DebugDump;
766*d415bd75Srobert if (Debuginfod) {
767*d415bd75Srobert HTTPClient::initialize();
768*d415bd75Srobert BIDFetcher = std::make_unique<DebuginfodFetcher>(DebugFileDirectory);
769*d415bd75Srobert } else {
770*d415bd75Srobert BIDFetcher = std::make_unique<object::BuildIDFetcher>(DebugFileDirectory);
771*d415bd75Srobert }
77209467b48Spatrick
77309467b48Spatrick if (!CovFilename.empty())
77409467b48Spatrick ObjectFilenames.emplace_back(CovFilename);
77509467b48Spatrick for (const std::string &Filename : CovFilenames)
77609467b48Spatrick ObjectFilenames.emplace_back(Filename);
777*d415bd75Srobert if (ObjectFilenames.empty() && !Debuginfod && DebugFileDirectory.empty()) {
77809467b48Spatrick errs() << "No filenames specified!\n";
77909467b48Spatrick ::exit(1);
78009467b48Spatrick }
78109467b48Spatrick
78273471bf0Spatrick if (DebugDumpCollectedObjects) {
78373471bf0Spatrick for (StringRef OF : ObjectFilenames)
78473471bf0Spatrick outs() << OF << '\n';
78573471bf0Spatrick ::exit(0);
78673471bf0Spatrick }
78773471bf0Spatrick
78809467b48Spatrick ViewOpts.Format = Format;
78909467b48Spatrick switch (ViewOpts.Format) {
79009467b48Spatrick case CoverageViewOptions::OutputFormat::Text:
79109467b48Spatrick ViewOpts.Colors = UseColor == cl::BOU_UNSET
79209467b48Spatrick ? sys::Process::StandardOutHasColors()
79309467b48Spatrick : UseColor == cl::BOU_TRUE;
79409467b48Spatrick break;
79509467b48Spatrick case CoverageViewOptions::OutputFormat::HTML:
79609467b48Spatrick if (UseColor == cl::BOU_FALSE)
79709467b48Spatrick errs() << "Color output cannot be disabled when generating html.\n";
79809467b48Spatrick ViewOpts.Colors = true;
79909467b48Spatrick break;
80009467b48Spatrick case CoverageViewOptions::OutputFormat::Lcov:
80109467b48Spatrick if (UseColor == cl::BOU_TRUE)
80209467b48Spatrick errs() << "Color output cannot be enabled when generating lcov.\n";
80309467b48Spatrick ViewOpts.Colors = false;
80409467b48Spatrick break;
80509467b48Spatrick }
80609467b48Spatrick
80709467b48Spatrick // If path-equivalence was given and is a comma seperated pair then set
80809467b48Spatrick // PathRemapping.
809*d415bd75Srobert if (!PathRemap.empty()) {
81009467b48Spatrick auto EquivPair = StringRef(PathRemap).split(',');
811*d415bd75Srobert if (EquivPair.first.empty() || EquivPair.second.empty()) {
812*d415bd75Srobert error("invalid argument '" + PathRemap +
813*d415bd75Srobert "', must be in format 'from,to'",
814*d415bd75Srobert "-path-equivalence");
815*d415bd75Srobert return 1;
816*d415bd75Srobert }
817*d415bd75Srobert
818097a140dSpatrick PathRemapping = {std::string(EquivPair.first),
819097a140dSpatrick std::string(EquivPair.second)};
820*d415bd75Srobert }
82109467b48Spatrick
82209467b48Spatrick // If a demangler is supplied, check if it exists and register it.
82309467b48Spatrick if (!DemanglerOpts.empty()) {
82409467b48Spatrick auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
82509467b48Spatrick if (!DemanglerPathOrErr) {
82609467b48Spatrick error("Could not find the demangler!",
82709467b48Spatrick DemanglerPathOrErr.getError().message());
82809467b48Spatrick return 1;
82909467b48Spatrick }
83009467b48Spatrick DemanglerOpts[0] = *DemanglerPathOrErr;
83109467b48Spatrick ViewOpts.DemanglerOpts.swap(DemanglerOpts);
83209467b48Spatrick }
83309467b48Spatrick
834*d415bd75Srobert // Read in -name-allowlist files.
83509467b48Spatrick if (!NameFilterFiles.empty()) {
83609467b48Spatrick std::string SpecialCaseListErr;
837*d415bd75Srobert NameAllowlist = SpecialCaseList::create(
83809467b48Spatrick NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr);
839*d415bd75Srobert if (!NameAllowlist)
84009467b48Spatrick error(SpecialCaseListErr);
84109467b48Spatrick }
84209467b48Spatrick
84309467b48Spatrick // Create the function filters
844*d415bd75Srobert if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) {
84509467b48Spatrick auto NameFilterer = std::make_unique<CoverageFilters>();
84609467b48Spatrick for (const auto &Name : NameFilters)
84709467b48Spatrick NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name));
848*d415bd75Srobert if (NameAllowlist && !NameFilterFiles.empty())
84909467b48Spatrick NameFilterer->push_back(
850*d415bd75Srobert std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist));
85109467b48Spatrick for (const auto &Regex : NameRegexFilters)
85209467b48Spatrick NameFilterer->push_back(
85309467b48Spatrick std::make_unique<NameRegexCoverageFilter>(Regex));
85409467b48Spatrick Filters.push_back(std::move(NameFilterer));
85509467b48Spatrick }
85609467b48Spatrick
85709467b48Spatrick if (RegionCoverageLtFilter.getNumOccurrences() ||
85809467b48Spatrick RegionCoverageGtFilter.getNumOccurrences() ||
85909467b48Spatrick LineCoverageLtFilter.getNumOccurrences() ||
86009467b48Spatrick LineCoverageGtFilter.getNumOccurrences()) {
86109467b48Spatrick auto StatFilterer = std::make_unique<CoverageFilters>();
86209467b48Spatrick if (RegionCoverageLtFilter.getNumOccurrences())
86309467b48Spatrick StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
86409467b48Spatrick RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
86509467b48Spatrick if (RegionCoverageGtFilter.getNumOccurrences())
86609467b48Spatrick StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
86709467b48Spatrick RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
86809467b48Spatrick if (LineCoverageLtFilter.getNumOccurrences())
86909467b48Spatrick StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
87009467b48Spatrick LineCoverageFilter::LessThan, LineCoverageLtFilter));
87109467b48Spatrick if (LineCoverageGtFilter.getNumOccurrences())
87209467b48Spatrick StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
87309467b48Spatrick RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
87409467b48Spatrick Filters.push_back(std::move(StatFilterer));
87509467b48Spatrick }
87609467b48Spatrick
87709467b48Spatrick // Create the ignore filename filters.
87809467b48Spatrick for (const auto &RE : IgnoreFilenameRegexFilters)
87909467b48Spatrick IgnoreFilenameFilters.push_back(
88009467b48Spatrick std::make_unique<NameRegexCoverageFilter>(RE));
88109467b48Spatrick
88209467b48Spatrick if (!Arches.empty()) {
88309467b48Spatrick for (const std::string &Arch : Arches) {
88409467b48Spatrick if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
88509467b48Spatrick error("Unknown architecture: " + Arch);
88609467b48Spatrick return 1;
88709467b48Spatrick }
88809467b48Spatrick CoverageArches.emplace_back(Arch);
88909467b48Spatrick }
890*d415bd75Srobert if (CoverageArches.size() != 1 &&
891*d415bd75Srobert CoverageArches.size() != ObjectFilenames.size()) {
89209467b48Spatrick error("Number of architectures doesn't match the number of objects");
89309467b48Spatrick return 1;
89409467b48Spatrick }
89509467b48Spatrick }
89609467b48Spatrick
89709467b48Spatrick // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
89809467b48Spatrick for (const std::string &File : InputSourceFiles)
89909467b48Spatrick collectPaths(File);
90009467b48Spatrick
90109467b48Spatrick if (DebugDumpCollectedPaths) {
90209467b48Spatrick for (const std::string &SF : SourceFiles)
90309467b48Spatrick outs() << SF << '\n';
90409467b48Spatrick ::exit(0);
90509467b48Spatrick }
90609467b48Spatrick
90773471bf0Spatrick ViewOpts.ShowBranchSummary = BranchSummary;
90809467b48Spatrick ViewOpts.ShowRegionSummary = RegionSummary;
90909467b48Spatrick ViewOpts.ShowInstantiationSummary = InstantiationSummary;
91009467b48Spatrick ViewOpts.ExportSummaryOnly = SummaryOnly;
91109467b48Spatrick ViewOpts.NumThreads = NumThreads;
91273471bf0Spatrick ViewOpts.CompilationDirectory = CompilationDirectory;
91309467b48Spatrick
91409467b48Spatrick return 0;
91509467b48Spatrick };
91609467b48Spatrick
91709467b48Spatrick switch (Cmd) {
91809467b48Spatrick case Show:
91909467b48Spatrick return doShow(argc, argv, commandLineParser);
92009467b48Spatrick case Report:
92109467b48Spatrick return doReport(argc, argv, commandLineParser);
92209467b48Spatrick case Export:
92309467b48Spatrick return doExport(argc, argv, commandLineParser);
92409467b48Spatrick }
92509467b48Spatrick return 0;
92609467b48Spatrick }
92709467b48Spatrick
doShow(int argc,const char ** argv,CommandLineParserType commandLineParser)92809467b48Spatrick int CodeCoverageTool::doShow(int argc, const char **argv,
92909467b48Spatrick CommandLineParserType commandLineParser) {
93009467b48Spatrick
93109467b48Spatrick cl::OptionCategory ViewCategory("Viewing options");
93209467b48Spatrick
93309467b48Spatrick cl::opt<bool> ShowLineExecutionCounts(
93409467b48Spatrick "show-line-counts", cl::Optional,
93509467b48Spatrick cl::desc("Show the execution counts for each line"), cl::init(true),
93609467b48Spatrick cl::cat(ViewCategory));
93709467b48Spatrick
93809467b48Spatrick cl::opt<bool> ShowRegions(
93909467b48Spatrick "show-regions", cl::Optional,
94009467b48Spatrick cl::desc("Show the execution counts for each region"),
94109467b48Spatrick cl::cat(ViewCategory));
94209467b48Spatrick
94373471bf0Spatrick cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
94473471bf0Spatrick "show-branches", cl::Optional,
94573471bf0Spatrick cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
94673471bf0Spatrick cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
94773471bf0Spatrick "count", "Show True/False counts"),
94873471bf0Spatrick clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
94973471bf0Spatrick "percent", "Show True/False percent")),
95073471bf0Spatrick cl::init(CoverageViewOptions::BranchOutputType::Off));
95173471bf0Spatrick
95209467b48Spatrick cl::opt<bool> ShowBestLineRegionsCounts(
95309467b48Spatrick "show-line-counts-or-regions", cl::Optional,
95409467b48Spatrick cl::desc("Show the execution counts for each line, or the execution "
95509467b48Spatrick "counts for each region on lines that have multiple regions"),
95609467b48Spatrick cl::cat(ViewCategory));
95709467b48Spatrick
95809467b48Spatrick cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
95909467b48Spatrick cl::desc("Show expanded source regions"),
96009467b48Spatrick cl::cat(ViewCategory));
96109467b48Spatrick
96209467b48Spatrick cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
96309467b48Spatrick cl::desc("Show function instantiations"),
96409467b48Spatrick cl::init(true), cl::cat(ViewCategory));
96509467b48Spatrick
96609467b48Spatrick cl::opt<std::string> ShowOutputDirectory(
96709467b48Spatrick "output-dir", cl::init(""),
96809467b48Spatrick cl::desc("Directory in which coverage information is written out"));
96909467b48Spatrick cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
97009467b48Spatrick cl::aliasopt(ShowOutputDirectory));
97109467b48Spatrick
97209467b48Spatrick cl::opt<uint32_t> TabSize(
97309467b48Spatrick "tab-size", cl::init(2),
97409467b48Spatrick cl::desc(
97509467b48Spatrick "Set tab expansion size for html coverage reports (default = 2)"));
97609467b48Spatrick
97709467b48Spatrick cl::opt<std::string> ProjectTitle(
97809467b48Spatrick "project-title", cl::Optional,
97909467b48Spatrick cl::desc("Set project title for the coverage report"));
98009467b48Spatrick
981*d415bd75Srobert cl::opt<std::string> CovWatermark(
982*d415bd75Srobert "coverage-watermark", cl::Optional,
983*d415bd75Srobert cl::desc("<high>,<low> value indicate thresholds for high and low"
984*d415bd75Srobert "coverage watermark"));
985*d415bd75Srobert
98609467b48Spatrick auto Err = commandLineParser(argc, argv);
98709467b48Spatrick if (Err)
98809467b48Spatrick return Err;
98909467b48Spatrick
99009467b48Spatrick if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
99109467b48Spatrick error("Lcov format should be used with 'llvm-cov export'.");
99209467b48Spatrick return 1;
99309467b48Spatrick }
99409467b48Spatrick
995*d415bd75Srobert ViewOpts.HighCovWatermark = 100.0;
996*d415bd75Srobert ViewOpts.LowCovWatermark = 80.0;
997*d415bd75Srobert if (!CovWatermark.empty()) {
998*d415bd75Srobert auto WaterMarkPair = StringRef(CovWatermark).split(',');
999*d415bd75Srobert if (WaterMarkPair.first.empty() || WaterMarkPair.second.empty()) {
1000*d415bd75Srobert error("invalid argument '" + CovWatermark +
1001*d415bd75Srobert "', must be in format 'high,low'",
1002*d415bd75Srobert "-coverage-watermark");
1003*d415bd75Srobert return 1;
1004*d415bd75Srobert }
1005*d415bd75Srobert
1006*d415bd75Srobert char *EndPointer = nullptr;
1007*d415bd75Srobert ViewOpts.HighCovWatermark =
1008*d415bd75Srobert strtod(WaterMarkPair.first.begin(), &EndPointer);
1009*d415bd75Srobert if (EndPointer != WaterMarkPair.first.end()) {
1010*d415bd75Srobert error("invalid number '" + WaterMarkPair.first +
1011*d415bd75Srobert "', invalid value for 'high'",
1012*d415bd75Srobert "-coverage-watermark");
1013*d415bd75Srobert return 1;
1014*d415bd75Srobert }
1015*d415bd75Srobert
1016*d415bd75Srobert ViewOpts.LowCovWatermark =
1017*d415bd75Srobert strtod(WaterMarkPair.second.begin(), &EndPointer);
1018*d415bd75Srobert if (EndPointer != WaterMarkPair.second.end()) {
1019*d415bd75Srobert error("invalid number '" + WaterMarkPair.second +
1020*d415bd75Srobert "', invalid value for 'low'",
1021*d415bd75Srobert "-coverage-watermark");
1022*d415bd75Srobert return 1;
1023*d415bd75Srobert }
1024*d415bd75Srobert
1025*d415bd75Srobert if (ViewOpts.HighCovWatermark > 100 || ViewOpts.LowCovWatermark < 0 ||
1026*d415bd75Srobert ViewOpts.HighCovWatermark <= ViewOpts.LowCovWatermark) {
1027*d415bd75Srobert error(
1028*d415bd75Srobert "invalid number range '" + CovWatermark +
1029*d415bd75Srobert "', must be both high and low should be between 0-100, and high "
1030*d415bd75Srobert "> low",
1031*d415bd75Srobert "-coverage-watermark");
1032*d415bd75Srobert return 1;
1033*d415bd75Srobert }
1034*d415bd75Srobert }
1035*d415bd75Srobert
103609467b48Spatrick ViewOpts.ShowLineNumbers = true;
103709467b48Spatrick ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
103809467b48Spatrick !ShowRegions || ShowBestLineRegionsCounts;
103909467b48Spatrick ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
104009467b48Spatrick ViewOpts.ShowExpandedRegions = ShowExpansions;
104173471bf0Spatrick ViewOpts.ShowBranchCounts =
104273471bf0Spatrick ShowBranches == CoverageViewOptions::BranchOutputType::Count;
104373471bf0Spatrick ViewOpts.ShowBranchPercents =
104473471bf0Spatrick ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
104509467b48Spatrick ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
104609467b48Spatrick ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
104709467b48Spatrick ViewOpts.TabSize = TabSize;
104809467b48Spatrick ViewOpts.ProjectTitle = ProjectTitle;
104909467b48Spatrick
105009467b48Spatrick if (ViewOpts.hasOutputDirectory()) {
105109467b48Spatrick if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
105209467b48Spatrick error("Could not create output directory!", E.message());
105309467b48Spatrick return 1;
105409467b48Spatrick }
105509467b48Spatrick }
105609467b48Spatrick
105709467b48Spatrick sys::fs::file_status Status;
1058097a140dSpatrick if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1059*d415bd75Srobert error("Could not read profile data!" + EC.message(), PGOFilename);
106009467b48Spatrick return 1;
106109467b48Spatrick }
106209467b48Spatrick
106309467b48Spatrick auto ModifiedTime = Status.getLastModificationTime();
106409467b48Spatrick std::string ModifiedTimeStr = to_string(ModifiedTime);
106509467b48Spatrick size_t found = ModifiedTimeStr.rfind(':');
106609467b48Spatrick ViewOpts.CreatedTimeStr = (found != std::string::npos)
106709467b48Spatrick ? "Created: " + ModifiedTimeStr.substr(0, found)
106809467b48Spatrick : "Created: " + ModifiedTimeStr;
106909467b48Spatrick
107009467b48Spatrick auto Coverage = load();
107109467b48Spatrick if (!Coverage)
107209467b48Spatrick return 1;
107309467b48Spatrick
107409467b48Spatrick auto Printer = CoveragePrinter::create(ViewOpts);
107509467b48Spatrick
107673471bf0Spatrick if (SourceFiles.empty() && !HadSourceFiles)
107709467b48Spatrick // Get the source files from the function coverage mapping.
107809467b48Spatrick for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
107909467b48Spatrick if (!IgnoreFilenameFilters.matchesFilename(Filename))
1080097a140dSpatrick SourceFiles.push_back(std::string(Filename));
108109467b48Spatrick }
108209467b48Spatrick
108309467b48Spatrick // Create an index out of the source files.
108409467b48Spatrick if (ViewOpts.hasOutputDirectory()) {
108509467b48Spatrick if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) {
108609467b48Spatrick error("Could not create index file!", toString(std::move(E)));
108709467b48Spatrick return 1;
108809467b48Spatrick }
108909467b48Spatrick }
109009467b48Spatrick
109109467b48Spatrick if (!Filters.empty()) {
109209467b48Spatrick // Build the map of filenames to functions.
109309467b48Spatrick std::map<llvm::StringRef, std::vector<const FunctionRecord *>>
109409467b48Spatrick FilenameFunctionMap;
109509467b48Spatrick for (const auto &SourceFile : SourceFiles)
109609467b48Spatrick for (const auto &Function : Coverage->getCoveredFunctions(SourceFile))
1097*d415bd75Srobert if (Filters.matches(*Coverage, Function))
109809467b48Spatrick FilenameFunctionMap[SourceFile].push_back(&Function);
109909467b48Spatrick
110009467b48Spatrick // Only print filter matching functions for each file.
110109467b48Spatrick for (const auto &FileFunc : FilenameFunctionMap) {
110209467b48Spatrick StringRef File = FileFunc.first;
110309467b48Spatrick const auto &Functions = FileFunc.second;
110409467b48Spatrick
110509467b48Spatrick auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false);
110609467b48Spatrick if (Error E = OSOrErr.takeError()) {
110709467b48Spatrick error("Could not create view file!", toString(std::move(E)));
110809467b48Spatrick return 1;
110909467b48Spatrick }
111009467b48Spatrick auto OS = std::move(OSOrErr.get());
111109467b48Spatrick
111209467b48Spatrick bool ShowTitle = ViewOpts.hasOutputDirectory();
111309467b48Spatrick for (const auto *Function : Functions) {
111409467b48Spatrick auto FunctionView = createFunctionView(*Function, *Coverage);
111509467b48Spatrick if (!FunctionView) {
111609467b48Spatrick warning("Could not read coverage for '" + Function->Name + "'.");
111709467b48Spatrick continue;
111809467b48Spatrick }
111909467b48Spatrick FunctionView->print(*OS.get(), /*WholeFile=*/false,
112009467b48Spatrick /*ShowSourceName=*/true, ShowTitle);
112109467b48Spatrick ShowTitle = false;
112209467b48Spatrick }
112309467b48Spatrick
112409467b48Spatrick Printer->closeViewFile(std::move(OS));
112509467b48Spatrick }
112609467b48Spatrick return 0;
112709467b48Spatrick }
112809467b48Spatrick
112909467b48Spatrick // Show files
113009467b48Spatrick bool ShowFilenames =
113109467b48Spatrick (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
113209467b48Spatrick (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
113309467b48Spatrick
1134097a140dSpatrick ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
1135097a140dSpatrick if (ViewOpts.NumThreads == 0) {
1136097a140dSpatrick // If NumThreads is not specified, create one thread for each input, up to
1137097a140dSpatrick // the number of hardware cores.
1138097a140dSpatrick S = heavyweight_hardware_concurrency(SourceFiles.size());
1139097a140dSpatrick S.Limit = true;
1140097a140dSpatrick }
114109467b48Spatrick
1142097a140dSpatrick if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
114309467b48Spatrick for (const std::string &SourceFile : SourceFiles)
114409467b48Spatrick writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
114509467b48Spatrick ShowFilenames);
114609467b48Spatrick } else {
114709467b48Spatrick // In -output-dir mode, it's safe to use multiple threads to print files.
1148097a140dSpatrick ThreadPool Pool(S);
114909467b48Spatrick for (const std::string &SourceFile : SourceFiles)
115009467b48Spatrick Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
115109467b48Spatrick Coverage.get(), Printer.get(), ShowFilenames);
115209467b48Spatrick Pool.wait();
115309467b48Spatrick }
115409467b48Spatrick
115509467b48Spatrick return 0;
115609467b48Spatrick }
115709467b48Spatrick
doReport(int argc,const char ** argv,CommandLineParserType commandLineParser)115809467b48Spatrick int CodeCoverageTool::doReport(int argc, const char **argv,
115909467b48Spatrick CommandLineParserType commandLineParser) {
116009467b48Spatrick cl::opt<bool> ShowFunctionSummaries(
116109467b48Spatrick "show-functions", cl::Optional, cl::init(false),
116209467b48Spatrick cl::desc("Show coverage summaries for each function"));
116309467b48Spatrick
116409467b48Spatrick auto Err = commandLineParser(argc, argv);
116509467b48Spatrick if (Err)
116609467b48Spatrick return Err;
116709467b48Spatrick
116809467b48Spatrick if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) {
116909467b48Spatrick error("HTML output for summary reports is not yet supported.");
117009467b48Spatrick return 1;
117109467b48Spatrick } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
117209467b48Spatrick error("Lcov format should be used with 'llvm-cov export'.");
117309467b48Spatrick return 1;
117409467b48Spatrick }
117509467b48Spatrick
1176*d415bd75Srobert sys::fs::file_status Status;
1177*d415bd75Srobert if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1178*d415bd75Srobert error("Could not read profile data!" + EC.message(), PGOFilename);
1179*d415bd75Srobert return 1;
1180*d415bd75Srobert }
1181*d415bd75Srobert
118209467b48Spatrick auto Coverage = load();
118309467b48Spatrick if (!Coverage)
118409467b48Spatrick return 1;
118509467b48Spatrick
1186*d415bd75Srobert CoverageReport Report(ViewOpts, *Coverage);
118709467b48Spatrick if (!ShowFunctionSummaries) {
118809467b48Spatrick if (SourceFiles.empty())
118909467b48Spatrick Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
119009467b48Spatrick else
119109467b48Spatrick Report.renderFileReports(llvm::outs(), SourceFiles);
119209467b48Spatrick } else {
119309467b48Spatrick if (SourceFiles.empty()) {
119409467b48Spatrick error("Source files must be specified when -show-functions=true is "
119509467b48Spatrick "specified");
119609467b48Spatrick return 1;
119709467b48Spatrick }
119809467b48Spatrick
119909467b48Spatrick Report.renderFunctionReports(SourceFiles, DC, llvm::outs());
120009467b48Spatrick }
120109467b48Spatrick return 0;
120209467b48Spatrick }
120309467b48Spatrick
doExport(int argc,const char ** argv,CommandLineParserType commandLineParser)120409467b48Spatrick int CodeCoverageTool::doExport(int argc, const char **argv,
120509467b48Spatrick CommandLineParserType commandLineParser) {
120609467b48Spatrick
120709467b48Spatrick cl::OptionCategory ExportCategory("Exporting options");
120809467b48Spatrick
120909467b48Spatrick cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional,
121009467b48Spatrick cl::desc("Don't export expanded source regions"),
121109467b48Spatrick cl::cat(ExportCategory));
121209467b48Spatrick
121309467b48Spatrick cl::opt<bool> SkipFunctions("skip-functions", cl::Optional,
121409467b48Spatrick cl::desc("Don't export per-function data"),
121509467b48Spatrick cl::cat(ExportCategory));
121609467b48Spatrick
1217*d415bd75Srobert cl::opt<bool> SkipBranches("skip-branches", cl::Optional,
1218*d415bd75Srobert cl::desc("Don't export branch data (LCOV)"),
1219*d415bd75Srobert cl::cat(ExportCategory));
1220*d415bd75Srobert
122109467b48Spatrick auto Err = commandLineParser(argc, argv);
122209467b48Spatrick if (Err)
122309467b48Spatrick return Err;
122409467b48Spatrick
122509467b48Spatrick ViewOpts.SkipExpansions = SkipExpansions;
122609467b48Spatrick ViewOpts.SkipFunctions = SkipFunctions;
1227*d415bd75Srobert ViewOpts.SkipBranches = SkipBranches;
122809467b48Spatrick
122909467b48Spatrick if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
123009467b48Spatrick ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
123109467b48Spatrick error("Coverage data can only be exported as textual JSON or an "
123209467b48Spatrick "lcov tracefile.");
123309467b48Spatrick return 1;
123409467b48Spatrick }
123509467b48Spatrick
1236*d415bd75Srobert sys::fs::file_status Status;
1237*d415bd75Srobert if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1238*d415bd75Srobert error("Could not read profile data!" + EC.message(), PGOFilename);
1239*d415bd75Srobert return 1;
1240*d415bd75Srobert }
1241*d415bd75Srobert
124209467b48Spatrick auto Coverage = load();
124309467b48Spatrick if (!Coverage) {
124409467b48Spatrick error("Could not load coverage information");
124509467b48Spatrick return 1;
124609467b48Spatrick }
124709467b48Spatrick
124809467b48Spatrick std::unique_ptr<CoverageExporter> Exporter;
124909467b48Spatrick
125009467b48Spatrick switch (ViewOpts.Format) {
125109467b48Spatrick case CoverageViewOptions::OutputFormat::Text:
1252*d415bd75Srobert Exporter =
1253*d415bd75Srobert std::make_unique<CoverageExporterJson>(*Coverage, ViewOpts, outs());
125409467b48Spatrick break;
125509467b48Spatrick case CoverageViewOptions::OutputFormat::HTML:
125609467b48Spatrick // Unreachable because we should have gracefully terminated with an error
125709467b48Spatrick // above.
125809467b48Spatrick llvm_unreachable("Export in HTML is not supported!");
125909467b48Spatrick case CoverageViewOptions::OutputFormat::Lcov:
1260*d415bd75Srobert Exporter =
1261*d415bd75Srobert std::make_unique<CoverageExporterLcov>(*Coverage, ViewOpts, outs());
126209467b48Spatrick break;
126309467b48Spatrick }
126409467b48Spatrick
126509467b48Spatrick if (SourceFiles.empty())
126609467b48Spatrick Exporter->renderRoot(IgnoreFilenameFilters);
126709467b48Spatrick else
126809467b48Spatrick Exporter->renderRoot(SourceFiles);
126909467b48Spatrick
127009467b48Spatrick return 0;
127109467b48Spatrick }
127209467b48Spatrick
showMain(int argc,const char * argv[])127309467b48Spatrick int showMain(int argc, const char *argv[]) {
127409467b48Spatrick CodeCoverageTool Tool;
127509467b48Spatrick return Tool.run(CodeCoverageTool::Show, argc, argv);
127609467b48Spatrick }
127709467b48Spatrick
reportMain(int argc,const char * argv[])127809467b48Spatrick int reportMain(int argc, const char *argv[]) {
127909467b48Spatrick CodeCoverageTool Tool;
128009467b48Spatrick return Tool.run(CodeCoverageTool::Report, argc, argv);
128109467b48Spatrick }
128209467b48Spatrick
exportMain(int argc,const char * argv[])128309467b48Spatrick int exportMain(int argc, const char *argv[]) {
128409467b48Spatrick CodeCoverageTool Tool;
128509467b48Spatrick return Tool.run(CodeCoverageTool::Export, argc, argv);
128609467b48Spatrick }
1287