xref: /llvm-project/llvm/tools/llvm-cov/CodeCoverage.cpp (revision 2ad6d48b0c631d7fa8416c07f8b47f167cfd2fd8)
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 <functional>
32 #include <system_error>
33 
34 using namespace llvm;
35 using namespace coverage;
36 
37 namespace {
38 /// \brief The implementation of the coverage tool.
39 class CodeCoverageTool {
40 public:
41   enum Command {
42     /// \brief The show command.
43     Show,
44     /// \brief The report command.
45     Report
46   };
47 
48   /// \brief Print the error message to the error output stream.
49   void error(const Twine &Message, StringRef Whence = "");
50 
51   /// \brief Return a memory buffer for the given source file.
52   ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
53 
54   /// \brief Create source views for the expansions of the view.
55   void attachExpansionSubViews(SourceCoverageView &View,
56                                ArrayRef<ExpansionRecord> Expansions,
57                                CoverageMapping &Coverage);
58 
59   /// \brief Create the source view of a particular function.
60   std::unique_ptr<SourceCoverageView>
61   createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage);
62 
63   /// \brief Create the main source view of a particular source file.
64   std::unique_ptr<SourceCoverageView>
65   createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage);
66 
67   /// \brief Load the coverage mapping data. Return true if an error occured.
68   std::unique_ptr<CoverageMapping> load();
69 
70   int run(Command Cmd, int argc, const char **argv);
71 
72   typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
73 
74   int show(int argc, const char **argv,
75            CommandLineParserType commandLineParser);
76 
77   int report(int argc, const char **argv,
78              CommandLineParserType commandLineParser);
79 
80   std::string ObjectFilename;
81   CoverageViewOptions ViewOpts;
82   std::string PGOFilename;
83   CoverageFiltersMatchAll Filters;
84   std::vector<std::string> SourceFiles;
85   std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
86       LoadedSourceFiles;
87   bool CompareFilenamesOnly;
88   StringMap<std::string> RemappedFilenames;
89   std::string CoverageArch;
90 };
91 }
92 
93 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
94   errs() << "error: ";
95   if (!Whence.empty())
96     errs() << Whence << ": ";
97   errs() << Message << "\n";
98 }
99 
100 ErrorOr<const MemoryBuffer &>
101 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
102   // If we've remapped filenames, look up the real location for this file.
103   if (!RemappedFilenames.empty()) {
104     auto Loc = RemappedFilenames.find(SourceFile);
105     if (Loc != RemappedFilenames.end())
106       SourceFile = Loc->second;
107   }
108   for (const auto &Files : LoadedSourceFiles)
109     if (sys::fs::equivalent(SourceFile, Files.first))
110       return *Files.second;
111   auto Buffer = MemoryBuffer::getFile(SourceFile);
112   if (auto EC = Buffer.getError()) {
113     error(EC.message(), SourceFile);
114     return EC;
115   }
116   LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get()));
117   return *LoadedSourceFiles.back().second;
118 }
119 
120 void
121 CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View,
122                                           ArrayRef<ExpansionRecord> Expansions,
123                                           CoverageMapping &Coverage) {
124   if (!ViewOpts.ShowExpandedRegions)
125     return;
126   for (const auto &Expansion : Expansions) {
127     auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
128     if (ExpansionCoverage.empty())
129       continue;
130     auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
131     if (!SourceBuffer)
132       continue;
133 
134     auto SubViewExpansions = ExpansionCoverage.getExpansions();
135     auto SubView = llvm::make_unique<SourceCoverageView>(
136         SourceBuffer.get(), ViewOpts, std::move(ExpansionCoverage));
137     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
138     View.addExpansion(Expansion.Region, std::move(SubView));
139   }
140 }
141 
142 std::unique_ptr<SourceCoverageView>
143 CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
144                                      CoverageMapping &Coverage) {
145   auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
146   if (FunctionCoverage.empty())
147     return nullptr;
148   auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
149   if (!SourceBuffer)
150     return nullptr;
151 
152   auto Expansions = FunctionCoverage.getExpansions();
153   auto View = llvm::make_unique<SourceCoverageView>(
154       SourceBuffer.get(), ViewOpts, std::move(FunctionCoverage));
155   attachExpansionSubViews(*View, Expansions, Coverage);
156 
157   return View;
158 }
159 
160 std::unique_ptr<SourceCoverageView>
161 CodeCoverageTool::createSourceFileView(StringRef SourceFile,
162                                        CoverageMapping &Coverage) {
163   auto SourceBuffer = getSourceFile(SourceFile);
164   if (!SourceBuffer)
165     return nullptr;
166   auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
167   if (FileCoverage.empty())
168     return nullptr;
169 
170   auto Expansions = FileCoverage.getExpansions();
171   auto View = llvm::make_unique<SourceCoverageView>(
172       SourceBuffer.get(), ViewOpts, std::move(FileCoverage));
173   attachExpansionSubViews(*View, Expansions, Coverage);
174 
175   for (auto Function : Coverage.getInstantiations(SourceFile)) {
176     auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
177     auto SubViewExpansions = SubViewCoverage.getExpansions();
178     auto SubView = llvm::make_unique<SourceCoverageView>(
179         SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
180     attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
181 
182     if (SubView) {
183       unsigned FileID = Function->CountedRegions.front().FileID;
184       unsigned Line = 0;
185       for (const auto &CR : Function->CountedRegions)
186         if (CR.FileID == FileID)
187           Line = std::max(CR.LineEnd, Line);
188       View->addInstantiation(Function->Name, Line, std::move(SubView));
189     }
190   }
191   return View;
192 }
193 
194 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
195   sys::fs::file_status Status;
196   if (sys::fs::status(LHS, Status))
197     return false;
198   auto LHSTime = Status.getLastModificationTime();
199   if (sys::fs::status(RHS, Status))
200     return false;
201   auto RHSTime = Status.getLastModificationTime();
202   return LHSTime > RHSTime;
203 }
204 
205 std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
206   if (modifiedTimeGT(ObjectFilename, PGOFilename))
207     errs() << "warning: profile data may be out of date - object is newer\n";
208   auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename,
209                                              CoverageArch);
210   if (Error E = CoverageOrErr.takeError()) {
211     colored_ostream(errs(), raw_ostream::RED)
212         << "error: Failed to load coverage: " << toString(std::move(E)) << "\n";
213     return nullptr;
214   }
215   auto Coverage = std::move(CoverageOrErr.get());
216   unsigned Mismatched = Coverage->getMismatchedCount();
217   if (Mismatched) {
218     colored_ostream(errs(), raw_ostream::RED)
219         << "warning: " << Mismatched << " functions have mismatched data. ";
220     errs() << "\n";
221   }
222 
223   if (CompareFilenamesOnly) {
224     auto CoveredFiles = Coverage.get()->getUniqueSourceFiles();
225     for (auto &SF : SourceFiles) {
226       StringRef SFBase = sys::path::filename(SF);
227       for (const auto &CF : CoveredFiles)
228         if (SFBase == sys::path::filename(CF)) {
229           RemappedFilenames[CF] = SF;
230           SF = CF;
231           break;
232         }
233     }
234   }
235 
236   return Coverage;
237 }
238 
239 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
240   cl::opt<std::string, true> ObjectFilename(
241       cl::Positional, cl::Required, cl::location(this->ObjectFilename),
242       cl::desc("Covered executable or object file."));
243 
244   cl::list<std::string> InputSourceFiles(
245       cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
246 
247   cl::opt<std::string, true> PGOFilename(
248       "instr-profile", cl::Required, cl::location(this->PGOFilename),
249       cl::desc(
250           "File with the profile data obtained after an instrumented run"));
251 
252   cl::opt<std::string> Arch(
253       "arch", cl::desc("architecture of the coverage mapping binary"));
254 
255   cl::opt<bool> DebugDump("dump", cl::Optional,
256                           cl::desc("Show internal debug dump"));
257 
258   cl::opt<bool> FilenameEquivalence(
259       "filename-equivalence", cl::Optional,
260       cl::desc("Treat source files as equivalent to paths in the coverage data "
261                "when the file names match, even if the full paths do not"));
262 
263   cl::OptionCategory FilteringCategory("Function filtering options");
264 
265   cl::list<std::string> NameFilters(
266       "name", cl::Optional,
267       cl::desc("Show code coverage only for functions with the given name"),
268       cl::ZeroOrMore, cl::cat(FilteringCategory));
269 
270   cl::list<std::string> NameRegexFilters(
271       "name-regex", cl::Optional,
272       cl::desc("Show code coverage only for functions that match the given "
273                "regular expression"),
274       cl::ZeroOrMore, cl::cat(FilteringCategory));
275 
276   cl::opt<double> RegionCoverageLtFilter(
277       "region-coverage-lt", cl::Optional,
278       cl::desc("Show code coverage only for functions with region coverage "
279                "less than the given threshold"),
280       cl::cat(FilteringCategory));
281 
282   cl::opt<double> RegionCoverageGtFilter(
283       "region-coverage-gt", cl::Optional,
284       cl::desc("Show code coverage only for functions with region coverage "
285                "greater than the given threshold"),
286       cl::cat(FilteringCategory));
287 
288   cl::opt<double> LineCoverageLtFilter(
289       "line-coverage-lt", cl::Optional,
290       cl::desc("Show code coverage only for functions with line coverage less "
291                "than the given threshold"),
292       cl::cat(FilteringCategory));
293 
294   cl::opt<double> LineCoverageGtFilter(
295       "line-coverage-gt", cl::Optional,
296       cl::desc("Show code coverage only for functions with line coverage "
297                "greater than the given threshold"),
298       cl::cat(FilteringCategory));
299 
300   cl::opt<cl::boolOrDefault> UseColor(
301       "use-color", cl::desc("Emit colored output (default=autodetect)"),
302       cl::init(cl::BOU_UNSET));
303 
304   auto commandLineParser = [&, this](int argc, const char **argv) -> int {
305     cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
306     ViewOpts.Debug = DebugDump;
307     CompareFilenamesOnly = FilenameEquivalence;
308 
309     ViewOpts.Colors = UseColor == cl::BOU_UNSET
310                           ? sys::Process::StandardOutHasColors()
311                           : UseColor == cl::BOU_TRUE;
312 
313     // Create the function filters
314     if (!NameFilters.empty() || !NameRegexFilters.empty()) {
315       auto NameFilterer = new CoverageFilters;
316       for (const auto &Name : NameFilters)
317         NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
318       for (const auto &Regex : NameRegexFilters)
319         NameFilterer->push_back(
320             llvm::make_unique<NameRegexCoverageFilter>(Regex));
321       Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
322     }
323     if (RegionCoverageLtFilter.getNumOccurrences() ||
324         RegionCoverageGtFilter.getNumOccurrences() ||
325         LineCoverageLtFilter.getNumOccurrences() ||
326         LineCoverageGtFilter.getNumOccurrences()) {
327       auto StatFilterer = new CoverageFilters;
328       if (RegionCoverageLtFilter.getNumOccurrences())
329         StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
330             RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
331       if (RegionCoverageGtFilter.getNumOccurrences())
332         StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
333             RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
334       if (LineCoverageLtFilter.getNumOccurrences())
335         StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
336             LineCoverageFilter::LessThan, LineCoverageLtFilter));
337       if (LineCoverageGtFilter.getNumOccurrences())
338         StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
339             RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
340       Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
341     }
342 
343     if (!Arch.empty() &&
344         Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
345       errs() << "error: Unknown architecture: " << Arch << "\n";
346       return 1;
347     }
348     CoverageArch = Arch;
349 
350     for (const auto &File : InputSourceFiles) {
351       SmallString<128> Path(File);
352       if (!CompareFilenamesOnly)
353         if (std::error_code EC = sys::fs::make_absolute(Path)) {
354           errs() << "error: " << File << ": " << EC.message();
355           return 1;
356         }
357       SourceFiles.push_back(Path.str());
358     }
359     return 0;
360   };
361 
362   switch (Cmd) {
363   case Show:
364     return show(argc, argv, commandLineParser);
365   case Report:
366     return report(argc, argv, commandLineParser);
367   }
368   return 0;
369 }
370 
371 int CodeCoverageTool::show(int argc, const char **argv,
372                            CommandLineParserType commandLineParser) {
373 
374   cl::OptionCategory ViewCategory("Viewing options");
375 
376   cl::opt<bool> ShowLineExecutionCounts(
377       "show-line-counts", cl::Optional,
378       cl::desc("Show the execution counts for each line"), cl::init(true),
379       cl::cat(ViewCategory));
380 
381   cl::opt<bool> ShowRegions(
382       "show-regions", cl::Optional,
383       cl::desc("Show the execution counts for each region"),
384       cl::cat(ViewCategory));
385 
386   cl::opt<bool> ShowBestLineRegionsCounts(
387       "show-line-counts-or-regions", cl::Optional,
388       cl::desc("Show the execution counts for each line, or the execution "
389                "counts for each region on lines that have multiple regions"),
390       cl::cat(ViewCategory));
391 
392   cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
393                                cl::desc("Show expanded source regions"),
394                                cl::cat(ViewCategory));
395 
396   cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
397                                    cl::desc("Show function instantiations"),
398                                    cl::cat(ViewCategory));
399 
400   auto Err = commandLineParser(argc, argv);
401   if (Err)
402     return Err;
403 
404   ViewOpts.ShowLineNumbers = true;
405   ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
406                            !ShowRegions || ShowBestLineRegionsCounts;
407   ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
408   ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
409   ViewOpts.ShowExpandedRegions = ShowExpansions;
410   ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
411 
412   auto Coverage = load();
413   if (!Coverage)
414     return 1;
415 
416   if (!Filters.empty()) {
417     // Show functions
418     for (const auto &Function : Coverage->getCoveredFunctions()) {
419       if (!Filters.matches(Function))
420         continue;
421 
422       auto mainView = createFunctionView(Function, *Coverage);
423       if (!mainView) {
424         ViewOpts.colored_ostream(outs(), raw_ostream::RED)
425             << "warning: Could not read coverage for '" << Function.Name;
426         outs() << "\n";
427         continue;
428       }
429       ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << Function.Name
430                                                           << ":";
431       outs() << "\n";
432       mainView->render(outs(), /*WholeFile=*/false);
433       outs() << "\n";
434     }
435     return 0;
436   }
437 
438   // Show files
439   bool ShowFilenames = SourceFiles.size() != 1;
440 
441   if (SourceFiles.empty())
442     // Get the source files from the function coverage mapping
443     for (StringRef Filename : Coverage->getUniqueSourceFiles())
444       SourceFiles.push_back(Filename);
445 
446   for (const auto &SourceFile : SourceFiles) {
447     auto mainView = createSourceFileView(SourceFile, *Coverage);
448     if (!mainView) {
449       ViewOpts.colored_ostream(outs(), raw_ostream::RED)
450           << "warning: The file '" << SourceFile << "' isn't covered.";
451       outs() << "\n";
452       continue;
453     }
454 
455     if (ShowFilenames) {
456       ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
457       outs() << "\n";
458     }
459     mainView->render(outs(), /*Wholefile=*/true);
460     if (SourceFiles.size() > 1)
461       outs() << "\n";
462   }
463 
464   return 0;
465 }
466 
467 int CodeCoverageTool::report(int argc, const char **argv,
468                              CommandLineParserType commandLineParser) {
469   auto Err = commandLineParser(argc, argv);
470   if (Err)
471     return Err;
472 
473   auto Coverage = load();
474   if (!Coverage)
475     return 1;
476 
477   CoverageReport Report(ViewOpts, std::move(Coverage));
478   if (SourceFiles.empty())
479     Report.renderFileReports(llvm::outs());
480   else
481     Report.renderFunctionReports(SourceFiles, llvm::outs());
482   return 0;
483 }
484 
485 int showMain(int argc, const char *argv[]) {
486   CodeCoverageTool Tool;
487   return Tool.run(CodeCoverageTool::Show, argc, argv);
488 }
489 
490 int reportMain(int argc, const char *argv[]) {
491   CodeCoverageTool Tool;
492   return Tool.run(CodeCoverageTool::Report, argc, argv);
493 }
494