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