xref: /llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp (revision 5cbed6e09ec1e046cbcc58d5e684a09538596e7f)
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 "RenderingSupport.h"
17 #include "CoverageViewOptions.h"
18 #include "CoverageFilters.h"
19 #include "SourceCoverageDataManager.h"
20 #include "SourceCoverageView.h"
21 #include "CoverageSummary.h"
22 #include "CoverageReport.h"
23 #include "llvm/ADT/StringRef.h"
24 #include "llvm/ADT/SmallString.h"
25 #include "llvm/ADT/SmallSet.h"
26 #include "llvm/ADT/DenseSet.h"
27 #include "llvm/ProfileData/InstrProfReader.h"
28 #include "llvm/ProfileData/CoverageMapping.h"
29 #include "llvm/ProfileData/CoverageMappingReader.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Support/FileSystem.h"
32 #include "llvm/Support/ManagedStatic.h"
33 #include "llvm/Support/MemoryObject.h"
34 #include "llvm/Support/Format.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/Signals.h"
37 #include "llvm/Support/PrettyStackTrace.h"
38 #include <functional>
39 #include <system_error>
40 #include <unordered_map>
41 
42 using namespace llvm;
43 using namespace coverage;
44 
45 namespace {
46 /// \brief Distribute the functions into instantiation sets.
47 /// An instantiation set is a collection of functions
48 /// that have the same source code, e.g.
49 /// template functions specializations.
50 class FunctionInstantiationSetCollector {
51   ArrayRef<FunctionCoverageMapping> FunctionMappings;
52   typedef uint64_t KeyType;
53   typedef std::vector<const FunctionCoverageMapping *> SetType;
54   std::unordered_map<uint64_t, SetType> InstantiatedFunctions;
55 
56   static KeyType getKey(const CountedRegion &R) {
57     return uint64_t(R.LineStart) | uint64_t(R.ColumnStart) << 32;
58   }
59 
60 public:
61   void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
62     KeyType Key = 0;
63     for (const auto &R : Function.CountedRegions) {
64       if (R.FileID == FileID) {
65         Key = getKey(R);
66         break;
67       }
68     }
69     auto I = InstantiatedFunctions.find(Key);
70     if (I == InstantiatedFunctions.end()) {
71       SetType Set;
72       Set.push_back(&Function);
73       InstantiatedFunctions.insert(std::make_pair(Key, Set));
74     } else
75       I->second.push_back(&Function);
76   }
77 
78   std::unordered_map<KeyType, SetType>::iterator begin() {
79     return InstantiatedFunctions.begin();
80   }
81 
82   std::unordered_map<KeyType, SetType>::iterator end() {
83     return InstantiatedFunctions.end();
84   }
85 };
86 
87 /// \brief The implementation of the coverage tool.
88 class CodeCoverageTool {
89 public:
90   enum Command {
91     /// \brief The show command.
92     Show,
93     /// \brief The report command.
94     Report
95   };
96 
97   /// \brief Print the error message to the error output stream.
98   void error(const Twine &Message, StringRef Whence = "");
99 
100   /// \brief Return a memory buffer for the given source file.
101   ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
102 
103   /// \brief Return true if two filepaths refer to the same file.
104   bool equivalentFiles(StringRef A, StringRef B);
105 
106   /// \brief Collect a set of function's file ids which correspond to the
107   /// given source file. Return false if the set is empty.
108   bool gatherInterestingFileIDs(StringRef SourceFile,
109                                 const FunctionCoverageMapping &Function,
110                                 SmallSet<unsigned, 8> &InterestingFileIDs);
111 
112   /// \brief Find the file id which is not an expanded file id.
113   bool findMainViewFileID(StringRef SourceFile,
114                           const FunctionCoverageMapping &Function,
115                           unsigned &MainViewFileID);
116 
117   bool findMainViewFileID(const FunctionCoverageMapping &Function,
118                           unsigned &MainViewFileID);
119 
120   /// \brief Create a source view which shows coverage for an expansion
121   /// of a file.
122   void createExpansionSubView(const CountedRegion &ExpandedRegion,
123                               const FunctionCoverageMapping &Function,
124                               SourceCoverageView &Parent);
125 
126   void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,
127                                const FunctionCoverageMapping &Function);
128 
129   /// \brief Create a source view which shows coverage for an instantiation
130   /// of a funciton.
131   void createInstantiationSubView(StringRef SourceFile,
132                                   const FunctionCoverageMapping &Function,
133                                   SourceCoverageView &View);
134 
135   /// \brief Create the main source view of a particular source file.
136   /// Return true if this particular source file is not covered.
137   bool
138   createSourceFileView(StringRef SourceFile, SourceCoverageView &View,
139                        ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
140                        bool UseOnlyRegionsInMainFile = false);
141 
142   /// \brief Load the coverage mapping data. Return true if an error occured.
143   bool load();
144 
145   int run(Command Cmd, int argc, const char **argv);
146 
147   typedef std::function<int(int, const char **)> CommandLineParserType;
148 
149   int show(int argc, const char **argv,
150            CommandLineParserType commandLineParser);
151 
152   int report(int argc, const char **argv,
153              CommandLineParserType commandLineParser);
154 
155   StringRef ObjectFilename;
156   CoverageViewOptions ViewOpts;
157   std::unique_ptr<IndexedInstrProfReader> PGOReader;
158   CoverageFiltersMatchAll Filters;
159   std::vector<std::string> SourceFiles;
160   std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
161       LoadedSourceFiles;
162   std::vector<FunctionCoverageMapping> FunctionMappingRecords;
163   bool CompareFilenamesOnly;
164 };
165 }
166 
167 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
168   errs() << "error: ";
169   if (!Whence.empty())
170     errs() << Whence << ": ";
171   errs() << Message << "\n";
172 }
173 
174 ErrorOr<const MemoryBuffer &>
175 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
176   SmallString<256> Path(SourceFile);
177   sys::fs::make_absolute(Path);
178   for (const auto &Files : LoadedSourceFiles) {
179     if (equivalentFiles(Path.str(), Files.first)) {
180       return *Files.second;
181     }
182   }
183   auto Buffer = MemoryBuffer::getFile(SourceFile);
184   if (auto EC = Buffer.getError()) {
185     error(EC.message(), SourceFile);
186     return EC;
187   }
188   LoadedSourceFiles.push_back(std::make_pair(
189       std::string(Path.begin(), Path.end()), std::move(Buffer.get())));
190   return *LoadedSourceFiles.back().second;
191 }
192 
193 bool CodeCoverageTool::equivalentFiles(StringRef A, StringRef B) {
194   if (CompareFilenamesOnly)
195     return sys::path::filename(A).equals_lower(sys::path::filename(B));
196   return sys::fs::equivalent(A, B);
197 }
198 
199 bool CodeCoverageTool::gatherInterestingFileIDs(
200     StringRef SourceFile, const FunctionCoverageMapping &Function,
201     SmallSet<unsigned, 8> &InterestingFileIDs) {
202   bool Interesting = false;
203   for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
204     if (equivalentFiles(SourceFile, Function.Filenames[I])) {
205       InterestingFileIDs.insert(I);
206       Interesting = true;
207     }
208   }
209   return Interesting;
210 }
211 
212 bool
213 CodeCoverageTool::findMainViewFileID(StringRef SourceFile,
214                                      const FunctionCoverageMapping &Function,
215                                      unsigned &MainViewFileID) {
216   llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
217   llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(),
218                                                  false);
219   for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
220     if (equivalentFiles(SourceFile, Function.Filenames[I]))
221       FilenameEquivalence[I] = true;
222   }
223   for (const auto &CR : Function.CountedRegions) {
224     if (CR.Kind == CounterMappingRegion::ExpansionRegion &&
225         FilenameEquivalence[CR.FileID])
226       IsExpandedFile[CR.ExpandedFileID] = true;
227   }
228   for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
229     if (!FilenameEquivalence[I] || IsExpandedFile[I])
230       continue;
231     MainViewFileID = I;
232     return false;
233   }
234   return true;
235 }
236 
237 bool
238 CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,
239                                      unsigned &MainViewFileID) {
240   llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
241   for (const auto &CR : Function.CountedRegions) {
242     if (CR.Kind == CounterMappingRegion::ExpansionRegion)
243       IsExpandedFile[CR.ExpandedFileID] = true;
244   }
245   for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
246     if (IsExpandedFile[I])
247       continue;
248     MainViewFileID = I;
249     return false;
250   }
251   return true;
252 }
253 
254 void CodeCoverageTool::createExpansionSubView(
255     const CountedRegion &ExpandedRegion,
256     const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {
257   auto SourceBuffer =
258       getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
259   if (!SourceBuffer)
260     return;
261   auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
262                                                        Parent.getOptions());
263   auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
264   for (const auto &CR : Function.CountedRegions) {
265     if (CR.FileID == ExpandedRegion.ExpandedFileID)
266       RegionManager->insert(CR);
267   }
268   SubView->load(std::move(RegionManager));
269   createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
270   Parent.addExpansion(ExpandedRegion, std::move(SubView));
271 }
272 
273 void CodeCoverageTool::createExpansionSubViews(
274     SourceCoverageView &View, unsigned ViewFileID,
275     const FunctionCoverageMapping &Function) {
276   if (!ViewOpts.ShowExpandedRegions)
277     return;
278   for (const auto &CR : Function.CountedRegions) {
279     if (CR.Kind != CounterMappingRegion::ExpansionRegion)
280       continue;
281     if (CR.FileID != ViewFileID)
282       continue;
283     createExpansionSubView(CR, Function, View);
284   }
285 }
286 
287 void CodeCoverageTool::createInstantiationSubView(
288     StringRef SourceFile, const FunctionCoverageMapping &Function,
289     SourceCoverageView &View) {
290   auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
291   SmallSet<unsigned, 8> InterestingFileIDs;
292   if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
293     return;
294   // Get the interesting regions
295   for (const auto &CR : Function.CountedRegions) {
296     if (InterestingFileIDs.count(CR.FileID))
297       RegionManager->insert(CR);
298   }
299   View.load(std::move(RegionManager));
300   unsigned MainFileID;
301   if (findMainViewFileID(SourceFile, Function, MainFileID))
302     return;
303   createExpansionSubViews(View, MainFileID, Function);
304 }
305 
306 bool CodeCoverageTool::createSourceFileView(
307     StringRef SourceFile, SourceCoverageView &View,
308     ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
309     bool UseOnlyRegionsInMainFile) {
310   auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
311   FunctionInstantiationSetCollector InstantiationSetCollector;
312 
313   for (const auto &Function : FunctionMappingRecords) {
314     unsigned MainFileID;
315     if (findMainViewFileID(SourceFile, Function, MainFileID))
316       continue;
317     SmallSet<unsigned, 8> InterestingFileIDs;
318     if (UseOnlyRegionsInMainFile) {
319       InterestingFileIDs.insert(MainFileID);
320     } else if (!gatherInterestingFileIDs(SourceFile, Function,
321                                          InterestingFileIDs))
322       continue;
323     // Get the interesting regions
324     for (const auto &CR : Function.CountedRegions) {
325       if (InterestingFileIDs.count(CR.FileID))
326         RegionManager->insert(CR);
327     }
328     InstantiationSetCollector.insert(Function, MainFileID);
329     createExpansionSubViews(View, MainFileID, Function);
330   }
331   if (RegionManager->getCoverageSegments().empty())
332     return true;
333   View.load(std::move(RegionManager));
334   // Show instantiations
335   if (!ViewOpts.ShowFunctionInstantiations)
336     return false;
337   for (const auto &InstantiationSet : InstantiationSetCollector) {
338     if (InstantiationSet.second.size() < 2)
339       continue;
340     for (auto Function : InstantiationSet.second) {
341       unsigned FileID = Function->CountedRegions.front().FileID;
342       unsigned Line = 0;
343       for (const auto &CR : Function->CountedRegions)
344         if (CR.FileID == FileID)
345           Line = std::max(CR.LineEnd, Line);
346       auto SourceBuffer = getSourceFile(Function->Filenames[FileID]);
347       if (!SourceBuffer)
348         continue;
349       auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
350                                                            View.getOptions());
351       createInstantiationSubView(SourceFile, *Function, *SubView);
352       View.addInstantiation(Function->Name, Line, std::move(SubView));
353     }
354   }
355   return false;
356 }
357 
358 bool CodeCoverageTool::load() {
359   auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
360   if (auto EC = CounterMappingBuff.getError()) {
361     error(EC.message(), ObjectFilename);
362     return true;
363   }
364   ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());
365   if (auto EC = MappingReader.readHeader()) {
366     error(EC.message(), ObjectFilename);
367     return true;
368   }
369 
370   std::vector<uint64_t> Counts;
371   for (const auto &I : MappingReader) {
372     FunctionCoverageMapping Function(I.FunctionName, I.Filenames);
373 
374     // Create the mapping regions with evaluated execution counts
375     Counts.clear();
376     PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts);
377 
378     // Get the biggest referenced counters
379     bool RegionError = false;
380     CounterMappingContext Ctx(I.Expressions, Counts);
381     for (const auto &R : I.MappingRegions) {
382       // Compute the values of mapped regions
383       if (ViewOpts.Debug) {
384         errs() << "File " << R.FileID << "| " << R.LineStart << ":"
385                << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd
386                << " = ";
387         Ctx.dump(R.Count);
388         if (R.Kind == CounterMappingRegion::ExpansionRegion) {
389           errs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
390         }
391         errs() << "\n";
392       }
393       ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count);
394       if (ExecutionCount) {
395         Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount));
396       } else if (!RegionError) {
397         colored_ostream(errs(), raw_ostream::RED)
398             << "error: Regions and counters don't match in a function '"
399             << Function.Name << "' (re-run the instrumented binary).";
400         errs() << "\n";
401         RegionError = true;
402       }
403     }
404 
405     if (RegionError || !Filters.matches(Function))
406       continue;
407 
408     FunctionMappingRecords.push_back(Function);
409   }
410   return false;
411 }
412 
413 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
414   // Print a stack trace if we signal out.
415   sys::PrintStackTraceOnErrorSignal();
416   PrettyStackTraceProgram X(argc, argv);
417   llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
418 
419   cl::list<std::string> InputSourceFiles(
420       cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
421 
422   cl::opt<std::string> PGOFilename(
423       "instr-profile", cl::Required,
424       cl::desc(
425           "File with the profile data obtained after an instrumented run"));
426 
427   cl::opt<bool> DebugDump("dump", cl::Optional,
428                           cl::desc("Show internal debug dump"));
429 
430   cl::opt<bool> FilenameEquivalence(
431       "filename-equivalence", cl::Optional,
432       cl::desc("Compare the filenames instead of full filepaths"));
433 
434   cl::OptionCategory FilteringCategory("Function filtering options");
435 
436   cl::list<std::string> NameFilters(
437       "name", cl::Optional,
438       cl::desc("Show code coverage only for functions with the given name"),
439       cl::ZeroOrMore, cl::cat(FilteringCategory));
440 
441   cl::list<std::string> NameRegexFilters(
442       "name-regex", cl::Optional,
443       cl::desc("Show code coverage only for functions that match the given "
444                "regular expression"),
445       cl::ZeroOrMore, cl::cat(FilteringCategory));
446 
447   cl::opt<double> RegionCoverageLtFilter(
448       "region-coverage-lt", cl::Optional,
449       cl::desc("Show code coverage only for functions with region coverage "
450                "less than the given threshold"),
451       cl::cat(FilteringCategory));
452 
453   cl::opt<double> RegionCoverageGtFilter(
454       "region-coverage-gt", cl::Optional,
455       cl::desc("Show code coverage only for functions with region coverage "
456                "greater than the given threshold"),
457       cl::cat(FilteringCategory));
458 
459   cl::opt<double> LineCoverageLtFilter(
460       "line-coverage-lt", cl::Optional,
461       cl::desc("Show code coverage only for functions with line coverage less "
462                "than the given threshold"),
463       cl::cat(FilteringCategory));
464 
465   cl::opt<double> LineCoverageGtFilter(
466       "line-coverage-gt", cl::Optional,
467       cl::desc("Show code coverage only for functions with line coverage "
468                "greater than the given threshold"),
469       cl::cat(FilteringCategory));
470 
471   auto commandLineParser = [&, this](int argc, const char **argv) -> int {
472     cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
473     ViewOpts.Debug = DebugDump;
474     CompareFilenamesOnly = FilenameEquivalence;
475 
476     if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
477       error(EC.message(), PGOFilename);
478       return 1;
479     }
480 
481     // Create the function filters
482     if (!NameFilters.empty() || !NameRegexFilters.empty()) {
483       auto NameFilterer = new CoverageFilters;
484       for (const auto &Name : NameFilters)
485         NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
486       for (const auto &Regex : NameRegexFilters)
487         NameFilterer->push_back(
488             llvm::make_unique<NameRegexCoverageFilter>(Regex));
489       Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
490     }
491     if (RegionCoverageLtFilter.getNumOccurrences() ||
492         RegionCoverageGtFilter.getNumOccurrences() ||
493         LineCoverageLtFilter.getNumOccurrences() ||
494         LineCoverageGtFilter.getNumOccurrences()) {
495       auto StatFilterer = new CoverageFilters;
496       if (RegionCoverageLtFilter.getNumOccurrences())
497         StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
498             RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
499       if (RegionCoverageGtFilter.getNumOccurrences())
500         StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
501             RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
502       if (LineCoverageLtFilter.getNumOccurrences())
503         StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
504             LineCoverageFilter::LessThan, LineCoverageLtFilter));
505       if (LineCoverageGtFilter.getNumOccurrences())
506         StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
507             RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
508       Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
509     }
510 
511     SourceFiles = InputSourceFiles;
512     return 0;
513   };
514 
515   // Parse the object filename
516   if (argc > 1) {
517     StringRef Arg(argv[1]);
518     if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {
519       cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");
520       return 0;
521     }
522     ObjectFilename = Arg;
523 
524     argv[1] = argv[0];
525     --argc;
526     ++argv;
527   } else {
528     errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";
529     return 1;
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<bool> NoColors("no-colors", cl::Optional,
571                          cl::desc("Don't show text colors"), cl::init(false),
572                          cl::cat(ViewCategory));
573 
574   auto Err = commandLineParser(argc, argv);
575   if (Err)
576     return Err;
577 
578   ViewOpts.Colors = !NoColors;
579   ViewOpts.ShowLineNumbers = true;
580   ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
581                            !ShowRegions || ShowBestLineRegionsCounts;
582   ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
583   ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
584   ViewOpts.ShowExpandedRegions = ShowExpansions;
585   ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
586 
587   if (load())
588     return 1;
589 
590   if (!Filters.empty()) {
591     // Show functions
592     for (const auto &Function : FunctionMappingRecords) {
593       unsigned MainFileID;
594       if (findMainViewFileID(Function, MainFileID))
595         continue;
596       StringRef SourceFile = Function.Filenames[MainFileID];
597       auto SourceBuffer = getSourceFile(SourceFile);
598       if (!SourceBuffer)
599         return 1;
600       SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
601       createSourceFileView(SourceFile, mainView, Function, true);
602       ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
603           << Function.Name << " from " << SourceFile << ":";
604       outs() << "\n";
605       mainView.render(outs(), /*WholeFile=*/false);
606       if (FunctionMappingRecords.size() > 1)
607         outs() << "\n";
608     }
609     return 0;
610   }
611 
612   // Show files
613   bool ShowFilenames = SourceFiles.size() != 1;
614 
615   if (SourceFiles.empty()) {
616     // Get the source files from the function coverage mapping
617     std::set<StringRef> UniqueFilenames;
618     for (const auto &Function : FunctionMappingRecords) {
619       for (const auto &Filename : Function.Filenames)
620         UniqueFilenames.insert(Filename);
621     }
622     for (const auto &Filename : UniqueFilenames)
623       SourceFiles.push_back(Filename);
624   }
625 
626   for (const auto &SourceFile : SourceFiles) {
627     auto SourceBuffer = getSourceFile(SourceFile);
628     if (!SourceBuffer)
629       return 1;
630     SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
631     if (createSourceFileView(SourceFile, mainView, FunctionMappingRecords)) {
632       ViewOpts.colored_ostream(outs(), raw_ostream::RED)
633           << "warning: The file '" << SourceFile << "' isn't covered.";
634       outs() << "\n";
635       continue;
636     }
637 
638     if (ShowFilenames) {
639       ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
640       outs() << "\n";
641     }
642     mainView.render(outs(), /*Wholefile=*/true);
643     if (SourceFiles.size() > 1)
644       outs() << "\n";
645   }
646 
647   return 0;
648 }
649 
650 int CodeCoverageTool::report(int argc, const char **argv,
651                              CommandLineParserType commandLineParser) {
652   cl::opt<bool> NoColors("no-colors", cl::Optional,
653                          cl::desc("Don't show text colors"), cl::init(false));
654 
655   auto Err = commandLineParser(argc, argv);
656   if (Err)
657     return Err;
658 
659   ViewOpts.Colors = !NoColors;
660 
661   if (load())
662     return 1;
663 
664   CoverageSummary Summarizer;
665   Summarizer.createSummaries(FunctionMappingRecords);
666   CoverageReport Report(ViewOpts, Summarizer);
667   if (SourceFiles.empty() && Filters.empty()) {
668     Report.renderFileReports(llvm::outs());
669     return 0;
670   }
671 
672   Report.renderFunctionReports(llvm::outs());
673   return 0;
674 }
675 
676 int show_main(int argc, const char **argv) {
677   CodeCoverageTool Tool;
678   return Tool.run(CodeCoverageTool::Show, argc, argv);
679 }
680 
681 int report_main(int argc, const char **argv) {
682   CodeCoverageTool Tool;
683   return Tool.run(CodeCoverageTool::Report, argc, argv);
684 }
685