xref: /llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp (revision 6ab6b36475a4339e4a788b9f8ea40b25f6c9d033)
1 //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // The 'CodeCoverageTool' class implements a command line tool to analyze and
11 // report coverage information using the profiling instrumentation and code
12 // coverage mapping.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "CoverageFilters.h"
17 #include "CoverageReport.h"
18 #include "CoverageViewOptions.h"
19 #include "RenderingSupport.h"
20 #include "SourceCoverageView.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/Triple.h"
24 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
25 #include "llvm/ProfileData/InstrProfReader.h"
26 #include "llvm/Support/CommandLine.h"
27 #include "llvm/Support/FileSystem.h"
28 #include "llvm/Support/Format.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/Process.h"
31 #include "llvm/Support/ThreadPool.h"
32 #include <functional>
33 #include <system_error>
34 
35 using namespace llvm;
36 using namespace coverage;
37 
38 namespace {
39 /// \brief The implementation of the coverage tool.
40 class CodeCoverageTool {
41 public:
42   enum Command {
43     /// \brief The show command.
44     Show,
45     /// \brief The report command.
46     Report
47   };
48 
49   /// \brief Print the error message to the error output stream.
50   void error(const Twine &Message, StringRef Whence = "");
51 
52   /// \brief Record (but do not print) an error message in a thread-safe way.
53   void deferError(const Twine &Message, StringRef Whence = "");
54 
55   /// \brief Record (but do not print) a warning message in a thread-safe way.
56   void deferWarning(const Twine &Message, StringRef Whence = "");
57 
58   /// \brief Print (and then clear) all deferred error and warning messages.
59   void consumeDeferredMessages();
60 
61   /// \brief Append a reference to a private copy of \p Path into SourceFiles.
62   void addCollectedPath(const std::string &Path);
63 
64   /// \brief Return a memory buffer for the given source file.
65   ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
66 
67   /// \brief Create source views for the expansions of the view.
68   void attachExpansionSubViews(SourceCoverageView &View,
69                                ArrayRef<ExpansionRecord> Expansions,
70                                const CoverageMapping &Coverage);
71 
72   /// \brief Create the source view of a particular function.
73   std::unique_ptr<SourceCoverageView>
74   createFunctionView(const FunctionRecord &Function,
75                      const CoverageMapping &Coverage);
76 
77   /// \brief Create the main source view of a particular source file.
78   std::unique_ptr<SourceCoverageView>
79   createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
80 
81   /// \brief Load the coverage mapping data. Return nullptr if an error occured.
82   std::unique_ptr<CoverageMapping> load();
83 
84   int run(Command Cmd, int argc, const char **argv);
85 
86   typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
87 
88   int show(int argc, const char **argv,
89            CommandLineParserType commandLineParser);
90 
91   int report(int argc, const char **argv,
92              CommandLineParserType commandLineParser);
93 
94   std::string ObjectFilename;
95   CoverageViewOptions ViewOpts;
96   std::string PGOFilename;
97   CoverageFiltersMatchAll Filters;
98   std::vector<StringRef> SourceFiles;
99   bool CompareFilenamesOnly;
100   StringMap<std::string> RemappedFilenames;
101   std::string CoverageArch;
102 
103 private:
104   /// File paths (absolute, or otherwise) to input source files.
105   std::vector<std::string> CollectedPaths;
106 
107   /// Errors and warnings which have not been printed.
108   std::mutex DeferredMessagesLock;
109   std::vector<std::string> DeferredMessages;
110 
111   /// A container for input source file buffers.
112   std::mutex LoadedSourceFilesLock;
113   std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
114       LoadedSourceFiles;
115 };
116 }
117 
118 static std::string getErrorString(const Twine &Message, StringRef Whence,
119                                   bool Warning) {
120   std::string Str = (Warning ? "warning" : "error");
121   Str += ": ";
122   if (!Whence.empty())
123     Str += Whence.str() + ": ";
124   Str += Message.str() + "\n";
125   return Str;
126 }
127 
128 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
129   errs() << getErrorString(Message, Whence, false);
130 }
131 
132 void CodeCoverageTool::deferError(const Twine &Message, StringRef Whence) {
133   std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
134   DeferredMessages.emplace_back(getErrorString(Message, Whence, false));
135 }
136 
137 void CodeCoverageTool::deferWarning(const Twine &Message, StringRef Whence) {
138   std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
139   DeferredMessages.emplace_back(getErrorString(Message, Whence, true));
140 }
141 
142 void CodeCoverageTool::consumeDeferredMessages() {
143   std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
144   for (const std::string &Message : DeferredMessages)
145     ViewOpts.colored_ostream(errs(), raw_ostream::RED) << Message;
146   DeferredMessages.clear();
147 }
148 
149 void CodeCoverageTool::addCollectedPath(const std::string &Path) {
150   CollectedPaths.push_back(Path);
151   SourceFiles.emplace_back(CollectedPaths.back());
152 }
153 
154 ErrorOr<const MemoryBuffer &>
155 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
156   // If we've remapped filenames, look up the real location for this file.
157   std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
158   if (!RemappedFilenames.empty()) {
159     auto Loc = RemappedFilenames.find(SourceFile);
160     if (Loc != RemappedFilenames.end())
161       SourceFile = Loc->second;
162   }
163   for (const auto &Files : LoadedSourceFiles)
164     if (sys::fs::equivalent(SourceFile, Files.first))
165       return *Files.second;
166   auto Buffer = MemoryBuffer::getFile(SourceFile);
167   if (auto EC = Buffer.getError()) {
168     deferError(EC.message(), SourceFile);
169     return EC;
170   }
171   LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get()));
172   return *LoadedSourceFiles.back().second;
173 }
174 
175 void CodeCoverageTool::attachExpansionSubViews(
176     SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
177     const CoverageMapping &Coverage) {
178   if (!ViewOpts.ShowExpandedRegions)
179     return;
180   for (const auto &Expansion : Expansions) {
181     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
182     if (ExpansionCoverage.empty())
183       continue;
184     auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
185     if (!SourceBuffer)
186       continue;
187 
188     auto SubViewExpansions = ExpansionCoverage.getExpansions();
189     auto SubView =
190         SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
191                                    ViewOpts, std::move(ExpansionCoverage));
192     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
193     View.addExpansion(Expansion.Region, std::move(SubView));
194   }
195 }
196 
197 std::unique_ptr<SourceCoverageView>
198 CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
199                                      const CoverageMapping &Coverage) {
200   auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
201   if (FunctionCoverage.empty())
202     return nullptr;
203   auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
204   if (!SourceBuffer)
205     return nullptr;
206 
207   auto Expansions = FunctionCoverage.getExpansions();
208   auto View = SourceCoverageView::create(Function.Name, SourceBuffer.get(),
209                                          ViewOpts, std::move(FunctionCoverage));
210   attachExpansionSubViews(*View, Expansions, Coverage);
211 
212   return View;
213 }
214 
215 std::unique_ptr<SourceCoverageView>
216 CodeCoverageTool::createSourceFileView(StringRef SourceFile,
217                                        const CoverageMapping &Coverage) {
218   auto SourceBuffer = getSourceFile(SourceFile);
219   if (!SourceBuffer)
220     return nullptr;
221   auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
222   if (FileCoverage.empty())
223     return nullptr;
224 
225   auto Expansions = FileCoverage.getExpansions();
226   auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
227                                          ViewOpts, std::move(FileCoverage));
228   attachExpansionSubViews(*View, Expansions, Coverage);
229 
230   for (const auto *Function : Coverage.getInstantiations(SourceFile)) {
231     auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
232     auto SubViewExpansions = SubViewCoverage.getExpansions();
233     auto SubView =
234         SourceCoverageView::create(Function->Name, SourceBuffer.get(), ViewOpts,
235                                    std::move(SubViewCoverage));
236     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
237 
238     if (SubView) {
239       unsigned FileID = Function->CountedRegions.front().FileID;
240       unsigned Line = 0;
241       for (const auto &CR : Function->CountedRegions)
242         if (CR.FileID == FileID)
243           Line = std::max(CR.LineEnd, Line);
244       View->addInstantiation(Function->Name, Line, std::move(SubView));
245     }
246   }
247   return View;
248 }
249 
250 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
251   sys::fs::file_status Status;
252   if (sys::fs::status(LHS, Status))
253     return false;
254   auto LHSTime = Status.getLastModificationTime();
255   if (sys::fs::status(RHS, Status))
256     return false;
257   auto RHSTime = Status.getLastModificationTime();
258   return LHSTime > RHSTime;
259 }
260 
261 std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
262   if (modifiedTimeGT(ObjectFilename, PGOFilename))
263     errs() << "warning: profile data may be out of date - object is newer\n";
264   auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename,
265                                              CoverageArch);
266   if (Error E = CoverageOrErr.takeError()) {
267     colored_ostream(errs(), raw_ostream::RED)
268         << "error: Failed to load coverage: " << toString(std::move(E)) << "\n";
269     return nullptr;
270   }
271   auto Coverage = std::move(CoverageOrErr.get());
272   unsigned Mismatched = Coverage->getMismatchedCount();
273   if (Mismatched) {
274     colored_ostream(errs(), raw_ostream::RED)
275         << "warning: " << Mismatched << " functions have mismatched data. ";
276     errs() << "\n";
277   }
278 
279   if (CompareFilenamesOnly) {
280     auto CoveredFiles = Coverage.get()->getUniqueSourceFiles();
281     for (auto &SF : SourceFiles) {
282       StringRef SFBase = sys::path::filename(SF);
283       for (const auto &CF : CoveredFiles)
284         if (SFBase == sys::path::filename(CF)) {
285           RemappedFilenames[CF] = SF;
286           SF = CF;
287           break;
288         }
289     }
290   }
291 
292   return Coverage;
293 }
294 
295 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
296   cl::opt<std::string, true> ObjectFilename(
297       cl::Positional, cl::Required, cl::location(this->ObjectFilename),
298       cl::desc("Covered executable or object file."));
299 
300   cl::list<std::string> InputSourceFiles(
301       cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
302 
303   cl::opt<std::string, true> PGOFilename(
304       "instr-profile", cl::Required, cl::location(this->PGOFilename),
305       cl::desc(
306           "File with the profile data obtained after an instrumented run"));
307 
308   cl::opt<std::string> Arch(
309       "arch", cl::desc("architecture of the coverage mapping binary"));
310 
311   cl::opt<bool> DebugDump("dump", cl::Optional,
312                           cl::desc("Show internal debug dump"));
313 
314   cl::opt<CoverageViewOptions::OutputFormat> Format(
315       "format", cl::desc("Output format for line-based coverage reports"),
316       cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
317                             "Text output"),
318                  clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
319                             "HTML output"),
320                  clEnumValEnd),
321       cl::init(CoverageViewOptions::OutputFormat::Text));
322 
323   cl::opt<bool> FilenameEquivalence(
324       "filename-equivalence", cl::Optional,
325       cl::desc("Treat source files as equivalent to paths in the coverage data "
326                "when the file names match, even if the full paths do not"));
327 
328   cl::OptionCategory FilteringCategory("Function filtering options");
329 
330   cl::list<std::string> NameFilters(
331       "name", cl::Optional,
332       cl::desc("Show code coverage only for functions with the given name"),
333       cl::ZeroOrMore, cl::cat(FilteringCategory));
334 
335   cl::list<std::string> NameRegexFilters(
336       "name-regex", cl::Optional,
337       cl::desc("Show code coverage only for functions that match the given "
338                "regular expression"),
339       cl::ZeroOrMore, cl::cat(FilteringCategory));
340 
341   cl::opt<double> RegionCoverageLtFilter(
342       "region-coverage-lt", cl::Optional,
343       cl::desc("Show code coverage only for functions with region coverage "
344                "less than the given threshold"),
345       cl::cat(FilteringCategory));
346 
347   cl::opt<double> RegionCoverageGtFilter(
348       "region-coverage-gt", cl::Optional,
349       cl::desc("Show code coverage only for functions with region coverage "
350                "greater than the given threshold"),
351       cl::cat(FilteringCategory));
352 
353   cl::opt<double> LineCoverageLtFilter(
354       "line-coverage-lt", cl::Optional,
355       cl::desc("Show code coverage only for functions with line coverage less "
356                "than the given threshold"),
357       cl::cat(FilteringCategory));
358 
359   cl::opt<double> LineCoverageGtFilter(
360       "line-coverage-gt", cl::Optional,
361       cl::desc("Show code coverage only for functions with line coverage "
362                "greater than the given threshold"),
363       cl::cat(FilteringCategory));
364 
365   cl::opt<cl::boolOrDefault> UseColor(
366       "use-color", cl::desc("Emit colored output (default=autodetect)"),
367       cl::init(cl::BOU_UNSET));
368 
369   auto commandLineParser = [&, this](int argc, const char **argv) -> int {
370     cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
371     ViewOpts.Debug = DebugDump;
372     CompareFilenamesOnly = FilenameEquivalence;
373 
374     ViewOpts.Format = Format;
375     switch (ViewOpts.Format) {
376     case CoverageViewOptions::OutputFormat::Text:
377       ViewOpts.Colors = UseColor == cl::BOU_UNSET
378                             ? sys::Process::StandardOutHasColors()
379                             : UseColor == cl::BOU_TRUE;
380       break;
381     case CoverageViewOptions::OutputFormat::HTML:
382       if (UseColor == cl::BOU_FALSE)
383         error("Color output cannot be disabled when generating html.");
384       ViewOpts.Colors = true;
385       break;
386     }
387 
388     // Create the function filters
389     if (!NameFilters.empty() || !NameRegexFilters.empty()) {
390       auto NameFilterer = new CoverageFilters;
391       for (const auto &Name : NameFilters)
392         NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
393       for (const auto &Regex : NameRegexFilters)
394         NameFilterer->push_back(
395             llvm::make_unique<NameRegexCoverageFilter>(Regex));
396       Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
397     }
398     if (RegionCoverageLtFilter.getNumOccurrences() ||
399         RegionCoverageGtFilter.getNumOccurrences() ||
400         LineCoverageLtFilter.getNumOccurrences() ||
401         LineCoverageGtFilter.getNumOccurrences()) {
402       auto StatFilterer = new CoverageFilters;
403       if (RegionCoverageLtFilter.getNumOccurrences())
404         StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
405             RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
406       if (RegionCoverageGtFilter.getNumOccurrences())
407         StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
408             RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
409       if (LineCoverageLtFilter.getNumOccurrences())
410         StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
411             LineCoverageFilter::LessThan, LineCoverageLtFilter));
412       if (LineCoverageGtFilter.getNumOccurrences())
413         StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
414             RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
415       Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
416     }
417 
418     if (!Arch.empty() &&
419         Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
420       errs() << "error: Unknown architecture: " << Arch << "\n";
421       return 1;
422     }
423     CoverageArch = Arch;
424 
425     for (const auto &File : InputSourceFiles) {
426       SmallString<128> Path(File);
427       if (!CompareFilenamesOnly)
428         if (std::error_code EC = sys::fs::make_absolute(Path)) {
429           errs() << "error: " << File << ": " << EC.message();
430           return 1;
431         }
432       addCollectedPath(Path.str());
433     }
434     return 0;
435   };
436 
437   switch (Cmd) {
438   case Show:
439     return show(argc, argv, commandLineParser);
440   case Report:
441     return report(argc, argv, commandLineParser);
442   }
443   return 0;
444 }
445 
446 int CodeCoverageTool::show(int argc, const char **argv,
447                            CommandLineParserType commandLineParser) {
448 
449   cl::OptionCategory ViewCategory("Viewing options");
450 
451   cl::opt<bool> ShowLineExecutionCounts(
452       "show-line-counts", cl::Optional,
453       cl::desc("Show the execution counts for each line"), cl::init(true),
454       cl::cat(ViewCategory));
455 
456   cl::opt<bool> ShowRegions(
457       "show-regions", cl::Optional,
458       cl::desc("Show the execution counts for each region"),
459       cl::cat(ViewCategory));
460 
461   cl::opt<bool> ShowBestLineRegionsCounts(
462       "show-line-counts-or-regions", cl::Optional,
463       cl::desc("Show the execution counts for each line, or the execution "
464                "counts for each region on lines that have multiple regions"),
465       cl::cat(ViewCategory));
466 
467   cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
468                                cl::desc("Show expanded source regions"),
469                                cl::cat(ViewCategory));
470 
471   cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
472                                    cl::desc("Show function instantiations"),
473                                    cl::cat(ViewCategory));
474 
475   cl::opt<std::string> ShowOutputDirectory(
476       "output-dir", cl::init(""),
477       cl::desc("Directory in which coverage information is written out"));
478   cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
479                                  cl::aliasopt(ShowOutputDirectory));
480 
481   auto Err = commandLineParser(argc, argv);
482   if (Err)
483     return Err;
484 
485   ViewOpts.ShowLineNumbers = true;
486   ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
487                            !ShowRegions || ShowBestLineRegionsCounts;
488   ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
489   ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
490   ViewOpts.ShowExpandedRegions = ShowExpansions;
491   ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
492   ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
493 
494   if (ViewOpts.hasOutputDirectory()) {
495     if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
496       error("Could not create output directory!", E.message());
497       return 1;
498     }
499   }
500 
501   auto Coverage = load();
502   if (!Coverage)
503     return 1;
504 
505   auto Printer = CoveragePrinter::create(ViewOpts);
506 
507   if (!Filters.empty()) {
508     auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true);
509     if (Error E = OSOrErr.takeError()) {
510       error("Could not create view file!", toString(std::move(E)));
511       return 1;
512     }
513     auto OS = std::move(OSOrErr.get());
514 
515     // Show functions.
516     for (const auto &Function : Coverage->getCoveredFunctions()) {
517       if (!Filters.matches(Function))
518         continue;
519 
520       auto mainView = createFunctionView(Function, *Coverage);
521       if (!mainView) {
522         ViewOpts.colored_ostream(errs(), raw_ostream::RED)
523             << "warning: Could not read coverage for '" << Function.Name << "'."
524             << "\n";
525         continue;
526       }
527 
528       mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true);
529     }
530 
531     Printer->closeViewFile(std::move(OS));
532     return 0;
533   }
534 
535   // Show files
536   bool ShowFilenames = SourceFiles.size() != 1;
537 
538   if (SourceFiles.empty())
539     // Get the source files from the function coverage mapping.
540     for (StringRef Filename : Coverage->getUniqueSourceFiles())
541       SourceFiles.push_back(Filename);
542 
543   // Create an index out of the source files.
544   if (ViewOpts.hasOutputDirectory()) {
545     if (Error E = Printer->createIndexFile(SourceFiles)) {
546       error("Could not create index file!", toString(std::move(E)));
547       return 1;
548     }
549   }
550 
551   // In -output-dir mode, it's safe to use multiple threads to print files.
552   unsigned ThreadCount = 1;
553   if (ViewOpts.hasOutputDirectory())
554     ThreadCount = std::thread::hardware_concurrency();
555   ThreadPool Pool(ThreadCount);
556 
557   for (StringRef SourceFile : SourceFiles) {
558     Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames] {
559       auto View = createSourceFileView(SourceFile, *Coverage);
560       if (!View) {
561         deferWarning("The file '" + SourceFile.str() + "' isn't covered.");
562         return;
563       }
564 
565       auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
566       if (Error E = OSOrErr.takeError()) {
567         deferError("Could not create view file!", toString(std::move(E)));
568         return;
569       }
570       auto OS = std::move(OSOrErr.get());
571 
572       View->print(*OS.get(), /*Wholefile=*/true,
573                   /*ShowSourceName=*/ShowFilenames);
574       Printer->closeViewFile(std::move(OS));
575     });
576   }
577 
578   Pool.wait();
579 
580   consumeDeferredMessages();
581 
582   return 0;
583 }
584 
585 int CodeCoverageTool::report(int argc, const char **argv,
586                              CommandLineParserType commandLineParser) {
587   auto Err = commandLineParser(argc, argv);
588   if (Err)
589     return Err;
590 
591   if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML)
592     error("HTML output for summary reports is not yet supported.");
593 
594   auto Coverage = load();
595   if (!Coverage)
596     return 1;
597 
598   CoverageReport Report(ViewOpts, std::move(Coverage));
599   if (SourceFiles.empty())
600     Report.renderFileReports(llvm::outs());
601   else
602     Report.renderFunctionReports(SourceFiles, llvm::outs());
603   return 0;
604 }
605 
606 int showMain(int argc, const char *argv[]) {
607   CodeCoverageTool Tool;
608   return Tool.run(CodeCoverageTool::Show, argc, argv);
609 }
610 
611 int reportMain(int argc, const char *argv[]) {
612   CodeCoverageTool Tool;
613   return Tool.run(CodeCoverageTool::Report, argc, argv);
614 }
615