xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp (revision 1ac55f4cb0001fed92329746c730aa9a947c09a5)
10b57cec5SDimitry Andric //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // The 'CodeCoverageTool' class implements a command line tool to analyze and
100b57cec5SDimitry Andric // report coverage information using the profiling instrumentation and code
110b57cec5SDimitry Andric // coverage mapping.
120b57cec5SDimitry Andric //
130b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
140b57cec5SDimitry Andric 
150b57cec5SDimitry Andric #include "CoverageExporterJson.h"
160b57cec5SDimitry Andric #include "CoverageExporterLcov.h"
170b57cec5SDimitry Andric #include "CoverageFilters.h"
180b57cec5SDimitry Andric #include "CoverageReport.h"
190b57cec5SDimitry Andric #include "CoverageSummaryInfo.h"
200b57cec5SDimitry Andric #include "CoverageViewOptions.h"
210b57cec5SDimitry Andric #include "RenderingSupport.h"
220b57cec5SDimitry Andric #include "SourceCoverageView.h"
230b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
240b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
250b57cec5SDimitry Andric #include "llvm/ADT/Triple.h"
26*1ac55f4cSDimitry Andric #include "llvm/Debuginfod/BuildIDFetcher.h"
27*1ac55f4cSDimitry Andric #include "llvm/Debuginfod/Debuginfod.h"
28*1ac55f4cSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h"
29*1ac55f4cSDimitry Andric #include "llvm/Object/BuildID.h"
300b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMapping.h"
310b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h"
320b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
330b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
340b57cec5SDimitry Andric #include "llvm/Support/Format.h"
350b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
360b57cec5SDimitry Andric #include "llvm/Support/Path.h"
370b57cec5SDimitry Andric #include "llvm/Support/Process.h"
380b57cec5SDimitry Andric #include "llvm/Support/Program.h"
390b57cec5SDimitry Andric #include "llvm/Support/ScopedPrinter.h"
405ffd83dbSDimitry Andric #include "llvm/Support/SpecialCaseList.h"
410b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
420b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
430b57cec5SDimitry Andric #include "llvm/Support/ToolOutputFile.h"
44480093f4SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric #include <functional>
470b57cec5SDimitry Andric #include <map>
48bdd1243dSDimitry Andric #include <optional>
490b57cec5SDimitry Andric #include <system_error>
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric using namespace llvm;
520b57cec5SDimitry Andric using namespace coverage;
530b57cec5SDimitry Andric 
540b57cec5SDimitry Andric void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping,
550b57cec5SDimitry Andric                               const CoverageViewOptions &Options,
560b57cec5SDimitry Andric                               raw_ostream &OS);
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric namespace {
590b57cec5SDimitry Andric /// The implementation of the coverage tool.
600b57cec5SDimitry Andric class CodeCoverageTool {
610b57cec5SDimitry Andric public:
620b57cec5SDimitry Andric   enum Command {
630b57cec5SDimitry Andric     /// The show command.
640b57cec5SDimitry Andric     Show,
650b57cec5SDimitry Andric     /// The report command.
660b57cec5SDimitry Andric     Report,
670b57cec5SDimitry Andric     /// The export command.
680b57cec5SDimitry Andric     Export
690b57cec5SDimitry Andric   };
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric   int run(Command Cmd, int argc, const char **argv);
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric private:
740b57cec5SDimitry Andric   /// Print the error message to the error output stream.
750b57cec5SDimitry Andric   void error(const Twine &Message, StringRef Whence = "");
760b57cec5SDimitry Andric 
770b57cec5SDimitry Andric   /// Print the warning message to the error output stream.
780b57cec5SDimitry Andric   void warning(const Twine &Message, StringRef Whence = "");
790b57cec5SDimitry Andric 
800b57cec5SDimitry Andric   /// Convert \p Path into an absolute path and append it to the list
810b57cec5SDimitry Andric   /// of collected paths.
820b57cec5SDimitry Andric   void addCollectedPath(const std::string &Path);
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric   /// If \p Path is a regular file, collect the path. If it's a
850b57cec5SDimitry Andric   /// directory, recursively collect all of the paths within the directory.
860b57cec5SDimitry Andric   void collectPaths(const std::string &Path);
870b57cec5SDimitry Andric 
88fe6060f1SDimitry Andric   /// Check if the two given files are the same file.
89fe6060f1SDimitry Andric   bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2);
90fe6060f1SDimitry Andric 
91fe6060f1SDimitry Andric   /// Retrieve a file status with a cache.
92bdd1243dSDimitry Andric   std::optional<sys::fs::file_status> getFileStatus(StringRef FilePath);
93fe6060f1SDimitry Andric 
940b57cec5SDimitry Andric   /// Return a memory buffer for the given source file.
950b57cec5SDimitry Andric   ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric   /// Create source views for the expansions of the view.
980b57cec5SDimitry Andric   void attachExpansionSubViews(SourceCoverageView &View,
990b57cec5SDimitry Andric                                ArrayRef<ExpansionRecord> Expansions,
1000b57cec5SDimitry Andric                                const CoverageMapping &Coverage);
1010b57cec5SDimitry Andric 
102e8d8bef9SDimitry Andric   /// Create source views for the branches of the view.
103e8d8bef9SDimitry Andric   void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
104e8d8bef9SDimitry Andric                             ArrayRef<CountedRegion> Branches,
105e8d8bef9SDimitry Andric                             const MemoryBuffer &File,
106e8d8bef9SDimitry Andric                             CoverageData &CoverageInfo);
107e8d8bef9SDimitry Andric 
1080b57cec5SDimitry Andric   /// Create the source view of a particular function.
1090b57cec5SDimitry Andric   std::unique_ptr<SourceCoverageView>
1100b57cec5SDimitry Andric   createFunctionView(const FunctionRecord &Function,
1110b57cec5SDimitry Andric                      const CoverageMapping &Coverage);
1120b57cec5SDimitry Andric 
1130b57cec5SDimitry Andric   /// Create the main source view of a particular source file.
1140b57cec5SDimitry Andric   std::unique_ptr<SourceCoverageView>
1150b57cec5SDimitry Andric   createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
1160b57cec5SDimitry Andric 
1170b57cec5SDimitry Andric   /// Load the coverage mapping data. Return nullptr if an error occurred.
1180b57cec5SDimitry Andric   std::unique_ptr<CoverageMapping> load();
1190b57cec5SDimitry Andric 
1200b57cec5SDimitry Andric   /// Create a mapping from files in the Coverage data to local copies
1210b57cec5SDimitry Andric   /// (path-equivalence).
1220b57cec5SDimitry Andric   void remapPathNames(const CoverageMapping &Coverage);
1230b57cec5SDimitry Andric 
1240b57cec5SDimitry Andric   /// Remove input source files which aren't mapped by \p Coverage.
1250b57cec5SDimitry Andric   void removeUnmappedInputs(const CoverageMapping &Coverage);
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   /// If a demangler is available, demangle all symbol names.
1280b57cec5SDimitry Andric   void demangleSymbols(const CoverageMapping &Coverage);
1290b57cec5SDimitry Andric 
1300b57cec5SDimitry Andric   /// Write out a source file view to the filesystem.
1310b57cec5SDimitry Andric   void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
1320b57cec5SDimitry Andric                            CoveragePrinter *Printer, bool ShowFilenames);
1330b57cec5SDimitry Andric 
1340b57cec5SDimitry Andric   typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   int doShow(int argc, const char **argv,
1370b57cec5SDimitry Andric              CommandLineParserType commandLineParser);
1380b57cec5SDimitry Andric 
1390b57cec5SDimitry Andric   int doReport(int argc, const char **argv,
1400b57cec5SDimitry Andric                CommandLineParserType commandLineParser);
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric   int doExport(int argc, const char **argv,
1430b57cec5SDimitry Andric                CommandLineParserType commandLineParser);
1440b57cec5SDimitry Andric 
1450b57cec5SDimitry Andric   std::vector<StringRef> ObjectFilenames;
1460b57cec5SDimitry Andric   CoverageViewOptions ViewOpts;
1470b57cec5SDimitry Andric   CoverageFiltersMatchAll Filters;
1480b57cec5SDimitry Andric   CoverageFilters IgnoreFilenameFilters;
1490b57cec5SDimitry Andric 
150e8d8bef9SDimitry Andric   /// True if InputSourceFiles are provided.
151e8d8bef9SDimitry Andric   bool HadSourceFiles = false;
152e8d8bef9SDimitry Andric 
1530b57cec5SDimitry Andric   /// The path to the indexed profile.
1540b57cec5SDimitry Andric   std::string PGOFilename;
1550b57cec5SDimitry Andric 
1560b57cec5SDimitry Andric   /// A list of input source files.
1570b57cec5SDimitry Andric   std::vector<std::string> SourceFiles;
1580b57cec5SDimitry Andric 
1590b57cec5SDimitry Andric   /// In -path-equivalence mode, this maps the absolute paths from the coverage
1600b57cec5SDimitry Andric   /// mapping data to the input source files.
1610b57cec5SDimitry Andric   StringMap<std::string> RemappedFilenames;
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric   /// The coverage data path to be remapped from, and the source path to be
1640b57cec5SDimitry Andric   /// remapped to, when using -path-equivalence.
165bdd1243dSDimitry Andric   std::optional<std::pair<std::string, std::string>> PathRemapping;
1660b57cec5SDimitry Andric 
167fe6060f1SDimitry Andric   /// File status cache used when finding the same file.
168bdd1243dSDimitry Andric   StringMap<std::optional<sys::fs::file_status>> FileStatusCache;
169fe6060f1SDimitry Andric 
1700b57cec5SDimitry Andric   /// The architecture the coverage mapping data targets.
1710b57cec5SDimitry Andric   std::vector<StringRef> CoverageArches;
1720b57cec5SDimitry Andric 
1730b57cec5SDimitry Andric   /// A cache for demangled symbols.
1740b57cec5SDimitry Andric   DemangleCache DC;
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric   /// A lock which guards printing to stderr.
1770b57cec5SDimitry Andric   std::mutex ErrsLock;
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric   /// A container for input source file buffers.
1800b57cec5SDimitry Andric   std::mutex LoadedSourceFilesLock;
1810b57cec5SDimitry Andric   std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
1820b57cec5SDimitry Andric       LoadedSourceFiles;
1830b57cec5SDimitry Andric 
1844824e7fdSDimitry Andric   /// Allowlist from -name-allowlist to be used for filtering.
1854824e7fdSDimitry Andric   std::unique_ptr<SpecialCaseList> NameAllowlist;
186*1ac55f4cSDimitry Andric 
187*1ac55f4cSDimitry Andric   std::unique_ptr<object::BuildIDFetcher> BIDFetcher;
1880b57cec5SDimitry Andric };
1890b57cec5SDimitry Andric }
1900b57cec5SDimitry Andric 
1910b57cec5SDimitry Andric static std::string getErrorString(const Twine &Message, StringRef Whence,
1920b57cec5SDimitry Andric                                   bool Warning) {
1930b57cec5SDimitry Andric   std::string Str = (Warning ? "warning" : "error");
1940b57cec5SDimitry Andric   Str += ": ";
1950b57cec5SDimitry Andric   if (!Whence.empty())
1960b57cec5SDimitry Andric     Str += Whence.str() + ": ";
1970b57cec5SDimitry Andric   Str += Message.str() + "\n";
1980b57cec5SDimitry Andric   return Str;
1990b57cec5SDimitry Andric }
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
2020b57cec5SDimitry Andric   std::unique_lock<std::mutex> Guard{ErrsLock};
2030b57cec5SDimitry Andric   ViewOpts.colored_ostream(errs(), raw_ostream::RED)
2040b57cec5SDimitry Andric       << getErrorString(Message, Whence, false);
2050b57cec5SDimitry Andric }
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
2080b57cec5SDimitry Andric   std::unique_lock<std::mutex> Guard{ErrsLock};
2090b57cec5SDimitry Andric   ViewOpts.colored_ostream(errs(), raw_ostream::RED)
2100b57cec5SDimitry Andric       << getErrorString(Message, Whence, true);
2110b57cec5SDimitry Andric }
2120b57cec5SDimitry Andric 
2130b57cec5SDimitry Andric void CodeCoverageTool::addCollectedPath(const std::string &Path) {
2140b57cec5SDimitry Andric   SmallString<128> EffectivePath(Path);
2150b57cec5SDimitry Andric   if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
2160b57cec5SDimitry Andric     error(EC.message(), Path);
2170b57cec5SDimitry Andric     return;
2180b57cec5SDimitry Andric   }
21904eeddc0SDimitry Andric   sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true);
2200b57cec5SDimitry Andric   if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
2210b57cec5SDimitry Andric     SourceFiles.emplace_back(EffectivePath.str());
222e8d8bef9SDimitry Andric   HadSourceFiles = !SourceFiles.empty();
2230b57cec5SDimitry Andric }
2240b57cec5SDimitry Andric 
2250b57cec5SDimitry Andric void CodeCoverageTool::collectPaths(const std::string &Path) {
2260b57cec5SDimitry Andric   llvm::sys::fs::file_status Status;
2270b57cec5SDimitry Andric   llvm::sys::fs::status(Path, Status);
2280b57cec5SDimitry Andric   if (!llvm::sys::fs::exists(Status)) {
2290b57cec5SDimitry Andric     if (PathRemapping)
2300b57cec5SDimitry Andric       addCollectedPath(Path);
2310b57cec5SDimitry Andric     else
2320b57cec5SDimitry Andric       warning("Source file doesn't exist, proceeded by ignoring it.", Path);
2330b57cec5SDimitry Andric     return;
2340b57cec5SDimitry Andric   }
2350b57cec5SDimitry Andric 
2360b57cec5SDimitry Andric   if (llvm::sys::fs::is_regular_file(Status)) {
2370b57cec5SDimitry Andric     addCollectedPath(Path);
2380b57cec5SDimitry Andric     return;
2390b57cec5SDimitry Andric   }
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   if (llvm::sys::fs::is_directory(Status)) {
2420b57cec5SDimitry Andric     std::error_code EC;
2430b57cec5SDimitry Andric     for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
2440b57cec5SDimitry Andric          F != E; F.increment(EC)) {
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric       auto Status = F->status();
2470b57cec5SDimitry Andric       if (!Status) {
2480b57cec5SDimitry Andric         warning(Status.getError().message(), F->path());
2490b57cec5SDimitry Andric         continue;
2500b57cec5SDimitry Andric       }
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric       if (Status->type() == llvm::sys::fs::file_type::regular_file)
2530b57cec5SDimitry Andric         addCollectedPath(F->path());
2540b57cec5SDimitry Andric     }
2550b57cec5SDimitry Andric   }
2560b57cec5SDimitry Andric }
2570b57cec5SDimitry Andric 
258bdd1243dSDimitry Andric std::optional<sys::fs::file_status>
259fe6060f1SDimitry Andric CodeCoverageTool::getFileStatus(StringRef FilePath) {
260fe6060f1SDimitry Andric   auto It = FileStatusCache.try_emplace(FilePath);
261fe6060f1SDimitry Andric   auto &CachedStatus = It.first->getValue();
262fe6060f1SDimitry Andric   if (!It.second)
263fe6060f1SDimitry Andric     return CachedStatus;
264fe6060f1SDimitry Andric 
265fe6060f1SDimitry Andric   sys::fs::file_status Status;
266fe6060f1SDimitry Andric   if (!sys::fs::status(FilePath, Status))
267fe6060f1SDimitry Andric     CachedStatus = Status;
268fe6060f1SDimitry Andric   return CachedStatus;
269fe6060f1SDimitry Andric }
270fe6060f1SDimitry Andric 
271fe6060f1SDimitry Andric bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1,
272fe6060f1SDimitry Andric                                         StringRef FilePath2) {
273fe6060f1SDimitry Andric   auto Status1 = getFileStatus(FilePath1);
274fe6060f1SDimitry Andric   auto Status2 = getFileStatus(FilePath2);
27581ad6265SDimitry Andric   return Status1 && Status2 && sys::fs::equivalent(*Status1, *Status2);
276fe6060f1SDimitry Andric }
277fe6060f1SDimitry Andric 
2780b57cec5SDimitry Andric ErrorOr<const MemoryBuffer &>
2790b57cec5SDimitry Andric CodeCoverageTool::getSourceFile(StringRef SourceFile) {
2800b57cec5SDimitry Andric   // If we've remapped filenames, look up the real location for this file.
2810b57cec5SDimitry Andric   std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
2820b57cec5SDimitry Andric   if (!RemappedFilenames.empty()) {
2830b57cec5SDimitry Andric     auto Loc = RemappedFilenames.find(SourceFile);
2840b57cec5SDimitry Andric     if (Loc != RemappedFilenames.end())
2850b57cec5SDimitry Andric       SourceFile = Loc->second;
2860b57cec5SDimitry Andric   }
2870b57cec5SDimitry Andric   for (const auto &Files : LoadedSourceFiles)
288fe6060f1SDimitry Andric     if (isEquivalentFile(SourceFile, Files.first))
2890b57cec5SDimitry Andric       return *Files.second;
2900b57cec5SDimitry Andric   auto Buffer = MemoryBuffer::getFile(SourceFile);
2910b57cec5SDimitry Andric   if (auto EC = Buffer.getError()) {
2920b57cec5SDimitry Andric     error(EC.message(), SourceFile);
2930b57cec5SDimitry Andric     return EC;
2940b57cec5SDimitry Andric   }
2955ffd83dbSDimitry Andric   LoadedSourceFiles.emplace_back(std::string(SourceFile),
2965ffd83dbSDimitry Andric                                  std::move(Buffer.get()));
2970b57cec5SDimitry Andric   return *LoadedSourceFiles.back().second;
2980b57cec5SDimitry Andric }
2990b57cec5SDimitry Andric 
3000b57cec5SDimitry Andric void CodeCoverageTool::attachExpansionSubViews(
3010b57cec5SDimitry Andric     SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
3020b57cec5SDimitry Andric     const CoverageMapping &Coverage) {
3030b57cec5SDimitry Andric   if (!ViewOpts.ShowExpandedRegions)
3040b57cec5SDimitry Andric     return;
3050b57cec5SDimitry Andric   for (const auto &Expansion : Expansions) {
3060b57cec5SDimitry Andric     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
3070b57cec5SDimitry Andric     if (ExpansionCoverage.empty())
3080b57cec5SDimitry Andric       continue;
3090b57cec5SDimitry Andric     auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
3100b57cec5SDimitry Andric     if (!SourceBuffer)
3110b57cec5SDimitry Andric       continue;
3120b57cec5SDimitry Andric 
313e8d8bef9SDimitry Andric     auto SubViewBranches = ExpansionCoverage.getBranches();
3140b57cec5SDimitry Andric     auto SubViewExpansions = ExpansionCoverage.getExpansions();
3150b57cec5SDimitry Andric     auto SubView =
3160b57cec5SDimitry Andric         SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
3170b57cec5SDimitry Andric                                    ViewOpts, std::move(ExpansionCoverage));
3180b57cec5SDimitry Andric     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
319e8d8bef9SDimitry Andric     attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
320e8d8bef9SDimitry Andric                          SourceBuffer.get(), ExpansionCoverage);
3210b57cec5SDimitry Andric     View.addExpansion(Expansion.Region, std::move(SubView));
3220b57cec5SDimitry Andric   }
3230b57cec5SDimitry Andric }
3240b57cec5SDimitry Andric 
325e8d8bef9SDimitry Andric void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
326e8d8bef9SDimitry Andric                                             StringRef SourceName,
327e8d8bef9SDimitry Andric                                             ArrayRef<CountedRegion> Branches,
328e8d8bef9SDimitry Andric                                             const MemoryBuffer &File,
329e8d8bef9SDimitry Andric                                             CoverageData &CoverageInfo) {
330e8d8bef9SDimitry Andric   if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
331e8d8bef9SDimitry Andric     return;
332e8d8bef9SDimitry Andric 
333e8d8bef9SDimitry Andric   const auto *NextBranch = Branches.begin();
334e8d8bef9SDimitry Andric   const auto *EndBranch = Branches.end();
335e8d8bef9SDimitry Andric 
336e8d8bef9SDimitry Andric   // Group branches that have the same line number into the same subview.
337e8d8bef9SDimitry Andric   while (NextBranch != EndBranch) {
338e8d8bef9SDimitry Andric     std::vector<CountedRegion> ViewBranches;
339e8d8bef9SDimitry Andric     unsigned CurrentLine = NextBranch->LineStart;
340e8d8bef9SDimitry Andric 
341e8d8bef9SDimitry Andric     while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
342e8d8bef9SDimitry Andric       ViewBranches.push_back(*NextBranch++);
343e8d8bef9SDimitry Andric 
344e8d8bef9SDimitry Andric     if (!ViewBranches.empty()) {
345e8d8bef9SDimitry Andric       auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
346e8d8bef9SDimitry Andric                                                 std::move(CoverageInfo));
347e8d8bef9SDimitry Andric       View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
348e8d8bef9SDimitry Andric     }
349e8d8bef9SDimitry Andric   }
350e8d8bef9SDimitry Andric }
351e8d8bef9SDimitry Andric 
3520b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView>
3530b57cec5SDimitry Andric CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
3540b57cec5SDimitry Andric                                      const CoverageMapping &Coverage) {
3550b57cec5SDimitry Andric   auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
3560b57cec5SDimitry Andric   if (FunctionCoverage.empty())
3570b57cec5SDimitry Andric     return nullptr;
3580b57cec5SDimitry Andric   auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
3590b57cec5SDimitry Andric   if (!SourceBuffer)
3600b57cec5SDimitry Andric     return nullptr;
3610b57cec5SDimitry Andric 
362e8d8bef9SDimitry Andric   auto Branches = FunctionCoverage.getBranches();
3630b57cec5SDimitry Andric   auto Expansions = FunctionCoverage.getExpansions();
3640b57cec5SDimitry Andric   auto View = SourceCoverageView::create(DC.demangle(Function.Name),
3650b57cec5SDimitry Andric                                          SourceBuffer.get(), ViewOpts,
3660b57cec5SDimitry Andric                                          std::move(FunctionCoverage));
3670b57cec5SDimitry Andric   attachExpansionSubViews(*View, Expansions, Coverage);
368e8d8bef9SDimitry Andric   attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
369e8d8bef9SDimitry Andric                        SourceBuffer.get(), FunctionCoverage);
3700b57cec5SDimitry Andric 
3710b57cec5SDimitry Andric   return View;
3720b57cec5SDimitry Andric }
3730b57cec5SDimitry Andric 
3740b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView>
3750b57cec5SDimitry Andric CodeCoverageTool::createSourceFileView(StringRef SourceFile,
3760b57cec5SDimitry Andric                                        const CoverageMapping &Coverage) {
3770b57cec5SDimitry Andric   auto SourceBuffer = getSourceFile(SourceFile);
3780b57cec5SDimitry Andric   if (!SourceBuffer)
3790b57cec5SDimitry Andric     return nullptr;
3800b57cec5SDimitry Andric   auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
3810b57cec5SDimitry Andric   if (FileCoverage.empty())
3820b57cec5SDimitry Andric     return nullptr;
3830b57cec5SDimitry Andric 
384e8d8bef9SDimitry Andric   auto Branches = FileCoverage.getBranches();
3850b57cec5SDimitry Andric   auto Expansions = FileCoverage.getExpansions();
3860b57cec5SDimitry Andric   auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
3870b57cec5SDimitry Andric                                          ViewOpts, std::move(FileCoverage));
3880b57cec5SDimitry Andric   attachExpansionSubViews(*View, Expansions, Coverage);
389e8d8bef9SDimitry Andric   attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
390e8d8bef9SDimitry Andric                        FileCoverage);
3910b57cec5SDimitry Andric   if (!ViewOpts.ShowFunctionInstantiations)
3920b57cec5SDimitry Andric     return View;
3930b57cec5SDimitry Andric 
3940b57cec5SDimitry Andric   for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) {
3950b57cec5SDimitry Andric     // Skip functions which have a single instantiation.
3960b57cec5SDimitry Andric     if (Group.size() < 2)
3970b57cec5SDimitry Andric       continue;
3980b57cec5SDimitry Andric 
3990b57cec5SDimitry Andric     for (const FunctionRecord *Function : Group.getInstantiations()) {
4000b57cec5SDimitry Andric       std::unique_ptr<SourceCoverageView> SubView{nullptr};
4010b57cec5SDimitry Andric 
4020b57cec5SDimitry Andric       StringRef Funcname = DC.demangle(Function->Name);
4030b57cec5SDimitry Andric 
4040b57cec5SDimitry Andric       if (Function->ExecutionCount > 0) {
4050b57cec5SDimitry Andric         auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
4060b57cec5SDimitry Andric         auto SubViewExpansions = SubViewCoverage.getExpansions();
407e8d8bef9SDimitry Andric         auto SubViewBranches = SubViewCoverage.getBranches();
4080b57cec5SDimitry Andric         SubView = SourceCoverageView::create(
4090b57cec5SDimitry Andric             Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
4100b57cec5SDimitry Andric         attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
411e8d8bef9SDimitry Andric         attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
412e8d8bef9SDimitry Andric                              SourceBuffer.get(), SubViewCoverage);
4130b57cec5SDimitry Andric       }
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric       unsigned FileID = Function->CountedRegions.front().FileID;
4160b57cec5SDimitry Andric       unsigned Line = 0;
4170b57cec5SDimitry Andric       for (const auto &CR : Function->CountedRegions)
4180b57cec5SDimitry Andric         if (CR.FileID == FileID)
4190b57cec5SDimitry Andric           Line = std::max(CR.LineEnd, Line);
4200b57cec5SDimitry Andric       View->addInstantiation(Funcname, Line, std::move(SubView));
4210b57cec5SDimitry Andric     }
4220b57cec5SDimitry Andric   }
4230b57cec5SDimitry Andric   return View;
4240b57cec5SDimitry Andric }
4250b57cec5SDimitry Andric 
4260b57cec5SDimitry Andric static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
4270b57cec5SDimitry Andric   sys::fs::file_status Status;
4280b57cec5SDimitry Andric   if (sys::fs::status(LHS, Status))
4290b57cec5SDimitry Andric     return false;
4300b57cec5SDimitry Andric   auto LHSTime = Status.getLastModificationTime();
4310b57cec5SDimitry Andric   if (sys::fs::status(RHS, Status))
4320b57cec5SDimitry Andric     return false;
4330b57cec5SDimitry Andric   auto RHSTime = Status.getLastModificationTime();
4340b57cec5SDimitry Andric   return LHSTime > RHSTime;
4350b57cec5SDimitry Andric }
4360b57cec5SDimitry Andric 
4370b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
4380b57cec5SDimitry Andric   for (StringRef ObjectFilename : ObjectFilenames)
4390b57cec5SDimitry Andric     if (modifiedTimeGT(ObjectFilename, PGOFilename))
4400b57cec5SDimitry Andric       warning("profile data may be out of date - object is newer",
4410b57cec5SDimitry Andric               ObjectFilename);
4420b57cec5SDimitry Andric   auto CoverageOrErr =
443fe6060f1SDimitry Andric       CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches,
444*1ac55f4cSDimitry Andric                             ViewOpts.CompilationDirectory, BIDFetcher.get());
4450b57cec5SDimitry Andric   if (Error E = CoverageOrErr.takeError()) {
446fcaf7f86SDimitry Andric     error("Failed to load coverage: " + toString(std::move(E)));
4470b57cec5SDimitry Andric     return nullptr;
4480b57cec5SDimitry Andric   }
4490b57cec5SDimitry Andric   auto Coverage = std::move(CoverageOrErr.get());
4500b57cec5SDimitry Andric   unsigned Mismatched = Coverage->getMismatchedCount();
4510b57cec5SDimitry Andric   if (Mismatched) {
4520b57cec5SDimitry Andric     warning(Twine(Mismatched) + " functions have mismatched data");
4530b57cec5SDimitry Andric 
4540b57cec5SDimitry Andric     if (ViewOpts.Debug) {
4550b57cec5SDimitry Andric       for (const auto &HashMismatch : Coverage->getHashMismatches())
4560b57cec5SDimitry Andric         errs() << "hash-mismatch: "
4570b57cec5SDimitry Andric                << "No profile record found for '" << HashMismatch.first << "'"
4580b57cec5SDimitry Andric                << " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
4590b57cec5SDimitry Andric                << '\n';
4600b57cec5SDimitry Andric     }
4610b57cec5SDimitry Andric   }
4620b57cec5SDimitry Andric 
4630b57cec5SDimitry Andric   remapPathNames(*Coverage);
4640b57cec5SDimitry Andric 
4650b57cec5SDimitry Andric   if (!SourceFiles.empty())
4660b57cec5SDimitry Andric     removeUnmappedInputs(*Coverage);
4670b57cec5SDimitry Andric 
4680b57cec5SDimitry Andric   demangleSymbols(*Coverage);
4690b57cec5SDimitry Andric 
4700b57cec5SDimitry Andric   return Coverage;
4710b57cec5SDimitry Andric }
4720b57cec5SDimitry Andric 
4730b57cec5SDimitry Andric void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
4740b57cec5SDimitry Andric   if (!PathRemapping)
4750b57cec5SDimitry Andric     return;
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric   // Convert remapping paths to native paths with trailing seperators.
4780b57cec5SDimitry Andric   auto nativeWithTrailing = [](StringRef Path) -> std::string {
4790b57cec5SDimitry Andric     if (Path.empty())
4800b57cec5SDimitry Andric       return "";
4810b57cec5SDimitry Andric     SmallString<128> NativePath;
4820b57cec5SDimitry Andric     sys::path::native(Path, NativePath);
483e8d8bef9SDimitry Andric     sys::path::remove_dots(NativePath, true);
484fe6060f1SDimitry Andric     if (!NativePath.empty() && !sys::path::is_separator(NativePath.back()))
4850b57cec5SDimitry Andric       NativePath += sys::path::get_separator();
4860b57cec5SDimitry Andric     return NativePath.c_str();
4870b57cec5SDimitry Andric   };
4880b57cec5SDimitry Andric   std::string RemapFrom = nativeWithTrailing(PathRemapping->first);
4890b57cec5SDimitry Andric   std::string RemapTo = nativeWithTrailing(PathRemapping->second);
4900b57cec5SDimitry Andric 
4910b57cec5SDimitry Andric   // Create a mapping from coverage data file paths to local paths.
4920b57cec5SDimitry Andric   for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
4930b57cec5SDimitry Andric     SmallString<128> NativeFilename;
4940b57cec5SDimitry Andric     sys::path::native(Filename, NativeFilename);
495e8d8bef9SDimitry Andric     sys::path::remove_dots(NativeFilename, true);
4960b57cec5SDimitry Andric     if (NativeFilename.startswith(RemapFrom)) {
4970b57cec5SDimitry Andric       RemappedFilenames[Filename] =
4980b57cec5SDimitry Andric           RemapTo + NativeFilename.substr(RemapFrom.size()).str();
4990b57cec5SDimitry Andric     }
5000b57cec5SDimitry Andric   }
5010b57cec5SDimitry Andric 
5020b57cec5SDimitry Andric   // Convert input files from local paths to coverage data file paths.
5030b57cec5SDimitry Andric   StringMap<std::string> InvRemappedFilenames;
5040b57cec5SDimitry Andric   for (const auto &RemappedFilename : RemappedFilenames)
5055ffd83dbSDimitry Andric     InvRemappedFilenames[RemappedFilename.getValue()] =
5065ffd83dbSDimitry Andric         std::string(RemappedFilename.getKey());
5070b57cec5SDimitry Andric 
5080b57cec5SDimitry Andric   for (std::string &Filename : SourceFiles) {
5090b57cec5SDimitry Andric     SmallString<128> NativeFilename;
5100b57cec5SDimitry Andric     sys::path::native(Filename, NativeFilename);
5110b57cec5SDimitry Andric     auto CovFileName = InvRemappedFilenames.find(NativeFilename);
5120b57cec5SDimitry Andric     if (CovFileName != InvRemappedFilenames.end())
5130b57cec5SDimitry Andric       Filename = CovFileName->second;
5140b57cec5SDimitry Andric   }
5150b57cec5SDimitry Andric }
5160b57cec5SDimitry Andric 
5170b57cec5SDimitry Andric void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
5180b57cec5SDimitry Andric   std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
5190b57cec5SDimitry Andric 
5200b57cec5SDimitry Andric   // The user may have specified source files which aren't in the coverage
5210b57cec5SDimitry Andric   // mapping. Filter these files away.
522e8d8bef9SDimitry Andric   llvm::erase_if(SourceFiles, [&](const std::string &SF) {
523e8d8bef9SDimitry Andric     return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF);
5240b57cec5SDimitry Andric   });
5250b57cec5SDimitry Andric }
5260b57cec5SDimitry Andric 
5270b57cec5SDimitry Andric void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
5280b57cec5SDimitry Andric   if (!ViewOpts.hasDemangler())
5290b57cec5SDimitry Andric     return;
5300b57cec5SDimitry Andric 
5310b57cec5SDimitry Andric   // Pass function names to the demangler in a temporary file.
5320b57cec5SDimitry Andric   int InputFD;
5330b57cec5SDimitry Andric   SmallString<256> InputPath;
5340b57cec5SDimitry Andric   std::error_code EC =
5350b57cec5SDimitry Andric       sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
5360b57cec5SDimitry Andric   if (EC) {
5370b57cec5SDimitry Andric     error(InputPath, EC.message());
5380b57cec5SDimitry Andric     return;
5390b57cec5SDimitry Andric   }
5400b57cec5SDimitry Andric   ToolOutputFile InputTOF{InputPath, InputFD};
5410b57cec5SDimitry Andric 
5420b57cec5SDimitry Andric   unsigned NumSymbols = 0;
5430b57cec5SDimitry Andric   for (const auto &Function : Coverage.getCoveredFunctions()) {
5440b57cec5SDimitry Andric     InputTOF.os() << Function.Name << '\n';
5450b57cec5SDimitry Andric     ++NumSymbols;
5460b57cec5SDimitry Andric   }
5470b57cec5SDimitry Andric   InputTOF.os().close();
5480b57cec5SDimitry Andric 
5490b57cec5SDimitry Andric   // Use another temporary file to store the demangler's output.
5500b57cec5SDimitry Andric   int OutputFD;
5510b57cec5SDimitry Andric   SmallString<256> OutputPath;
5520b57cec5SDimitry Andric   EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
5530b57cec5SDimitry Andric                                     OutputPath);
5540b57cec5SDimitry Andric   if (EC) {
5550b57cec5SDimitry Andric     error(OutputPath, EC.message());
5560b57cec5SDimitry Andric     return;
5570b57cec5SDimitry Andric   }
5580b57cec5SDimitry Andric   ToolOutputFile OutputTOF{OutputPath, OutputFD};
5590b57cec5SDimitry Andric   OutputTOF.os().close();
5600b57cec5SDimitry Andric 
5610b57cec5SDimitry Andric   // Invoke the demangler.
5620b57cec5SDimitry Andric   std::vector<StringRef> ArgsV;
563bdd1243dSDimitry Andric   ArgsV.reserve(ViewOpts.DemanglerOpts.size());
5640b57cec5SDimitry Andric   for (StringRef Arg : ViewOpts.DemanglerOpts)
5650b57cec5SDimitry Andric     ArgsV.push_back(Arg);
566bdd1243dSDimitry Andric   std::optional<StringRef> Redirects[] = {
567bdd1243dSDimitry Andric       InputPath.str(), OutputPath.str(), {""}};
5680b57cec5SDimitry Andric   std::string ErrMsg;
569bdd1243dSDimitry Andric   int RC =
570bdd1243dSDimitry Andric       sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
571bdd1243dSDimitry Andric                           /*env=*/std::nullopt, Redirects, /*secondsToWait=*/0,
5720b57cec5SDimitry Andric                           /*memoryLimit=*/0, &ErrMsg);
5730b57cec5SDimitry Andric   if (RC) {
5740b57cec5SDimitry Andric     error(ErrMsg, ViewOpts.DemanglerOpts[0]);
5750b57cec5SDimitry Andric     return;
5760b57cec5SDimitry Andric   }
5770b57cec5SDimitry Andric 
5780b57cec5SDimitry Andric   // Parse the demangler's output.
5790b57cec5SDimitry Andric   auto BufOrError = MemoryBuffer::getFile(OutputPath);
5800b57cec5SDimitry Andric   if (!BufOrError) {
5810b57cec5SDimitry Andric     error(OutputPath, BufOrError.getError().message());
5820b57cec5SDimitry Andric     return;
5830b57cec5SDimitry Andric   }
5840b57cec5SDimitry Andric 
5850b57cec5SDimitry Andric   std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
5860b57cec5SDimitry Andric 
5870b57cec5SDimitry Andric   SmallVector<StringRef, 8> Symbols;
5880b57cec5SDimitry Andric   StringRef DemanglerData = DemanglerBuf->getBuffer();
5890b57cec5SDimitry Andric   DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
5900b57cec5SDimitry Andric                       /*KeepEmpty=*/false);
5910b57cec5SDimitry Andric   if (Symbols.size() != NumSymbols) {
5920b57cec5SDimitry Andric     error("Demangler did not provide expected number of symbols");
5930b57cec5SDimitry Andric     return;
5940b57cec5SDimitry Andric   }
5950b57cec5SDimitry Andric 
5960b57cec5SDimitry Andric   // Cache the demangled names.
5970b57cec5SDimitry Andric   unsigned I = 0;
5980b57cec5SDimitry Andric   for (const auto &Function : Coverage.getCoveredFunctions())
5990b57cec5SDimitry Andric     // On Windows, lines in the demangler's output file end with "\r\n".
6000b57cec5SDimitry Andric     // Splitting by '\n' keeps '\r's, so cut them now.
6015ffd83dbSDimitry Andric     DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
6020b57cec5SDimitry Andric }
6030b57cec5SDimitry Andric 
6040b57cec5SDimitry Andric void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
6050b57cec5SDimitry Andric                                            CoverageMapping *Coverage,
6060b57cec5SDimitry Andric                                            CoveragePrinter *Printer,
6070b57cec5SDimitry Andric                                            bool ShowFilenames) {
6080b57cec5SDimitry Andric   auto View = createSourceFileView(SourceFile, *Coverage);
6090b57cec5SDimitry Andric   if (!View) {
6100b57cec5SDimitry Andric     warning("The file '" + SourceFile + "' isn't covered.");
6110b57cec5SDimitry Andric     return;
6120b57cec5SDimitry Andric   }
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric   auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
6150b57cec5SDimitry Andric   if (Error E = OSOrErr.takeError()) {
6160b57cec5SDimitry Andric     error("Could not create view file!", toString(std::move(E)));
6170b57cec5SDimitry Andric     return;
6180b57cec5SDimitry Andric   }
6190b57cec5SDimitry Andric   auto OS = std::move(OSOrErr.get());
6200b57cec5SDimitry Andric 
6210b57cec5SDimitry Andric   View->print(*OS.get(), /*Wholefile=*/true,
6220b57cec5SDimitry Andric               /*ShowSourceName=*/ShowFilenames,
6230b57cec5SDimitry Andric               /*ShowTitle=*/ViewOpts.hasOutputDirectory());
6240b57cec5SDimitry Andric   Printer->closeViewFile(std::move(OS));
6250b57cec5SDimitry Andric }
6260b57cec5SDimitry Andric 
6270b57cec5SDimitry Andric int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
6280b57cec5SDimitry Andric   cl::opt<std::string> CovFilename(
6290b57cec5SDimitry Andric       cl::Positional, cl::desc("Covered executable or object file."));
6300b57cec5SDimitry Andric 
6310b57cec5SDimitry Andric   cl::list<std::string> CovFilenames(
63281ad6265SDimitry Andric       "object", cl::desc("Coverage executable or object file"));
633e8d8bef9SDimitry Andric 
634e8d8bef9SDimitry Andric   cl::opt<bool> DebugDumpCollectedObjects(
635e8d8bef9SDimitry Andric       "dump-collected-objects", cl::Optional, cl::Hidden,
636e8d8bef9SDimitry Andric       cl::desc("Show the collected coverage object files"));
6370b57cec5SDimitry Andric 
638*1ac55f4cSDimitry Andric   cl::list<std::string> InputSourceFiles("sources", cl::Positional,
63981ad6265SDimitry Andric                                          cl::desc("<Source files>"));
6400b57cec5SDimitry Andric 
6410b57cec5SDimitry Andric   cl::opt<bool> DebugDumpCollectedPaths(
6420b57cec5SDimitry Andric       "dump-collected-paths", cl::Optional, cl::Hidden,
6430b57cec5SDimitry Andric       cl::desc("Show the collected paths to source files"));
6440b57cec5SDimitry Andric 
6450b57cec5SDimitry Andric   cl::opt<std::string, true> PGOFilename(
6460b57cec5SDimitry Andric       "instr-profile", cl::Required, cl::location(this->PGOFilename),
6470b57cec5SDimitry Andric       cl::desc(
6480b57cec5SDimitry Andric           "File with the profile data obtained after an instrumented run"));
6490b57cec5SDimitry Andric 
6500b57cec5SDimitry Andric   cl::list<std::string> Arches(
6510b57cec5SDimitry Andric       "arch", cl::desc("architectures of the coverage mapping binaries"));
6520b57cec5SDimitry Andric 
6530b57cec5SDimitry Andric   cl::opt<bool> DebugDump("dump", cl::Optional,
6540b57cec5SDimitry Andric                           cl::desc("Show internal debug dump"));
6550b57cec5SDimitry Andric 
656*1ac55f4cSDimitry Andric   cl::list<std::string> DebugFileDirectory(
657*1ac55f4cSDimitry Andric       "debug-file-directory",
658*1ac55f4cSDimitry Andric       cl::desc("Directories to search for object files by build ID"));
659*1ac55f4cSDimitry Andric   cl::opt<bool> Debuginfod(
660*1ac55f4cSDimitry Andric       "debuginfod", cl::ZeroOrMore,
661*1ac55f4cSDimitry Andric       cl::desc("Use debuginfod to look up object files from profile"),
662*1ac55f4cSDimitry Andric       cl::init(canUseDebuginfod()));
663*1ac55f4cSDimitry Andric 
6640b57cec5SDimitry Andric   cl::opt<CoverageViewOptions::OutputFormat> Format(
6650b57cec5SDimitry Andric       "format", cl::desc("Output format for line-based coverage reports"),
6660b57cec5SDimitry Andric       cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
6670b57cec5SDimitry Andric                             "Text output"),
6680b57cec5SDimitry Andric                  clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
6690b57cec5SDimitry Andric                             "HTML output"),
6700b57cec5SDimitry Andric                  clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
6710b57cec5SDimitry Andric                             "lcov tracefile output")),
6720b57cec5SDimitry Andric       cl::init(CoverageViewOptions::OutputFormat::Text));
6730b57cec5SDimitry Andric 
6740b57cec5SDimitry Andric   cl::opt<std::string> PathRemap(
6750b57cec5SDimitry Andric       "path-equivalence", cl::Optional,
6760b57cec5SDimitry Andric       cl::desc("<from>,<to> Map coverage data paths to local source file "
6770b57cec5SDimitry Andric                "paths"));
6780b57cec5SDimitry Andric 
6790b57cec5SDimitry Andric   cl::OptionCategory FilteringCategory("Function filtering options");
6800b57cec5SDimitry Andric 
6810b57cec5SDimitry Andric   cl::list<std::string> NameFilters(
6820b57cec5SDimitry Andric       "name", cl::Optional,
6830b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with the given name"),
68481ad6265SDimitry Andric       cl::cat(FilteringCategory));
6850b57cec5SDimitry Andric 
6860b57cec5SDimitry Andric   cl::list<std::string> NameFilterFiles(
6874824e7fdSDimitry Andric       "name-allowlist", cl::Optional,
6880b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions listed in the given "
6890b57cec5SDimitry Andric                "file"),
69081ad6265SDimitry Andric       cl::cat(FilteringCategory));
6910b57cec5SDimitry Andric 
6920b57cec5SDimitry Andric   cl::list<std::string> NameRegexFilters(
6930b57cec5SDimitry Andric       "name-regex", cl::Optional,
6940b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions that match the given "
6950b57cec5SDimitry Andric                "regular expression"),
69681ad6265SDimitry Andric       cl::cat(FilteringCategory));
6970b57cec5SDimitry Andric 
6980b57cec5SDimitry Andric   cl::list<std::string> IgnoreFilenameRegexFilters(
6990b57cec5SDimitry Andric       "ignore-filename-regex", cl::Optional,
7000b57cec5SDimitry Andric       cl::desc("Skip source code files with file paths that match the given "
7010b57cec5SDimitry Andric                "regular expression"),
70281ad6265SDimitry Andric       cl::cat(FilteringCategory));
7030b57cec5SDimitry Andric 
7040b57cec5SDimitry Andric   cl::opt<double> RegionCoverageLtFilter(
7050b57cec5SDimitry Andric       "region-coverage-lt", cl::Optional,
7060b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with region coverage "
7070b57cec5SDimitry Andric                "less than the given threshold"),
7080b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7090b57cec5SDimitry Andric 
7100b57cec5SDimitry Andric   cl::opt<double> RegionCoverageGtFilter(
7110b57cec5SDimitry Andric       "region-coverage-gt", cl::Optional,
7120b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with region coverage "
7130b57cec5SDimitry Andric                "greater than the given threshold"),
7140b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7150b57cec5SDimitry Andric 
7160b57cec5SDimitry Andric   cl::opt<double> LineCoverageLtFilter(
7170b57cec5SDimitry Andric       "line-coverage-lt", cl::Optional,
7180b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with line coverage less "
7190b57cec5SDimitry Andric                "than the given threshold"),
7200b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7210b57cec5SDimitry Andric 
7220b57cec5SDimitry Andric   cl::opt<double> LineCoverageGtFilter(
7230b57cec5SDimitry Andric       "line-coverage-gt", cl::Optional,
7240b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with line coverage "
7250b57cec5SDimitry Andric                "greater than the given threshold"),
7260b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7270b57cec5SDimitry Andric 
7280b57cec5SDimitry Andric   cl::opt<cl::boolOrDefault> UseColor(
7290b57cec5SDimitry Andric       "use-color", cl::desc("Emit colored output (default=autodetect)"),
7300b57cec5SDimitry Andric       cl::init(cl::BOU_UNSET));
7310b57cec5SDimitry Andric 
7320b57cec5SDimitry Andric   cl::list<std::string> DemanglerOpts(
7330b57cec5SDimitry Andric       "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
7340b57cec5SDimitry Andric 
7350b57cec5SDimitry Andric   cl::opt<bool> RegionSummary(
7360b57cec5SDimitry Andric       "show-region-summary", cl::Optional,
7370b57cec5SDimitry Andric       cl::desc("Show region statistics in summary table"),
7380b57cec5SDimitry Andric       cl::init(true));
7390b57cec5SDimitry Andric 
740e8d8bef9SDimitry Andric   cl::opt<bool> BranchSummary(
741e8d8bef9SDimitry Andric       "show-branch-summary", cl::Optional,
742e8d8bef9SDimitry Andric       cl::desc("Show branch condition statistics in summary table"),
743e8d8bef9SDimitry Andric       cl::init(true));
744e8d8bef9SDimitry Andric 
7450b57cec5SDimitry Andric   cl::opt<bool> InstantiationSummary(
7460b57cec5SDimitry Andric       "show-instantiation-summary", cl::Optional,
7470b57cec5SDimitry Andric       cl::desc("Show instantiation statistics in summary table"));
7480b57cec5SDimitry Andric 
7490b57cec5SDimitry Andric   cl::opt<bool> SummaryOnly(
7500b57cec5SDimitry Andric       "summary-only", cl::Optional,
7510b57cec5SDimitry Andric       cl::desc("Export only summary information for each source file"));
7520b57cec5SDimitry Andric 
7530b57cec5SDimitry Andric   cl::opt<unsigned> NumThreads(
7540b57cec5SDimitry Andric       "num-threads", cl::init(0),
7550b57cec5SDimitry Andric       cl::desc("Number of merge threads to use (default: autodetect)"));
7560b57cec5SDimitry Andric   cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
7570b57cec5SDimitry Andric                         cl::aliasopt(NumThreads));
7580b57cec5SDimitry Andric 
759fe6060f1SDimitry Andric   cl::opt<std::string> CompilationDirectory(
760fe6060f1SDimitry Andric       "compilation-dir", cl::init(""),
761fe6060f1SDimitry Andric       cl::desc("Directory used as a base for relative coverage mapping paths"));
762fe6060f1SDimitry Andric 
7630b57cec5SDimitry Andric   auto commandLineParser = [&, this](int argc, const char **argv) -> int {
7640b57cec5SDimitry Andric     cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
7650b57cec5SDimitry Andric     ViewOpts.Debug = DebugDump;
766*1ac55f4cSDimitry Andric     if (Debuginfod) {
767*1ac55f4cSDimitry Andric       HTTPClient::initialize();
768*1ac55f4cSDimitry Andric       BIDFetcher = std::make_unique<DebuginfodFetcher>(DebugFileDirectory);
769*1ac55f4cSDimitry Andric     } else {
770*1ac55f4cSDimitry Andric       BIDFetcher = std::make_unique<object::BuildIDFetcher>(DebugFileDirectory);
771*1ac55f4cSDimitry Andric     }
7720b57cec5SDimitry Andric 
7730b57cec5SDimitry Andric     if (!CovFilename.empty())
7740b57cec5SDimitry Andric       ObjectFilenames.emplace_back(CovFilename);
7750b57cec5SDimitry Andric     for (const std::string &Filename : CovFilenames)
7760b57cec5SDimitry Andric       ObjectFilenames.emplace_back(Filename);
777*1ac55f4cSDimitry Andric     if (ObjectFilenames.empty() && !Debuginfod && DebugFileDirectory.empty()) {
7780b57cec5SDimitry Andric       errs() << "No filenames specified!\n";
7790b57cec5SDimitry Andric       ::exit(1);
7800b57cec5SDimitry Andric     }
7810b57cec5SDimitry Andric 
782e8d8bef9SDimitry Andric     if (DebugDumpCollectedObjects) {
783e8d8bef9SDimitry Andric       for (StringRef OF : ObjectFilenames)
784e8d8bef9SDimitry Andric         outs() << OF << '\n';
785e8d8bef9SDimitry Andric       ::exit(0);
786e8d8bef9SDimitry Andric     }
787e8d8bef9SDimitry Andric 
7880b57cec5SDimitry Andric     ViewOpts.Format = Format;
7890b57cec5SDimitry Andric     switch (ViewOpts.Format) {
7900b57cec5SDimitry Andric     case CoverageViewOptions::OutputFormat::Text:
7910b57cec5SDimitry Andric       ViewOpts.Colors = UseColor == cl::BOU_UNSET
7920b57cec5SDimitry Andric                             ? sys::Process::StandardOutHasColors()
7930b57cec5SDimitry Andric                             : UseColor == cl::BOU_TRUE;
7940b57cec5SDimitry Andric       break;
7950b57cec5SDimitry Andric     case CoverageViewOptions::OutputFormat::HTML:
7960b57cec5SDimitry Andric       if (UseColor == cl::BOU_FALSE)
7970b57cec5SDimitry Andric         errs() << "Color output cannot be disabled when generating html.\n";
7980b57cec5SDimitry Andric       ViewOpts.Colors = true;
7990b57cec5SDimitry Andric       break;
8000b57cec5SDimitry Andric     case CoverageViewOptions::OutputFormat::Lcov:
8010b57cec5SDimitry Andric       if (UseColor == cl::BOU_TRUE)
8020b57cec5SDimitry Andric         errs() << "Color output cannot be enabled when generating lcov.\n";
8030b57cec5SDimitry Andric       ViewOpts.Colors = false;
8040b57cec5SDimitry Andric       break;
8050b57cec5SDimitry Andric     }
8060b57cec5SDimitry Andric 
8070b57cec5SDimitry Andric     // If path-equivalence was given and is a comma seperated pair then set
8080b57cec5SDimitry Andric     // PathRemapping.
809349cc55cSDimitry Andric     if (!PathRemap.empty()) {
8100b57cec5SDimitry Andric       auto EquivPair = StringRef(PathRemap).split(',');
811349cc55cSDimitry Andric       if (EquivPair.first.empty() || EquivPair.second.empty()) {
812349cc55cSDimitry Andric         error("invalid argument '" + PathRemap +
813349cc55cSDimitry Andric                   "', must be in format 'from,to'",
814349cc55cSDimitry Andric               "-path-equivalence");
815349cc55cSDimitry Andric         return 1;
816349cc55cSDimitry Andric       }
817349cc55cSDimitry Andric 
8185ffd83dbSDimitry Andric       PathRemapping = {std::string(EquivPair.first),
8195ffd83dbSDimitry Andric                        std::string(EquivPair.second)};
820349cc55cSDimitry Andric     }
8210b57cec5SDimitry Andric 
8220b57cec5SDimitry Andric     // If a demangler is supplied, check if it exists and register it.
8230b57cec5SDimitry Andric     if (!DemanglerOpts.empty()) {
8240b57cec5SDimitry Andric       auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
8250b57cec5SDimitry Andric       if (!DemanglerPathOrErr) {
8260b57cec5SDimitry Andric         error("Could not find the demangler!",
8270b57cec5SDimitry Andric               DemanglerPathOrErr.getError().message());
8280b57cec5SDimitry Andric         return 1;
8290b57cec5SDimitry Andric       }
8300b57cec5SDimitry Andric       DemanglerOpts[0] = *DemanglerPathOrErr;
8310b57cec5SDimitry Andric       ViewOpts.DemanglerOpts.swap(DemanglerOpts);
8320b57cec5SDimitry Andric     }
8330b57cec5SDimitry Andric 
8344824e7fdSDimitry Andric     // Read in -name-allowlist files.
835bdd1243dSDimitry Andric     if (!NameFilterFiles.empty()) {
8360b57cec5SDimitry Andric       std::string SpecialCaseListErr;
8374824e7fdSDimitry Andric       NameAllowlist = SpecialCaseList::create(
838480093f4SDimitry Andric           NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr);
8394824e7fdSDimitry Andric       if (!NameAllowlist)
8400b57cec5SDimitry Andric         error(SpecialCaseListErr);
8410b57cec5SDimitry Andric     }
8420b57cec5SDimitry Andric 
8430b57cec5SDimitry Andric     // Create the function filters
8444824e7fdSDimitry Andric     if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) {
8458bcb0991SDimitry Andric       auto NameFilterer = std::make_unique<CoverageFilters>();
8460b57cec5SDimitry Andric       for (const auto &Name : NameFilters)
8478bcb0991SDimitry Andric         NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name));
848bdd1243dSDimitry Andric       if (NameAllowlist && !NameFilterFiles.empty())
8490b57cec5SDimitry Andric         NameFilterer->push_back(
8504824e7fdSDimitry Andric             std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist));
8510b57cec5SDimitry Andric       for (const auto &Regex : NameRegexFilters)
8520b57cec5SDimitry Andric         NameFilterer->push_back(
8538bcb0991SDimitry Andric             std::make_unique<NameRegexCoverageFilter>(Regex));
8540b57cec5SDimitry Andric       Filters.push_back(std::move(NameFilterer));
8550b57cec5SDimitry Andric     }
8560b57cec5SDimitry Andric 
8570b57cec5SDimitry Andric     if (RegionCoverageLtFilter.getNumOccurrences() ||
8580b57cec5SDimitry Andric         RegionCoverageGtFilter.getNumOccurrences() ||
8590b57cec5SDimitry Andric         LineCoverageLtFilter.getNumOccurrences() ||
8600b57cec5SDimitry Andric         LineCoverageGtFilter.getNumOccurrences()) {
8618bcb0991SDimitry Andric       auto StatFilterer = std::make_unique<CoverageFilters>();
8620b57cec5SDimitry Andric       if (RegionCoverageLtFilter.getNumOccurrences())
8638bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
8640b57cec5SDimitry Andric             RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
8650b57cec5SDimitry Andric       if (RegionCoverageGtFilter.getNumOccurrences())
8668bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
8670b57cec5SDimitry Andric             RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
8680b57cec5SDimitry Andric       if (LineCoverageLtFilter.getNumOccurrences())
8698bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
8700b57cec5SDimitry Andric             LineCoverageFilter::LessThan, LineCoverageLtFilter));
8710b57cec5SDimitry Andric       if (LineCoverageGtFilter.getNumOccurrences())
8728bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
8730b57cec5SDimitry Andric             RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
8740b57cec5SDimitry Andric       Filters.push_back(std::move(StatFilterer));
8750b57cec5SDimitry Andric     }
8760b57cec5SDimitry Andric 
8770b57cec5SDimitry Andric     // Create the ignore filename filters.
8780b57cec5SDimitry Andric     for (const auto &RE : IgnoreFilenameRegexFilters)
8790b57cec5SDimitry Andric       IgnoreFilenameFilters.push_back(
8808bcb0991SDimitry Andric           std::make_unique<NameRegexCoverageFilter>(RE));
8810b57cec5SDimitry Andric 
8820b57cec5SDimitry Andric     if (!Arches.empty()) {
8830b57cec5SDimitry Andric       for (const std::string &Arch : Arches) {
8840b57cec5SDimitry Andric         if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
8850b57cec5SDimitry Andric           error("Unknown architecture: " + Arch);
8860b57cec5SDimitry Andric           return 1;
8870b57cec5SDimitry Andric         }
8880b57cec5SDimitry Andric         CoverageArches.emplace_back(Arch);
8890b57cec5SDimitry Andric       }
890*1ac55f4cSDimitry Andric       if (CoverageArches.size() != 1 &&
891*1ac55f4cSDimitry Andric           CoverageArches.size() != ObjectFilenames.size()) {
8920b57cec5SDimitry Andric         error("Number of architectures doesn't match the number of objects");
8930b57cec5SDimitry Andric         return 1;
8940b57cec5SDimitry Andric       }
8950b57cec5SDimitry Andric     }
8960b57cec5SDimitry Andric 
8970b57cec5SDimitry Andric     // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
8980b57cec5SDimitry Andric     for (const std::string &File : InputSourceFiles)
8990b57cec5SDimitry Andric       collectPaths(File);
9000b57cec5SDimitry Andric 
9010b57cec5SDimitry Andric     if (DebugDumpCollectedPaths) {
9020b57cec5SDimitry Andric       for (const std::string &SF : SourceFiles)
9030b57cec5SDimitry Andric         outs() << SF << '\n';
9040b57cec5SDimitry Andric       ::exit(0);
9050b57cec5SDimitry Andric     }
9060b57cec5SDimitry Andric 
907e8d8bef9SDimitry Andric     ViewOpts.ShowBranchSummary = BranchSummary;
9080b57cec5SDimitry Andric     ViewOpts.ShowRegionSummary = RegionSummary;
9090b57cec5SDimitry Andric     ViewOpts.ShowInstantiationSummary = InstantiationSummary;
9100b57cec5SDimitry Andric     ViewOpts.ExportSummaryOnly = SummaryOnly;
9110b57cec5SDimitry Andric     ViewOpts.NumThreads = NumThreads;
912fe6060f1SDimitry Andric     ViewOpts.CompilationDirectory = CompilationDirectory;
9130b57cec5SDimitry Andric 
9140b57cec5SDimitry Andric     return 0;
9150b57cec5SDimitry Andric   };
9160b57cec5SDimitry Andric 
9170b57cec5SDimitry Andric   switch (Cmd) {
9180b57cec5SDimitry Andric   case Show:
9190b57cec5SDimitry Andric     return doShow(argc, argv, commandLineParser);
9200b57cec5SDimitry Andric   case Report:
9210b57cec5SDimitry Andric     return doReport(argc, argv, commandLineParser);
9220b57cec5SDimitry Andric   case Export:
9230b57cec5SDimitry Andric     return doExport(argc, argv, commandLineParser);
9240b57cec5SDimitry Andric   }
9250b57cec5SDimitry Andric   return 0;
9260b57cec5SDimitry Andric }
9270b57cec5SDimitry Andric 
9280b57cec5SDimitry Andric int CodeCoverageTool::doShow(int argc, const char **argv,
9290b57cec5SDimitry Andric                              CommandLineParserType commandLineParser) {
9300b57cec5SDimitry Andric 
9310b57cec5SDimitry Andric   cl::OptionCategory ViewCategory("Viewing options");
9320b57cec5SDimitry Andric 
9330b57cec5SDimitry Andric   cl::opt<bool> ShowLineExecutionCounts(
9340b57cec5SDimitry Andric       "show-line-counts", cl::Optional,
9350b57cec5SDimitry Andric       cl::desc("Show the execution counts for each line"), cl::init(true),
9360b57cec5SDimitry Andric       cl::cat(ViewCategory));
9370b57cec5SDimitry Andric 
9380b57cec5SDimitry Andric   cl::opt<bool> ShowRegions(
9390b57cec5SDimitry Andric       "show-regions", cl::Optional,
9400b57cec5SDimitry Andric       cl::desc("Show the execution counts for each region"),
9410b57cec5SDimitry Andric       cl::cat(ViewCategory));
9420b57cec5SDimitry Andric 
943e8d8bef9SDimitry Andric   cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
944e8d8bef9SDimitry Andric       "show-branches", cl::Optional,
945e8d8bef9SDimitry Andric       cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
946e8d8bef9SDimitry Andric       cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
947e8d8bef9SDimitry Andric                             "count", "Show True/False counts"),
948e8d8bef9SDimitry Andric                  clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
949e8d8bef9SDimitry Andric                             "percent", "Show True/False percent")),
950e8d8bef9SDimitry Andric       cl::init(CoverageViewOptions::BranchOutputType::Off));
951e8d8bef9SDimitry Andric 
9520b57cec5SDimitry Andric   cl::opt<bool> ShowBestLineRegionsCounts(
9530b57cec5SDimitry Andric       "show-line-counts-or-regions", cl::Optional,
9540b57cec5SDimitry Andric       cl::desc("Show the execution counts for each line, or the execution "
9550b57cec5SDimitry Andric                "counts for each region on lines that have multiple regions"),
9560b57cec5SDimitry Andric       cl::cat(ViewCategory));
9570b57cec5SDimitry Andric 
9580b57cec5SDimitry Andric   cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
9590b57cec5SDimitry Andric                                cl::desc("Show expanded source regions"),
9600b57cec5SDimitry Andric                                cl::cat(ViewCategory));
9610b57cec5SDimitry Andric 
9620b57cec5SDimitry Andric   cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
9630b57cec5SDimitry Andric                                    cl::desc("Show function instantiations"),
9640b57cec5SDimitry Andric                                    cl::init(true), cl::cat(ViewCategory));
9650b57cec5SDimitry Andric 
9660b57cec5SDimitry Andric   cl::opt<std::string> ShowOutputDirectory(
9670b57cec5SDimitry Andric       "output-dir", cl::init(""),
9680b57cec5SDimitry Andric       cl::desc("Directory in which coverage information is written out"));
9690b57cec5SDimitry Andric   cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
9700b57cec5SDimitry Andric                                  cl::aliasopt(ShowOutputDirectory));
9710b57cec5SDimitry Andric 
9720b57cec5SDimitry Andric   cl::opt<uint32_t> TabSize(
9730b57cec5SDimitry Andric       "tab-size", cl::init(2),
9740b57cec5SDimitry Andric       cl::desc(
9750b57cec5SDimitry Andric           "Set tab expansion size for html coverage reports (default = 2)"));
9760b57cec5SDimitry Andric 
9770b57cec5SDimitry Andric   cl::opt<std::string> ProjectTitle(
9780b57cec5SDimitry Andric       "project-title", cl::Optional,
9790b57cec5SDimitry Andric       cl::desc("Set project title for the coverage report"));
9800b57cec5SDimitry Andric 
98181ad6265SDimitry Andric   cl::opt<std::string> CovWatermark(
98281ad6265SDimitry Andric       "coverage-watermark", cl::Optional,
98381ad6265SDimitry Andric       cl::desc("<high>,<low> value indicate thresholds for high and low"
98481ad6265SDimitry Andric                "coverage watermark"));
98581ad6265SDimitry Andric 
9860b57cec5SDimitry Andric   auto Err = commandLineParser(argc, argv);
9870b57cec5SDimitry Andric   if (Err)
9880b57cec5SDimitry Andric     return Err;
9890b57cec5SDimitry Andric 
9900b57cec5SDimitry Andric   if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
9910b57cec5SDimitry Andric     error("Lcov format should be used with 'llvm-cov export'.");
9920b57cec5SDimitry Andric     return 1;
9930b57cec5SDimitry Andric   }
9940b57cec5SDimitry Andric 
99581ad6265SDimitry Andric   ViewOpts.HighCovWatermark = 100.0;
99681ad6265SDimitry Andric   ViewOpts.LowCovWatermark = 80.0;
99781ad6265SDimitry Andric   if (!CovWatermark.empty()) {
99881ad6265SDimitry Andric     auto WaterMarkPair = StringRef(CovWatermark).split(',');
99981ad6265SDimitry Andric     if (WaterMarkPair.first.empty() || WaterMarkPair.second.empty()) {
100081ad6265SDimitry Andric       error("invalid argument '" + CovWatermark +
100181ad6265SDimitry Andric                 "', must be in format 'high,low'",
100281ad6265SDimitry Andric             "-coverage-watermark");
100381ad6265SDimitry Andric       return 1;
100481ad6265SDimitry Andric     }
100581ad6265SDimitry Andric 
100681ad6265SDimitry Andric     char *EndPointer = nullptr;
100781ad6265SDimitry Andric     ViewOpts.HighCovWatermark =
100881ad6265SDimitry Andric         strtod(WaterMarkPair.first.begin(), &EndPointer);
100981ad6265SDimitry Andric     if (EndPointer != WaterMarkPair.first.end()) {
101081ad6265SDimitry Andric       error("invalid number '" + WaterMarkPair.first +
101181ad6265SDimitry Andric                 "', invalid value for 'high'",
101281ad6265SDimitry Andric             "-coverage-watermark");
101381ad6265SDimitry Andric       return 1;
101481ad6265SDimitry Andric     }
101581ad6265SDimitry Andric 
101681ad6265SDimitry Andric     ViewOpts.LowCovWatermark =
101781ad6265SDimitry Andric         strtod(WaterMarkPair.second.begin(), &EndPointer);
101881ad6265SDimitry Andric     if (EndPointer != WaterMarkPair.second.end()) {
101981ad6265SDimitry Andric       error("invalid number '" + WaterMarkPair.second +
102081ad6265SDimitry Andric                 "', invalid value for 'low'",
102181ad6265SDimitry Andric             "-coverage-watermark");
102281ad6265SDimitry Andric       return 1;
102381ad6265SDimitry Andric     }
102481ad6265SDimitry Andric 
102581ad6265SDimitry Andric     if (ViewOpts.HighCovWatermark > 100 || ViewOpts.LowCovWatermark < 0 ||
102681ad6265SDimitry Andric         ViewOpts.HighCovWatermark <= ViewOpts.LowCovWatermark) {
102781ad6265SDimitry Andric       error(
102881ad6265SDimitry Andric           "invalid number range '" + CovWatermark +
102981ad6265SDimitry Andric               "', must be both high and low should be between 0-100, and high "
103081ad6265SDimitry Andric               "> low",
103181ad6265SDimitry Andric           "-coverage-watermark");
103281ad6265SDimitry Andric       return 1;
103381ad6265SDimitry Andric     }
103481ad6265SDimitry Andric   }
103581ad6265SDimitry Andric 
10360b57cec5SDimitry Andric   ViewOpts.ShowLineNumbers = true;
10370b57cec5SDimitry Andric   ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
10380b57cec5SDimitry Andric                            !ShowRegions || ShowBestLineRegionsCounts;
10390b57cec5SDimitry Andric   ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
10400b57cec5SDimitry Andric   ViewOpts.ShowExpandedRegions = ShowExpansions;
1041e8d8bef9SDimitry Andric   ViewOpts.ShowBranchCounts =
1042e8d8bef9SDimitry Andric       ShowBranches == CoverageViewOptions::BranchOutputType::Count;
1043e8d8bef9SDimitry Andric   ViewOpts.ShowBranchPercents =
1044e8d8bef9SDimitry Andric       ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
10450b57cec5SDimitry Andric   ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
10460b57cec5SDimitry Andric   ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
10470b57cec5SDimitry Andric   ViewOpts.TabSize = TabSize;
10480b57cec5SDimitry Andric   ViewOpts.ProjectTitle = ProjectTitle;
10490b57cec5SDimitry Andric 
10500b57cec5SDimitry Andric   if (ViewOpts.hasOutputDirectory()) {
10510b57cec5SDimitry Andric     if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
10520b57cec5SDimitry Andric       error("Could not create output directory!", E.message());
10530b57cec5SDimitry Andric       return 1;
10540b57cec5SDimitry Andric     }
10550b57cec5SDimitry Andric   }
10560b57cec5SDimitry Andric 
10570b57cec5SDimitry Andric   sys::fs::file_status Status;
10585ffd83dbSDimitry Andric   if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1059fcaf7f86SDimitry Andric     error("Could not read profile data!" + EC.message(), PGOFilename);
10600b57cec5SDimitry Andric     return 1;
10610b57cec5SDimitry Andric   }
10620b57cec5SDimitry Andric 
10630b57cec5SDimitry Andric   auto ModifiedTime = Status.getLastModificationTime();
10640b57cec5SDimitry Andric   std::string ModifiedTimeStr = to_string(ModifiedTime);
10650b57cec5SDimitry Andric   size_t found = ModifiedTimeStr.rfind(':');
10660b57cec5SDimitry Andric   ViewOpts.CreatedTimeStr = (found != std::string::npos)
10670b57cec5SDimitry Andric                                 ? "Created: " + ModifiedTimeStr.substr(0, found)
10680b57cec5SDimitry Andric                                 : "Created: " + ModifiedTimeStr;
10690b57cec5SDimitry Andric 
10700b57cec5SDimitry Andric   auto Coverage = load();
10710b57cec5SDimitry Andric   if (!Coverage)
10720b57cec5SDimitry Andric     return 1;
10730b57cec5SDimitry Andric 
10740b57cec5SDimitry Andric   auto Printer = CoveragePrinter::create(ViewOpts);
10750b57cec5SDimitry Andric 
1076e8d8bef9SDimitry Andric   if (SourceFiles.empty() && !HadSourceFiles)
10770b57cec5SDimitry Andric     // Get the source files from the function coverage mapping.
10780b57cec5SDimitry Andric     for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
10790b57cec5SDimitry Andric       if (!IgnoreFilenameFilters.matchesFilename(Filename))
10805ffd83dbSDimitry Andric         SourceFiles.push_back(std::string(Filename));
10810b57cec5SDimitry Andric     }
10820b57cec5SDimitry Andric 
10830b57cec5SDimitry Andric   // Create an index out of the source files.
10840b57cec5SDimitry Andric   if (ViewOpts.hasOutputDirectory()) {
10850b57cec5SDimitry Andric     if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) {
10860b57cec5SDimitry Andric       error("Could not create index file!", toString(std::move(E)));
10870b57cec5SDimitry Andric       return 1;
10880b57cec5SDimitry Andric     }
10890b57cec5SDimitry Andric   }
10900b57cec5SDimitry Andric 
10910b57cec5SDimitry Andric   if (!Filters.empty()) {
10920b57cec5SDimitry Andric     // Build the map of filenames to functions.
10930b57cec5SDimitry Andric     std::map<llvm::StringRef, std::vector<const FunctionRecord *>>
10940b57cec5SDimitry Andric         FilenameFunctionMap;
10950b57cec5SDimitry Andric     for (const auto &SourceFile : SourceFiles)
10960b57cec5SDimitry Andric       for (const auto &Function : Coverage->getCoveredFunctions(SourceFile))
1097bdd1243dSDimitry Andric         if (Filters.matches(*Coverage, Function))
10980b57cec5SDimitry Andric           FilenameFunctionMap[SourceFile].push_back(&Function);
10990b57cec5SDimitry Andric 
11000b57cec5SDimitry Andric     // Only print filter matching functions for each file.
11010b57cec5SDimitry Andric     for (const auto &FileFunc : FilenameFunctionMap) {
11020b57cec5SDimitry Andric       StringRef File = FileFunc.first;
11030b57cec5SDimitry Andric       const auto &Functions = FileFunc.second;
11040b57cec5SDimitry Andric 
11050b57cec5SDimitry Andric       auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false);
11060b57cec5SDimitry Andric       if (Error E = OSOrErr.takeError()) {
11070b57cec5SDimitry Andric         error("Could not create view file!", toString(std::move(E)));
11080b57cec5SDimitry Andric         return 1;
11090b57cec5SDimitry Andric       }
11100b57cec5SDimitry Andric       auto OS = std::move(OSOrErr.get());
11110b57cec5SDimitry Andric 
11120b57cec5SDimitry Andric       bool ShowTitle = ViewOpts.hasOutputDirectory();
11130b57cec5SDimitry Andric       for (const auto *Function : Functions) {
11140b57cec5SDimitry Andric         auto FunctionView = createFunctionView(*Function, *Coverage);
11150b57cec5SDimitry Andric         if (!FunctionView) {
11160b57cec5SDimitry Andric           warning("Could not read coverage for '" + Function->Name + "'.");
11170b57cec5SDimitry Andric           continue;
11180b57cec5SDimitry Andric         }
11190b57cec5SDimitry Andric         FunctionView->print(*OS.get(), /*WholeFile=*/false,
11200b57cec5SDimitry Andric                             /*ShowSourceName=*/true, ShowTitle);
11210b57cec5SDimitry Andric         ShowTitle = false;
11220b57cec5SDimitry Andric       }
11230b57cec5SDimitry Andric 
11240b57cec5SDimitry Andric       Printer->closeViewFile(std::move(OS));
11250b57cec5SDimitry Andric     }
11260b57cec5SDimitry Andric     return 0;
11270b57cec5SDimitry Andric   }
11280b57cec5SDimitry Andric 
11290b57cec5SDimitry Andric   // Show files
11300b57cec5SDimitry Andric   bool ShowFilenames =
11310b57cec5SDimitry Andric       (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
11320b57cec5SDimitry Andric       (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
11330b57cec5SDimitry Andric 
11345ffd83dbSDimitry Andric   ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
11355ffd83dbSDimitry Andric   if (ViewOpts.NumThreads == 0) {
11365ffd83dbSDimitry Andric     // If NumThreads is not specified, create one thread for each input, up to
11375ffd83dbSDimitry Andric     // the number of hardware cores.
11385ffd83dbSDimitry Andric     S = heavyweight_hardware_concurrency(SourceFiles.size());
11395ffd83dbSDimitry Andric     S.Limit = true;
11405ffd83dbSDimitry Andric   }
11410b57cec5SDimitry Andric 
11425ffd83dbSDimitry Andric   if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
11430b57cec5SDimitry Andric     for (const std::string &SourceFile : SourceFiles)
11440b57cec5SDimitry Andric       writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
11450b57cec5SDimitry Andric                           ShowFilenames);
11460b57cec5SDimitry Andric   } else {
11470b57cec5SDimitry Andric     // In -output-dir mode, it's safe to use multiple threads to print files.
11485ffd83dbSDimitry Andric     ThreadPool Pool(S);
11490b57cec5SDimitry Andric     for (const std::string &SourceFile : SourceFiles)
11500b57cec5SDimitry Andric       Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
11510b57cec5SDimitry Andric                  Coverage.get(), Printer.get(), ShowFilenames);
11520b57cec5SDimitry Andric     Pool.wait();
11530b57cec5SDimitry Andric   }
11540b57cec5SDimitry Andric 
11550b57cec5SDimitry Andric   return 0;
11560b57cec5SDimitry Andric }
11570b57cec5SDimitry Andric 
11580b57cec5SDimitry Andric int CodeCoverageTool::doReport(int argc, const char **argv,
11590b57cec5SDimitry Andric                                CommandLineParserType commandLineParser) {
11600b57cec5SDimitry Andric   cl::opt<bool> ShowFunctionSummaries(
11610b57cec5SDimitry Andric       "show-functions", cl::Optional, cl::init(false),
11620b57cec5SDimitry Andric       cl::desc("Show coverage summaries for each function"));
11630b57cec5SDimitry Andric 
11640b57cec5SDimitry Andric   auto Err = commandLineParser(argc, argv);
11650b57cec5SDimitry Andric   if (Err)
11660b57cec5SDimitry Andric     return Err;
11670b57cec5SDimitry Andric 
11680b57cec5SDimitry Andric   if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) {
11690b57cec5SDimitry Andric     error("HTML output for summary reports is not yet supported.");
11700b57cec5SDimitry Andric     return 1;
11710b57cec5SDimitry Andric   } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
11720b57cec5SDimitry Andric     error("Lcov format should be used with 'llvm-cov export'.");
11730b57cec5SDimitry Andric     return 1;
11740b57cec5SDimitry Andric   }
11750b57cec5SDimitry Andric 
1176fcaf7f86SDimitry Andric   sys::fs::file_status Status;
1177fcaf7f86SDimitry Andric   if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1178fcaf7f86SDimitry Andric     error("Could not read profile data!" + EC.message(), PGOFilename);
1179fcaf7f86SDimitry Andric     return 1;
1180fcaf7f86SDimitry Andric   }
1181fcaf7f86SDimitry Andric 
11820b57cec5SDimitry Andric   auto Coverage = load();
11830b57cec5SDimitry Andric   if (!Coverage)
11840b57cec5SDimitry Andric     return 1;
11850b57cec5SDimitry Andric 
1186bdd1243dSDimitry Andric   CoverageReport Report(ViewOpts, *Coverage);
11870b57cec5SDimitry Andric   if (!ShowFunctionSummaries) {
11880b57cec5SDimitry Andric     if (SourceFiles.empty())
11890b57cec5SDimitry Andric       Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
11900b57cec5SDimitry Andric     else
11910b57cec5SDimitry Andric       Report.renderFileReports(llvm::outs(), SourceFiles);
11920b57cec5SDimitry Andric   } else {
11930b57cec5SDimitry Andric     if (SourceFiles.empty()) {
11940b57cec5SDimitry Andric       error("Source files must be specified when -show-functions=true is "
11950b57cec5SDimitry Andric             "specified");
11960b57cec5SDimitry Andric       return 1;
11970b57cec5SDimitry Andric     }
11980b57cec5SDimitry Andric 
11990b57cec5SDimitry Andric     Report.renderFunctionReports(SourceFiles, DC, llvm::outs());
12000b57cec5SDimitry Andric   }
12010b57cec5SDimitry Andric   return 0;
12020b57cec5SDimitry Andric }
12030b57cec5SDimitry Andric 
12040b57cec5SDimitry Andric int CodeCoverageTool::doExport(int argc, const char **argv,
12050b57cec5SDimitry Andric                                CommandLineParserType commandLineParser) {
12060b57cec5SDimitry Andric 
12070b57cec5SDimitry Andric   cl::OptionCategory ExportCategory("Exporting options");
12080b57cec5SDimitry Andric 
12090b57cec5SDimitry Andric   cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional,
12100b57cec5SDimitry Andric                                cl::desc("Don't export expanded source regions"),
12110b57cec5SDimitry Andric                                cl::cat(ExportCategory));
12120b57cec5SDimitry Andric 
12130b57cec5SDimitry Andric   cl::opt<bool> SkipFunctions("skip-functions", cl::Optional,
12140b57cec5SDimitry Andric                               cl::desc("Don't export per-function data"),
12150b57cec5SDimitry Andric                               cl::cat(ExportCategory));
12160b57cec5SDimitry Andric 
1217bdd1243dSDimitry Andric   cl::opt<bool> SkipBranches("skip-branches", cl::Optional,
1218bdd1243dSDimitry Andric                               cl::desc("Don't export branch data (LCOV)"),
1219bdd1243dSDimitry Andric                               cl::cat(ExportCategory));
1220bdd1243dSDimitry Andric 
12210b57cec5SDimitry Andric   auto Err = commandLineParser(argc, argv);
12220b57cec5SDimitry Andric   if (Err)
12230b57cec5SDimitry Andric     return Err;
12240b57cec5SDimitry Andric 
12250b57cec5SDimitry Andric   ViewOpts.SkipExpansions = SkipExpansions;
12260b57cec5SDimitry Andric   ViewOpts.SkipFunctions = SkipFunctions;
1227bdd1243dSDimitry Andric   ViewOpts.SkipBranches = SkipBranches;
12280b57cec5SDimitry Andric 
12290b57cec5SDimitry Andric   if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
12300b57cec5SDimitry Andric       ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
12310b57cec5SDimitry Andric     error("Coverage data can only be exported as textual JSON or an "
12320b57cec5SDimitry Andric           "lcov tracefile.");
12330b57cec5SDimitry Andric     return 1;
12340b57cec5SDimitry Andric   }
12350b57cec5SDimitry Andric 
1236fcaf7f86SDimitry Andric   sys::fs::file_status Status;
1237fcaf7f86SDimitry Andric   if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1238fcaf7f86SDimitry Andric     error("Could not read profile data!" + EC.message(), PGOFilename);
1239fcaf7f86SDimitry Andric     return 1;
1240fcaf7f86SDimitry Andric   }
1241fcaf7f86SDimitry Andric 
12420b57cec5SDimitry Andric   auto Coverage = load();
12430b57cec5SDimitry Andric   if (!Coverage) {
12440b57cec5SDimitry Andric     error("Could not load coverage information");
12450b57cec5SDimitry Andric     return 1;
12460b57cec5SDimitry Andric   }
12470b57cec5SDimitry Andric 
12480b57cec5SDimitry Andric   std::unique_ptr<CoverageExporter> Exporter;
12490b57cec5SDimitry Andric 
12500b57cec5SDimitry Andric   switch (ViewOpts.Format) {
12510b57cec5SDimitry Andric   case CoverageViewOptions::OutputFormat::Text:
1252bdd1243dSDimitry Andric     Exporter =
1253bdd1243dSDimitry Andric         std::make_unique<CoverageExporterJson>(*Coverage, ViewOpts, outs());
12540b57cec5SDimitry Andric     break;
12550b57cec5SDimitry Andric   case CoverageViewOptions::OutputFormat::HTML:
12560b57cec5SDimitry Andric     // Unreachable because we should have gracefully terminated with an error
12570b57cec5SDimitry Andric     // above.
12580b57cec5SDimitry Andric     llvm_unreachable("Export in HTML is not supported!");
12590b57cec5SDimitry Andric   case CoverageViewOptions::OutputFormat::Lcov:
1260bdd1243dSDimitry Andric     Exporter =
1261bdd1243dSDimitry Andric         std::make_unique<CoverageExporterLcov>(*Coverage, ViewOpts, outs());
12620b57cec5SDimitry Andric     break;
12630b57cec5SDimitry Andric   }
12640b57cec5SDimitry Andric 
12650b57cec5SDimitry Andric   if (SourceFiles.empty())
12660b57cec5SDimitry Andric     Exporter->renderRoot(IgnoreFilenameFilters);
12670b57cec5SDimitry Andric   else
12680b57cec5SDimitry Andric     Exporter->renderRoot(SourceFiles);
12690b57cec5SDimitry Andric 
12700b57cec5SDimitry Andric   return 0;
12710b57cec5SDimitry Andric }
12720b57cec5SDimitry Andric 
12730b57cec5SDimitry Andric int showMain(int argc, const char *argv[]) {
12740b57cec5SDimitry Andric   CodeCoverageTool Tool;
12750b57cec5SDimitry Andric   return Tool.run(CodeCoverageTool::Show, argc, argv);
12760b57cec5SDimitry Andric }
12770b57cec5SDimitry Andric 
12780b57cec5SDimitry Andric int reportMain(int argc, const char *argv[]) {
12790b57cec5SDimitry Andric   CodeCoverageTool Tool;
12800b57cec5SDimitry Andric   return Tool.run(CodeCoverageTool::Report, argc, argv);
12810b57cec5SDimitry Andric }
12820b57cec5SDimitry Andric 
12830b57cec5SDimitry Andric int exportMain(int argc, const char *argv[]) {
12840b57cec5SDimitry Andric   CodeCoverageTool Tool;
12850b57cec5SDimitry Andric   return Tool.run(CodeCoverageTool::Export, argc, argv);
12860b57cec5SDimitry Andric }
1287