xref: /freebsd-src/contrib/llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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"
251ac55f4cSDimitry Andric #include "llvm/Debuginfod/BuildIDFetcher.h"
261ac55f4cSDimitry Andric #include "llvm/Debuginfod/Debuginfod.h"
271ac55f4cSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h"
281ac55f4cSDimitry Andric #include "llvm/Object/BuildID.h"
290b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMapping.h"
300b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h"
310b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h"
320b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
330b57cec5SDimitry Andric #include "llvm/Support/Format.h"
340b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
350b57cec5SDimitry Andric #include "llvm/Support/Path.h"
360b57cec5SDimitry Andric #include "llvm/Support/Process.h"
370b57cec5SDimitry Andric #include "llvm/Support/Program.h"
380b57cec5SDimitry Andric #include "llvm/Support/ScopedPrinter.h"
395ffd83dbSDimitry Andric #include "llvm/Support/SpecialCaseList.h"
400b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h"
410b57cec5SDimitry Andric #include "llvm/Support/Threading.h"
420b57cec5SDimitry Andric #include "llvm/Support/ToolOutputFile.h"
43480093f4SDimitry Andric #include "llvm/Support/VirtualFileSystem.h"
4406c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.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.
103*0fca6ea1SDimitry Andric   void attachBranchSubViews(SourceCoverageView &View,
104*0fca6ea1SDimitry Andric                             ArrayRef<CountedRegion> Branches);
105e8d8bef9SDimitry Andric 
1065f757f3fSDimitry Andric   /// Create source views for the MCDC records.
107*0fca6ea1SDimitry Andric   void attachMCDCSubViews(SourceCoverageView &View,
108*0fca6ea1SDimitry Andric                           ArrayRef<MCDCRecord> MCDCRecords);
1095f757f3fSDimitry Andric 
1100b57cec5SDimitry Andric   /// Create the source view of a particular function.
1110b57cec5SDimitry Andric   std::unique_ptr<SourceCoverageView>
1120b57cec5SDimitry Andric   createFunctionView(const FunctionRecord &Function,
1130b57cec5SDimitry Andric                      const CoverageMapping &Coverage);
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   /// Create the main source view of a particular source file.
1160b57cec5SDimitry Andric   std::unique_ptr<SourceCoverageView>
1170b57cec5SDimitry Andric   createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric   /// Load the coverage mapping data. Return nullptr if an error occurred.
1200b57cec5SDimitry Andric   std::unique_ptr<CoverageMapping> load();
1210b57cec5SDimitry Andric 
1220b57cec5SDimitry Andric   /// Create a mapping from files in the Coverage data to local copies
1230b57cec5SDimitry Andric   /// (path-equivalence).
1240b57cec5SDimitry Andric   void remapPathNames(const CoverageMapping &Coverage);
1250b57cec5SDimitry Andric 
1260b57cec5SDimitry Andric   /// Remove input source files which aren't mapped by \p Coverage.
1270b57cec5SDimitry Andric   void removeUnmappedInputs(const CoverageMapping &Coverage);
1280b57cec5SDimitry Andric 
1290b57cec5SDimitry Andric   /// If a demangler is available, demangle all symbol names.
1300b57cec5SDimitry Andric   void demangleSymbols(const CoverageMapping &Coverage);
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric   /// Write out a source file view to the filesystem.
1330b57cec5SDimitry Andric   void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
1340b57cec5SDimitry Andric                            CoveragePrinter *Printer, bool ShowFilenames);
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric   typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric   int doShow(int argc, const char **argv,
1390b57cec5SDimitry Andric              CommandLineParserType commandLineParser);
1400b57cec5SDimitry Andric 
1410b57cec5SDimitry Andric   int doReport(int argc, const char **argv,
1420b57cec5SDimitry Andric                CommandLineParserType commandLineParser);
1430b57cec5SDimitry Andric 
1440b57cec5SDimitry Andric   int doExport(int argc, const char **argv,
1450b57cec5SDimitry Andric                CommandLineParserType commandLineParser);
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric   std::vector<StringRef> ObjectFilenames;
1480b57cec5SDimitry Andric   CoverageViewOptions ViewOpts;
1490b57cec5SDimitry Andric   CoverageFiltersMatchAll Filters;
1500b57cec5SDimitry Andric   CoverageFilters IgnoreFilenameFilters;
1510b57cec5SDimitry Andric 
152e8d8bef9SDimitry Andric   /// True if InputSourceFiles are provided.
153e8d8bef9SDimitry Andric   bool HadSourceFiles = false;
154e8d8bef9SDimitry Andric 
1550b57cec5SDimitry Andric   /// The path to the indexed profile.
1560b57cec5SDimitry Andric   std::string PGOFilename;
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric   /// A list of input source files.
1590b57cec5SDimitry Andric   std::vector<std::string> SourceFiles;
1600b57cec5SDimitry Andric 
1610b57cec5SDimitry Andric   /// In -path-equivalence mode, this maps the absolute paths from the coverage
1620b57cec5SDimitry Andric   /// mapping data to the input source files.
1630b57cec5SDimitry Andric   StringMap<std::string> RemappedFilenames;
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric   /// The coverage data path to be remapped from, and the source path to be
1660b57cec5SDimitry Andric   /// remapped to, when using -path-equivalence.
1675f757f3fSDimitry Andric   std::optional<std::vector<std::pair<std::string, std::string>>>
1685f757f3fSDimitry Andric       PathRemappings;
1690b57cec5SDimitry Andric 
170fe6060f1SDimitry Andric   /// File status cache used when finding the same file.
171bdd1243dSDimitry Andric   StringMap<std::optional<sys::fs::file_status>> FileStatusCache;
172fe6060f1SDimitry Andric 
1730b57cec5SDimitry Andric   /// The architecture the coverage mapping data targets.
1740b57cec5SDimitry Andric   std::vector<StringRef> CoverageArches;
1750b57cec5SDimitry Andric 
1760b57cec5SDimitry Andric   /// A cache for demangled symbols.
1770b57cec5SDimitry Andric   DemangleCache DC;
1780b57cec5SDimitry Andric 
1790b57cec5SDimitry Andric   /// A lock which guards printing to stderr.
1800b57cec5SDimitry Andric   std::mutex ErrsLock;
1810b57cec5SDimitry Andric 
1820b57cec5SDimitry Andric   /// A container for input source file buffers.
1830b57cec5SDimitry Andric   std::mutex LoadedSourceFilesLock;
1840b57cec5SDimitry Andric   std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
1850b57cec5SDimitry Andric       LoadedSourceFiles;
1860b57cec5SDimitry Andric 
1874824e7fdSDimitry Andric   /// Allowlist from -name-allowlist to be used for filtering.
1884824e7fdSDimitry Andric   std::unique_ptr<SpecialCaseList> NameAllowlist;
1891ac55f4cSDimitry Andric 
1901ac55f4cSDimitry Andric   std::unique_ptr<object::BuildIDFetcher> BIDFetcher;
19106c3fb27SDimitry Andric 
19206c3fb27SDimitry Andric   bool CheckBinaryIDs;
1930b57cec5SDimitry Andric };
1940b57cec5SDimitry Andric }
1950b57cec5SDimitry Andric 
1960b57cec5SDimitry Andric static std::string getErrorString(const Twine &Message, StringRef Whence,
1970b57cec5SDimitry Andric                                   bool Warning) {
1980b57cec5SDimitry Andric   std::string Str = (Warning ? "warning" : "error");
1990b57cec5SDimitry Andric   Str += ": ";
2000b57cec5SDimitry Andric   if (!Whence.empty())
2010b57cec5SDimitry Andric     Str += Whence.str() + ": ";
2020b57cec5SDimitry Andric   Str += Message.str() + "\n";
2030b57cec5SDimitry Andric   return Str;
2040b57cec5SDimitry Andric }
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
2070b57cec5SDimitry Andric   std::unique_lock<std::mutex> Guard{ErrsLock};
2080b57cec5SDimitry Andric   ViewOpts.colored_ostream(errs(), raw_ostream::RED)
2090b57cec5SDimitry Andric       << getErrorString(Message, Whence, false);
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric 
2120b57cec5SDimitry Andric void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
2130b57cec5SDimitry Andric   std::unique_lock<std::mutex> Guard{ErrsLock};
2140b57cec5SDimitry Andric   ViewOpts.colored_ostream(errs(), raw_ostream::RED)
2150b57cec5SDimitry Andric       << getErrorString(Message, Whence, true);
2160b57cec5SDimitry Andric }
2170b57cec5SDimitry Andric 
2180b57cec5SDimitry Andric void CodeCoverageTool::addCollectedPath(const std::string &Path) {
2190b57cec5SDimitry Andric   SmallString<128> EffectivePath(Path);
2200b57cec5SDimitry Andric   if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
2210b57cec5SDimitry Andric     error(EC.message(), Path);
2220b57cec5SDimitry Andric     return;
2230b57cec5SDimitry Andric   }
22404eeddc0SDimitry Andric   sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true);
2250b57cec5SDimitry Andric   if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
2260b57cec5SDimitry Andric     SourceFiles.emplace_back(EffectivePath.str());
227e8d8bef9SDimitry Andric   HadSourceFiles = !SourceFiles.empty();
2280b57cec5SDimitry Andric }
2290b57cec5SDimitry Andric 
2300b57cec5SDimitry Andric void CodeCoverageTool::collectPaths(const std::string &Path) {
2310b57cec5SDimitry Andric   llvm::sys::fs::file_status Status;
2320b57cec5SDimitry Andric   llvm::sys::fs::status(Path, Status);
2330b57cec5SDimitry Andric   if (!llvm::sys::fs::exists(Status)) {
2345f757f3fSDimitry Andric     if (PathRemappings)
2350b57cec5SDimitry Andric       addCollectedPath(Path);
2360b57cec5SDimitry Andric     else
2370b57cec5SDimitry Andric       warning("Source file doesn't exist, proceeded by ignoring it.", Path);
2380b57cec5SDimitry Andric     return;
2390b57cec5SDimitry Andric   }
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   if (llvm::sys::fs::is_regular_file(Status)) {
2420b57cec5SDimitry Andric     addCollectedPath(Path);
2430b57cec5SDimitry Andric     return;
2440b57cec5SDimitry Andric   }
2450b57cec5SDimitry Andric 
2460b57cec5SDimitry Andric   if (llvm::sys::fs::is_directory(Status)) {
2470b57cec5SDimitry Andric     std::error_code EC;
2480b57cec5SDimitry Andric     for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
2490b57cec5SDimitry Andric          F != E; F.increment(EC)) {
2500b57cec5SDimitry Andric 
2510b57cec5SDimitry Andric       auto Status = F->status();
2520b57cec5SDimitry Andric       if (!Status) {
2530b57cec5SDimitry Andric         warning(Status.getError().message(), F->path());
2540b57cec5SDimitry Andric         continue;
2550b57cec5SDimitry Andric       }
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric       if (Status->type() == llvm::sys::fs::file_type::regular_file)
2580b57cec5SDimitry Andric         addCollectedPath(F->path());
2590b57cec5SDimitry Andric     }
2600b57cec5SDimitry Andric   }
2610b57cec5SDimitry Andric }
2620b57cec5SDimitry Andric 
263bdd1243dSDimitry Andric std::optional<sys::fs::file_status>
264fe6060f1SDimitry Andric CodeCoverageTool::getFileStatus(StringRef FilePath) {
265fe6060f1SDimitry Andric   auto It = FileStatusCache.try_emplace(FilePath);
266fe6060f1SDimitry Andric   auto &CachedStatus = It.first->getValue();
267fe6060f1SDimitry Andric   if (!It.second)
268fe6060f1SDimitry Andric     return CachedStatus;
269fe6060f1SDimitry Andric 
270fe6060f1SDimitry Andric   sys::fs::file_status Status;
271fe6060f1SDimitry Andric   if (!sys::fs::status(FilePath, Status))
272fe6060f1SDimitry Andric     CachedStatus = Status;
273fe6060f1SDimitry Andric   return CachedStatus;
274fe6060f1SDimitry Andric }
275fe6060f1SDimitry Andric 
276fe6060f1SDimitry Andric bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1,
277fe6060f1SDimitry Andric                                         StringRef FilePath2) {
278fe6060f1SDimitry Andric   auto Status1 = getFileStatus(FilePath1);
279fe6060f1SDimitry Andric   auto Status2 = getFileStatus(FilePath2);
28081ad6265SDimitry Andric   return Status1 && Status2 && sys::fs::equivalent(*Status1, *Status2);
281fe6060f1SDimitry Andric }
282fe6060f1SDimitry Andric 
2830b57cec5SDimitry Andric ErrorOr<const MemoryBuffer &>
2840b57cec5SDimitry Andric CodeCoverageTool::getSourceFile(StringRef SourceFile) {
2850b57cec5SDimitry Andric   // If we've remapped filenames, look up the real location for this file.
2860b57cec5SDimitry Andric   std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
2870b57cec5SDimitry Andric   if (!RemappedFilenames.empty()) {
2880b57cec5SDimitry Andric     auto Loc = RemappedFilenames.find(SourceFile);
2890b57cec5SDimitry Andric     if (Loc != RemappedFilenames.end())
2900b57cec5SDimitry Andric       SourceFile = Loc->second;
2910b57cec5SDimitry Andric   }
2920b57cec5SDimitry Andric   for (const auto &Files : LoadedSourceFiles)
293fe6060f1SDimitry Andric     if (isEquivalentFile(SourceFile, Files.first))
2940b57cec5SDimitry Andric       return *Files.second;
2950b57cec5SDimitry Andric   auto Buffer = MemoryBuffer::getFile(SourceFile);
2960b57cec5SDimitry Andric   if (auto EC = Buffer.getError()) {
2970b57cec5SDimitry Andric     error(EC.message(), SourceFile);
2980b57cec5SDimitry Andric     return EC;
2990b57cec5SDimitry Andric   }
3005ffd83dbSDimitry Andric   LoadedSourceFiles.emplace_back(std::string(SourceFile),
3015ffd83dbSDimitry Andric                                  std::move(Buffer.get()));
3020b57cec5SDimitry Andric   return *LoadedSourceFiles.back().second;
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric void CodeCoverageTool::attachExpansionSubViews(
3060b57cec5SDimitry Andric     SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
3070b57cec5SDimitry Andric     const CoverageMapping &Coverage) {
3080b57cec5SDimitry Andric   if (!ViewOpts.ShowExpandedRegions)
3090b57cec5SDimitry Andric     return;
3100b57cec5SDimitry Andric   for (const auto &Expansion : Expansions) {
3110b57cec5SDimitry Andric     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
3120b57cec5SDimitry Andric     if (ExpansionCoverage.empty())
3130b57cec5SDimitry Andric       continue;
3140b57cec5SDimitry Andric     auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
3150b57cec5SDimitry Andric     if (!SourceBuffer)
3160b57cec5SDimitry Andric       continue;
3170b57cec5SDimitry Andric 
318e8d8bef9SDimitry Andric     auto SubViewBranches = ExpansionCoverage.getBranches();
3190b57cec5SDimitry Andric     auto SubViewExpansions = ExpansionCoverage.getExpansions();
3200b57cec5SDimitry Andric     auto SubView =
3210b57cec5SDimitry Andric         SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
3220b57cec5SDimitry Andric                                    ViewOpts, std::move(ExpansionCoverage));
3230b57cec5SDimitry Andric     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
324*0fca6ea1SDimitry Andric     attachBranchSubViews(*SubView, SubViewBranches);
3250b57cec5SDimitry Andric     View.addExpansion(Expansion.Region, std::move(SubView));
3260b57cec5SDimitry Andric   }
3270b57cec5SDimitry Andric }
3280b57cec5SDimitry Andric 
329e8d8bef9SDimitry Andric void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
330*0fca6ea1SDimitry Andric                                             ArrayRef<CountedRegion> Branches) {
331e8d8bef9SDimitry Andric   if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
332e8d8bef9SDimitry Andric     return;
333e8d8bef9SDimitry Andric 
334e8d8bef9SDimitry Andric   const auto *NextBranch = Branches.begin();
335e8d8bef9SDimitry Andric   const auto *EndBranch = Branches.end();
336e8d8bef9SDimitry Andric 
337e8d8bef9SDimitry Andric   // Group branches that have the same line number into the same subview.
338e8d8bef9SDimitry Andric   while (NextBranch != EndBranch) {
339*0fca6ea1SDimitry Andric     SmallVector<CountedRegion, 0> ViewBranches;
340e8d8bef9SDimitry Andric     unsigned CurrentLine = NextBranch->LineStart;
341e8d8bef9SDimitry Andric     while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
342e8d8bef9SDimitry Andric       ViewBranches.push_back(*NextBranch++);
343e8d8bef9SDimitry Andric 
344*0fca6ea1SDimitry Andric     View.addBranch(CurrentLine, std::move(ViewBranches));
345e8d8bef9SDimitry Andric   }
346e8d8bef9SDimitry Andric }
347e8d8bef9SDimitry Andric 
3485f757f3fSDimitry Andric void CodeCoverageTool::attachMCDCSubViews(SourceCoverageView &View,
349*0fca6ea1SDimitry Andric                                           ArrayRef<MCDCRecord> MCDCRecords) {
3505f757f3fSDimitry Andric   if (!ViewOpts.ShowMCDC)
3515f757f3fSDimitry Andric     return;
3525f757f3fSDimitry Andric 
3535f757f3fSDimitry Andric   const auto *NextRecord = MCDCRecords.begin();
3545f757f3fSDimitry Andric   const auto *EndRecord = MCDCRecords.end();
3555f757f3fSDimitry Andric 
3565f757f3fSDimitry Andric   // Group and process MCDC records that have the same line number into the
3575f757f3fSDimitry Andric   // same subview.
3585f757f3fSDimitry Andric   while (NextRecord != EndRecord) {
359*0fca6ea1SDimitry Andric     SmallVector<MCDCRecord, 0> ViewMCDCRecords;
3605f757f3fSDimitry Andric     unsigned CurrentLine = NextRecord->getDecisionRegion().LineEnd;
3615f757f3fSDimitry Andric     while (NextRecord != EndRecord &&
362*0fca6ea1SDimitry Andric            CurrentLine == NextRecord->getDecisionRegion().LineEnd)
3635f757f3fSDimitry Andric       ViewMCDCRecords.push_back(*NextRecord++);
3645f757f3fSDimitry Andric 
365*0fca6ea1SDimitry Andric     View.addMCDCRecord(CurrentLine, std::move(ViewMCDCRecords));
3665f757f3fSDimitry Andric   }
3675f757f3fSDimitry Andric }
3685f757f3fSDimitry Andric 
3690b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView>
3700b57cec5SDimitry Andric CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
3710b57cec5SDimitry Andric                                      const CoverageMapping &Coverage) {
3720b57cec5SDimitry Andric   auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
3730b57cec5SDimitry Andric   if (FunctionCoverage.empty())
3740b57cec5SDimitry Andric     return nullptr;
3750b57cec5SDimitry Andric   auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
3760b57cec5SDimitry Andric   if (!SourceBuffer)
3770b57cec5SDimitry Andric     return nullptr;
3780b57cec5SDimitry Andric 
379e8d8bef9SDimitry Andric   auto Branches = FunctionCoverage.getBranches();
3800b57cec5SDimitry Andric   auto Expansions = FunctionCoverage.getExpansions();
3815f757f3fSDimitry Andric   auto MCDCRecords = FunctionCoverage.getMCDCRecords();
3820b57cec5SDimitry Andric   auto View = SourceCoverageView::create(DC.demangle(Function.Name),
3830b57cec5SDimitry Andric                                          SourceBuffer.get(), ViewOpts,
3840b57cec5SDimitry Andric                                          std::move(FunctionCoverage));
3850b57cec5SDimitry Andric   attachExpansionSubViews(*View, Expansions, Coverage);
386*0fca6ea1SDimitry Andric   attachBranchSubViews(*View, Branches);
387*0fca6ea1SDimitry Andric   attachMCDCSubViews(*View, MCDCRecords);
3880b57cec5SDimitry Andric 
3890b57cec5SDimitry Andric   return View;
3900b57cec5SDimitry Andric }
3910b57cec5SDimitry Andric 
3920b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView>
3930b57cec5SDimitry Andric CodeCoverageTool::createSourceFileView(StringRef SourceFile,
3940b57cec5SDimitry Andric                                        const CoverageMapping &Coverage) {
3950b57cec5SDimitry Andric   auto SourceBuffer = getSourceFile(SourceFile);
3960b57cec5SDimitry Andric   if (!SourceBuffer)
3970b57cec5SDimitry Andric     return nullptr;
3980b57cec5SDimitry Andric   auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
3990b57cec5SDimitry Andric   if (FileCoverage.empty())
4000b57cec5SDimitry Andric     return nullptr;
4010b57cec5SDimitry Andric 
402e8d8bef9SDimitry Andric   auto Branches = FileCoverage.getBranches();
4030b57cec5SDimitry Andric   auto Expansions = FileCoverage.getExpansions();
4045f757f3fSDimitry Andric   auto MCDCRecords = FileCoverage.getMCDCRecords();
4050b57cec5SDimitry Andric   auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
4060b57cec5SDimitry Andric                                          ViewOpts, std::move(FileCoverage));
4070b57cec5SDimitry Andric   attachExpansionSubViews(*View, Expansions, Coverage);
408*0fca6ea1SDimitry Andric   attachBranchSubViews(*View, Branches);
409*0fca6ea1SDimitry Andric   attachMCDCSubViews(*View, MCDCRecords);
4100b57cec5SDimitry Andric   if (!ViewOpts.ShowFunctionInstantiations)
4110b57cec5SDimitry Andric     return View;
4120b57cec5SDimitry Andric 
4130b57cec5SDimitry Andric   for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) {
4140b57cec5SDimitry Andric     // Skip functions which have a single instantiation.
4150b57cec5SDimitry Andric     if (Group.size() < 2)
4160b57cec5SDimitry Andric       continue;
4170b57cec5SDimitry Andric 
4180b57cec5SDimitry Andric     for (const FunctionRecord *Function : Group.getInstantiations()) {
4190b57cec5SDimitry Andric       std::unique_ptr<SourceCoverageView> SubView{nullptr};
4200b57cec5SDimitry Andric 
4210b57cec5SDimitry Andric       StringRef Funcname = DC.demangle(Function->Name);
4220b57cec5SDimitry Andric 
4230b57cec5SDimitry Andric       if (Function->ExecutionCount > 0) {
4240b57cec5SDimitry Andric         auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
4250b57cec5SDimitry Andric         auto SubViewExpansions = SubViewCoverage.getExpansions();
426e8d8bef9SDimitry Andric         auto SubViewBranches = SubViewCoverage.getBranches();
4275f757f3fSDimitry Andric         auto SubViewMCDCRecords = SubViewCoverage.getMCDCRecords();
4280b57cec5SDimitry Andric         SubView = SourceCoverageView::create(
4290b57cec5SDimitry Andric             Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
4300b57cec5SDimitry Andric         attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
431*0fca6ea1SDimitry Andric         attachBranchSubViews(*SubView, SubViewBranches);
432*0fca6ea1SDimitry Andric         attachMCDCSubViews(*SubView, SubViewMCDCRecords);
4330b57cec5SDimitry Andric       }
4340b57cec5SDimitry Andric 
4350b57cec5SDimitry Andric       unsigned FileID = Function->CountedRegions.front().FileID;
4360b57cec5SDimitry Andric       unsigned Line = 0;
4370b57cec5SDimitry Andric       for (const auto &CR : Function->CountedRegions)
4380b57cec5SDimitry Andric         if (CR.FileID == FileID)
4390b57cec5SDimitry Andric           Line = std::max(CR.LineEnd, Line);
4400b57cec5SDimitry Andric       View->addInstantiation(Funcname, Line, std::move(SubView));
4410b57cec5SDimitry Andric     }
4420b57cec5SDimitry Andric   }
4430b57cec5SDimitry Andric   return View;
4440b57cec5SDimitry Andric }
4450b57cec5SDimitry Andric 
4460b57cec5SDimitry Andric static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
4470b57cec5SDimitry Andric   sys::fs::file_status Status;
4480b57cec5SDimitry Andric   if (sys::fs::status(LHS, Status))
4490b57cec5SDimitry Andric     return false;
4500b57cec5SDimitry Andric   auto LHSTime = Status.getLastModificationTime();
4510b57cec5SDimitry Andric   if (sys::fs::status(RHS, Status))
4520b57cec5SDimitry Andric     return false;
4530b57cec5SDimitry Andric   auto RHSTime = Status.getLastModificationTime();
4540b57cec5SDimitry Andric   return LHSTime > RHSTime;
4550b57cec5SDimitry Andric }
4560b57cec5SDimitry Andric 
4570b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
4580b57cec5SDimitry Andric   for (StringRef ObjectFilename : ObjectFilenames)
4590b57cec5SDimitry Andric     if (modifiedTimeGT(ObjectFilename, PGOFilename))
4600b57cec5SDimitry Andric       warning("profile data may be out of date - object is newer",
4610b57cec5SDimitry Andric               ObjectFilename);
46206c3fb27SDimitry Andric   auto FS = vfs::getRealFileSystem();
46306c3fb27SDimitry Andric   auto CoverageOrErr = CoverageMapping::load(
46406c3fb27SDimitry Andric       ObjectFilenames, PGOFilename, *FS, CoverageArches,
46506c3fb27SDimitry Andric       ViewOpts.CompilationDirectory, BIDFetcher.get(), CheckBinaryIDs);
4660b57cec5SDimitry Andric   if (Error E = CoverageOrErr.takeError()) {
4675f757f3fSDimitry Andric     error("failed to load coverage: " + toString(std::move(E)));
4680b57cec5SDimitry Andric     return nullptr;
4690b57cec5SDimitry Andric   }
4700b57cec5SDimitry Andric   auto Coverage = std::move(CoverageOrErr.get());
4710b57cec5SDimitry Andric   unsigned Mismatched = Coverage->getMismatchedCount();
4720b57cec5SDimitry Andric   if (Mismatched) {
4730b57cec5SDimitry Andric     warning(Twine(Mismatched) + " functions have mismatched data");
4740b57cec5SDimitry Andric 
4750b57cec5SDimitry Andric     if (ViewOpts.Debug) {
4760b57cec5SDimitry Andric       for (const auto &HashMismatch : Coverage->getHashMismatches())
4770b57cec5SDimitry Andric         errs() << "hash-mismatch: "
4780b57cec5SDimitry Andric                << "No profile record found for '" << HashMismatch.first << "'"
4790b57cec5SDimitry Andric                << " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
4800b57cec5SDimitry Andric                << '\n';
4810b57cec5SDimitry Andric     }
4820b57cec5SDimitry Andric   }
4830b57cec5SDimitry Andric 
4840b57cec5SDimitry Andric   remapPathNames(*Coverage);
4850b57cec5SDimitry Andric 
4860b57cec5SDimitry Andric   if (!SourceFiles.empty())
4870b57cec5SDimitry Andric     removeUnmappedInputs(*Coverage);
4880b57cec5SDimitry Andric 
4890b57cec5SDimitry Andric   demangleSymbols(*Coverage);
4900b57cec5SDimitry Andric 
4910b57cec5SDimitry Andric   return Coverage;
4920b57cec5SDimitry Andric }
4930b57cec5SDimitry Andric 
4940b57cec5SDimitry Andric void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
4955f757f3fSDimitry Andric   if (!PathRemappings)
4960b57cec5SDimitry Andric     return;
4970b57cec5SDimitry Andric 
498*0fca6ea1SDimitry Andric   // Convert remapping paths to native paths with trailing separators.
4990b57cec5SDimitry Andric   auto nativeWithTrailing = [](StringRef Path) -> std::string {
5000b57cec5SDimitry Andric     if (Path.empty())
5010b57cec5SDimitry Andric       return "";
5020b57cec5SDimitry Andric     SmallString<128> NativePath;
5030b57cec5SDimitry Andric     sys::path::native(Path, NativePath);
504e8d8bef9SDimitry Andric     sys::path::remove_dots(NativePath, true);
505fe6060f1SDimitry Andric     if (!NativePath.empty() && !sys::path::is_separator(NativePath.back()))
5060b57cec5SDimitry Andric       NativePath += sys::path::get_separator();
5070b57cec5SDimitry Andric     return NativePath.c_str();
5080b57cec5SDimitry Andric   };
5095f757f3fSDimitry Andric 
5105f757f3fSDimitry Andric   for (std::pair<std::string, std::string> &PathRemapping : *PathRemappings) {
5115f757f3fSDimitry Andric     std::string RemapFrom = nativeWithTrailing(PathRemapping.first);
5125f757f3fSDimitry Andric     std::string RemapTo = nativeWithTrailing(PathRemapping.second);
5130b57cec5SDimitry Andric 
5140b57cec5SDimitry Andric     // Create a mapping from coverage data file paths to local paths.
5150b57cec5SDimitry Andric     for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
5165f757f3fSDimitry Andric       if (RemappedFilenames.count(Filename) == 1)
5175f757f3fSDimitry Andric         continue;
5185f757f3fSDimitry Andric 
5190b57cec5SDimitry Andric       SmallString<128> NativeFilename;
5200b57cec5SDimitry Andric       sys::path::native(Filename, NativeFilename);
521e8d8bef9SDimitry Andric       sys::path::remove_dots(NativeFilename, true);
5225f757f3fSDimitry Andric       if (NativeFilename.starts_with(RemapFrom)) {
5230b57cec5SDimitry Andric         RemappedFilenames[Filename] =
5240b57cec5SDimitry Andric             RemapTo + NativeFilename.substr(RemapFrom.size()).str();
5250b57cec5SDimitry Andric       }
5260b57cec5SDimitry Andric     }
5275f757f3fSDimitry Andric   }
5280b57cec5SDimitry Andric 
5290b57cec5SDimitry Andric   // Convert input files from local paths to coverage data file paths.
5300b57cec5SDimitry Andric   StringMap<std::string> InvRemappedFilenames;
5310b57cec5SDimitry Andric   for (const auto &RemappedFilename : RemappedFilenames)
5325ffd83dbSDimitry Andric     InvRemappedFilenames[RemappedFilename.getValue()] =
5335ffd83dbSDimitry Andric         std::string(RemappedFilename.getKey());
5340b57cec5SDimitry Andric 
5350b57cec5SDimitry Andric   for (std::string &Filename : SourceFiles) {
5360b57cec5SDimitry Andric     SmallString<128> NativeFilename;
5370b57cec5SDimitry Andric     sys::path::native(Filename, NativeFilename);
5380b57cec5SDimitry Andric     auto CovFileName = InvRemappedFilenames.find(NativeFilename);
5390b57cec5SDimitry Andric     if (CovFileName != InvRemappedFilenames.end())
5400b57cec5SDimitry Andric       Filename = CovFileName->second;
5410b57cec5SDimitry Andric   }
5420b57cec5SDimitry Andric }
5430b57cec5SDimitry Andric 
5440b57cec5SDimitry Andric void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
5450b57cec5SDimitry Andric   std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
5460b57cec5SDimitry Andric 
5470b57cec5SDimitry Andric   // The user may have specified source files which aren't in the coverage
5480b57cec5SDimitry Andric   // mapping. Filter these files away.
549e8d8bef9SDimitry Andric   llvm::erase_if(SourceFiles, [&](const std::string &SF) {
550e8d8bef9SDimitry Andric     return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF);
5510b57cec5SDimitry Andric   });
5520b57cec5SDimitry Andric }
5530b57cec5SDimitry Andric 
5540b57cec5SDimitry Andric void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
5550b57cec5SDimitry Andric   if (!ViewOpts.hasDemangler())
5560b57cec5SDimitry Andric     return;
5570b57cec5SDimitry Andric 
5580b57cec5SDimitry Andric   // Pass function names to the demangler in a temporary file.
5590b57cec5SDimitry Andric   int InputFD;
5600b57cec5SDimitry Andric   SmallString<256> InputPath;
5610b57cec5SDimitry Andric   std::error_code EC =
5620b57cec5SDimitry Andric       sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
5630b57cec5SDimitry Andric   if (EC) {
5640b57cec5SDimitry Andric     error(InputPath, EC.message());
5650b57cec5SDimitry Andric     return;
5660b57cec5SDimitry Andric   }
5670b57cec5SDimitry Andric   ToolOutputFile InputTOF{InputPath, InputFD};
5680b57cec5SDimitry Andric 
5690b57cec5SDimitry Andric   unsigned NumSymbols = 0;
5700b57cec5SDimitry Andric   for (const auto &Function : Coverage.getCoveredFunctions()) {
5710b57cec5SDimitry Andric     InputTOF.os() << Function.Name << '\n';
5720b57cec5SDimitry Andric     ++NumSymbols;
5730b57cec5SDimitry Andric   }
5740b57cec5SDimitry Andric   InputTOF.os().close();
5750b57cec5SDimitry Andric 
5760b57cec5SDimitry Andric   // Use another temporary file to store the demangler's output.
5770b57cec5SDimitry Andric   int OutputFD;
5780b57cec5SDimitry Andric   SmallString<256> OutputPath;
5790b57cec5SDimitry Andric   EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
5800b57cec5SDimitry Andric                                     OutputPath);
5810b57cec5SDimitry Andric   if (EC) {
5820b57cec5SDimitry Andric     error(OutputPath, EC.message());
5830b57cec5SDimitry Andric     return;
5840b57cec5SDimitry Andric   }
5850b57cec5SDimitry Andric   ToolOutputFile OutputTOF{OutputPath, OutputFD};
5860b57cec5SDimitry Andric   OutputTOF.os().close();
5870b57cec5SDimitry Andric 
5880b57cec5SDimitry Andric   // Invoke the demangler.
5890b57cec5SDimitry Andric   std::vector<StringRef> ArgsV;
590bdd1243dSDimitry Andric   ArgsV.reserve(ViewOpts.DemanglerOpts.size());
5910b57cec5SDimitry Andric   for (StringRef Arg : ViewOpts.DemanglerOpts)
5920b57cec5SDimitry Andric     ArgsV.push_back(Arg);
593bdd1243dSDimitry Andric   std::optional<StringRef> Redirects[] = {
594bdd1243dSDimitry Andric       InputPath.str(), OutputPath.str(), {""}};
5950b57cec5SDimitry Andric   std::string ErrMsg;
596bdd1243dSDimitry Andric   int RC =
597bdd1243dSDimitry Andric       sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
598bdd1243dSDimitry Andric                           /*env=*/std::nullopt, Redirects, /*secondsToWait=*/0,
5990b57cec5SDimitry Andric                           /*memoryLimit=*/0, &ErrMsg);
6000b57cec5SDimitry Andric   if (RC) {
6010b57cec5SDimitry Andric     error(ErrMsg, ViewOpts.DemanglerOpts[0]);
6020b57cec5SDimitry Andric     return;
6030b57cec5SDimitry Andric   }
6040b57cec5SDimitry Andric 
6050b57cec5SDimitry Andric   // Parse the demangler's output.
6060b57cec5SDimitry Andric   auto BufOrError = MemoryBuffer::getFile(OutputPath);
6070b57cec5SDimitry Andric   if (!BufOrError) {
6080b57cec5SDimitry Andric     error(OutputPath, BufOrError.getError().message());
6090b57cec5SDimitry Andric     return;
6100b57cec5SDimitry Andric   }
6110b57cec5SDimitry Andric 
6120b57cec5SDimitry Andric   std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
6130b57cec5SDimitry Andric 
6140b57cec5SDimitry Andric   SmallVector<StringRef, 8> Symbols;
6150b57cec5SDimitry Andric   StringRef DemanglerData = DemanglerBuf->getBuffer();
6160b57cec5SDimitry Andric   DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
6170b57cec5SDimitry Andric                       /*KeepEmpty=*/false);
6180b57cec5SDimitry Andric   if (Symbols.size() != NumSymbols) {
6195f757f3fSDimitry Andric     error("demangler did not provide expected number of symbols");
6200b57cec5SDimitry Andric     return;
6210b57cec5SDimitry Andric   }
6220b57cec5SDimitry Andric 
6230b57cec5SDimitry Andric   // Cache the demangled names.
6240b57cec5SDimitry Andric   unsigned I = 0;
6250b57cec5SDimitry Andric   for (const auto &Function : Coverage.getCoveredFunctions())
6260b57cec5SDimitry Andric     // On Windows, lines in the demangler's output file end with "\r\n".
6270b57cec5SDimitry Andric     // Splitting by '\n' keeps '\r's, so cut them now.
6285ffd83dbSDimitry Andric     DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
6290b57cec5SDimitry Andric }
6300b57cec5SDimitry Andric 
6310b57cec5SDimitry Andric void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
6320b57cec5SDimitry Andric                                            CoverageMapping *Coverage,
6330b57cec5SDimitry Andric                                            CoveragePrinter *Printer,
6340b57cec5SDimitry Andric                                            bool ShowFilenames) {
6350b57cec5SDimitry Andric   auto View = createSourceFileView(SourceFile, *Coverage);
6360b57cec5SDimitry Andric   if (!View) {
6370b57cec5SDimitry Andric     warning("The file '" + SourceFile + "' isn't covered.");
6380b57cec5SDimitry Andric     return;
6390b57cec5SDimitry Andric   }
6400b57cec5SDimitry Andric 
6410b57cec5SDimitry Andric   auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
6420b57cec5SDimitry Andric   if (Error E = OSOrErr.takeError()) {
6435f757f3fSDimitry Andric     error("could not create view file!", toString(std::move(E)));
6440b57cec5SDimitry Andric     return;
6450b57cec5SDimitry Andric   }
6460b57cec5SDimitry Andric   auto OS = std::move(OSOrErr.get());
6470b57cec5SDimitry Andric 
6480b57cec5SDimitry Andric   View->print(*OS.get(), /*Wholefile=*/true,
6490b57cec5SDimitry Andric               /*ShowSourceName=*/ShowFilenames,
6500b57cec5SDimitry Andric               /*ShowTitle=*/ViewOpts.hasOutputDirectory());
6510b57cec5SDimitry Andric   Printer->closeViewFile(std::move(OS));
6520b57cec5SDimitry Andric }
6530b57cec5SDimitry Andric 
6540b57cec5SDimitry Andric int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
6550b57cec5SDimitry Andric   cl::opt<std::string> CovFilename(
6560b57cec5SDimitry Andric       cl::Positional, cl::desc("Covered executable or object file."));
6570b57cec5SDimitry Andric 
6580b57cec5SDimitry Andric   cl::list<std::string> CovFilenames(
65981ad6265SDimitry Andric       "object", cl::desc("Coverage executable or object file"));
660e8d8bef9SDimitry Andric 
661e8d8bef9SDimitry Andric   cl::opt<bool> DebugDumpCollectedObjects(
662e8d8bef9SDimitry Andric       "dump-collected-objects", cl::Optional, cl::Hidden,
663e8d8bef9SDimitry Andric       cl::desc("Show the collected coverage object files"));
6640b57cec5SDimitry Andric 
6651ac55f4cSDimitry Andric   cl::list<std::string> InputSourceFiles("sources", cl::Positional,
66681ad6265SDimitry Andric                                          cl::desc("<Source files>"));
6670b57cec5SDimitry Andric 
6680b57cec5SDimitry Andric   cl::opt<bool> DebugDumpCollectedPaths(
6690b57cec5SDimitry Andric       "dump-collected-paths", cl::Optional, cl::Hidden,
6700b57cec5SDimitry Andric       cl::desc("Show the collected paths to source files"));
6710b57cec5SDimitry Andric 
6720b57cec5SDimitry Andric   cl::opt<std::string, true> PGOFilename(
6730b57cec5SDimitry Andric       "instr-profile", cl::Required, cl::location(this->PGOFilename),
6740b57cec5SDimitry Andric       cl::desc(
6750b57cec5SDimitry Andric           "File with the profile data obtained after an instrumented run"));
6760b57cec5SDimitry Andric 
6770b57cec5SDimitry Andric   cl::list<std::string> Arches(
6780b57cec5SDimitry Andric       "arch", cl::desc("architectures of the coverage mapping binaries"));
6790b57cec5SDimitry Andric 
6800b57cec5SDimitry Andric   cl::opt<bool> DebugDump("dump", cl::Optional,
6810b57cec5SDimitry Andric                           cl::desc("Show internal debug dump"));
6820b57cec5SDimitry Andric 
6831ac55f4cSDimitry Andric   cl::list<std::string> DebugFileDirectory(
6841ac55f4cSDimitry Andric       "debug-file-directory",
6851ac55f4cSDimitry Andric       cl::desc("Directories to search for object files by build ID"));
6861ac55f4cSDimitry Andric   cl::opt<bool> Debuginfod(
6871ac55f4cSDimitry Andric       "debuginfod", cl::ZeroOrMore,
6881ac55f4cSDimitry Andric       cl::desc("Use debuginfod to look up object files from profile"),
6891ac55f4cSDimitry Andric       cl::init(canUseDebuginfod()));
6901ac55f4cSDimitry Andric 
6910b57cec5SDimitry Andric   cl::opt<CoverageViewOptions::OutputFormat> Format(
6920b57cec5SDimitry Andric       "format", cl::desc("Output format for line-based coverage reports"),
6930b57cec5SDimitry Andric       cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
6940b57cec5SDimitry Andric                             "Text output"),
6950b57cec5SDimitry Andric                  clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
6960b57cec5SDimitry Andric                             "HTML output"),
6970b57cec5SDimitry Andric                  clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
6980b57cec5SDimitry Andric                             "lcov tracefile output")),
6990b57cec5SDimitry Andric       cl::init(CoverageViewOptions::OutputFormat::Text));
7000b57cec5SDimitry Andric 
7015f757f3fSDimitry Andric   cl::list<std::string> PathRemaps(
7020b57cec5SDimitry Andric       "path-equivalence", cl::Optional,
7030b57cec5SDimitry Andric       cl::desc("<from>,<to> Map coverage data paths to local source file "
7040b57cec5SDimitry Andric                "paths"));
7050b57cec5SDimitry Andric 
7060b57cec5SDimitry Andric   cl::OptionCategory FilteringCategory("Function filtering options");
7070b57cec5SDimitry Andric 
7080b57cec5SDimitry Andric   cl::list<std::string> NameFilters(
7090b57cec5SDimitry Andric       "name", cl::Optional,
7100b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with the given name"),
71181ad6265SDimitry Andric       cl::cat(FilteringCategory));
7120b57cec5SDimitry Andric 
7130b57cec5SDimitry Andric   cl::list<std::string> NameFilterFiles(
7144824e7fdSDimitry Andric       "name-allowlist", cl::Optional,
7150b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions listed in the given "
7160b57cec5SDimitry Andric                "file"),
71781ad6265SDimitry Andric       cl::cat(FilteringCategory));
7180b57cec5SDimitry Andric 
7190b57cec5SDimitry Andric   cl::list<std::string> NameRegexFilters(
7200b57cec5SDimitry Andric       "name-regex", cl::Optional,
7210b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions that match the given "
7220b57cec5SDimitry Andric                "regular expression"),
72381ad6265SDimitry Andric       cl::cat(FilteringCategory));
7240b57cec5SDimitry Andric 
7250b57cec5SDimitry Andric   cl::list<std::string> IgnoreFilenameRegexFilters(
7260b57cec5SDimitry Andric       "ignore-filename-regex", cl::Optional,
7270b57cec5SDimitry Andric       cl::desc("Skip source code files with file paths that match the given "
7280b57cec5SDimitry Andric                "regular expression"),
72981ad6265SDimitry Andric       cl::cat(FilteringCategory));
7300b57cec5SDimitry Andric 
7310b57cec5SDimitry Andric   cl::opt<double> RegionCoverageLtFilter(
7320b57cec5SDimitry Andric       "region-coverage-lt", cl::Optional,
7330b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with region coverage "
7340b57cec5SDimitry Andric                "less than the given threshold"),
7350b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7360b57cec5SDimitry Andric 
7370b57cec5SDimitry Andric   cl::opt<double> RegionCoverageGtFilter(
7380b57cec5SDimitry Andric       "region-coverage-gt", cl::Optional,
7390b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with region coverage "
7400b57cec5SDimitry Andric                "greater than the given threshold"),
7410b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7420b57cec5SDimitry Andric 
7430b57cec5SDimitry Andric   cl::opt<double> LineCoverageLtFilter(
7440b57cec5SDimitry Andric       "line-coverage-lt", cl::Optional,
7450b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with line coverage less "
7460b57cec5SDimitry Andric                "than the given threshold"),
7470b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7480b57cec5SDimitry Andric 
7490b57cec5SDimitry Andric   cl::opt<double> LineCoverageGtFilter(
7500b57cec5SDimitry Andric       "line-coverage-gt", cl::Optional,
7510b57cec5SDimitry Andric       cl::desc("Show code coverage only for functions with line coverage "
7520b57cec5SDimitry Andric                "greater than the given threshold"),
7530b57cec5SDimitry Andric       cl::cat(FilteringCategory));
7540b57cec5SDimitry Andric 
7550b57cec5SDimitry Andric   cl::opt<cl::boolOrDefault> UseColor(
7560b57cec5SDimitry Andric       "use-color", cl::desc("Emit colored output (default=autodetect)"),
7570b57cec5SDimitry Andric       cl::init(cl::BOU_UNSET));
7580b57cec5SDimitry Andric 
7590b57cec5SDimitry Andric   cl::list<std::string> DemanglerOpts(
7600b57cec5SDimitry Andric       "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
7610b57cec5SDimitry Andric 
7620b57cec5SDimitry Andric   cl::opt<bool> RegionSummary(
7630b57cec5SDimitry Andric       "show-region-summary", cl::Optional,
7640b57cec5SDimitry Andric       cl::desc("Show region statistics in summary table"),
7650b57cec5SDimitry Andric       cl::init(true));
7660b57cec5SDimitry Andric 
767e8d8bef9SDimitry Andric   cl::opt<bool> BranchSummary(
768e8d8bef9SDimitry Andric       "show-branch-summary", cl::Optional,
769e8d8bef9SDimitry Andric       cl::desc("Show branch condition statistics in summary table"),
770e8d8bef9SDimitry Andric       cl::init(true));
771e8d8bef9SDimitry Andric 
7725f757f3fSDimitry Andric   cl::opt<bool> MCDCSummary("show-mcdc-summary", cl::Optional,
7735f757f3fSDimitry Andric                             cl::desc("Show MCDC statistics in summary table"),
7745f757f3fSDimitry Andric                             cl::init(false));
7755f757f3fSDimitry Andric 
7760b57cec5SDimitry Andric   cl::opt<bool> InstantiationSummary(
7770b57cec5SDimitry Andric       "show-instantiation-summary", cl::Optional,
7780b57cec5SDimitry Andric       cl::desc("Show instantiation statistics in summary table"));
7790b57cec5SDimitry Andric 
7800b57cec5SDimitry Andric   cl::opt<bool> SummaryOnly(
7810b57cec5SDimitry Andric       "summary-only", cl::Optional,
7820b57cec5SDimitry Andric       cl::desc("Export only summary information for each source file"));
7830b57cec5SDimitry Andric 
7840b57cec5SDimitry Andric   cl::opt<unsigned> NumThreads(
7850b57cec5SDimitry Andric       "num-threads", cl::init(0),
7860b57cec5SDimitry Andric       cl::desc("Number of merge threads to use (default: autodetect)"));
7870b57cec5SDimitry Andric   cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
7880b57cec5SDimitry Andric                         cl::aliasopt(NumThreads));
7890b57cec5SDimitry Andric 
790fe6060f1SDimitry Andric   cl::opt<std::string> CompilationDirectory(
791fe6060f1SDimitry Andric       "compilation-dir", cl::init(""),
792fe6060f1SDimitry Andric       cl::desc("Directory used as a base for relative coverage mapping paths"));
793fe6060f1SDimitry Andric 
79406c3fb27SDimitry Andric   cl::opt<bool> CheckBinaryIDs(
79506c3fb27SDimitry Andric       "check-binary-ids", cl::desc("Fail if an object couldn't be found for a "
79606c3fb27SDimitry Andric                                    "binary ID in the profile"));
79706c3fb27SDimitry Andric 
7980b57cec5SDimitry Andric   auto commandLineParser = [&, this](int argc, const char **argv) -> int {
7990b57cec5SDimitry Andric     cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
8000b57cec5SDimitry Andric     ViewOpts.Debug = DebugDump;
8011ac55f4cSDimitry Andric     if (Debuginfod) {
8021ac55f4cSDimitry Andric       HTTPClient::initialize();
8031ac55f4cSDimitry Andric       BIDFetcher = std::make_unique<DebuginfodFetcher>(DebugFileDirectory);
8041ac55f4cSDimitry Andric     } else {
8051ac55f4cSDimitry Andric       BIDFetcher = std::make_unique<object::BuildIDFetcher>(DebugFileDirectory);
8061ac55f4cSDimitry Andric     }
80706c3fb27SDimitry Andric     this->CheckBinaryIDs = CheckBinaryIDs;
8080b57cec5SDimitry Andric 
8090b57cec5SDimitry Andric     if (!CovFilename.empty())
8100b57cec5SDimitry Andric       ObjectFilenames.emplace_back(CovFilename);
8110b57cec5SDimitry Andric     for (const std::string &Filename : CovFilenames)
8120b57cec5SDimitry Andric       ObjectFilenames.emplace_back(Filename);
8131ac55f4cSDimitry Andric     if (ObjectFilenames.empty() && !Debuginfod && DebugFileDirectory.empty()) {
8140b57cec5SDimitry Andric       errs() << "No filenames specified!\n";
8150b57cec5SDimitry Andric       ::exit(1);
8160b57cec5SDimitry Andric     }
8170b57cec5SDimitry Andric 
818e8d8bef9SDimitry Andric     if (DebugDumpCollectedObjects) {
819e8d8bef9SDimitry Andric       for (StringRef OF : ObjectFilenames)
820e8d8bef9SDimitry Andric         outs() << OF << '\n';
821e8d8bef9SDimitry Andric       ::exit(0);
822e8d8bef9SDimitry Andric     }
823e8d8bef9SDimitry Andric 
8240b57cec5SDimitry Andric     ViewOpts.Format = Format;
8250b57cec5SDimitry Andric     switch (ViewOpts.Format) {
8260b57cec5SDimitry Andric     case CoverageViewOptions::OutputFormat::Text:
8270b57cec5SDimitry Andric       ViewOpts.Colors = UseColor == cl::BOU_UNSET
8280b57cec5SDimitry Andric                             ? sys::Process::StandardOutHasColors()
8290b57cec5SDimitry Andric                             : UseColor == cl::BOU_TRUE;
8300b57cec5SDimitry Andric       break;
8310b57cec5SDimitry Andric     case CoverageViewOptions::OutputFormat::HTML:
8320b57cec5SDimitry Andric       if (UseColor == cl::BOU_FALSE)
8330b57cec5SDimitry Andric         errs() << "Color output cannot be disabled when generating html.\n";
8340b57cec5SDimitry Andric       ViewOpts.Colors = true;
8350b57cec5SDimitry Andric       break;
8360b57cec5SDimitry Andric     case CoverageViewOptions::OutputFormat::Lcov:
8370b57cec5SDimitry Andric       if (UseColor == cl::BOU_TRUE)
8380b57cec5SDimitry Andric         errs() << "Color output cannot be enabled when generating lcov.\n";
8390b57cec5SDimitry Andric       ViewOpts.Colors = false;
8400b57cec5SDimitry Andric       break;
8410b57cec5SDimitry Andric     }
8420b57cec5SDimitry Andric 
8435f757f3fSDimitry Andric     if (!PathRemaps.empty()) {
8445f757f3fSDimitry Andric       std::vector<std::pair<std::string, std::string>> Remappings;
8455f757f3fSDimitry Andric 
8465f757f3fSDimitry Andric       for (const std::string &PathRemap : PathRemaps) {
8470b57cec5SDimitry Andric         auto EquivPair = StringRef(PathRemap).split(',');
848349cc55cSDimitry Andric         if (EquivPair.first.empty() || EquivPair.second.empty()) {
849349cc55cSDimitry Andric           error("invalid argument '" + PathRemap +
850349cc55cSDimitry Andric                     "', must be in format 'from,to'",
851349cc55cSDimitry Andric                 "-path-equivalence");
852349cc55cSDimitry Andric           return 1;
853349cc55cSDimitry Andric         }
854349cc55cSDimitry Andric 
8555f757f3fSDimitry Andric         Remappings.push_back(
8565f757f3fSDimitry Andric             {std::string(EquivPair.first), std::string(EquivPair.second)});
8575f757f3fSDimitry Andric       }
8585f757f3fSDimitry Andric 
8595f757f3fSDimitry Andric       PathRemappings = Remappings;
860349cc55cSDimitry Andric     }
8610b57cec5SDimitry Andric 
8620b57cec5SDimitry Andric     // If a demangler is supplied, check if it exists and register it.
8630b57cec5SDimitry Andric     if (!DemanglerOpts.empty()) {
8640b57cec5SDimitry Andric       auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
8650b57cec5SDimitry Andric       if (!DemanglerPathOrErr) {
8665f757f3fSDimitry Andric         error("could not find the demangler!",
8670b57cec5SDimitry Andric               DemanglerPathOrErr.getError().message());
8680b57cec5SDimitry Andric         return 1;
8690b57cec5SDimitry Andric       }
8700b57cec5SDimitry Andric       DemanglerOpts[0] = *DemanglerPathOrErr;
8710b57cec5SDimitry Andric       ViewOpts.DemanglerOpts.swap(DemanglerOpts);
8720b57cec5SDimitry Andric     }
8730b57cec5SDimitry Andric 
8744824e7fdSDimitry Andric     // Read in -name-allowlist files.
875bdd1243dSDimitry Andric     if (!NameFilterFiles.empty()) {
8760b57cec5SDimitry Andric       std::string SpecialCaseListErr;
8774824e7fdSDimitry Andric       NameAllowlist = SpecialCaseList::create(
878480093f4SDimitry Andric           NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr);
8794824e7fdSDimitry Andric       if (!NameAllowlist)
8800b57cec5SDimitry Andric         error(SpecialCaseListErr);
8810b57cec5SDimitry Andric     }
8820b57cec5SDimitry Andric 
8830b57cec5SDimitry Andric     // Create the function filters
8844824e7fdSDimitry Andric     if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) {
8858bcb0991SDimitry Andric       auto NameFilterer = std::make_unique<CoverageFilters>();
8860b57cec5SDimitry Andric       for (const auto &Name : NameFilters)
8878bcb0991SDimitry Andric         NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name));
888bdd1243dSDimitry Andric       if (NameAllowlist && !NameFilterFiles.empty())
8890b57cec5SDimitry Andric         NameFilterer->push_back(
8904824e7fdSDimitry Andric             std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist));
8910b57cec5SDimitry Andric       for (const auto &Regex : NameRegexFilters)
8920b57cec5SDimitry Andric         NameFilterer->push_back(
8938bcb0991SDimitry Andric             std::make_unique<NameRegexCoverageFilter>(Regex));
8940b57cec5SDimitry Andric       Filters.push_back(std::move(NameFilterer));
8950b57cec5SDimitry Andric     }
8960b57cec5SDimitry Andric 
8970b57cec5SDimitry Andric     if (RegionCoverageLtFilter.getNumOccurrences() ||
8980b57cec5SDimitry Andric         RegionCoverageGtFilter.getNumOccurrences() ||
8990b57cec5SDimitry Andric         LineCoverageLtFilter.getNumOccurrences() ||
9000b57cec5SDimitry Andric         LineCoverageGtFilter.getNumOccurrences()) {
9018bcb0991SDimitry Andric       auto StatFilterer = std::make_unique<CoverageFilters>();
9020b57cec5SDimitry Andric       if (RegionCoverageLtFilter.getNumOccurrences())
9038bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
9040b57cec5SDimitry Andric             RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
9050b57cec5SDimitry Andric       if (RegionCoverageGtFilter.getNumOccurrences())
9068bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
9070b57cec5SDimitry Andric             RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
9080b57cec5SDimitry Andric       if (LineCoverageLtFilter.getNumOccurrences())
9098bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
9100b57cec5SDimitry Andric             LineCoverageFilter::LessThan, LineCoverageLtFilter));
9110b57cec5SDimitry Andric       if (LineCoverageGtFilter.getNumOccurrences())
9128bcb0991SDimitry Andric         StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
9130b57cec5SDimitry Andric             RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
9140b57cec5SDimitry Andric       Filters.push_back(std::move(StatFilterer));
9150b57cec5SDimitry Andric     }
9160b57cec5SDimitry Andric 
9170b57cec5SDimitry Andric     // Create the ignore filename filters.
9180b57cec5SDimitry Andric     for (const auto &RE : IgnoreFilenameRegexFilters)
9190b57cec5SDimitry Andric       IgnoreFilenameFilters.push_back(
9208bcb0991SDimitry Andric           std::make_unique<NameRegexCoverageFilter>(RE));
9210b57cec5SDimitry Andric 
9220b57cec5SDimitry Andric     if (!Arches.empty()) {
9230b57cec5SDimitry Andric       for (const std::string &Arch : Arches) {
9240b57cec5SDimitry Andric         if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
9255f757f3fSDimitry Andric           error("unknown architecture: " + Arch);
9260b57cec5SDimitry Andric           return 1;
9270b57cec5SDimitry Andric         }
9280b57cec5SDimitry Andric         CoverageArches.emplace_back(Arch);
9290b57cec5SDimitry Andric       }
9301ac55f4cSDimitry Andric       if (CoverageArches.size() != 1 &&
9311ac55f4cSDimitry Andric           CoverageArches.size() != ObjectFilenames.size()) {
9325f757f3fSDimitry Andric         error("number of architectures doesn't match the number of objects");
9330b57cec5SDimitry Andric         return 1;
9340b57cec5SDimitry Andric       }
9350b57cec5SDimitry Andric     }
9360b57cec5SDimitry Andric 
9370b57cec5SDimitry Andric     // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
9380b57cec5SDimitry Andric     for (const std::string &File : InputSourceFiles)
9390b57cec5SDimitry Andric       collectPaths(File);
9400b57cec5SDimitry Andric 
9410b57cec5SDimitry Andric     if (DebugDumpCollectedPaths) {
9420b57cec5SDimitry Andric       for (const std::string &SF : SourceFiles)
9430b57cec5SDimitry Andric         outs() << SF << '\n';
9440b57cec5SDimitry Andric       ::exit(0);
9450b57cec5SDimitry Andric     }
9460b57cec5SDimitry Andric 
9475f757f3fSDimitry Andric     ViewOpts.ShowMCDCSummary = MCDCSummary;
948e8d8bef9SDimitry Andric     ViewOpts.ShowBranchSummary = BranchSummary;
9490b57cec5SDimitry Andric     ViewOpts.ShowRegionSummary = RegionSummary;
9500b57cec5SDimitry Andric     ViewOpts.ShowInstantiationSummary = InstantiationSummary;
9510b57cec5SDimitry Andric     ViewOpts.ExportSummaryOnly = SummaryOnly;
9520b57cec5SDimitry Andric     ViewOpts.NumThreads = NumThreads;
953fe6060f1SDimitry Andric     ViewOpts.CompilationDirectory = CompilationDirectory;
9540b57cec5SDimitry Andric 
9550b57cec5SDimitry Andric     return 0;
9560b57cec5SDimitry Andric   };
9570b57cec5SDimitry Andric 
9580b57cec5SDimitry Andric   switch (Cmd) {
9590b57cec5SDimitry Andric   case Show:
9600b57cec5SDimitry Andric     return doShow(argc, argv, commandLineParser);
9610b57cec5SDimitry Andric   case Report:
9620b57cec5SDimitry Andric     return doReport(argc, argv, commandLineParser);
9630b57cec5SDimitry Andric   case Export:
9640b57cec5SDimitry Andric     return doExport(argc, argv, commandLineParser);
9650b57cec5SDimitry Andric   }
9660b57cec5SDimitry Andric   return 0;
9670b57cec5SDimitry Andric }
9680b57cec5SDimitry Andric 
9690b57cec5SDimitry Andric int CodeCoverageTool::doShow(int argc, const char **argv,
9700b57cec5SDimitry Andric                              CommandLineParserType commandLineParser) {
9710b57cec5SDimitry Andric 
9720b57cec5SDimitry Andric   cl::OptionCategory ViewCategory("Viewing options");
9730b57cec5SDimitry Andric 
9740b57cec5SDimitry Andric   cl::opt<bool> ShowLineExecutionCounts(
9750b57cec5SDimitry Andric       "show-line-counts", cl::Optional,
9760b57cec5SDimitry Andric       cl::desc("Show the execution counts for each line"), cl::init(true),
9770b57cec5SDimitry Andric       cl::cat(ViewCategory));
9780b57cec5SDimitry Andric 
9790b57cec5SDimitry Andric   cl::opt<bool> ShowRegions(
9800b57cec5SDimitry Andric       "show-regions", cl::Optional,
9810b57cec5SDimitry Andric       cl::desc("Show the execution counts for each region"),
9820b57cec5SDimitry Andric       cl::cat(ViewCategory));
9830b57cec5SDimitry Andric 
984e8d8bef9SDimitry Andric   cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
985e8d8bef9SDimitry Andric       "show-branches", cl::Optional,
986e8d8bef9SDimitry Andric       cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
987e8d8bef9SDimitry Andric       cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
988e8d8bef9SDimitry Andric                             "count", "Show True/False counts"),
989e8d8bef9SDimitry Andric                  clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
990e8d8bef9SDimitry Andric                             "percent", "Show True/False percent")),
991e8d8bef9SDimitry Andric       cl::init(CoverageViewOptions::BranchOutputType::Off));
992e8d8bef9SDimitry Andric 
9935f757f3fSDimitry Andric   cl::opt<bool> ShowMCDC(
9945f757f3fSDimitry Andric       "show-mcdc", cl::Optional,
9955f757f3fSDimitry Andric       cl::desc("Show the MCDC Coverage for each applicable boolean expression"),
9965f757f3fSDimitry Andric       cl::cat(ViewCategory));
9975f757f3fSDimitry Andric 
9980b57cec5SDimitry Andric   cl::opt<bool> ShowBestLineRegionsCounts(
9990b57cec5SDimitry Andric       "show-line-counts-or-regions", cl::Optional,
10000b57cec5SDimitry Andric       cl::desc("Show the execution counts for each line, or the execution "
10010b57cec5SDimitry Andric                "counts for each region on lines that have multiple regions"),
10020b57cec5SDimitry Andric       cl::cat(ViewCategory));
10030b57cec5SDimitry Andric 
10040b57cec5SDimitry Andric   cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
10050b57cec5SDimitry Andric                                cl::desc("Show expanded source regions"),
10060b57cec5SDimitry Andric                                cl::cat(ViewCategory));
10070b57cec5SDimitry Andric 
10080b57cec5SDimitry Andric   cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
10090b57cec5SDimitry Andric                                    cl::desc("Show function instantiations"),
10100b57cec5SDimitry Andric                                    cl::init(true), cl::cat(ViewCategory));
10110b57cec5SDimitry Andric 
10125f757f3fSDimitry Andric   cl::opt<bool> ShowDirectoryCoverage("show-directory-coverage", cl::Optional,
10135f757f3fSDimitry Andric                                       cl::desc("Show directory coverage"),
10145f757f3fSDimitry Andric                                       cl::cat(ViewCategory));
10155f757f3fSDimitry Andric 
10160b57cec5SDimitry Andric   cl::opt<std::string> ShowOutputDirectory(
10170b57cec5SDimitry Andric       "output-dir", cl::init(""),
10180b57cec5SDimitry Andric       cl::desc("Directory in which coverage information is written out"));
10190b57cec5SDimitry Andric   cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
10200b57cec5SDimitry Andric                                  cl::aliasopt(ShowOutputDirectory));
10210b57cec5SDimitry Andric 
10220b57cec5SDimitry Andric   cl::opt<uint32_t> TabSize(
10230b57cec5SDimitry Andric       "tab-size", cl::init(2),
10240b57cec5SDimitry Andric       cl::desc(
10250b57cec5SDimitry Andric           "Set tab expansion size for html coverage reports (default = 2)"));
10260b57cec5SDimitry Andric 
10270b57cec5SDimitry Andric   cl::opt<std::string> ProjectTitle(
10280b57cec5SDimitry Andric       "project-title", cl::Optional,
10290b57cec5SDimitry Andric       cl::desc("Set project title for the coverage report"));
10300b57cec5SDimitry Andric 
103181ad6265SDimitry Andric   cl::opt<std::string> CovWatermark(
103281ad6265SDimitry Andric       "coverage-watermark", cl::Optional,
103381ad6265SDimitry Andric       cl::desc("<high>,<low> value indicate thresholds for high and low"
103481ad6265SDimitry Andric                "coverage watermark"));
103581ad6265SDimitry Andric 
10360b57cec5SDimitry Andric   auto Err = commandLineParser(argc, argv);
10370b57cec5SDimitry Andric   if (Err)
10380b57cec5SDimitry Andric     return Err;
10390b57cec5SDimitry Andric 
10400b57cec5SDimitry Andric   if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
10415f757f3fSDimitry Andric     error("lcov format should be used with 'llvm-cov export'.");
10420b57cec5SDimitry Andric     return 1;
10430b57cec5SDimitry Andric   }
10440b57cec5SDimitry Andric 
104581ad6265SDimitry Andric   ViewOpts.HighCovWatermark = 100.0;
104681ad6265SDimitry Andric   ViewOpts.LowCovWatermark = 80.0;
104781ad6265SDimitry Andric   if (!CovWatermark.empty()) {
104881ad6265SDimitry Andric     auto WaterMarkPair = StringRef(CovWatermark).split(',');
104981ad6265SDimitry Andric     if (WaterMarkPair.first.empty() || WaterMarkPair.second.empty()) {
105081ad6265SDimitry Andric       error("invalid argument '" + CovWatermark +
105181ad6265SDimitry Andric                 "', must be in format 'high,low'",
105281ad6265SDimitry Andric             "-coverage-watermark");
105381ad6265SDimitry Andric       return 1;
105481ad6265SDimitry Andric     }
105581ad6265SDimitry Andric 
105681ad6265SDimitry Andric     char *EndPointer = nullptr;
105781ad6265SDimitry Andric     ViewOpts.HighCovWatermark =
105881ad6265SDimitry Andric         strtod(WaterMarkPair.first.begin(), &EndPointer);
105981ad6265SDimitry Andric     if (EndPointer != WaterMarkPair.first.end()) {
106081ad6265SDimitry Andric       error("invalid number '" + WaterMarkPair.first +
106181ad6265SDimitry Andric                 "', invalid value for 'high'",
106281ad6265SDimitry Andric             "-coverage-watermark");
106381ad6265SDimitry Andric       return 1;
106481ad6265SDimitry Andric     }
106581ad6265SDimitry Andric 
106681ad6265SDimitry Andric     ViewOpts.LowCovWatermark =
106781ad6265SDimitry Andric         strtod(WaterMarkPair.second.begin(), &EndPointer);
106881ad6265SDimitry Andric     if (EndPointer != WaterMarkPair.second.end()) {
106981ad6265SDimitry Andric       error("invalid number '" + WaterMarkPair.second +
107081ad6265SDimitry Andric                 "', invalid value for 'low'",
107181ad6265SDimitry Andric             "-coverage-watermark");
107281ad6265SDimitry Andric       return 1;
107381ad6265SDimitry Andric     }
107481ad6265SDimitry Andric 
107581ad6265SDimitry Andric     if (ViewOpts.HighCovWatermark > 100 || ViewOpts.LowCovWatermark < 0 ||
107681ad6265SDimitry Andric         ViewOpts.HighCovWatermark <= ViewOpts.LowCovWatermark) {
107781ad6265SDimitry Andric       error(
107881ad6265SDimitry Andric           "invalid number range '" + CovWatermark +
107981ad6265SDimitry Andric               "', must be both high and low should be between 0-100, and high "
108081ad6265SDimitry Andric               "> low",
108181ad6265SDimitry Andric           "-coverage-watermark");
108281ad6265SDimitry Andric       return 1;
108381ad6265SDimitry Andric     }
108481ad6265SDimitry Andric   }
108581ad6265SDimitry Andric 
10860b57cec5SDimitry Andric   ViewOpts.ShowLineNumbers = true;
10870b57cec5SDimitry Andric   ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
10880b57cec5SDimitry Andric                            !ShowRegions || ShowBestLineRegionsCounts;
10890b57cec5SDimitry Andric   ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
10900b57cec5SDimitry Andric   ViewOpts.ShowExpandedRegions = ShowExpansions;
1091e8d8bef9SDimitry Andric   ViewOpts.ShowBranchCounts =
1092e8d8bef9SDimitry Andric       ShowBranches == CoverageViewOptions::BranchOutputType::Count;
10935f757f3fSDimitry Andric   ViewOpts.ShowMCDC = ShowMCDC;
1094e8d8bef9SDimitry Andric   ViewOpts.ShowBranchPercents =
1095e8d8bef9SDimitry Andric       ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
10960b57cec5SDimitry Andric   ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
10975f757f3fSDimitry Andric   ViewOpts.ShowDirectoryCoverage = ShowDirectoryCoverage;
10980b57cec5SDimitry Andric   ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
10990b57cec5SDimitry Andric   ViewOpts.TabSize = TabSize;
11000b57cec5SDimitry Andric   ViewOpts.ProjectTitle = ProjectTitle;
11010b57cec5SDimitry Andric 
11020b57cec5SDimitry Andric   if (ViewOpts.hasOutputDirectory()) {
11030b57cec5SDimitry Andric     if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
11045f757f3fSDimitry Andric       error("could not create output directory!", E.message());
11050b57cec5SDimitry Andric       return 1;
11060b57cec5SDimitry Andric     }
11070b57cec5SDimitry Andric   }
11080b57cec5SDimitry Andric 
11090b57cec5SDimitry Andric   sys::fs::file_status Status;
11105ffd83dbSDimitry Andric   if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
11115f757f3fSDimitry Andric     error("could not read profile data!" + EC.message(), PGOFilename);
11120b57cec5SDimitry Andric     return 1;
11130b57cec5SDimitry Andric   }
11140b57cec5SDimitry Andric 
11150b57cec5SDimitry Andric   auto ModifiedTime = Status.getLastModificationTime();
11160b57cec5SDimitry Andric   std::string ModifiedTimeStr = to_string(ModifiedTime);
11170b57cec5SDimitry Andric   size_t found = ModifiedTimeStr.rfind(':');
11180b57cec5SDimitry Andric   ViewOpts.CreatedTimeStr = (found != std::string::npos)
11190b57cec5SDimitry Andric                                 ? "Created: " + ModifiedTimeStr.substr(0, found)
11200b57cec5SDimitry Andric                                 : "Created: " + ModifiedTimeStr;
11210b57cec5SDimitry Andric 
11220b57cec5SDimitry Andric   auto Coverage = load();
11230b57cec5SDimitry Andric   if (!Coverage)
11240b57cec5SDimitry Andric     return 1;
11250b57cec5SDimitry Andric 
11260b57cec5SDimitry Andric   auto Printer = CoveragePrinter::create(ViewOpts);
11270b57cec5SDimitry Andric 
1128e8d8bef9SDimitry Andric   if (SourceFiles.empty() && !HadSourceFiles)
11290b57cec5SDimitry Andric     // Get the source files from the function coverage mapping.
11300b57cec5SDimitry Andric     for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
11310b57cec5SDimitry Andric       if (!IgnoreFilenameFilters.matchesFilename(Filename))
11325ffd83dbSDimitry Andric         SourceFiles.push_back(std::string(Filename));
11330b57cec5SDimitry Andric     }
11340b57cec5SDimitry Andric 
11350b57cec5SDimitry Andric   // Create an index out of the source files.
11360b57cec5SDimitry Andric   if (ViewOpts.hasOutputDirectory()) {
11370b57cec5SDimitry Andric     if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) {
11385f757f3fSDimitry Andric       error("could not create index file!", toString(std::move(E)));
11390b57cec5SDimitry Andric       return 1;
11400b57cec5SDimitry Andric     }
11410b57cec5SDimitry Andric   }
11420b57cec5SDimitry Andric 
11430b57cec5SDimitry Andric   if (!Filters.empty()) {
11440b57cec5SDimitry Andric     // Build the map of filenames to functions.
11450b57cec5SDimitry Andric     std::map<llvm::StringRef, std::vector<const FunctionRecord *>>
11460b57cec5SDimitry Andric         FilenameFunctionMap;
11470b57cec5SDimitry Andric     for (const auto &SourceFile : SourceFiles)
11480b57cec5SDimitry Andric       for (const auto &Function : Coverage->getCoveredFunctions(SourceFile))
1149bdd1243dSDimitry Andric         if (Filters.matches(*Coverage, Function))
11500b57cec5SDimitry Andric           FilenameFunctionMap[SourceFile].push_back(&Function);
11510b57cec5SDimitry Andric 
11520b57cec5SDimitry Andric     // Only print filter matching functions for each file.
11530b57cec5SDimitry Andric     for (const auto &FileFunc : FilenameFunctionMap) {
11540b57cec5SDimitry Andric       StringRef File = FileFunc.first;
11550b57cec5SDimitry Andric       const auto &Functions = FileFunc.second;
11560b57cec5SDimitry Andric 
11570b57cec5SDimitry Andric       auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false);
11580b57cec5SDimitry Andric       if (Error E = OSOrErr.takeError()) {
11595f757f3fSDimitry Andric         error("could not create view file!", toString(std::move(E)));
11600b57cec5SDimitry Andric         return 1;
11610b57cec5SDimitry Andric       }
11620b57cec5SDimitry Andric       auto OS = std::move(OSOrErr.get());
11630b57cec5SDimitry Andric 
11640b57cec5SDimitry Andric       bool ShowTitle = ViewOpts.hasOutputDirectory();
11650b57cec5SDimitry Andric       for (const auto *Function : Functions) {
11660b57cec5SDimitry Andric         auto FunctionView = createFunctionView(*Function, *Coverage);
11670b57cec5SDimitry Andric         if (!FunctionView) {
11680b57cec5SDimitry Andric           warning("Could not read coverage for '" + Function->Name + "'.");
11690b57cec5SDimitry Andric           continue;
11700b57cec5SDimitry Andric         }
11710b57cec5SDimitry Andric         FunctionView->print(*OS.get(), /*WholeFile=*/false,
11720b57cec5SDimitry Andric                             /*ShowSourceName=*/true, ShowTitle);
11730b57cec5SDimitry Andric         ShowTitle = false;
11740b57cec5SDimitry Andric       }
11750b57cec5SDimitry Andric 
11760b57cec5SDimitry Andric       Printer->closeViewFile(std::move(OS));
11770b57cec5SDimitry Andric     }
11780b57cec5SDimitry Andric     return 0;
11790b57cec5SDimitry Andric   }
11800b57cec5SDimitry Andric 
11810b57cec5SDimitry Andric   // Show files
11820b57cec5SDimitry Andric   bool ShowFilenames =
11830b57cec5SDimitry Andric       (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
11840b57cec5SDimitry Andric       (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
11850b57cec5SDimitry Andric 
11865ffd83dbSDimitry Andric   ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
11875ffd83dbSDimitry Andric   if (ViewOpts.NumThreads == 0) {
11885ffd83dbSDimitry Andric     // If NumThreads is not specified, create one thread for each input, up to
11895ffd83dbSDimitry Andric     // the number of hardware cores.
11905ffd83dbSDimitry Andric     S = heavyweight_hardware_concurrency(SourceFiles.size());
11915ffd83dbSDimitry Andric     S.Limit = true;
11925ffd83dbSDimitry Andric   }
11930b57cec5SDimitry Andric 
11945ffd83dbSDimitry Andric   if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
11950b57cec5SDimitry Andric     for (const std::string &SourceFile : SourceFiles)
11960b57cec5SDimitry Andric       writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
11970b57cec5SDimitry Andric                           ShowFilenames);
11980b57cec5SDimitry Andric   } else {
11990b57cec5SDimitry Andric     // In -output-dir mode, it's safe to use multiple threads to print files.
1200*0fca6ea1SDimitry Andric     DefaultThreadPool Pool(S);
12010b57cec5SDimitry Andric     for (const std::string &SourceFile : SourceFiles)
12020b57cec5SDimitry Andric       Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
12030b57cec5SDimitry Andric                  Coverage.get(), Printer.get(), ShowFilenames);
12040b57cec5SDimitry Andric     Pool.wait();
12050b57cec5SDimitry Andric   }
12060b57cec5SDimitry Andric 
12070b57cec5SDimitry Andric   return 0;
12080b57cec5SDimitry Andric }
12090b57cec5SDimitry Andric 
12100b57cec5SDimitry Andric int CodeCoverageTool::doReport(int argc, const char **argv,
12110b57cec5SDimitry Andric                                CommandLineParserType commandLineParser) {
12120b57cec5SDimitry Andric   cl::opt<bool> ShowFunctionSummaries(
12130b57cec5SDimitry Andric       "show-functions", cl::Optional, cl::init(false),
12140b57cec5SDimitry Andric       cl::desc("Show coverage summaries for each function"));
12150b57cec5SDimitry Andric 
12160b57cec5SDimitry Andric   auto Err = commandLineParser(argc, argv);
12170b57cec5SDimitry Andric   if (Err)
12180b57cec5SDimitry Andric     return Err;
12190b57cec5SDimitry Andric 
12200b57cec5SDimitry Andric   if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) {
12210b57cec5SDimitry Andric     error("HTML output for summary reports is not yet supported.");
12220b57cec5SDimitry Andric     return 1;
12230b57cec5SDimitry Andric   } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
12245f757f3fSDimitry Andric     error("lcov format should be used with 'llvm-cov export'.");
12250b57cec5SDimitry Andric     return 1;
12260b57cec5SDimitry Andric   }
12270b57cec5SDimitry Andric 
1228fcaf7f86SDimitry Andric   sys::fs::file_status Status;
1229fcaf7f86SDimitry Andric   if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
12305f757f3fSDimitry Andric     error("could not read profile data!" + EC.message(), PGOFilename);
1231fcaf7f86SDimitry Andric     return 1;
1232fcaf7f86SDimitry Andric   }
1233fcaf7f86SDimitry Andric 
12340b57cec5SDimitry Andric   auto Coverage = load();
12350b57cec5SDimitry Andric   if (!Coverage)
12360b57cec5SDimitry Andric     return 1;
12370b57cec5SDimitry Andric 
1238bdd1243dSDimitry Andric   CoverageReport Report(ViewOpts, *Coverage);
12390b57cec5SDimitry Andric   if (!ShowFunctionSummaries) {
12400b57cec5SDimitry Andric     if (SourceFiles.empty())
12410b57cec5SDimitry Andric       Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
12420b57cec5SDimitry Andric     else
12430b57cec5SDimitry Andric       Report.renderFileReports(llvm::outs(), SourceFiles);
12440b57cec5SDimitry Andric   } else {
12450b57cec5SDimitry Andric     if (SourceFiles.empty()) {
12465f757f3fSDimitry Andric       error("source files must be specified when -show-functions=true is "
12470b57cec5SDimitry Andric             "specified");
12480b57cec5SDimitry Andric       return 1;
12490b57cec5SDimitry Andric     }
12500b57cec5SDimitry Andric 
12510b57cec5SDimitry Andric     Report.renderFunctionReports(SourceFiles, DC, llvm::outs());
12520b57cec5SDimitry Andric   }
12530b57cec5SDimitry Andric   return 0;
12540b57cec5SDimitry Andric }
12550b57cec5SDimitry Andric 
12560b57cec5SDimitry Andric int CodeCoverageTool::doExport(int argc, const char **argv,
12570b57cec5SDimitry Andric                                CommandLineParserType commandLineParser) {
12580b57cec5SDimitry Andric 
12590b57cec5SDimitry Andric   cl::OptionCategory ExportCategory("Exporting options");
12600b57cec5SDimitry Andric 
12610b57cec5SDimitry Andric   cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional,
12620b57cec5SDimitry Andric                                cl::desc("Don't export expanded source regions"),
12630b57cec5SDimitry Andric                                cl::cat(ExportCategory));
12640b57cec5SDimitry Andric 
12650b57cec5SDimitry Andric   cl::opt<bool> SkipFunctions("skip-functions", cl::Optional,
12660b57cec5SDimitry Andric                               cl::desc("Don't export per-function data"),
12670b57cec5SDimitry Andric                               cl::cat(ExportCategory));
12680b57cec5SDimitry Andric 
1269bdd1243dSDimitry Andric   cl::opt<bool> SkipBranches("skip-branches", cl::Optional,
1270bdd1243dSDimitry Andric                               cl::desc("Don't export branch data (LCOV)"),
1271bdd1243dSDimitry Andric                               cl::cat(ExportCategory));
1272bdd1243dSDimitry Andric 
12730b57cec5SDimitry Andric   auto Err = commandLineParser(argc, argv);
12740b57cec5SDimitry Andric   if (Err)
12750b57cec5SDimitry Andric     return Err;
12760b57cec5SDimitry Andric 
12770b57cec5SDimitry Andric   ViewOpts.SkipExpansions = SkipExpansions;
12780b57cec5SDimitry Andric   ViewOpts.SkipFunctions = SkipFunctions;
1279bdd1243dSDimitry Andric   ViewOpts.SkipBranches = SkipBranches;
12800b57cec5SDimitry Andric 
12810b57cec5SDimitry Andric   if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
12820b57cec5SDimitry Andric       ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
12835f757f3fSDimitry Andric     error("coverage data can only be exported as textual JSON or an "
12840b57cec5SDimitry Andric           "lcov tracefile.");
12850b57cec5SDimitry Andric     return 1;
12860b57cec5SDimitry Andric   }
12870b57cec5SDimitry Andric 
1288fcaf7f86SDimitry Andric   sys::fs::file_status Status;
1289fcaf7f86SDimitry Andric   if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
12905f757f3fSDimitry Andric     error("could not read profile data!" + EC.message(), PGOFilename);
1291fcaf7f86SDimitry Andric     return 1;
1292fcaf7f86SDimitry Andric   }
1293fcaf7f86SDimitry Andric 
12940b57cec5SDimitry Andric   auto Coverage = load();
12950b57cec5SDimitry Andric   if (!Coverage) {
12965f757f3fSDimitry Andric     error("could not load coverage information");
12970b57cec5SDimitry Andric     return 1;
12980b57cec5SDimitry Andric   }
12990b57cec5SDimitry Andric 
13000b57cec5SDimitry Andric   std::unique_ptr<CoverageExporter> Exporter;
13010b57cec5SDimitry Andric 
13020b57cec5SDimitry Andric   switch (ViewOpts.Format) {
13030b57cec5SDimitry Andric   case CoverageViewOptions::OutputFormat::Text:
1304bdd1243dSDimitry Andric     Exporter =
1305bdd1243dSDimitry Andric         std::make_unique<CoverageExporterJson>(*Coverage, ViewOpts, outs());
13060b57cec5SDimitry Andric     break;
13070b57cec5SDimitry Andric   case CoverageViewOptions::OutputFormat::HTML:
13080b57cec5SDimitry Andric     // Unreachable because we should have gracefully terminated with an error
13090b57cec5SDimitry Andric     // above.
13100b57cec5SDimitry Andric     llvm_unreachable("Export in HTML is not supported!");
13110b57cec5SDimitry Andric   case CoverageViewOptions::OutputFormat::Lcov:
1312bdd1243dSDimitry Andric     Exporter =
1313bdd1243dSDimitry Andric         std::make_unique<CoverageExporterLcov>(*Coverage, ViewOpts, outs());
13140b57cec5SDimitry Andric     break;
13150b57cec5SDimitry Andric   }
13160b57cec5SDimitry Andric 
13170b57cec5SDimitry Andric   if (SourceFiles.empty())
13180b57cec5SDimitry Andric     Exporter->renderRoot(IgnoreFilenameFilters);
13190b57cec5SDimitry Andric   else
13200b57cec5SDimitry Andric     Exporter->renderRoot(SourceFiles);
13210b57cec5SDimitry Andric 
13220b57cec5SDimitry Andric   return 0;
13230b57cec5SDimitry Andric }
13240b57cec5SDimitry Andric 
13250b57cec5SDimitry Andric int showMain(int argc, const char *argv[]) {
13260b57cec5SDimitry Andric   CodeCoverageTool Tool;
13270b57cec5SDimitry Andric   return Tool.run(CodeCoverageTool::Show, argc, argv);
13280b57cec5SDimitry Andric }
13290b57cec5SDimitry Andric 
13300b57cec5SDimitry Andric int reportMain(int argc, const char *argv[]) {
13310b57cec5SDimitry Andric   CodeCoverageTool Tool;
13320b57cec5SDimitry Andric   return Tool.run(CodeCoverageTool::Report, argc, argv);
13330b57cec5SDimitry Andric }
13340b57cec5SDimitry Andric 
13350b57cec5SDimitry Andric int exportMain(int argc, const char *argv[]) {
13360b57cec5SDimitry Andric   CodeCoverageTool Tool;
13370b57cec5SDimitry Andric   return Tool.run(CodeCoverageTool::Export, argc, argv);
13380b57cec5SDimitry Andric }
1339