1 //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // The 'CodeCoverageTool' class implements a command line tool to analyze and
11 // report coverage information using the profiling instrumentation and code
14 //===----------------------------------------------------------------------===//
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/DenseMap.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/ADT/SmallSet.h"
27 #include "llvm/ADT/DenseSet.h"
28 #include "llvm/ProfileData/InstrProfReader.h"
29 #include "llvm/ProfileData/CoverageMapping.h"
30 #include "llvm/ProfileData/CoverageMappingReader.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/ManagedStatic.h"
34 #include "llvm/Support/MemoryObject.h"
35 #include "llvm/Support/Format.h"
36 #include "llvm/Support/Path.h"
37 #include "llvm/Support/Signals.h"
38 #include "llvm/Support/PrettyStackTrace.h"
40 #include <system_error>
43 using namespace coverage;
46 /// \brief Distribute the functions into instantiation sets.
48 /// An instantiation set is a collection of functions that have the same source
49 /// code, ie, template functions specializations.
50 class FunctionInstantiationSetCollector {
51 typedef DenseMap<std::pair<unsigned, unsigned>,
52 std::vector<const FunctionCoverageMapping *>> MapT;
53 MapT InstantiatedFunctions;
56 void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
57 auto I = Function.CountedRegions.begin(), E = Function.CountedRegions.end();
58 while (I != E && I->FileID != FileID)
60 assert(I != E && "function does not cover the given file");
61 auto &Functions = InstantiatedFunctions[I->startLoc()];
62 Functions.push_back(&Function);
65 MapT::iterator begin() {
66 return InstantiatedFunctions.begin();
69 MapT::iterator end() {
70 return InstantiatedFunctions.end();
74 /// \brief The implementation of the coverage tool.
75 class CodeCoverageTool {
78 /// \brief The show command.
80 /// \brief The report command.
84 /// \brief Print the error message to the error output stream.
85 void error(const Twine &Message, StringRef Whence = "");
87 /// \brief Return a memory buffer for the given source file.
88 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
90 /// \brief Return true if two filepaths refer to the same file.
91 bool equivalentFiles(StringRef A, StringRef B);
93 /// \brief Collect a set of function's file ids which correspond to the
94 /// given source file. Return false if the set is empty.
95 bool gatherInterestingFileIDs(StringRef SourceFile,
96 const FunctionCoverageMapping &Function,
97 SmallSet<unsigned, 8> &InterestingFileIDs);
99 /// \brief Find the file id which is not an expanded file id.
100 bool findMainViewFileID(StringRef SourceFile,
101 const FunctionCoverageMapping &Function,
102 unsigned &MainViewFileID);
104 bool findMainViewFileID(const FunctionCoverageMapping &Function,
105 unsigned &MainViewFileID);
107 /// \brief Create a source view which shows coverage for an expansion
109 void createExpansionSubView(const CountedRegion &ExpandedRegion,
110 const FunctionCoverageMapping &Function,
111 SourceCoverageView &Parent);
113 void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,
114 const FunctionCoverageMapping &Function);
116 /// \brief Create a source view which shows coverage for an instantiation
118 void createInstantiationSubView(StringRef SourceFile,
119 const FunctionCoverageMapping &Function,
120 SourceCoverageView &View);
122 /// \brief Create the main source view of a particular source file.
123 /// Return true if this particular source file is not covered.
125 createSourceFileView(StringRef SourceFile, SourceCoverageView &View,
126 ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
127 bool UseOnlyRegionsInMainFile = false);
129 /// \brief Load the coverage mapping data. Return true if an error occured.
132 int run(Command Cmd, int argc, const char **argv);
134 typedef std::function<int(int, const char **)> CommandLineParserType;
136 int show(int argc, const char **argv,
137 CommandLineParserType commandLineParser);
139 int report(int argc, const char **argv,
140 CommandLineParserType commandLineParser);
142 StringRef ObjectFilename;
143 CoverageViewOptions ViewOpts;
144 std::unique_ptr<IndexedInstrProfReader> PGOReader;
145 CoverageFiltersMatchAll Filters;
146 std::vector<std::string> SourceFiles;
147 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
149 std::vector<FunctionCoverageMapping> FunctionMappingRecords;
150 bool CompareFilenamesOnly;
154 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
157 errs() << Whence << ": ";
158 errs() << Message << "\n";
161 ErrorOr<const MemoryBuffer &>
162 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
163 SmallString<256> Path(SourceFile);
164 sys::fs::make_absolute(Path);
165 for (const auto &Files : LoadedSourceFiles) {
166 if (equivalentFiles(Path.str(), Files.first)) {
167 return *Files.second;
170 auto Buffer = MemoryBuffer::getFile(SourceFile);
171 if (auto EC = Buffer.getError()) {
172 error(EC.message(), SourceFile);
175 LoadedSourceFiles.push_back(std::make_pair(
176 std::string(Path.begin(), Path.end()), std::move(Buffer.get())));
177 return *LoadedSourceFiles.back().second;
180 bool CodeCoverageTool::equivalentFiles(StringRef A, StringRef B) {
181 if (CompareFilenamesOnly)
182 return sys::path::filename(A).equals_lower(sys::path::filename(B));
183 return sys::fs::equivalent(A, B);
186 bool CodeCoverageTool::gatherInterestingFileIDs(
187 StringRef SourceFile, const FunctionCoverageMapping &Function,
188 SmallSet<unsigned, 8> &InterestingFileIDs) {
189 bool Interesting = false;
190 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
191 if (equivalentFiles(SourceFile, Function.Filenames[I])) {
192 InterestingFileIDs.insert(I);
200 CodeCoverageTool::findMainViewFileID(StringRef SourceFile,
201 const FunctionCoverageMapping &Function,
202 unsigned &MainViewFileID) {
203 llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
204 llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(),
206 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
207 if (equivalentFiles(SourceFile, Function.Filenames[I]))
208 FilenameEquivalence[I] = true;
210 for (const auto &CR : Function.CountedRegions) {
211 if (CR.Kind == CounterMappingRegion::ExpansionRegion &&
212 FilenameEquivalence[CR.FileID])
213 IsExpandedFile[CR.ExpandedFileID] = true;
215 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
216 if (!FilenameEquivalence[I] || IsExpandedFile[I])
225 CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,
226 unsigned &MainViewFileID) {
227 llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
228 for (const auto &CR : Function.CountedRegions) {
229 if (CR.Kind == CounterMappingRegion::ExpansionRegion)
230 IsExpandedFile[CR.ExpandedFileID] = true;
232 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
233 if (IsExpandedFile[I])
241 void CodeCoverageTool::createExpansionSubView(
242 const CountedRegion &ExpandedRegion,
243 const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {
245 getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
248 auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
249 Parent.getOptions());
250 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
251 for (const auto &CR : Function.CountedRegions) {
252 if (CR.FileID == ExpandedRegion.ExpandedFileID)
253 RegionManager->insert(CR);
255 SubView->load(std::move(RegionManager));
256 createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
257 Parent.addExpansion(ExpandedRegion, std::move(SubView));
260 void CodeCoverageTool::createExpansionSubViews(
261 SourceCoverageView &View, unsigned ViewFileID,
262 const FunctionCoverageMapping &Function) {
263 if (!ViewOpts.ShowExpandedRegions)
265 for (const auto &CR : Function.CountedRegions) {
266 if (CR.Kind != CounterMappingRegion::ExpansionRegion)
268 if (CR.FileID != ViewFileID)
270 createExpansionSubView(CR, Function, View);
274 void CodeCoverageTool::createInstantiationSubView(
275 StringRef SourceFile, const FunctionCoverageMapping &Function,
276 SourceCoverageView &View) {
277 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
278 SmallSet<unsigned, 8> InterestingFileIDs;
279 if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
281 // Get the interesting regions
282 for (const auto &CR : Function.CountedRegions) {
283 if (InterestingFileIDs.count(CR.FileID))
284 RegionManager->insert(CR);
286 View.load(std::move(RegionManager));
288 if (findMainViewFileID(SourceFile, Function, MainFileID))
290 createExpansionSubViews(View, MainFileID, Function);
293 bool CodeCoverageTool::createSourceFileView(
294 StringRef SourceFile, SourceCoverageView &View,
295 ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
296 bool UseOnlyRegionsInMainFile) {
297 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
298 FunctionInstantiationSetCollector InstantiationSetCollector;
300 for (const auto &Function : FunctionMappingRecords) {
302 if (findMainViewFileID(SourceFile, Function, MainFileID))
304 SmallSet<unsigned, 8> InterestingFileIDs;
305 if (UseOnlyRegionsInMainFile) {
306 InterestingFileIDs.insert(MainFileID);
307 } else if (!gatherInterestingFileIDs(SourceFile, Function,
310 // Get the interesting regions
311 for (const auto &CR : Function.CountedRegions) {
312 if (InterestingFileIDs.count(CR.FileID))
313 RegionManager->insert(CR);
315 InstantiationSetCollector.insert(Function, MainFileID);
316 createExpansionSubViews(View, MainFileID, Function);
318 if (RegionManager->getCoverageSegments().empty())
320 View.load(std::move(RegionManager));
321 // Show instantiations
322 if (!ViewOpts.ShowFunctionInstantiations)
324 for (const auto &InstantiationSet : InstantiationSetCollector) {
325 if (InstantiationSet.second.size() < 2)
327 for (auto Function : InstantiationSet.second) {
328 unsigned FileID = Function->CountedRegions.front().FileID;
330 for (const auto &CR : Function->CountedRegions)
331 if (CR.FileID == FileID)
332 Line = std::max(CR.LineEnd, Line);
333 auto SourceBuffer = getSourceFile(Function->Filenames[FileID]);
336 auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
338 createInstantiationSubView(SourceFile, *Function, *SubView);
339 View.addInstantiation(Function->Name, Line, std::move(SubView));
345 bool CodeCoverageTool::load() {
346 auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
347 if (auto EC = CounterMappingBuff.getError()) {
348 error(EC.message(), ObjectFilename);
351 ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());
352 if (auto EC = MappingReader.readHeader()) {
353 error(EC.message(), ObjectFilename);
357 std::vector<uint64_t> Counts;
358 for (const auto &I : MappingReader) {
359 FunctionCoverageMapping Function(I.FunctionName, I.Filenames);
361 // Create the mapping regions with evaluated execution counts
363 PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts);
365 // Get the biggest referenced counters
366 bool RegionError = false;
367 CounterMappingContext Ctx(I.Expressions, Counts);
368 for (const auto &R : I.MappingRegions) {
369 // Compute the values of mapped regions
370 if (ViewOpts.Debug) {
371 errs() << "File " << R.FileID << "| " << R.LineStart << ":"
372 << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd
375 if (R.Kind == CounterMappingRegion::ExpansionRegion) {
376 errs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
380 ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count);
381 if (ExecutionCount) {
382 Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount));
383 } else if (!RegionError) {
384 colored_ostream(errs(), raw_ostream::RED)
385 << "error: Regions and counters don't match in a function '"
386 << Function.Name << "' (re-run the instrumented binary).";
392 if (RegionError || !Filters.matches(Function))
395 FunctionMappingRecords.push_back(Function);
400 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
401 // Print a stack trace if we signal out.
402 sys::PrintStackTraceOnErrorSignal();
403 PrettyStackTraceProgram X(argc, argv);
404 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
406 cl::list<std::string> InputSourceFiles(
407 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
409 cl::opt<std::string> PGOFilename(
410 "instr-profile", cl::Required,
412 "File with the profile data obtained after an instrumented run"));
414 cl::opt<bool> DebugDump("dump", cl::Optional,
415 cl::desc("Show internal debug dump"));
417 cl::opt<bool> FilenameEquivalence(
418 "filename-equivalence", cl::Optional,
419 cl::desc("Compare the filenames instead of full filepaths"));
421 cl::OptionCategory FilteringCategory("Function filtering options");
423 cl::list<std::string> NameFilters(
424 "name", cl::Optional,
425 cl::desc("Show code coverage only for functions with the given name"),
426 cl::ZeroOrMore, cl::cat(FilteringCategory));
428 cl::list<std::string> NameRegexFilters(
429 "name-regex", cl::Optional,
430 cl::desc("Show code coverage only for functions that match the given "
431 "regular expression"),
432 cl::ZeroOrMore, cl::cat(FilteringCategory));
434 cl::opt<double> RegionCoverageLtFilter(
435 "region-coverage-lt", cl::Optional,
436 cl::desc("Show code coverage only for functions with region coverage "
437 "less than the given threshold"),
438 cl::cat(FilteringCategory));
440 cl::opt<double> RegionCoverageGtFilter(
441 "region-coverage-gt", cl::Optional,
442 cl::desc("Show code coverage only for functions with region coverage "
443 "greater than the given threshold"),
444 cl::cat(FilteringCategory));
446 cl::opt<double> LineCoverageLtFilter(
447 "line-coverage-lt", cl::Optional,
448 cl::desc("Show code coverage only for functions with line coverage less "
449 "than the given threshold"),
450 cl::cat(FilteringCategory));
452 cl::opt<double> LineCoverageGtFilter(
453 "line-coverage-gt", cl::Optional,
454 cl::desc("Show code coverage only for functions with line coverage "
455 "greater than the given threshold"),
456 cl::cat(FilteringCategory));
458 auto commandLineParser = [&, this](int argc, const char **argv) -> int {
459 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
460 ViewOpts.Debug = DebugDump;
461 CompareFilenamesOnly = FilenameEquivalence;
463 if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
464 error(EC.message(), PGOFilename);
468 // Create the function filters
469 if (!NameFilters.empty() || !NameRegexFilters.empty()) {
470 auto NameFilterer = new CoverageFilters;
471 for (const auto &Name : NameFilters)
472 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
473 for (const auto &Regex : NameRegexFilters)
474 NameFilterer->push_back(
475 llvm::make_unique<NameRegexCoverageFilter>(Regex));
476 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
478 if (RegionCoverageLtFilter.getNumOccurrences() ||
479 RegionCoverageGtFilter.getNumOccurrences() ||
480 LineCoverageLtFilter.getNumOccurrences() ||
481 LineCoverageGtFilter.getNumOccurrences()) {
482 auto StatFilterer = new CoverageFilters;
483 if (RegionCoverageLtFilter.getNumOccurrences())
484 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
485 RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
486 if (RegionCoverageGtFilter.getNumOccurrences())
487 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
488 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
489 if (LineCoverageLtFilter.getNumOccurrences())
490 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
491 LineCoverageFilter::LessThan, LineCoverageLtFilter));
492 if (LineCoverageGtFilter.getNumOccurrences())
493 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
494 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
495 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
498 SourceFiles = InputSourceFiles;
502 // Parse the object filename
504 StringRef Arg(argv[1]);
505 if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {
506 cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");
509 ObjectFilename = Arg;
515 errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";
521 return show(argc, argv, commandLineParser);
523 return report(argc, argv, commandLineParser);
528 int CodeCoverageTool::show(int argc, const char **argv,
529 CommandLineParserType commandLineParser) {
531 cl::OptionCategory ViewCategory("Viewing options");
533 cl::opt<bool> ShowLineExecutionCounts(
534 "show-line-counts", cl::Optional,
535 cl::desc("Show the execution counts for each line"), cl::init(true),
536 cl::cat(ViewCategory));
538 cl::opt<bool> ShowRegions(
539 "show-regions", cl::Optional,
540 cl::desc("Show the execution counts for each region"),
541 cl::cat(ViewCategory));
543 cl::opt<bool> ShowBestLineRegionsCounts(
544 "show-line-counts-or-regions", cl::Optional,
545 cl::desc("Show the execution counts for each line, or the execution "
546 "counts for each region on lines that have multiple regions"),
547 cl::cat(ViewCategory));
549 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
550 cl::desc("Show expanded source regions"),
551 cl::cat(ViewCategory));
553 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
554 cl::desc("Show function instantiations"),
555 cl::cat(ViewCategory));
557 cl::opt<bool> NoColors("no-colors", cl::Optional,
558 cl::desc("Don't show text colors"), cl::init(false),
559 cl::cat(ViewCategory));
561 auto Err = commandLineParser(argc, argv);
565 ViewOpts.Colors = !NoColors;
566 ViewOpts.ShowLineNumbers = true;
567 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
568 !ShowRegions || ShowBestLineRegionsCounts;
569 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
570 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
571 ViewOpts.ShowExpandedRegions = ShowExpansions;
572 ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
577 if (!Filters.empty()) {
579 for (const auto &Function : FunctionMappingRecords) {
581 if (findMainViewFileID(Function, MainFileID))
583 StringRef SourceFile = Function.Filenames[MainFileID];
584 auto SourceBuffer = getSourceFile(SourceFile);
587 SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
588 createSourceFileView(SourceFile, mainView, Function, true);
589 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
590 << Function.Name << " from " << SourceFile << ":";
592 mainView.render(outs(), /*WholeFile=*/false);
593 if (FunctionMappingRecords.size() > 1)
600 bool ShowFilenames = SourceFiles.size() != 1;
602 if (SourceFiles.empty()) {
603 // Get the source files from the function coverage mapping
604 std::set<StringRef> UniqueFilenames;
605 for (const auto &Function : FunctionMappingRecords) {
606 for (const auto &Filename : Function.Filenames)
607 UniqueFilenames.insert(Filename);
609 for (const auto &Filename : UniqueFilenames)
610 SourceFiles.push_back(Filename);
613 for (const auto &SourceFile : SourceFiles) {
614 auto SourceBuffer = getSourceFile(SourceFile);
617 SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
618 if (createSourceFileView(SourceFile, mainView, FunctionMappingRecords)) {
619 ViewOpts.colored_ostream(outs(), raw_ostream::RED)
620 << "warning: The file '" << SourceFile << "' isn't covered.";
626 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
629 mainView.render(outs(), /*Wholefile=*/true);
630 if (SourceFiles.size() > 1)
637 int CodeCoverageTool::report(int argc, const char **argv,
638 CommandLineParserType commandLineParser) {
639 cl::opt<bool> NoColors("no-colors", cl::Optional,
640 cl::desc("Don't show text colors"), cl::init(false));
642 auto Err = commandLineParser(argc, argv);
646 ViewOpts.Colors = !NoColors;
651 CoverageSummary Summarizer;
652 Summarizer.createSummaries(FunctionMappingRecords);
653 CoverageReport Report(ViewOpts, Summarizer);
654 if (SourceFiles.empty() && Filters.empty()) {
655 Report.renderFileReports(llvm::outs());
659 Report.renderFunctionReports(llvm::outs());
663 int show_main(int argc, const char **argv) {
664 CodeCoverageTool Tool;
665 return Tool.run(CodeCoverageTool::Show, argc, argv);
668 int report_main(int argc, const char **argv) {
669 CodeCoverageTool Tool;
670 return Tool.run(CodeCoverageTool::Report, argc, argv);