1e82d89ccSAlex Lorenz //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
2e82d89ccSAlex Lorenz //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e82d89ccSAlex Lorenz //
7e82d89ccSAlex Lorenz //===----------------------------------------------------------------------===//
8e82d89ccSAlex Lorenz //
9e82d89ccSAlex Lorenz // The 'CodeCoverageTool' class implements a command line tool to analyze and
10e82d89ccSAlex Lorenz // report coverage information using the profiling instrumentation and code
11e82d89ccSAlex Lorenz // coverage mapping.
12e82d89ccSAlex Lorenz //
13e82d89ccSAlex Lorenz //===----------------------------------------------------------------------===//
14e82d89ccSAlex Lorenz
151ef3a778SMax Moroz #include "CoverageExporterJson.h"
16b2091c93SMax Moroz #include "CoverageExporterLcov.h"
17e82d89ccSAlex Lorenz #include "CoverageFilters.h"
18e82d89ccSAlex Lorenz #include "CoverageReport.h"
196e28bcdcSVedant Kumar #include "CoverageSummaryInfo.h"
20d9903888SChandler Carruth #include "CoverageViewOptions.h"
21dc707122SEaswaran Raman #include "RenderingSupport.h"
22d9903888SChandler Carruth #include "SourceCoverageView.h"
23e82d89ccSAlex Lorenz #include "llvm/ADT/SmallString.h"
24d9903888SChandler Carruth #include "llvm/ADT/StringRef.h"
254379535eSJustin Bogner #include "llvm/ADT/Triple.h"
26dc707122SEaswaran Raman #include "llvm/ProfileData/Coverage/CoverageMapping.h"
27d9903888SChandler Carruth #include "llvm/ProfileData/InstrProfReader.h"
28e82d89ccSAlex Lorenz #include "llvm/Support/CommandLine.h"
29e82d89ccSAlex Lorenz #include "llvm/Support/FileSystem.h"
30e82d89ccSAlex Lorenz #include "llvm/Support/Format.h"
31424f51bbSVedant Kumar #include "llvm/Support/MemoryBuffer.h"
32e82d89ccSAlex Lorenz #include "llvm/Support/Path.h"
33cfb53e49SJustin Bogner #include "llvm/Support/Process.h"
34424f51bbSVedant Kumar #include "llvm/Support/Program.h"
35757ca886SPavel Labath #include "llvm/Support/ScopedPrinter.h"
366022efb0SSimon Pilgrim #include "llvm/Support/SpecialCaseList.h"
3786b2ac63SVedant Kumar #include "llvm/Support/ThreadPool.h"
38cc254ba4SMax Moroz #include "llvm/Support/Threading.h"
39424f51bbSVedant Kumar #include "llvm/Support/ToolOutputFile.h"
40aa981c18SIlya Biryukov #include "llvm/Support/VirtualFileSystem.h"
41fa8ef35eSSean Eveson
42e82d89ccSAlex Lorenz #include <functional>
43fa8ef35eSSean Eveson #include <map>
44e53be066SJustin Bogner #include <system_error>
45e82d89ccSAlex Lorenz
46e82d89ccSAlex Lorenz using namespace llvm;
47e82d89ccSAlex Lorenz using namespace coverage;
48e82d89ccSAlex Lorenz
495c61c703SVedant Kumar void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping,
5072c3a114SVedant Kumar const CoverageViewOptions &Options,
517101d73cSVedant Kumar raw_ostream &OS);
527101d73cSVedant Kumar
53e82d89ccSAlex Lorenz namespace {
545f8f34e4SAdrian Prantl /// The implementation of the coverage tool.
55e82d89ccSAlex Lorenz class CodeCoverageTool {
56e82d89ccSAlex Lorenz public:
57e82d89ccSAlex Lorenz enum Command {
585f8f34e4SAdrian Prantl /// The show command.
59e82d89ccSAlex Lorenz Show,
605f8f34e4SAdrian Prantl /// The report command.
617101d73cSVedant Kumar Report,
625f8f34e4SAdrian Prantl /// The export command.
637101d73cSVedant Kumar Export
64e82d89ccSAlex Lorenz };
65e82d89ccSAlex Lorenz
664610367cSVedant Kumar int run(Command Cmd, int argc, const char **argv);
674610367cSVedant Kumar
684610367cSVedant Kumar private:
695f8f34e4SAdrian Prantl /// Print the error message to the error output stream.
70e82d89ccSAlex Lorenz void error(const Twine &Message, StringRef Whence = "");
71e82d89ccSAlex Lorenz
725f8f34e4SAdrian Prantl /// Print the warning message to the error output stream.
73b3020630SVedant Kumar void warning(const Twine &Message, StringRef Whence = "");
7486b2ac63SVedant Kumar
755f8f34e4SAdrian Prantl /// Convert \p Path into an absolute path and append it to the list
76bc647985SVedant Kumar /// of collected paths.
77cef440f7SVedant Kumar void addCollectedPath(const std::string &Path);
78cef440f7SVedant Kumar
795f8f34e4SAdrian Prantl /// If \p Path is a regular file, collect the path. If it's a
801ce90d88SVedant Kumar /// directory, recursively collect all of the paths within the directory.
811ce90d88SVedant Kumar void collectPaths(const std::string &Path);
821ce90d88SVedant Kumar
839d8a3e75SChoongwoo Han /// Check if the two given files are the same file.
849d8a3e75SChoongwoo Han bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2);
859d8a3e75SChoongwoo Han
869d8a3e75SChoongwoo Han /// Retrieve a file status with a cache.
879d8a3e75SChoongwoo Han Optional<sys::fs::file_status> getFileStatus(StringRef FilePath);
889d8a3e75SChoongwoo Han
895f8f34e4SAdrian Prantl /// Return a memory buffer for the given source file.
90e82d89ccSAlex Lorenz ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
91e82d89ccSAlex Lorenz
925f8f34e4SAdrian Prantl /// Create source views for the expansions of the view.
93953e2407SJustin Bogner void attachExpansionSubViews(SourceCoverageView &View,
94953e2407SJustin Bogner ArrayRef<ExpansionRecord> Expansions,
95f681e2e5SVedant Kumar const CoverageMapping &Coverage);
96e82d89ccSAlex Lorenz
979f2967bcSAlan Phipps /// Create source views for the branches of the view.
989f2967bcSAlan Phipps void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
999f2967bcSAlan Phipps ArrayRef<CountedRegion> Branches,
1009f2967bcSAlan Phipps const MemoryBuffer &File,
1019f2967bcSAlan Phipps CoverageData &CoverageInfo);
1029f2967bcSAlan Phipps
1035f8f34e4SAdrian Prantl /// Create the source view of a particular function.
1045a6edad3SJustin Bogner std::unique_ptr<SourceCoverageView>
105f681e2e5SVedant Kumar createFunctionView(const FunctionRecord &Function,
106f681e2e5SVedant Kumar const CoverageMapping &Coverage);
107e82d89ccSAlex Lorenz
1085f8f34e4SAdrian Prantl /// Create the main source view of a particular source file.
1095a6edad3SJustin Bogner std::unique_ptr<SourceCoverageView>
110f681e2e5SVedant Kumar createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
111e82d89ccSAlex Lorenz
1125f8f34e4SAdrian Prantl /// Load the coverage mapping data. Return nullptr if an error occurred.
113953e2407SJustin Bogner std::unique_ptr<CoverageMapping> load();
114e82d89ccSAlex Lorenz
1155f8f34e4SAdrian Prantl /// Create a mapping from files in the Coverage data to local copies
1169edfeac9SSean Eveson /// (path-equivalence).
1179edfeac9SSean Eveson void remapPathNames(const CoverageMapping &Coverage);
1189edfeac9SSean Eveson
1195f8f34e4SAdrian Prantl /// Remove input source files which aren't mapped by \p Coverage.
120cab52addSVedant Kumar void removeUnmappedInputs(const CoverageMapping &Coverage);
121cab52addSVedant Kumar
1225f8f34e4SAdrian Prantl /// If a demangler is available, demangle all symbol names.
123424f51bbSVedant Kumar void demangleSymbols(const CoverageMapping &Coverage);
124424f51bbSVedant Kumar
1255f8f34e4SAdrian Prantl /// Write out a source file view to the filesystem.
1266fd94bf4SVedant Kumar void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
1276fd94bf4SVedant Kumar CoveragePrinter *Printer, bool ShowFilenames);
1286fd94bf4SVedant Kumar
129c321e534SBenjamin Kramer typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
130e82d89ccSAlex Lorenz
1311ef3a778SMax Moroz int doShow(int argc, const char **argv,
132e82d89ccSAlex Lorenz CommandLineParserType commandLineParser);
133e82d89ccSAlex Lorenz
1341ef3a778SMax Moroz int doReport(int argc, const char **argv,
135e82d89ccSAlex Lorenz CommandLineParserType commandLineParser);
136e82d89ccSAlex Lorenz
1371ef3a778SMax Moroz int doExport(int argc, const char **argv,
1387101d73cSVedant Kumar CommandLineParserType commandLineParser);
1397101d73cSVedant Kumar
140a3661effSVedant Kumar std::vector<StringRef> ObjectFilenames;
141e82d89ccSAlex Lorenz CoverageViewOptions ViewOpts;
142e82d89ccSAlex Lorenz CoverageFiltersMatchAll Filters;
1438d24d72fSVlad Tsyrklevich CoverageFilters IgnoreFilenameFilters;
1444610367cSVedant Kumar
145e92eeaf3SZequan Wu /// True if InputSourceFiles are provided.
146e92eeaf3SZequan Wu bool HadSourceFiles = false;
147e92eeaf3SZequan Wu
1484610367cSVedant Kumar /// The path to the indexed profile.
1494610367cSVedant Kumar std::string PGOFilename;
1504610367cSVedant Kumar
1514610367cSVedant Kumar /// A list of input source files.
152bc647985SVedant Kumar std::vector<std::string> SourceFiles;
1534610367cSVedant Kumar
1549edfeac9SSean Eveson /// In -path-equivalence mode, this maps the absolute paths from the coverage
1559edfeac9SSean Eveson /// mapping data to the input source files.
156116c1664SJustin Bogner StringMap<std::string> RemappedFilenames;
1574610367cSVedant Kumar
1589edfeac9SSean Eveson /// The coverage data path to be remapped from, and the source path to be
1599edfeac9SSean Eveson /// remapped to, when using -path-equivalence.
1609edfeac9SSean Eveson Optional<std::pair<std::string, std::string>> PathRemapping;
1619edfeac9SSean Eveson
1629d8a3e75SChoongwoo Han /// File status cache used when finding the same file.
1639d8a3e75SChoongwoo Han StringMap<Optional<sys::fs::file_status>> FileStatusCache;
1649d8a3e75SChoongwoo Han
1654610367cSVedant Kumar /// The architecture the coverage mapping data targets.
1664b102c3dSVedant Kumar std::vector<StringRef> CoverageArches;
167cef440f7SVedant Kumar
1686e28bcdcSVedant Kumar /// A cache for demangled symbols.
1696e28bcdcSVedant Kumar DemangleCache DC;
170424f51bbSVedant Kumar
171b6bfd47fSVedant Kumar /// A lock which guards printing to stderr.
172b3020630SVedant Kumar std::mutex ErrsLock;
17386b2ac63SVedant Kumar
1746ab6b364SVedant Kumar /// A container for input source file buffers.
17586b2ac63SVedant Kumar std::mutex LoadedSourceFilesLock;
17686b2ac63SVedant Kumar std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
17786b2ac63SVedant Kumar LoadedSourceFiles;
178e15300ecSSean Eveson
179e714394aSZarko Todorovski /// Allowlist from -name-allowlist to be used for filtering.
180e714394aSZarko Todorovski std::unique_ptr<SpecialCaseList> NameAllowlist;
181e82d89ccSAlex Lorenz };
182e82d89ccSAlex Lorenz }
183e82d89ccSAlex Lorenz
getErrorString(const Twine & Message,StringRef Whence,bool Warning)18486b2ac63SVedant Kumar static std::string getErrorString(const Twine &Message, StringRef Whence,
18586b2ac63SVedant Kumar bool Warning) {
18686b2ac63SVedant Kumar std::string Str = (Warning ? "warning" : "error");
18786b2ac63SVedant Kumar Str += ": ";
188e82d89ccSAlex Lorenz if (!Whence.empty())
189b95dc460SVedant Kumar Str += Whence.str() + ": ";
19086b2ac63SVedant Kumar Str += Message.str() + "\n";
19186b2ac63SVedant Kumar return Str;
19286b2ac63SVedant Kumar }
19386b2ac63SVedant Kumar
error(const Twine & Message,StringRef Whence)19486b2ac63SVedant Kumar void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
195b3020630SVedant Kumar std::unique_lock<std::mutex> Guard{ErrsLock};
196b3020630SVedant Kumar ViewOpts.colored_ostream(errs(), raw_ostream::RED)
197b3020630SVedant Kumar << getErrorString(Message, Whence, false);
19886b2ac63SVedant Kumar }
19986b2ac63SVedant Kumar
warning(const Twine & Message,StringRef Whence)200b3020630SVedant Kumar void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
201b3020630SVedant Kumar std::unique_lock<std::mutex> Guard{ErrsLock};
202b3020630SVedant Kumar ViewOpts.colored_ostream(errs(), raw_ostream::RED)
203b3020630SVedant Kumar << getErrorString(Message, Whence, true);
204e82d89ccSAlex Lorenz }
205e82d89ccSAlex Lorenz
addCollectedPath(const std::string & Path)206cef440f7SVedant Kumar void CodeCoverageTool::addCollectedPath(const std::string &Path) {
2071ce90d88SVedant Kumar SmallString<128> EffectivePath(Path);
2081ce90d88SVedant Kumar if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
2091ce90d88SVedant Kumar error(EC.message(), Path);
2101ce90d88SVedant Kumar return;
2111ce90d88SVedant Kumar }
212435a5a36SKazu Hirata sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true);
2138d24d72fSVlad Tsyrklevich if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
214bc647985SVedant Kumar SourceFiles.emplace_back(EffectivePath.str());
215e92eeaf3SZequan Wu HadSourceFiles = !SourceFiles.empty();
2161ce90d88SVedant Kumar }
217cef440f7SVedant Kumar
collectPaths(const std::string & Path)2181ce90d88SVedant Kumar void CodeCoverageTool::collectPaths(const std::string &Path) {
2191ce90d88SVedant Kumar llvm::sys::fs::file_status Status;
2201ce90d88SVedant Kumar llvm::sys::fs::status(Path, Status);
2211ce90d88SVedant Kumar if (!llvm::sys::fs::exists(Status)) {
2229edfeac9SSean Eveson if (PathRemapping)
2231ce90d88SVedant Kumar addCollectedPath(Path);
2241ce90d88SVedant Kumar else
225650fd6c3SMax Moroz warning("Source file doesn't exist, proceeded by ignoring it.", Path);
2261ce90d88SVedant Kumar return;
2271ce90d88SVedant Kumar }
2281ce90d88SVedant Kumar
2291ce90d88SVedant Kumar if (llvm::sys::fs::is_regular_file(Status)) {
2301ce90d88SVedant Kumar addCollectedPath(Path);
2311ce90d88SVedant Kumar return;
2321ce90d88SVedant Kumar }
2331ce90d88SVedant Kumar
2341ce90d88SVedant Kumar if (llvm::sys::fs::is_directory(Status)) {
2351ce90d88SVedant Kumar std::error_code EC;
2361ce90d88SVedant Kumar for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
237650fd6c3SMax Moroz F != E; F.increment(EC)) {
238650fd6c3SMax Moroz
23979c995c0SSam McCall auto Status = F->status();
24079c995c0SSam McCall if (!Status) {
24179c995c0SSam McCall warning(Status.getError().message(), F->path());
242650fd6c3SMax Moroz continue;
243650fd6c3SMax Moroz }
244650fd6c3SMax Moroz
24579c995c0SSam McCall if (Status->type() == llvm::sys::fs::file_type::regular_file)
2461ce90d88SVedant Kumar addCollectedPath(F->path());
2471ce90d88SVedant Kumar }
2481ce90d88SVedant Kumar }
2491ce90d88SVedant Kumar }
2501ce90d88SVedant Kumar
2519d8a3e75SChoongwoo Han Optional<sys::fs::file_status>
getFileStatus(StringRef FilePath)2529d8a3e75SChoongwoo Han CodeCoverageTool::getFileStatus(StringRef FilePath) {
2539d8a3e75SChoongwoo Han auto It = FileStatusCache.try_emplace(FilePath);
2549d8a3e75SChoongwoo Han auto &CachedStatus = It.first->getValue();
2559d8a3e75SChoongwoo Han if (!It.second)
2569d8a3e75SChoongwoo Han return CachedStatus;
2579d8a3e75SChoongwoo Han
2589d8a3e75SChoongwoo Han sys::fs::file_status Status;
2599d8a3e75SChoongwoo Han if (!sys::fs::status(FilePath, Status))
2609d8a3e75SChoongwoo Han CachedStatus = Status;
2619d8a3e75SChoongwoo Han return CachedStatus;
2629d8a3e75SChoongwoo Han }
2639d8a3e75SChoongwoo Han
isEquivalentFile(StringRef FilePath1,StringRef FilePath2)2649d8a3e75SChoongwoo Han bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1,
2659d8a3e75SChoongwoo Han StringRef FilePath2) {
2669d8a3e75SChoongwoo Han auto Status1 = getFileStatus(FilePath1);
2679d8a3e75SChoongwoo Han auto Status2 = getFileStatus(FilePath2);
268d66cbc56SKazu Hirata return Status1 && Status2 && sys::fs::equivalent(*Status1, *Status2);
2699d8a3e75SChoongwoo Han }
2709d8a3e75SChoongwoo Han
271e82d89ccSAlex Lorenz ErrorOr<const MemoryBuffer &>
getSourceFile(StringRef SourceFile)272e82d89ccSAlex Lorenz CodeCoverageTool::getSourceFile(StringRef SourceFile) {
273116c1664SJustin Bogner // If we've remapped filenames, look up the real location for this file.
274615b85d9SVedant Kumar std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
275116c1664SJustin Bogner if (!RemappedFilenames.empty()) {
276116c1664SJustin Bogner auto Loc = RemappedFilenames.find(SourceFile);
277116c1664SJustin Bogner if (Loc != RemappedFilenames.end())
278116c1664SJustin Bogner SourceFile = Loc->second;
279116c1664SJustin Bogner }
280116c1664SJustin Bogner for (const auto &Files : LoadedSourceFiles)
2819d8a3e75SChoongwoo Han if (isEquivalentFile(SourceFile, Files.first))
282e82d89ccSAlex Lorenz return *Files.second;
283e82d89ccSAlex Lorenz auto Buffer = MemoryBuffer::getFile(SourceFile);
284e82d89ccSAlex Lorenz if (auto EC = Buffer.getError()) {
285b3020630SVedant Kumar error(EC.message(), SourceFile);
286e82d89ccSAlex Lorenz return EC;
287e82d89ccSAlex Lorenz }
288bd31243aSBenjamin Kramer LoadedSourceFiles.emplace_back(std::string(SourceFile),
289bd31243aSBenjamin Kramer std::move(Buffer.get()));
290e82d89ccSAlex Lorenz return *LoadedSourceFiles.back().second;
291e82d89ccSAlex Lorenz }
292e82d89ccSAlex Lorenz
attachExpansionSubViews(SourceCoverageView & View,ArrayRef<ExpansionRecord> Expansions,const CoverageMapping & Coverage)293f681e2e5SVedant Kumar void CodeCoverageTool::attachExpansionSubViews(
294f681e2e5SVedant Kumar SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
295f681e2e5SVedant Kumar const CoverageMapping &Coverage) {
296e82d89ccSAlex Lorenz if (!ViewOpts.ShowExpandedRegions)
297e82d89ccSAlex Lorenz return;
298953e2407SJustin Bogner for (const auto &Expansion : Expansions) {
299953e2407SJustin Bogner auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
300953e2407SJustin Bogner if (ExpansionCoverage.empty())
301e82d89ccSAlex Lorenz continue;
302953e2407SJustin Bogner auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
303953e2407SJustin Bogner if (!SourceBuffer)
304e82d89ccSAlex Lorenz continue;
305953e2407SJustin Bogner
3069f2967bcSAlan Phipps auto SubViewBranches = ExpansionCoverage.getBranches();
307953e2407SJustin Bogner auto SubViewExpansions = ExpansionCoverage.getExpansions();
308f9151b93SVedant Kumar auto SubView =
309f9151b93SVedant Kumar SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
310f9151b93SVedant Kumar ViewOpts, std::move(ExpansionCoverage));
311953e2407SJustin Bogner attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
3129f2967bcSAlan Phipps attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
3139f2967bcSAlan Phipps SourceBuffer.get(), ExpansionCoverage);
314953e2407SJustin Bogner View.addExpansion(Expansion.Region, std::move(SubView));
315e82d89ccSAlex Lorenz }
316e82d89ccSAlex Lorenz }
317e82d89ccSAlex Lorenz
attachBranchSubViews(SourceCoverageView & View,StringRef SourceName,ArrayRef<CountedRegion> Branches,const MemoryBuffer & File,CoverageData & CoverageInfo)3189f2967bcSAlan Phipps void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
3199f2967bcSAlan Phipps StringRef SourceName,
3209f2967bcSAlan Phipps ArrayRef<CountedRegion> Branches,
3219f2967bcSAlan Phipps const MemoryBuffer &File,
3229f2967bcSAlan Phipps CoverageData &CoverageInfo) {
3239f2967bcSAlan Phipps if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
3249f2967bcSAlan Phipps return;
3259f2967bcSAlan Phipps
3269f2967bcSAlan Phipps const auto *NextBranch = Branches.begin();
3279f2967bcSAlan Phipps const auto *EndBranch = Branches.end();
3289f2967bcSAlan Phipps
3299f2967bcSAlan Phipps // Group branches that have the same line number into the same subview.
3309f2967bcSAlan Phipps while (NextBranch != EndBranch) {
3319f2967bcSAlan Phipps std::vector<CountedRegion> ViewBranches;
3329f2967bcSAlan Phipps unsigned CurrentLine = NextBranch->LineStart;
3339f2967bcSAlan Phipps
3349f2967bcSAlan Phipps while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
3359f2967bcSAlan Phipps ViewBranches.push_back(*NextBranch++);
3369f2967bcSAlan Phipps
3379f2967bcSAlan Phipps if (!ViewBranches.empty()) {
3389f2967bcSAlan Phipps auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
3399f2967bcSAlan Phipps std::move(CoverageInfo));
3409f2967bcSAlan Phipps View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
3419f2967bcSAlan Phipps }
3429f2967bcSAlan Phipps }
3439f2967bcSAlan Phipps }
3449f2967bcSAlan Phipps
3455a6edad3SJustin Bogner std::unique_ptr<SourceCoverageView>
createFunctionView(const FunctionRecord & Function,const CoverageMapping & Coverage)346953e2407SJustin Bogner CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
347f681e2e5SVedant Kumar const CoverageMapping &Coverage) {
348953e2407SJustin Bogner auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
349953e2407SJustin Bogner if (FunctionCoverage.empty())
3505a6edad3SJustin Bogner return nullptr;
351953e2407SJustin Bogner auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
3525a6edad3SJustin Bogner if (!SourceBuffer)
3535a6edad3SJustin Bogner return nullptr;
354e82d89ccSAlex Lorenz
3559f2967bcSAlan Phipps auto Branches = FunctionCoverage.getBranches();
356953e2407SJustin Bogner auto Expansions = FunctionCoverage.getExpansions();
3576e28bcdcSVedant Kumar auto View = SourceCoverageView::create(DC.demangle(Function.Name),
3580053c0b6SVedant Kumar SourceBuffer.get(), ViewOpts,
3590053c0b6SVedant Kumar std::move(FunctionCoverage));
360953e2407SJustin Bogner attachExpansionSubViews(*View, Expansions, Coverage);
3619f2967bcSAlan Phipps attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
3629f2967bcSAlan Phipps SourceBuffer.get(), FunctionCoverage);
363e82d89ccSAlex Lorenz
3645a6edad3SJustin Bogner return View;
365953e2407SJustin Bogner }
366953e2407SJustin Bogner
367953e2407SJustin Bogner std::unique_ptr<SourceCoverageView>
createSourceFileView(StringRef SourceFile,const CoverageMapping & Coverage)368953e2407SJustin Bogner CodeCoverageTool::createSourceFileView(StringRef SourceFile,
369f681e2e5SVedant Kumar const CoverageMapping &Coverage) {
370953e2407SJustin Bogner auto SourceBuffer = getSourceFile(SourceFile);
371953e2407SJustin Bogner if (!SourceBuffer)
372953e2407SJustin Bogner return nullptr;
373953e2407SJustin Bogner auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
374953e2407SJustin Bogner if (FileCoverage.empty())
375953e2407SJustin Bogner return nullptr;
376953e2407SJustin Bogner
3779f2967bcSAlan Phipps auto Branches = FileCoverage.getBranches();
378953e2407SJustin Bogner auto Expansions = FileCoverage.getExpansions();
379f9151b93SVedant Kumar auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
380f9151b93SVedant Kumar ViewOpts, std::move(FileCoverage));
381953e2407SJustin Bogner attachExpansionSubViews(*View, Expansions, Coverage);
3829f2967bcSAlan Phipps attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
3839f2967bcSAlan Phipps FileCoverage);
38479554e45SVedant Kumar if (!ViewOpts.ShowFunctionInstantiations)
38579554e45SVedant Kumar return View;
386953e2407SJustin Bogner
387dde19c5aSVedant Kumar for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) {
388dde19c5aSVedant Kumar // Skip functions which have a single instantiation.
389dde19c5aSVedant Kumar if (Group.size() < 2)
390dde19c5aSVedant Kumar continue;
391dde19c5aSVedant Kumar
392dde19c5aSVedant Kumar for (const FunctionRecord *Function : Group.getInstantiations()) {
393a8c396d9SVedant Kumar std::unique_ptr<SourceCoverageView> SubView{nullptr};
394a8c396d9SVedant Kumar
3956e28bcdcSVedant Kumar StringRef Funcname = DC.demangle(Function->Name);
396e9079779SVedant Kumar
397a8c396d9SVedant Kumar if (Function->ExecutionCount > 0) {
398953e2407SJustin Bogner auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
399953e2407SJustin Bogner auto SubViewExpansions = SubViewCoverage.getExpansions();
4009f2967bcSAlan Phipps auto SubViewBranches = SubViewCoverage.getBranches();
401a8c396d9SVedant Kumar SubView = SourceCoverageView::create(
402e9079779SVedant Kumar Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
403953e2407SJustin Bogner attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
4049f2967bcSAlan Phipps attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
4059f2967bcSAlan Phipps SourceBuffer.get(), SubViewCoverage);
406a8c396d9SVedant Kumar }
407953e2407SJustin Bogner
4085e1400a8SJustin Bogner unsigned FileID = Function->CountedRegions.front().FileID;
4095e1400a8SJustin Bogner unsigned Line = 0;
4105e1400a8SJustin Bogner for (const auto &CR : Function->CountedRegions)
4115e1400a8SJustin Bogner if (CR.FileID == FileID)
4125e1400a8SJustin Bogner Line = std::max(CR.LineEnd, Line);
413e9079779SVedant Kumar View->addInstantiation(Funcname, Line, std::move(SubView));
414e82d89ccSAlex Lorenz }
415dde19c5aSVedant Kumar }
4165a6edad3SJustin Bogner return View;
417e82d89ccSAlex Lorenz }
418e82d89ccSAlex Lorenz
modifiedTimeGT(StringRef LHS,StringRef RHS)41965337d1fSJustin Bogner static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
42065337d1fSJustin Bogner sys::fs::file_status Status;
42165337d1fSJustin Bogner if (sys::fs::status(LHS, Status))
42265337d1fSJustin Bogner return false;
42365337d1fSJustin Bogner auto LHSTime = Status.getLastModificationTime();
42465337d1fSJustin Bogner if (sys::fs::status(RHS, Status))
42565337d1fSJustin Bogner return false;
42665337d1fSJustin Bogner auto RHSTime = Status.getLastModificationTime();
42765337d1fSJustin Bogner return LHSTime > RHSTime;
42865337d1fSJustin Bogner }
42965337d1fSJustin Bogner
load()430953e2407SJustin Bogner std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
431a3661effSVedant Kumar for (StringRef ObjectFilename : ObjectFilenames)
43265337d1fSJustin Bogner if (modifiedTimeGT(ObjectFilename, PGOFilename))
433b3020630SVedant Kumar warning("profile data may be out of date - object is newer",
434b3020630SVedant Kumar ObjectFilename);
435b3020630SVedant Kumar auto CoverageOrErr =
4368280ece0SPetr Hosek CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches,
4378280ece0SPetr Hosek ViewOpts.CompilationDirectory);
4389152fd17SVedant Kumar if (Error E = CoverageOrErr.takeError()) {
439*4979b16dSZequan Wu error("Failed to load coverage: " + toString(std::move(E)));
440953e2407SJustin Bogner return nullptr;
441e82d89ccSAlex Lorenz }
442953e2407SJustin Bogner auto Coverage = std::move(CoverageOrErr.get());
443953e2407SJustin Bogner unsigned Mismatched = Coverage->getMismatchedCount();
44418dd9e88SVedant Kumar if (Mismatched) {
4453a13ed60SBenjamin Kramer warning(Twine(Mismatched) + " functions have mismatched data");
446116c1664SJustin Bogner
44718dd9e88SVedant Kumar if (ViewOpts.Debug) {
44818dd9e88SVedant Kumar for (const auto &HashMismatch : Coverage->getHashMismatches())
44918dd9e88SVedant Kumar errs() << "hash-mismatch: "
45018dd9e88SVedant Kumar << "No profile record found for '" << HashMismatch.first << "'"
4513a13ed60SBenjamin Kramer << " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
4523a13ed60SBenjamin Kramer << '\n';
45318dd9e88SVedant Kumar }
45418dd9e88SVedant Kumar }
45518dd9e88SVedant Kumar
4569edfeac9SSean Eveson remapPathNames(*Coverage);
4579edfeac9SSean Eveson
458cab52addSVedant Kumar if (!SourceFiles.empty())
459cab52addSVedant Kumar removeUnmappedInputs(*Coverage);
460cab52addSVedant Kumar
461cab52addSVedant Kumar demangleSymbols(*Coverage);
462cab52addSVedant Kumar
463cab52addSVedant Kumar return Coverage;
464cab52addSVedant Kumar }
465cab52addSVedant Kumar
remapPathNames(const CoverageMapping & Coverage)4669edfeac9SSean Eveson void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
4679edfeac9SSean Eveson if (!PathRemapping)
4689edfeac9SSean Eveson return;
4699edfeac9SSean Eveson
4709edfeac9SSean Eveson // Convert remapping paths to native paths with trailing seperators.
4719edfeac9SSean Eveson auto nativeWithTrailing = [](StringRef Path) -> std::string {
4729edfeac9SSean Eveson if (Path.empty())
4739edfeac9SSean Eveson return "";
4749edfeac9SSean Eveson SmallString<128> NativePath;
4759edfeac9SSean Eveson sys::path::native(Path, NativePath);
476c75a0a1eSZequan Wu sys::path::remove_dots(NativePath, true);
477dd388ba3SZequan Wu if (!NativePath.empty() && !sys::path::is_separator(NativePath.back()))
4789edfeac9SSean Eveson NativePath += sys::path::get_separator();
4799edfeac9SSean Eveson return NativePath.c_str();
4809edfeac9SSean Eveson };
4819edfeac9SSean Eveson std::string RemapFrom = nativeWithTrailing(PathRemapping->first);
4829edfeac9SSean Eveson std::string RemapTo = nativeWithTrailing(PathRemapping->second);
4839edfeac9SSean Eveson
4849edfeac9SSean Eveson // Create a mapping from coverage data file paths to local paths.
4859edfeac9SSean Eveson for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
4869edfeac9SSean Eveson SmallString<128> NativeFilename;
4879edfeac9SSean Eveson sys::path::native(Filename, NativeFilename);
488e92eeaf3SZequan Wu sys::path::remove_dots(NativeFilename, true);
4899edfeac9SSean Eveson if (NativeFilename.startswith(RemapFrom)) {
4909edfeac9SSean Eveson RemappedFilenames[Filename] =
4919edfeac9SSean Eveson RemapTo + NativeFilename.substr(RemapFrom.size()).str();
4929edfeac9SSean Eveson }
4939edfeac9SSean Eveson }
4949edfeac9SSean Eveson
4959edfeac9SSean Eveson // Convert input files from local paths to coverage data file paths.
4969edfeac9SSean Eveson StringMap<std::string> InvRemappedFilenames;
4979edfeac9SSean Eveson for (const auto &RemappedFilename : RemappedFilenames)
498adcd0268SBenjamin Kramer InvRemappedFilenames[RemappedFilename.getValue()] =
499adcd0268SBenjamin Kramer std::string(RemappedFilename.getKey());
5009edfeac9SSean Eveson
5019edfeac9SSean Eveson for (std::string &Filename : SourceFiles) {
5029edfeac9SSean Eveson SmallString<128> NativeFilename;
5039edfeac9SSean Eveson sys::path::native(Filename, NativeFilename);
5049edfeac9SSean Eveson auto CovFileName = InvRemappedFilenames.find(NativeFilename);
5059edfeac9SSean Eveson if (CovFileName != InvRemappedFilenames.end())
5069edfeac9SSean Eveson Filename = CovFileName->second;
5079edfeac9SSean Eveson }
5089edfeac9SSean Eveson }
5099edfeac9SSean Eveson
removeUnmappedInputs(const CoverageMapping & Coverage)510cab52addSVedant Kumar void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
511cab52addSVedant Kumar std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
51245880880SVedant Kumar
51345880880SVedant Kumar // The user may have specified source files which aren't in the coverage
51445880880SVedant Kumar // mapping. Filter these files away.
515b676f2feSKazu Hirata llvm::erase_if(SourceFiles, [&](const std::string &SF) {
516b676f2feSKazu Hirata return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF);
51745880880SVedant Kumar });
518e82d89ccSAlex Lorenz }
519e82d89ccSAlex Lorenz
demangleSymbols(const CoverageMapping & Coverage)520424f51bbSVedant Kumar void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
521424f51bbSVedant Kumar if (!ViewOpts.hasDemangler())
522424f51bbSVedant Kumar return;
523424f51bbSVedant Kumar
524424f51bbSVedant Kumar // Pass function names to the demangler in a temporary file.
525424f51bbSVedant Kumar int InputFD;
526424f51bbSVedant Kumar SmallString<256> InputPath;
527424f51bbSVedant Kumar std::error_code EC =
528424f51bbSVedant Kumar sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
529424f51bbSVedant Kumar if (EC) {
530424f51bbSVedant Kumar error(InputPath, EC.message());
531424f51bbSVedant Kumar return;
532424f51bbSVedant Kumar }
5333fc649cbSReid Kleckner ToolOutputFile InputTOF{InputPath, InputFD};
534424f51bbSVedant Kumar
535424f51bbSVedant Kumar unsigned NumSymbols = 0;
536424f51bbSVedant Kumar for (const auto &Function : Coverage.getCoveredFunctions()) {
537424f51bbSVedant Kumar InputTOF.os() << Function.Name << '\n';
538424f51bbSVedant Kumar ++NumSymbols;
539424f51bbSVedant Kumar }
540554357b6SVedant Kumar InputTOF.os().close();
541424f51bbSVedant Kumar
542424f51bbSVedant Kumar // Use another temporary file to store the demangler's output.
543424f51bbSVedant Kumar int OutputFD;
544424f51bbSVedant Kumar SmallString<256> OutputPath;
545424f51bbSVedant Kumar EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
546424f51bbSVedant Kumar OutputPath);
547424f51bbSVedant Kumar if (EC) {
548424f51bbSVedant Kumar error(OutputPath, EC.message());
549424f51bbSVedant Kumar return;
550424f51bbSVedant Kumar }
5513fc649cbSReid Kleckner ToolOutputFile OutputTOF{OutputPath, OutputFD};
552554357b6SVedant Kumar OutputTOF.os().close();
553424f51bbSVedant Kumar
554424f51bbSVedant Kumar // Invoke the demangler.
55508426e1fSZachary Turner std::vector<StringRef> ArgsV;
55608426e1fSZachary Turner for (StringRef Arg : ViewOpts.DemanglerOpts)
55708426e1fSZachary Turner ArgsV.push_back(Arg);
558208eecd5SAlexander Kornienko Optional<StringRef> Redirects[] = {InputPath.str(), OutputPath.str(), {""}};
559424f51bbSVedant Kumar std::string ErrMsg;
56008426e1fSZachary Turner int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
56108426e1fSZachary Turner /*env=*/None, Redirects, /*secondsToWait=*/0,
562424f51bbSVedant Kumar /*memoryLimit=*/0, &ErrMsg);
563424f51bbSVedant Kumar if (RC) {
564424f51bbSVedant Kumar error(ErrMsg, ViewOpts.DemanglerOpts[0]);
565424f51bbSVedant Kumar return;
566424f51bbSVedant Kumar }
567424f51bbSVedant Kumar
568424f51bbSVedant Kumar // Parse the demangler's output.
569424f51bbSVedant Kumar auto BufOrError = MemoryBuffer::getFile(OutputPath);
570424f51bbSVedant Kumar if (!BufOrError) {
571424f51bbSVedant Kumar error(OutputPath, BufOrError.getError().message());
572424f51bbSVedant Kumar return;
573424f51bbSVedant Kumar }
574424f51bbSVedant Kumar
575424f51bbSVedant Kumar std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
576424f51bbSVedant Kumar
577424f51bbSVedant Kumar SmallVector<StringRef, 8> Symbols;
578424f51bbSVedant Kumar StringRef DemanglerData = DemanglerBuf->getBuffer();
579424f51bbSVedant Kumar DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
580424f51bbSVedant Kumar /*KeepEmpty=*/false);
581424f51bbSVedant Kumar if (Symbols.size() != NumSymbols) {
582424f51bbSVedant Kumar error("Demangler did not provide expected number of symbols");
583424f51bbSVedant Kumar return;
584424f51bbSVedant Kumar }
585424f51bbSVedant Kumar
586424f51bbSVedant Kumar // Cache the demangled names.
587424f51bbSVedant Kumar unsigned I = 0;
588424f51bbSVedant Kumar for (const auto &Function : Coverage.getCoveredFunctions())
5899e015dafSIgor Kudrin // On Windows, lines in the demangler's output file end with "\r\n".
5909e015dafSIgor Kudrin // Splitting by '\n' keeps '\r's, so cut them now.
591adcd0268SBenjamin Kramer DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
592424f51bbSVedant Kumar }
593424f51bbSVedant Kumar
writeSourceFileView(StringRef SourceFile,CoverageMapping * Coverage,CoveragePrinter * Printer,bool ShowFilenames)5946fd94bf4SVedant Kumar void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
5956fd94bf4SVedant Kumar CoverageMapping *Coverage,
5966fd94bf4SVedant Kumar CoveragePrinter *Printer,
5976fd94bf4SVedant Kumar bool ShowFilenames) {
5986fd94bf4SVedant Kumar auto View = createSourceFileView(SourceFile, *Coverage);
5996fd94bf4SVedant Kumar if (!View) {
6006fd94bf4SVedant Kumar warning("The file '" + SourceFile + "' isn't covered.");
6016fd94bf4SVedant Kumar return;
6026fd94bf4SVedant Kumar }
6036fd94bf4SVedant Kumar
6046fd94bf4SVedant Kumar auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
6056fd94bf4SVedant Kumar if (Error E = OSOrErr.takeError()) {
6066fd94bf4SVedant Kumar error("Could not create view file!", toString(std::move(E)));
6076fd94bf4SVedant Kumar return;
6086fd94bf4SVedant Kumar }
6096fd94bf4SVedant Kumar auto OS = std::move(OSOrErr.get());
6106fd94bf4SVedant Kumar
6116fd94bf4SVedant Kumar View->print(*OS.get(), /*Wholefile=*/true,
612fa8ef35eSSean Eveson /*ShowSourceName=*/ShowFilenames,
613fa8ef35eSSean Eveson /*ShowTitle=*/ViewOpts.hasOutputDirectory());
6146fd94bf4SVedant Kumar Printer->closeViewFile(std::move(OS));
6156fd94bf4SVedant Kumar }
6166fd94bf4SVedant Kumar
run(Command Cmd,int argc,const char ** argv)617e82d89ccSAlex Lorenz int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
618a3661effSVedant Kumar cl::opt<std::string> CovFilename(
619a3661effSVedant Kumar cl::Positional, cl::desc("Covered executable or object file."));
620a3661effSVedant Kumar
621a3661effSVedant Kumar cl::list<std::string> CovFilenames(
62236c7d79dSFangrui Song "object", cl::desc("Coverage executable or object file"));
6233c731ba5SVedant Kumar
6243c731ba5SVedant Kumar cl::opt<bool> DebugDumpCollectedObjects(
6253c731ba5SVedant Kumar "dump-collected-objects", cl::Optional, cl::Hidden,
6263c731ba5SVedant Kumar cl::desc("Show the collected coverage object files"));
627f6c50557SJustin Bogner
628d0d1c416SFangrui Song cl::list<std::string> InputSourceFiles(cl::Positional,
629d0d1c416SFangrui Song cl::desc("<Source files>"));
630e82d89ccSAlex Lorenz
6311ce90d88SVedant Kumar cl::opt<bool> DebugDumpCollectedPaths(
6321ce90d88SVedant Kumar "dump-collected-paths", cl::Optional, cl::Hidden,
6331ce90d88SVedant Kumar cl::desc("Show the collected paths to source files"));
6341ce90d88SVedant Kumar
635953e2407SJustin Bogner cl::opt<std::string, true> PGOFilename(
636953e2407SJustin Bogner "instr-profile", cl::Required, cl::location(this->PGOFilename),
637e82d89ccSAlex Lorenz cl::desc(
638e82d89ccSAlex Lorenz "File with the profile data obtained after an instrumented run"));
639e82d89ccSAlex Lorenz
6404b102c3dSVedant Kumar cl::list<std::string> Arches(
6414b102c3dSVedant Kumar "arch", cl::desc("architectures of the coverage mapping binaries"));
6424379535eSJustin Bogner
643e82d89ccSAlex Lorenz cl::opt<bool> DebugDump("dump", cl::Optional,
644e82d89ccSAlex Lorenz cl::desc("Show internal debug dump"));
645e82d89ccSAlex Lorenz
6468d74cb27SVedant Kumar cl::opt<CoverageViewOptions::OutputFormat> Format(
6478d74cb27SVedant Kumar "format", cl::desc("Output format for line-based coverage reports"),
6488d74cb27SVedant Kumar cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
6498d74cb27SVedant Kumar "Text output"),
6504c01092aSVedant Kumar clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
651b2091c93SMax Moroz "HTML output"),
652b2091c93SMax Moroz clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
653b2091c93SMax Moroz "lcov tracefile output")),
6548d74cb27SVedant Kumar cl::init(CoverageViewOptions::OutputFormat::Text));
6558d74cb27SVedant Kumar
6569edfeac9SSean Eveson cl::opt<std::string> PathRemap(
6579edfeac9SSean Eveson "path-equivalence", cl::Optional,
6589edfeac9SSean Eveson cl::desc("<from>,<to> Map coverage data paths to local source file "
6599edfeac9SSean Eveson "paths"));
660e82d89ccSAlex Lorenz
661e82d89ccSAlex Lorenz cl::OptionCategory FilteringCategory("Function filtering options");
662e82d89ccSAlex Lorenz
663e82d89ccSAlex Lorenz cl::list<std::string> NameFilters(
664e82d89ccSAlex Lorenz "name", cl::Optional,
665e82d89ccSAlex Lorenz cl::desc("Show code coverage only for functions with the given name"),
666d86a206fSFangrui Song cl::cat(FilteringCategory));
667e82d89ccSAlex Lorenz
668e15300ecSSean Eveson cl::list<std::string> NameFilterFiles(
669e714394aSZarko Todorovski "name-allowlist", cl::Optional,
670e15300ecSSean Eveson cl::desc("Show code coverage only for functions listed in the given "
671e15300ecSSean Eveson "file"),
67295a13425SFangrui Song cl::cat(FilteringCategory));
673e15300ecSSean Eveson
674e714394aSZarko Todorovski // Allow for accepting previous option name.
675e714394aSZarko Todorovski cl::list<std::string> NameFilterFilesDeprecated(
676e714394aSZarko Todorovski "name-whitelist", cl::Optional, cl::Hidden,
677e714394aSZarko Todorovski cl::desc("Show code coverage only for functions listed in the given "
678e714394aSZarko Todorovski "file. Deprecated, use -name-allowlist instead"),
679d86a206fSFangrui Song cl::cat(FilteringCategory));
680e714394aSZarko Todorovski
681e82d89ccSAlex Lorenz cl::list<std::string> NameRegexFilters(
682e82d89ccSAlex Lorenz "name-regex", cl::Optional,
683e82d89ccSAlex Lorenz cl::desc("Show code coverage only for functions that match the given "
684e82d89ccSAlex Lorenz "regular expression"),
68595a13425SFangrui Song cl::cat(FilteringCategory));
686e82d89ccSAlex Lorenz
6874220f891SMax Moroz cl::list<std::string> IgnoreFilenameRegexFilters(
6884220f891SMax Moroz "ignore-filename-regex", cl::Optional,
6894220f891SMax Moroz cl::desc("Skip source code files with file paths that match the given "
6904220f891SMax Moroz "regular expression"),
691d86a206fSFangrui Song cl::cat(FilteringCategory));
6924220f891SMax Moroz
693e82d89ccSAlex Lorenz cl::opt<double> RegionCoverageLtFilter(
694e82d89ccSAlex Lorenz "region-coverage-lt", cl::Optional,
695e82d89ccSAlex Lorenz cl::desc("Show code coverage only for functions with region coverage "
696e82d89ccSAlex Lorenz "less than the given threshold"),
697e82d89ccSAlex Lorenz cl::cat(FilteringCategory));
698e82d89ccSAlex Lorenz
699e82d89ccSAlex Lorenz cl::opt<double> RegionCoverageGtFilter(
700e82d89ccSAlex Lorenz "region-coverage-gt", cl::Optional,
701e82d89ccSAlex Lorenz cl::desc("Show code coverage only for functions with region coverage "
702e82d89ccSAlex Lorenz "greater than the given threshold"),
703e82d89ccSAlex Lorenz cl::cat(FilteringCategory));
704e82d89ccSAlex Lorenz
705e82d89ccSAlex Lorenz cl::opt<double> LineCoverageLtFilter(
706e82d89ccSAlex Lorenz "line-coverage-lt", cl::Optional,
707e82d89ccSAlex Lorenz cl::desc("Show code coverage only for functions with line coverage less "
708e82d89ccSAlex Lorenz "than the given threshold"),
709e82d89ccSAlex Lorenz cl::cat(FilteringCategory));
710e82d89ccSAlex Lorenz
711e82d89ccSAlex Lorenz cl::opt<double> LineCoverageGtFilter(
712e82d89ccSAlex Lorenz "line-coverage-gt", cl::Optional,
713e82d89ccSAlex Lorenz cl::desc("Show code coverage only for functions with line coverage "
714e82d89ccSAlex Lorenz "greater than the given threshold"),
715e82d89ccSAlex Lorenz cl::cat(FilteringCategory));
716e82d89ccSAlex Lorenz
7179deb1d40SJustin Bogner cl::opt<cl::boolOrDefault> UseColor(
7189deb1d40SJustin Bogner "use-color", cl::desc("Emit colored output (default=autodetect)"),
7199deb1d40SJustin Bogner cl::init(cl::BOU_UNSET));
720cfb53e49SJustin Bogner
721424f51bbSVedant Kumar cl::list<std::string> DemanglerOpts(
722424f51bbSVedant Kumar "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
723424f51bbSVedant Kumar
72450479f60SEli Friedman cl::opt<bool> RegionSummary(
72550479f60SEli Friedman "show-region-summary", cl::Optional,
72650479f60SEli Friedman cl::desc("Show region statistics in summary table"),
72750479f60SEli Friedman cl::init(true));
72850479f60SEli Friedman
7299f2967bcSAlan Phipps cl::opt<bool> BranchSummary(
7309f2967bcSAlan Phipps "show-branch-summary", cl::Optional,
7319f2967bcSAlan Phipps cl::desc("Show branch condition statistics in summary table"),
7329f2967bcSAlan Phipps cl::init(true));
7339f2967bcSAlan Phipps
73450479f60SEli Friedman cl::opt<bool> InstantiationSummary(
73550479f60SEli Friedman "show-instantiation-summary", cl::Optional,
73650479f60SEli Friedman cl::desc("Show instantiation statistics in summary table"));
73750479f60SEli Friedman
738fe4d9049SMax Moroz cl::opt<bool> SummaryOnly(
739fe4d9049SMax Moroz "summary-only", cl::Optional,
740fe4d9049SMax Moroz cl::desc("Export only summary information for each source file"));
741fe4d9049SMax Moroz
742cc254ba4SMax Moroz cl::opt<unsigned> NumThreads(
743cc254ba4SMax Moroz "num-threads", cl::init(0),
744cc254ba4SMax Moroz cl::desc("Number of merge threads to use (default: autodetect)"));
745cc254ba4SMax Moroz cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
746cc254ba4SMax Moroz cl::aliasopt(NumThreads));
747cc254ba4SMax Moroz
7488280ece0SPetr Hosek cl::opt<std::string> CompilationDirectory(
7498280ece0SPetr Hosek "compilation-dir", cl::init(""),
7508280ece0SPetr Hosek cl::desc("Directory used as a base for relative coverage mapping paths"));
7518280ece0SPetr Hosek
752e82d89ccSAlex Lorenz auto commandLineParser = [&, this](int argc, const char **argv) -> int {
753e82d89ccSAlex Lorenz cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
754e82d89ccSAlex Lorenz ViewOpts.Debug = DebugDump;
755e82d89ccSAlex Lorenz
756a3661effSVedant Kumar if (!CovFilename.empty())
757a3661effSVedant Kumar ObjectFilenames.emplace_back(CovFilename);
758a3661effSVedant Kumar for (const std::string &Filename : CovFilenames)
759a3661effSVedant Kumar ObjectFilenames.emplace_back(Filename);
760a3661effSVedant Kumar if (ObjectFilenames.empty()) {
76122c1b7c1SVedant Kumar errs() << "No filenames specified!\n";
762a3661effSVedant Kumar ::exit(1);
763a3661effSVedant Kumar }
764a3661effSVedant Kumar
7653c731ba5SVedant Kumar if (DebugDumpCollectedObjects) {
7663c731ba5SVedant Kumar for (StringRef OF : ObjectFilenames)
7673c731ba5SVedant Kumar outs() << OF << '\n';
7683c731ba5SVedant Kumar ::exit(0);
7693c731ba5SVedant Kumar }
7703c731ba5SVedant Kumar
7718d74cb27SVedant Kumar ViewOpts.Format = Format;
7728d74cb27SVedant Kumar switch (ViewOpts.Format) {
7738d74cb27SVedant Kumar case CoverageViewOptions::OutputFormat::Text:
7749deb1d40SJustin Bogner ViewOpts.Colors = UseColor == cl::BOU_UNSET
7759deb1d40SJustin Bogner ? sys::Process::StandardOutHasColors()
7769deb1d40SJustin Bogner : UseColor == cl::BOU_TRUE;
7778d74cb27SVedant Kumar break;
7784c01092aSVedant Kumar case CoverageViewOptions::OutputFormat::HTML:
7794c01092aSVedant Kumar if (UseColor == cl::BOU_FALSE)
78022c1b7c1SVedant Kumar errs() << "Color output cannot be disabled when generating html.\n";
7814c01092aSVedant Kumar ViewOpts.Colors = true;
7824c01092aSVedant Kumar break;
783b2091c93SMax Moroz case CoverageViewOptions::OutputFormat::Lcov:
784b2091c93SMax Moroz if (UseColor == cl::BOU_TRUE)
785b2091c93SMax Moroz errs() << "Color output cannot be enabled when generating lcov.\n";
786b2091c93SMax Moroz ViewOpts.Colors = false;
787b2091c93SMax Moroz break;
7888d74cb27SVedant Kumar }
789cfb53e49SJustin Bogner
7909edfeac9SSean Eveson // If path-equivalence was given and is a comma seperated pair then set
7919edfeac9SSean Eveson // PathRemapping.
792e972e49bSKeith Smiley if (!PathRemap.empty()) {
7939edfeac9SSean Eveson auto EquivPair = StringRef(PathRemap).split(',');
794e972e49bSKeith Smiley if (EquivPair.first.empty() || EquivPair.second.empty()) {
795e972e49bSKeith Smiley error("invalid argument '" + PathRemap +
796e972e49bSKeith Smiley "', must be in format 'from,to'",
797e972e49bSKeith Smiley "-path-equivalence");
798e972e49bSKeith Smiley return 1;
799e972e49bSKeith Smiley }
800e972e49bSKeith Smiley
801adcd0268SBenjamin Kramer PathRemapping = {std::string(EquivPair.first),
802adcd0268SBenjamin Kramer std::string(EquivPair.second)};
803e972e49bSKeith Smiley }
8049edfeac9SSean Eveson
805424f51bbSVedant Kumar // If a demangler is supplied, check if it exists and register it.
80616a0de2eSJordan Rupprecht if (!DemanglerOpts.empty()) {
807424f51bbSVedant Kumar auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
808424f51bbSVedant Kumar if (!DemanglerPathOrErr) {
809424f51bbSVedant Kumar error("Could not find the demangler!",
810424f51bbSVedant Kumar DemanglerPathOrErr.getError().message());
811424f51bbSVedant Kumar return 1;
812424f51bbSVedant Kumar }
813424f51bbSVedant Kumar DemanglerOpts[0] = *DemanglerPathOrErr;
814424f51bbSVedant Kumar ViewOpts.DemanglerOpts.swap(DemanglerOpts);
815424f51bbSVedant Kumar }
816424f51bbSVedant Kumar
817e714394aSZarko Todorovski // Read in -name-allowlist files.
818e714394aSZarko Todorovski if (!NameFilterFiles.empty() || !NameFilterFilesDeprecated.empty()) {
819e15300ecSSean Eveson std::string SpecialCaseListErr;
820e714394aSZarko Todorovski if (!NameFilterFiles.empty())
821e714394aSZarko Todorovski NameAllowlist = SpecialCaseList::create(
822aa981c18SIlya Biryukov NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr);
823e714394aSZarko Todorovski if (!NameFilterFilesDeprecated.empty())
824e714394aSZarko Todorovski NameAllowlist = SpecialCaseList::create(NameFilterFilesDeprecated,
825e714394aSZarko Todorovski *vfs::getRealFileSystem(),
826e714394aSZarko Todorovski SpecialCaseListErr);
827e714394aSZarko Todorovski
828e714394aSZarko Todorovski if (!NameAllowlist)
829e15300ecSSean Eveson error(SpecialCaseListErr);
830e15300ecSSean Eveson }
831e15300ecSSean Eveson
832e82d89ccSAlex Lorenz // Create the function filters
833e714394aSZarko Todorovski if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) {
8340eaee545SJonas Devlieghere auto NameFilterer = std::make_unique<CoverageFilters>();
835e82d89ccSAlex Lorenz for (const auto &Name : NameFilters)
8360eaee545SJonas Devlieghere NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name));
837e714394aSZarko Todorovski if (NameAllowlist) {
838e714394aSZarko Todorovski if (!NameFilterFiles.empty())
839e15300ecSSean Eveson NameFilterer->push_back(
840e714394aSZarko Todorovski std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist));
841e714394aSZarko Todorovski if (!NameFilterFilesDeprecated.empty())
842e714394aSZarko Todorovski NameFilterer->push_back(
843e714394aSZarko Todorovski std::make_unique<NameWhitelistCoverageFilter>(*NameAllowlist));
844e714394aSZarko Todorovski }
845e82d89ccSAlex Lorenz for (const auto &Regex : NameRegexFilters)
846e82d89ccSAlex Lorenz NameFilterer->push_back(
8470eaee545SJonas Devlieghere std::make_unique<NameRegexCoverageFilter>(Regex));
8488a622388SVedant Kumar Filters.push_back(std::move(NameFilterer));
849e82d89ccSAlex Lorenz }
8504220f891SMax Moroz
851e82d89ccSAlex Lorenz if (RegionCoverageLtFilter.getNumOccurrences() ||
852e82d89ccSAlex Lorenz RegionCoverageGtFilter.getNumOccurrences() ||
853e82d89ccSAlex Lorenz LineCoverageLtFilter.getNumOccurrences() ||
854e82d89ccSAlex Lorenz LineCoverageGtFilter.getNumOccurrences()) {
8550eaee545SJonas Devlieghere auto StatFilterer = std::make_unique<CoverageFilters>();
856e82d89ccSAlex Lorenz if (RegionCoverageLtFilter.getNumOccurrences())
8570eaee545SJonas Devlieghere StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
858e82d89ccSAlex Lorenz RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
859e82d89ccSAlex Lorenz if (RegionCoverageGtFilter.getNumOccurrences())
8600eaee545SJonas Devlieghere StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
861e82d89ccSAlex Lorenz RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
862e82d89ccSAlex Lorenz if (LineCoverageLtFilter.getNumOccurrences())
8630eaee545SJonas Devlieghere StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
864e82d89ccSAlex Lorenz LineCoverageFilter::LessThan, LineCoverageLtFilter));
865e82d89ccSAlex Lorenz if (LineCoverageGtFilter.getNumOccurrences())
8660eaee545SJonas Devlieghere StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
867e82d89ccSAlex Lorenz RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
8688a622388SVedant Kumar Filters.push_back(std::move(StatFilterer));
869e82d89ccSAlex Lorenz }
870e82d89ccSAlex Lorenz
8718d24d72fSVlad Tsyrklevich // Create the ignore filename filters.
8724220f891SMax Moroz for (const auto &RE : IgnoreFilenameRegexFilters)
8738d24d72fSVlad Tsyrklevich IgnoreFilenameFilters.push_back(
8748d24d72fSVlad Tsyrklevich std::make_unique<NameRegexCoverageFilter>(RE));
8754220f891SMax Moroz
8764b102c3dSVedant Kumar if (!Arches.empty()) {
8774b102c3dSVedant Kumar for (const std::string &Arch : Arches) {
8784b102c3dSVedant Kumar if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
879b3020630SVedant Kumar error("Unknown architecture: " + Arch);
8804379535eSJustin Bogner return 1;
8814379535eSJustin Bogner }
8824b102c3dSVedant Kumar CoverageArches.emplace_back(Arch);
8834b102c3dSVedant Kumar }
884427ba2bcSKeith Smiley if (CoverageArches.size() == 1)
885427ba2bcSKeith Smiley CoverageArches.insert(CoverageArches.end(), ObjectFilenames.size() - 1,
886427ba2bcSKeith Smiley CoverageArches[0]);
8874b102c3dSVedant Kumar if (CoverageArches.size() != ObjectFilenames.size()) {
8884b102c3dSVedant Kumar error("Number of architectures doesn't match the number of objects");
8894b102c3dSVedant Kumar return 1;
8904b102c3dSVedant Kumar }
8914b102c3dSVedant Kumar }
8924379535eSJustin Bogner
8938d24d72fSVlad Tsyrklevich // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
8941ce90d88SVedant Kumar for (const std::string &File : InputSourceFiles)
8951ce90d88SVedant Kumar collectPaths(File);
8961ce90d88SVedant Kumar
8971ce90d88SVedant Kumar if (DebugDumpCollectedPaths) {
898bc647985SVedant Kumar for (const std::string &SF : SourceFiles)
8991ce90d88SVedant Kumar outs() << SF << '\n';
9001ce90d88SVedant Kumar ::exit(0);
901116c1664SJustin Bogner }
9021ce90d88SVedant Kumar
9039f2967bcSAlan Phipps ViewOpts.ShowBranchSummary = BranchSummary;
90450479f60SEli Friedman ViewOpts.ShowRegionSummary = RegionSummary;
90550479f60SEli Friedman ViewOpts.ShowInstantiationSummary = InstantiationSummary;
906fe4d9049SMax Moroz ViewOpts.ExportSummaryOnly = SummaryOnly;
907cc254ba4SMax Moroz ViewOpts.NumThreads = NumThreads;
9088280ece0SPetr Hosek ViewOpts.CompilationDirectory = CompilationDirectory;
90950479f60SEli Friedman
910e82d89ccSAlex Lorenz return 0;
911e82d89ccSAlex Lorenz };
912e82d89ccSAlex Lorenz
913e82d89ccSAlex Lorenz switch (Cmd) {
914e82d89ccSAlex Lorenz case Show:
9151ef3a778SMax Moroz return doShow(argc, argv, commandLineParser);
916e82d89ccSAlex Lorenz case Report:
9171ef3a778SMax Moroz return doReport(argc, argv, commandLineParser);
9187101d73cSVedant Kumar case Export:
9191ef3a778SMax Moroz return doExport(argc, argv, commandLineParser);
920e82d89ccSAlex Lorenz }
921e82d89ccSAlex Lorenz return 0;
922e82d89ccSAlex Lorenz }
923e82d89ccSAlex Lorenz
doShow(int argc,const char ** argv,CommandLineParserType commandLineParser)9241ef3a778SMax Moroz int CodeCoverageTool::doShow(int argc, const char **argv,
925e82d89ccSAlex Lorenz CommandLineParserType commandLineParser) {
926e82d89ccSAlex Lorenz
927e82d89ccSAlex Lorenz cl::OptionCategory ViewCategory("Viewing options");
928e82d89ccSAlex Lorenz
929e82d89ccSAlex Lorenz cl::opt<bool> ShowLineExecutionCounts(
930e82d89ccSAlex Lorenz "show-line-counts", cl::Optional,
931e82d89ccSAlex Lorenz cl::desc("Show the execution counts for each line"), cl::init(true),
932e82d89ccSAlex Lorenz cl::cat(ViewCategory));
933e82d89ccSAlex Lorenz
934e82d89ccSAlex Lorenz cl::opt<bool> ShowRegions(
935e82d89ccSAlex Lorenz "show-regions", cl::Optional,
936e82d89ccSAlex Lorenz cl::desc("Show the execution counts for each region"),
937e82d89ccSAlex Lorenz cl::cat(ViewCategory));
938e82d89ccSAlex Lorenz
9399f2967bcSAlan Phipps cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
9409f2967bcSAlan Phipps "show-branches", cl::Optional,
9419f2967bcSAlan Phipps cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
9429f2967bcSAlan Phipps cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
9439f2967bcSAlan Phipps "count", "Show True/False counts"),
9449f2967bcSAlan Phipps clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
9459f2967bcSAlan Phipps "percent", "Show True/False percent")),
9469f2967bcSAlan Phipps cl::init(CoverageViewOptions::BranchOutputType::Off));
9479f2967bcSAlan Phipps
948e82d89ccSAlex Lorenz cl::opt<bool> ShowBestLineRegionsCounts(
949e82d89ccSAlex Lorenz "show-line-counts-or-regions", cl::Optional,
950e82d89ccSAlex Lorenz cl::desc("Show the execution counts for each line, or the execution "
951e82d89ccSAlex Lorenz "counts for each region on lines that have multiple regions"),
952e82d89ccSAlex Lorenz cl::cat(ViewCategory));
953e82d89ccSAlex Lorenz
954e82d89ccSAlex Lorenz cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
955e82d89ccSAlex Lorenz cl::desc("Show expanded source regions"),
956e82d89ccSAlex Lorenz cl::cat(ViewCategory));
957e82d89ccSAlex Lorenz
958e82d89ccSAlex Lorenz cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
959e82d89ccSAlex Lorenz cl::desc("Show function instantiations"),
96079554e45SVedant Kumar cl::init(true), cl::cat(ViewCategory));
961e82d89ccSAlex Lorenz
9627937ef37SVedant Kumar cl::opt<std::string> ShowOutputDirectory(
9637937ef37SVedant Kumar "output-dir", cl::init(""),
9647937ef37SVedant Kumar cl::desc("Directory in which coverage information is written out"));
9657937ef37SVedant Kumar cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
9667937ef37SVedant Kumar cl::aliasopt(ShowOutputDirectory));
9677937ef37SVedant Kumar
9680ef31b79SYing Yi cl::opt<uint32_t> TabSize(
969ad547d36SVedant Kumar "tab-size", cl::init(2),
970ad547d36SVedant Kumar cl::desc(
971ad547d36SVedant Kumar "Set tab expansion size for html coverage reports (default = 2)"));
9720ef31b79SYing Yi
97384dc971eSYing Yi cl::opt<std::string> ProjectTitle(
97484dc971eSYing Yi "project-title", cl::Optional,
97584dc971eSYing Yi cl::desc("Set project title for the coverage report"));
97684dc971eSYing Yi
977b5f1a8cfSPetr Hosek cl::opt<std::string> CovWatermark(
978b5f1a8cfSPetr Hosek "coverage-watermark", cl::Optional,
979b5f1a8cfSPetr Hosek cl::desc("<high>,<low> value indicate thresholds for high and low"
980b5f1a8cfSPetr Hosek "coverage watermark"));
981b5f1a8cfSPetr Hosek
982e82d89ccSAlex Lorenz auto Err = commandLineParser(argc, argv);
983e82d89ccSAlex Lorenz if (Err)
984e82d89ccSAlex Lorenz return Err;
985e82d89ccSAlex Lorenz
986b2091c93SMax Moroz if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
987b2091c93SMax Moroz error("Lcov format should be used with 'llvm-cov export'.");
988b2091c93SMax Moroz return 1;
989b2091c93SMax Moroz }
990b2091c93SMax Moroz
991b5f1a8cfSPetr Hosek ViewOpts.HighCovWatermark = 100.0;
992b5f1a8cfSPetr Hosek ViewOpts.LowCovWatermark = 80.0;
993b5f1a8cfSPetr Hosek if (!CovWatermark.empty()) {
994b5f1a8cfSPetr Hosek auto WaterMarkPair = StringRef(CovWatermark).split(',');
995b5f1a8cfSPetr Hosek if (WaterMarkPair.first.empty() || WaterMarkPair.second.empty()) {
996b5f1a8cfSPetr Hosek error("invalid argument '" + CovWatermark +
997b5f1a8cfSPetr Hosek "', must be in format 'high,low'",
998b5f1a8cfSPetr Hosek "-coverage-watermark");
999b5f1a8cfSPetr Hosek return 1;
1000b5f1a8cfSPetr Hosek }
1001b5f1a8cfSPetr Hosek
1002b5f1a8cfSPetr Hosek char *EndPointer = nullptr;
1003b5f1a8cfSPetr Hosek ViewOpts.HighCovWatermark =
1004b5f1a8cfSPetr Hosek strtod(WaterMarkPair.first.begin(), &EndPointer);
1005b5f1a8cfSPetr Hosek if (EndPointer != WaterMarkPair.first.end()) {
1006b5f1a8cfSPetr Hosek error("invalid number '" + WaterMarkPair.first +
1007b5f1a8cfSPetr Hosek "', invalid value for 'high'",
1008b5f1a8cfSPetr Hosek "-coverage-watermark");
1009b5f1a8cfSPetr Hosek return 1;
1010b5f1a8cfSPetr Hosek }
1011b5f1a8cfSPetr Hosek
1012b5f1a8cfSPetr Hosek ViewOpts.LowCovWatermark =
1013b5f1a8cfSPetr Hosek strtod(WaterMarkPair.second.begin(), &EndPointer);
1014b5f1a8cfSPetr Hosek if (EndPointer != WaterMarkPair.second.end()) {
1015b5f1a8cfSPetr Hosek error("invalid number '" + WaterMarkPair.second +
1016b5f1a8cfSPetr Hosek "', invalid value for 'low'",
1017b5f1a8cfSPetr Hosek "-coverage-watermark");
1018b5f1a8cfSPetr Hosek return 1;
1019b5f1a8cfSPetr Hosek }
1020b5f1a8cfSPetr Hosek
1021b5f1a8cfSPetr Hosek if (ViewOpts.HighCovWatermark > 100 || ViewOpts.LowCovWatermark < 0 ||
1022b5f1a8cfSPetr Hosek ViewOpts.HighCovWatermark <= ViewOpts.LowCovWatermark) {
1023b5f1a8cfSPetr Hosek error(
1024b5f1a8cfSPetr Hosek "invalid number range '" + CovWatermark +
1025b5f1a8cfSPetr Hosek "', must be both high and low should be between 0-100, and high "
1026b5f1a8cfSPetr Hosek "> low",
1027b5f1a8cfSPetr Hosek "-coverage-watermark");
1028b5f1a8cfSPetr Hosek return 1;
1029b5f1a8cfSPetr Hosek }
1030b5f1a8cfSPetr Hosek }
1031b5f1a8cfSPetr Hosek
1032e82d89ccSAlex Lorenz ViewOpts.ShowLineNumbers = true;
1033e82d89ccSAlex Lorenz ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
1034e82d89ccSAlex Lorenz !ShowRegions || ShowBestLineRegionsCounts;
1035e82d89ccSAlex Lorenz ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
1036e82d89ccSAlex Lorenz ViewOpts.ShowExpandedRegions = ShowExpansions;
10379f2967bcSAlan Phipps ViewOpts.ShowBranchCounts =
10389f2967bcSAlan Phipps ShowBranches == CoverageViewOptions::BranchOutputType::Count;
10399f2967bcSAlan Phipps ViewOpts.ShowBranchPercents =
10409f2967bcSAlan Phipps ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
1041e82d89ccSAlex Lorenz ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
10427937ef37SVedant Kumar ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
10430ef31b79SYing Yi ViewOpts.TabSize = TabSize;
104484dc971eSYing Yi ViewOpts.ProjectTitle = ProjectTitle;
10457937ef37SVedant Kumar
104664d8a029SVedant Kumar if (ViewOpts.hasOutputDirectory()) {
10477937ef37SVedant Kumar if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
10487937ef37SVedant Kumar error("Could not create output directory!", E.message());
10497937ef37SVedant Kumar return 1;
10507937ef37SVedant Kumar }
10517937ef37SVedant Kumar }
1052e82d89ccSAlex Lorenz
105384dc971eSYing Yi sys::fs::file_status Status;
10549eb1b418SEli Friedman if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
105544d9def7SZequan Wu error("Could not read profile data!" + EC.message(), PGOFilename);
105684dc971eSYing Yi return 1;
105784dc971eSYing Yi }
105884dc971eSYing Yi
105984dc971eSYing Yi auto ModifiedTime = Status.getLastModificationTime();
1060757ca886SPavel Labath std::string ModifiedTimeStr = to_string(ModifiedTime);
1061e6ba5efaSBenjamin Kramer size_t found = ModifiedTimeStr.rfind(':');
106284dc971eSYing Yi ViewOpts.CreatedTimeStr = (found != std::string::npos)
106384dc971eSYing Yi ? "Created: " + ModifiedTimeStr.substr(0, found)
106484dc971eSYing Yi : "Created: " + ModifiedTimeStr;
106584dc971eSYing Yi
1066953e2407SJustin Bogner auto Coverage = load();
1067953e2407SJustin Bogner if (!Coverage)
1068e82d89ccSAlex Lorenz return 1;
1069e82d89ccSAlex Lorenz
10709cbad2c2SVedant Kumar auto Printer = CoveragePrinter::create(ViewOpts);
10719cbad2c2SVedant Kumar
1072e92eeaf3SZequan Wu if (SourceFiles.empty() && !HadSourceFiles)
10731439fa62SSean Eveson // Get the source files from the function coverage mapping.
10744220f891SMax Moroz for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
10758d24d72fSVlad Tsyrklevich if (!IgnoreFilenameFilters.matchesFilename(Filename))
1076adcd0268SBenjamin Kramer SourceFiles.push_back(std::string(Filename));
10774220f891SMax Moroz }
10781439fa62SSean Eveson
10791439fa62SSean Eveson // Create an index out of the source files.
10801439fa62SSean Eveson if (ViewOpts.hasOutputDirectory()) {
1081fa8ef35eSSean Eveson if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) {
10821439fa62SSean Eveson error("Could not create index file!", toString(std::move(E)));
10831439fa62SSean Eveson return 1;
10841439fa62SSean Eveson }
10851439fa62SSean Eveson }
10861439fa62SSean Eveson
1087fa8ef35eSSean Eveson if (!Filters.empty()) {
1088fa8ef35eSSean Eveson // Build the map of filenames to functions.
1089fa8ef35eSSean Eveson std::map<llvm::StringRef, std::vector<const FunctionRecord *>>
1090fa8ef35eSSean Eveson FilenameFunctionMap;
1091fa8ef35eSSean Eveson for (const auto &SourceFile : SourceFiles)
1092fa8ef35eSSean Eveson for (const auto &Function : Coverage->getCoveredFunctions(SourceFile))
1093fa8ef35eSSean Eveson if (Filters.matches(*Coverage.get(), Function))
1094fa8ef35eSSean Eveson FilenameFunctionMap[SourceFile].push_back(&Function);
1095fa8ef35eSSean Eveson
1096fa8ef35eSSean Eveson // Only print filter matching functions for each file.
1097fa8ef35eSSean Eveson for (const auto &FileFunc : FilenameFunctionMap) {
1098fa8ef35eSSean Eveson StringRef File = FileFunc.first;
1099fa8ef35eSSean Eveson const auto &Functions = FileFunc.second;
1100fa8ef35eSSean Eveson
1101fa8ef35eSSean Eveson auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false);
1102fa8ef35eSSean Eveson if (Error E = OSOrErr.takeError()) {
1103fa8ef35eSSean Eveson error("Could not create view file!", toString(std::move(E)));
1104fa8ef35eSSean Eveson return 1;
1105fa8ef35eSSean Eveson }
1106fa8ef35eSSean Eveson auto OS = std::move(OSOrErr.get());
1107fa8ef35eSSean Eveson
1108ea9dceedSSean Eveson bool ShowTitle = ViewOpts.hasOutputDirectory();
1109fa8ef35eSSean Eveson for (const auto *Function : Functions) {
1110fa8ef35eSSean Eveson auto FunctionView = createFunctionView(*Function, *Coverage);
1111fa8ef35eSSean Eveson if (!FunctionView) {
1112fa8ef35eSSean Eveson warning("Could not read coverage for '" + Function->Name + "'.");
1113fa8ef35eSSean Eveson continue;
1114fa8ef35eSSean Eveson }
1115fa8ef35eSSean Eveson FunctionView->print(*OS.get(), /*WholeFile=*/false,
1116fa8ef35eSSean Eveson /*ShowSourceName=*/true, ShowTitle);
1117fa8ef35eSSean Eveson ShowTitle = false;
1118fa8ef35eSSean Eveson }
1119fa8ef35eSSean Eveson
1120fa8ef35eSSean Eveson Printer->closeViewFile(std::move(OS));
1121fa8ef35eSSean Eveson }
1122fa8ef35eSSean Eveson return 0;
1123fa8ef35eSSean Eveson }
1124fa8ef35eSSean Eveson
1125fa8ef35eSSean Eveson // Show files
1126fa8ef35eSSean Eveson bool ShowFilenames =
1127fa8ef35eSSean Eveson (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
1128fa8ef35eSSean Eveson (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
1129fa8ef35eSSean Eveson
11300e13a033SAlexandre Ganea ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
11310e13a033SAlexandre Ganea if (ViewOpts.NumThreads == 0) {
11320e13a033SAlexandre Ganea // If NumThreads is not specified, create one thread for each input, up to
11330e13a033SAlexandre Ganea // the number of hardware cores.
11340e13a033SAlexandre Ganea S = heavyweight_hardware_concurrency(SourceFiles.size());
11350e13a033SAlexandre Ganea S.Limit = true;
11360e13a033SAlexandre Ganea }
1137cc254ba4SMax Moroz
11380e13a033SAlexandre Ganea if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
11396fd94bf4SVedant Kumar for (const std::string &SourceFile : SourceFiles)
11406fd94bf4SVedant Kumar writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
11416fd94bf4SVedant Kumar ShowFilenames);
11426fd94bf4SVedant Kumar } else {
114386b2ac63SVedant Kumar // In -output-dir mode, it's safe to use multiple threads to print files.
11440e13a033SAlexandre Ganea ThreadPool Pool(S);
11456fd94bf4SVedant Kumar for (const std::string &SourceFile : SourceFiles)
11466fd94bf4SVedant Kumar Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
11476fd94bf4SVedant Kumar Coverage.get(), Printer.get(), ShowFilenames);
114886b2ac63SVedant Kumar Pool.wait();
11496fd94bf4SVedant Kumar }
115086b2ac63SVedant Kumar
1151e82d89ccSAlex Lorenz return 0;
1152e82d89ccSAlex Lorenz }
1153e82d89ccSAlex Lorenz
doReport(int argc,const char ** argv,CommandLineParserType commandLineParser)11541ef3a778SMax Moroz int CodeCoverageTool::doReport(int argc, const char **argv,
1155e82d89ccSAlex Lorenz CommandLineParserType commandLineParser) {
115662eb0fdbSVedant Kumar cl::opt<bool> ShowFunctionSummaries(
115762eb0fdbSVedant Kumar "show-functions", cl::Optional, cl::init(false),
115862eb0fdbSVedant Kumar cl::desc("Show coverage summaries for each function"));
115962eb0fdbSVedant Kumar
1160e82d89ccSAlex Lorenz auto Err = commandLineParser(argc, argv);
1161e82d89ccSAlex Lorenz if (Err)
1162e82d89ccSAlex Lorenz return Err;
1163e82d89ccSAlex Lorenz
1164431359aaSVedant Kumar if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) {
11654c01092aSVedant Kumar error("HTML output for summary reports is not yet supported.");
1166431359aaSVedant Kumar return 1;
1167b2091c93SMax Moroz } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
1168b2091c93SMax Moroz error("Lcov format should be used with 'llvm-cov export'.");
1169b2091c93SMax Moroz return 1;
1170431359aaSVedant Kumar }
11714c01092aSVedant Kumar
117244d9def7SZequan Wu sys::fs::file_status Status;
117344d9def7SZequan Wu if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
117444d9def7SZequan Wu error("Could not read profile data!" + EC.message(), PGOFilename);
117544d9def7SZequan Wu return 1;
117644d9def7SZequan Wu }
117744d9def7SZequan Wu
1178953e2407SJustin Bogner auto Coverage = load();
1179953e2407SJustin Bogner if (!Coverage)
1180e82d89ccSAlex Lorenz return 1;
1181e82d89ccSAlex Lorenz
1182702bb9d9SVedant Kumar CoverageReport Report(ViewOpts, *Coverage.get());
1183feb3f527SVedant Kumar if (!ShowFunctionSummaries) {
11844a4bfa4eSMax Moroz if (SourceFiles.empty())
11858d24d72fSVlad Tsyrklevich Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
11864a4bfa4eSMax Moroz else
11874a4bfa4eSMax Moroz Report.renderFileReports(llvm::outs(), SourceFiles);
1188feb3f527SVedant Kumar } else {
1189feb3f527SVedant Kumar if (SourceFiles.empty()) {
1190feb3f527SVedant Kumar error("Source files must be specified when -show-functions=true is "
1191feb3f527SVedant Kumar "specified");
1192feb3f527SVedant Kumar return 1;
1193feb3f527SVedant Kumar }
1194feb3f527SVedant Kumar
1195f2b067c4SVedant Kumar Report.renderFunctionReports(SourceFiles, DC, llvm::outs());
1196feb3f527SVedant Kumar }
1197e82d89ccSAlex Lorenz return 0;
1198e82d89ccSAlex Lorenz }
1199e82d89ccSAlex Lorenz
doExport(int argc,const char ** argv,CommandLineParserType commandLineParser)12001ef3a778SMax Moroz int CodeCoverageTool::doExport(int argc, const char **argv,
12017101d73cSVedant Kumar CommandLineParserType commandLineParser) {
12027101d73cSVedant Kumar
1203a80d9ce5SMax Moroz cl::OptionCategory ExportCategory("Exporting options");
1204a80d9ce5SMax Moroz
1205a80d9ce5SMax Moroz cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional,
1206a80d9ce5SMax Moroz cl::desc("Don't export expanded source regions"),
1207a80d9ce5SMax Moroz cl::cat(ExportCategory));
1208a80d9ce5SMax Moroz
1209a80d9ce5SMax Moroz cl::opt<bool> SkipFunctions("skip-functions", cl::Optional,
1210a80d9ce5SMax Moroz cl::desc("Don't export per-function data"),
1211a80d9ce5SMax Moroz cl::cat(ExportCategory));
1212a80d9ce5SMax Moroz
12137101d73cSVedant Kumar auto Err = commandLineParser(argc, argv);
12147101d73cSVedant Kumar if (Err)
12157101d73cSVedant Kumar return Err;
12167101d73cSVedant Kumar
1217a80d9ce5SMax Moroz ViewOpts.SkipExpansions = SkipExpansions;
1218a80d9ce5SMax Moroz ViewOpts.SkipFunctions = SkipFunctions;
1219a80d9ce5SMax Moroz
1220b2091c93SMax Moroz if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
1221b2091c93SMax Moroz ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
1222b2091c93SMax Moroz error("Coverage data can only be exported as textual JSON or an "
1223b2091c93SMax Moroz "lcov tracefile.");
1224431359aaSVedant Kumar return 1;
1225431359aaSVedant Kumar }
1226431359aaSVedant Kumar
122744d9def7SZequan Wu sys::fs::file_status Status;
122844d9def7SZequan Wu if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
122944d9def7SZequan Wu error("Could not read profile data!" + EC.message(), PGOFilename);
123044d9def7SZequan Wu return 1;
123144d9def7SZequan Wu }
123244d9def7SZequan Wu
12337101d73cSVedant Kumar auto Coverage = load();
12347101d73cSVedant Kumar if (!Coverage) {
12357101d73cSVedant Kumar error("Could not load coverage information");
12367101d73cSVedant Kumar return 1;
12377101d73cSVedant Kumar }
12387101d73cSVedant Kumar
1239b2091c93SMax Moroz std::unique_ptr<CoverageExporter> Exporter;
1240b2091c93SMax Moroz
1241b2091c93SMax Moroz switch (ViewOpts.Format) {
1242b2091c93SMax Moroz case CoverageViewOptions::OutputFormat::Text:
12430eaee545SJonas Devlieghere Exporter = std::make_unique<CoverageExporterJson>(*Coverage.get(),
1244b2091c93SMax Moroz ViewOpts, outs());
1245b2091c93SMax Moroz break;
1246b2091c93SMax Moroz case CoverageViewOptions::OutputFormat::HTML:
1247b2091c93SMax Moroz // Unreachable because we should have gracefully terminated with an error
1248b2091c93SMax Moroz // above.
1249b2091c93SMax Moroz llvm_unreachable("Export in HTML is not supported!");
1250b2091c93SMax Moroz case CoverageViewOptions::OutputFormat::Lcov:
12510eaee545SJonas Devlieghere Exporter = std::make_unique<CoverageExporterLcov>(*Coverage.get(),
1252b2091c93SMax Moroz ViewOpts, outs());
1253b2091c93SMax Moroz break;
1254b2091c93SMax Moroz }
12551ef3a778SMax Moroz
12561ef3a778SMax Moroz if (SourceFiles.empty())
12578d24d72fSVlad Tsyrklevich Exporter->renderRoot(IgnoreFilenameFilters);
12581ef3a778SMax Moroz else
1259b2091c93SMax Moroz Exporter->renderRoot(SourceFiles);
12607101d73cSVedant Kumar
12617101d73cSVedant Kumar return 0;
12627101d73cSVedant Kumar }
12637101d73cSVedant Kumar
showMain(int argc,const char * argv[])1264d249a3b3SJustin Bogner int showMain(int argc, const char *argv[]) {
1265e82d89ccSAlex Lorenz CodeCoverageTool Tool;
1266e82d89ccSAlex Lorenz return Tool.run(CodeCoverageTool::Show, argc, argv);
1267e82d89ccSAlex Lorenz }
1268e82d89ccSAlex Lorenz
reportMain(int argc,const char * argv[])1269d249a3b3SJustin Bogner int reportMain(int argc, const char *argv[]) {
1270e82d89ccSAlex Lorenz CodeCoverageTool Tool;
1271e82d89ccSAlex Lorenz return Tool.run(CodeCoverageTool::Report, argc, argv);
1272e82d89ccSAlex Lorenz }
12737101d73cSVedant Kumar
exportMain(int argc,const char * argv[])12747101d73cSVedant Kumar int exportMain(int argc, const char *argv[]) {
12757101d73cSVedant Kumar CodeCoverageTool Tool;
12767101d73cSVedant Kumar return Tool.run(CodeCoverageTool::Export, argc, argv);
12777101d73cSVedant Kumar }
1278