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