blob: 82b1175a378505dbd5ed6512da37be3356c287e5 [file] [log] [blame]
Alex Lorenze82d89c2014-08-22 22:56:03 +00001//===- 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
Alex Lorenze82d89c2014-08-22 22:56:03 +000016#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"
Justin Bogner1ae21d12014-09-18 20:31:26 +000023#include "llvm/ADT/DenseMap.h"
Alex Lorenze82d89c2014-08-22 22:56:03 +000024#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"
Alex Lorenze82d89c2014-08-22 22:56:03 +000039#include <functional>
Justin Bognere53be062014-09-09 05:32:18 +000040#include <system_error>
Alex Lorenze82d89c2014-08-22 22:56:03 +000041
42using namespace llvm;
43using namespace coverage;
44
45namespace {
46/// \brief Distribute the functions into instantiation sets.
Justin Bogner1ae21d12014-09-18 20:31:26 +000047///
48/// An instantiation set is a collection of functions that have the same source
49/// code, ie, template functions specializations.
Alex Lorenze82d89c2014-08-22 22:56:03 +000050class FunctionInstantiationSetCollector {
Justin Bogner1ae21d12014-09-18 20:31:26 +000051 typedef DenseMap<std::pair<unsigned, unsigned>,
52 std::vector<const FunctionCoverageMapping *>> MapT;
53 MapT InstantiatedFunctions;
Alex Lorenze82d89c2014-08-22 22:56:03 +000054
55public:
56 void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
Justin Bogner1ae21d12014-09-18 20:31:26 +000057 auto I = Function.CountedRegions.begin(), E = Function.CountedRegions.end();
58 while (I != E && I->FileID != FileID)
59 ++I;
60 assert(I != E && "function does not cover the given file");
61 auto &Functions = InstantiatedFunctions[I->startLoc()];
62 Functions.push_back(&Function);
Alex Lorenze82d89c2014-08-22 22:56:03 +000063 }
64
Justin Bogner1ae21d12014-09-18 20:31:26 +000065 MapT::iterator begin() {
Alex Lorenze82d89c2014-08-22 22:56:03 +000066 return InstantiatedFunctions.begin();
67 }
68
Justin Bogner1ae21d12014-09-18 20:31:26 +000069 MapT::iterator end() {
Alex Lorenze82d89c2014-08-22 22:56:03 +000070 return InstantiatedFunctions.end();
71 }
72};
73
74/// \brief The implementation of the coverage tool.
75class CodeCoverageTool {
76public:
77 enum Command {
78 /// \brief The show command.
79 Show,
80 /// \brief The report command.
81 Report
82 };
83
84 /// \brief Print the error message to the error output stream.
85 void error(const Twine &Message, StringRef Whence = "");
86
87 /// \brief Return a memory buffer for the given source file.
88 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
89
90 /// \brief Return true if two filepaths refer to the same file.
91 bool equivalentFiles(StringRef A, StringRef B);
92
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);
98
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);
103
104 bool findMainViewFileID(const FunctionCoverageMapping &Function,
105 unsigned &MainViewFileID);
106
107 /// \brief Create a source view which shows coverage for an expansion
108 /// of a file.
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000109 void createExpansionSubView(const CountedRegion &ExpandedRegion,
Alex Lorenze82d89c2014-08-22 22:56:03 +0000110 const FunctionCoverageMapping &Function,
111 SourceCoverageView &Parent);
112
113 void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,
114 const FunctionCoverageMapping &Function);
115
116 /// \brief Create a source view which shows coverage for an instantiation
117 /// of a funciton.
118 void createInstantiationSubView(StringRef SourceFile,
119 const FunctionCoverageMapping &Function,
120 SourceCoverageView &View);
121
122 /// \brief Create the main source view of a particular source file.
123 /// Return true if this particular source file is not covered.
124 bool
125 createSourceFileView(StringRef SourceFile, SourceCoverageView &View,
126 ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
127 bool UseOnlyRegionsInMainFile = false);
128
129 /// \brief Load the coverage mapping data. Return true if an error occured.
130 bool load();
131
132 int run(Command Cmd, int argc, const char **argv);
133
134 typedef std::function<int(int, const char **)> CommandLineParserType;
135
136 int show(int argc, const char **argv,
137 CommandLineParserType commandLineParser);
138
139 int report(int argc, const char **argv,
140 CommandLineParserType commandLineParser);
141
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>>>
148 LoadedSourceFiles;
149 std::vector<FunctionCoverageMapping> FunctionMappingRecords;
150 bool CompareFilenamesOnly;
151};
152}
153
154void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
155 errs() << "error: ";
156 if (!Whence.empty())
157 errs() << Whence << ": ";
158 errs() << Message << "\n";
159}
160
161ErrorOr<const MemoryBuffer &>
162CodeCoverageTool::getSourceFile(StringRef SourceFile) {
163 SmallString<256> Path(SourceFile);
164 sys::fs::make_absolute(Path);
165 for (const auto &Files : LoadedSourceFiles) {
Justin Bogner3d7260e2014-09-11 23:20:48 +0000166 if (equivalentFiles(Path.str(), Files.first)) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000167 return *Files.second;
168 }
169 }
170 auto Buffer = MemoryBuffer::getFile(SourceFile);
171 if (auto EC = Buffer.getError()) {
172 error(EC.message(), SourceFile);
173 return EC;
174 }
175 LoadedSourceFiles.push_back(std::make_pair(
176 std::string(Path.begin(), Path.end()), std::move(Buffer.get())));
177 return *LoadedSourceFiles.back().second;
178}
179
Alex Lorenze82d89c2014-08-22 22:56:03 +0000180bool 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);
184}
185
186bool 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);
193 Interesting = true;
194 }
195 }
196 return Interesting;
197}
198
199bool
200CodeCoverageTool::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(),
205 false);
206 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
207 if (equivalentFiles(SourceFile, Function.Filenames[I]))
208 FilenameEquivalence[I] = true;
209 }
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000210 for (const auto &CR : Function.CountedRegions) {
211 if (CR.Kind == CounterMappingRegion::ExpansionRegion &&
212 FilenameEquivalence[CR.FileID])
213 IsExpandedFile[CR.ExpandedFileID] = true;
Alex Lorenze82d89c2014-08-22 22:56:03 +0000214 }
215 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
216 if (!FilenameEquivalence[I] || IsExpandedFile[I])
217 continue;
218 MainViewFileID = I;
219 return false;
220 }
221 return true;
222}
223
224bool
225CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,
226 unsigned &MainViewFileID) {
227 llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000228 for (const auto &CR : Function.CountedRegions) {
229 if (CR.Kind == CounterMappingRegion::ExpansionRegion)
230 IsExpandedFile[CR.ExpandedFileID] = true;
Alex Lorenze82d89c2014-08-22 22:56:03 +0000231 }
232 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
233 if (IsExpandedFile[I])
234 continue;
235 MainViewFileID = I;
236 return false;
237 }
238 return true;
239}
240
241void CodeCoverageTool::createExpansionSubView(
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000242 const CountedRegion &ExpandedRegion,
Alex Lorenze82d89c2014-08-22 22:56:03 +0000243 const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000244 auto SourceBuffer =
245 getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
246 if (!SourceBuffer)
247 return;
Justin Bogner5e1400a2014-09-17 05:33:20 +0000248 auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
249 Parent.getOptions());
Justin Bognerfe357c02014-09-17 18:23:47 +0000250 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000251 for (const auto &CR : Function.CountedRegions) {
252 if (CR.FileID == ExpandedRegion.ExpandedFileID)
Justin Bognerfe357c02014-09-17 18:23:47 +0000253 RegionManager->insert(CR);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000254 }
Justin Bognerfe357c02014-09-17 18:23:47 +0000255 SubView->load(std::move(RegionManager));
Alex Lorenze82d89c2014-08-22 22:56:03 +0000256 createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
Justin Bogner5e1400a2014-09-17 05:33:20 +0000257 Parent.addExpansion(ExpandedRegion, std::move(SubView));
Alex Lorenze82d89c2014-08-22 22:56:03 +0000258}
259
260void CodeCoverageTool::createExpansionSubViews(
261 SourceCoverageView &View, unsigned ViewFileID,
262 const FunctionCoverageMapping &Function) {
263 if (!ViewOpts.ShowExpandedRegions)
264 return;
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000265 for (const auto &CR : Function.CountedRegions) {
266 if (CR.Kind != CounterMappingRegion::ExpansionRegion)
Alex Lorenze82d89c2014-08-22 22:56:03 +0000267 continue;
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000268 if (CR.FileID != ViewFileID)
Alex Lorenze82d89c2014-08-22 22:56:03 +0000269 continue;
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000270 createExpansionSubView(CR, Function, View);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000271 }
272}
273
274void CodeCoverageTool::createInstantiationSubView(
275 StringRef SourceFile, const FunctionCoverageMapping &Function,
276 SourceCoverageView &View) {
Justin Bognerfe357c02014-09-17 18:23:47 +0000277 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
Alex Lorenze82d89c2014-08-22 22:56:03 +0000278 SmallSet<unsigned, 8> InterestingFileIDs;
279 if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
280 return;
281 // Get the interesting regions
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000282 for (const auto &CR : Function.CountedRegions) {
283 if (InterestingFileIDs.count(CR.FileID))
Justin Bognerfe357c02014-09-17 18:23:47 +0000284 RegionManager->insert(CR);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000285 }
Justin Bognerfe357c02014-09-17 18:23:47 +0000286 View.load(std::move(RegionManager));
Alex Lorenze82d89c2014-08-22 22:56:03 +0000287 unsigned MainFileID;
288 if (findMainViewFileID(SourceFile, Function, MainFileID))
289 return;
290 createExpansionSubViews(View, MainFileID, Function);
291}
292
293bool CodeCoverageTool::createSourceFileView(
294 StringRef SourceFile, SourceCoverageView &View,
295 ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
296 bool UseOnlyRegionsInMainFile) {
Justin Bognerfe357c02014-09-17 18:23:47 +0000297 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>();
Alex Lorenze82d89c2014-08-22 22:56:03 +0000298 FunctionInstantiationSetCollector InstantiationSetCollector;
299
300 for (const auto &Function : FunctionMappingRecords) {
301 unsigned MainFileID;
302 if (findMainViewFileID(SourceFile, Function, MainFileID))
303 continue;
304 SmallSet<unsigned, 8> InterestingFileIDs;
305 if (UseOnlyRegionsInMainFile) {
306 InterestingFileIDs.insert(MainFileID);
307 } else if (!gatherInterestingFileIDs(SourceFile, Function,
308 InterestingFileIDs))
309 continue;
310 // Get the interesting regions
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000311 for (const auto &CR : Function.CountedRegions) {
312 if (InterestingFileIDs.count(CR.FileID))
Justin Bognerfe357c02014-09-17 18:23:47 +0000313 RegionManager->insert(CR);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000314 }
315 InstantiationSetCollector.insert(Function, MainFileID);
316 createExpansionSubViews(View, MainFileID, Function);
317 }
Justin Bognerfe357c02014-09-17 18:23:47 +0000318 if (RegionManager->getCoverageSegments().empty())
Alex Lorenze82d89c2014-08-22 22:56:03 +0000319 return true;
Justin Bognerfe357c02014-09-17 18:23:47 +0000320 View.load(std::move(RegionManager));
Alex Lorenze82d89c2014-08-22 22:56:03 +0000321 // Show instantiations
322 if (!ViewOpts.ShowFunctionInstantiations)
323 return false;
324 for (const auto &InstantiationSet : InstantiationSetCollector) {
325 if (InstantiationSet.second.size() < 2)
326 continue;
Alex Lorenze82d89c2014-08-22 22:56:03 +0000327 for (auto Function : InstantiationSet.second) {
Justin Bogner5e1400a2014-09-17 05:33:20 +0000328 unsigned FileID = Function->CountedRegions.front().FileID;
329 unsigned Line = 0;
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]);
334 if (!SourceBuffer)
335 continue;
336 auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(),
337 View.getOptions());
Alex Lorenze82d89c2014-08-22 22:56:03 +0000338 createInstantiationSubView(SourceFile, *Function, *SubView);
Justin Bogner5e1400a2014-09-17 05:33:20 +0000339 View.addInstantiation(Function->Name, Line, std::move(SubView));
Alex Lorenze82d89c2014-08-22 22:56:03 +0000340 }
341 }
342 return false;
343}
344
345bool CodeCoverageTool::load() {
346 auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
347 if (auto EC = CounterMappingBuff.getError()) {
348 error(EC.message(), ObjectFilename);
349 return true;
350 }
351 ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());
352 if (auto EC = MappingReader.readHeader()) {
353 error(EC.message(), ObjectFilename);
354 return true;
355 }
356
357 std::vector<uint64_t> Counts;
358 for (const auto &I : MappingReader) {
359 FunctionCoverageMapping Function(I.FunctionName, I.Filenames);
360
361 // Create the mapping regions with evaluated execution counts
362 Counts.clear();
363 PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts);
364
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) {
Justin Bogner92bb3022014-09-15 22:23:29 +0000371 errs() << "File " << R.FileID << "| " << R.LineStart << ":"
Alex Lorenze82d89c2014-08-22 22:56:03 +0000372 << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd
373 << " = ";
374 Ctx.dump(R.Count);
375 if (R.Kind == CounterMappingRegion::ExpansionRegion) {
Justin Bogner92bb3022014-09-15 22:23:29 +0000376 errs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
Alex Lorenze82d89c2014-08-22 22:56:03 +0000377 }
Justin Bogner92bb3022014-09-15 22:23:29 +0000378 errs() << "\n";
Alex Lorenze82d89c2014-08-22 22:56:03 +0000379 }
Justin Bogner85b0a032014-09-08 21:04:00 +0000380 ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count);
381 if (ExecutionCount) {
Justin Bogner8e8aa3f2014-09-09 05:32:14 +0000382 Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount));
Justin Bogner85b0a032014-09-08 21:04:00 +0000383 } else if (!RegionError) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000384 colored_ostream(errs(), raw_ostream::RED)
385 << "error: Regions and counters don't match in a function '"
Justin Bogner84ae9252014-09-08 19:51:21 +0000386 << Function.Name << "' (re-run the instrumented binary).";
Alex Lorenze82d89c2014-08-22 22:56:03 +0000387 errs() << "\n";
388 RegionError = true;
389 }
390 }
391
392 if (RegionError || !Filters.matches(Function))
393 continue;
394
395 FunctionMappingRecords.push_back(Function);
396 }
397 return false;
398}
399
400int 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.
405
406 cl::list<std::string> InputSourceFiles(
407 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
408
409 cl::opt<std::string> PGOFilename(
410 "instr-profile", cl::Required,
411 cl::desc(
412 "File with the profile data obtained after an instrumented run"));
413
414 cl::opt<bool> DebugDump("dump", cl::Optional,
415 cl::desc("Show internal debug dump"));
416
417 cl::opt<bool> FilenameEquivalence(
418 "filename-equivalence", cl::Optional,
419 cl::desc("Compare the filenames instead of full filepaths"));
420
421 cl::OptionCategory FilteringCategory("Function filtering options");
422
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));
427
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));
433
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));
439
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));
445
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));
451
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));
457
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;
462
463 if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
464 error(EC.message(), PGOFilename);
465 return 1;
466 }
467
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));
477 }
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));
496 }
497
498 SourceFiles = InputSourceFiles;
499 return 0;
500 };
501
502 // Parse the object filename
503 if (argc > 1) {
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");
507 return 0;
508 }
509 ObjectFilename = Arg;
510
511 argv[1] = argv[0];
512 --argc;
513 ++argv;
514 } else {
515 errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";
516 return 1;
517 }
518
519 switch (Cmd) {
520 case Show:
521 return show(argc, argv, commandLineParser);
522 case Report:
523 return report(argc, argv, commandLineParser);
524 }
525 return 0;
526}
527
528int CodeCoverageTool::show(int argc, const char **argv,
529 CommandLineParserType commandLineParser) {
530
531 cl::OptionCategory ViewCategory("Viewing options");
532
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));
537
538 cl::opt<bool> ShowRegions(
539 "show-regions", cl::Optional,
540 cl::desc("Show the execution counts for each region"),
541 cl::cat(ViewCategory));
542
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));
548
549 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
550 cl::desc("Show expanded source regions"),
551 cl::cat(ViewCategory));
552
553 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
554 cl::desc("Show function instantiations"),
555 cl::cat(ViewCategory));
556
557 cl::opt<bool> NoColors("no-colors", cl::Optional,
558 cl::desc("Don't show text colors"), cl::init(false),
559 cl::cat(ViewCategory));
560
561 auto Err = commandLineParser(argc, argv);
562 if (Err)
563 return Err;
564
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;
573
574 if (load())
575 return 1;
576
577 if (!Filters.empty()) {
578 // Show functions
579 for (const auto &Function : FunctionMappingRecords) {
580 unsigned MainFileID;
581 if (findMainViewFileID(Function, MainFileID))
582 continue;
583 StringRef SourceFile = Function.Filenames[MainFileID];
Alex Lorenze82d89c2014-08-22 22:56:03 +0000584 auto SourceBuffer = getSourceFile(SourceFile);
585 if (!SourceBuffer)
586 return 1;
Justin Bogner7dad93b2014-09-15 03:41:04 +0000587 SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
Justin Bognerbc1dc572014-09-04 00:04:54 +0000588 createSourceFileView(SourceFile, mainView, Function, true);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000589 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
Justin Bogner84ae9252014-09-08 19:51:21 +0000590 << Function.Name << " from " << SourceFile << ":";
Alex Lorenze82d89c2014-08-22 22:56:03 +0000591 outs() << "\n";
Justin Bognerfe357c02014-09-17 18:23:47 +0000592 mainView.render(outs(), /*WholeFile=*/false);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000593 if (FunctionMappingRecords.size() > 1)
594 outs() << "\n";
595 }
596 return 0;
597 }
598
599 // Show files
600 bool ShowFilenames = SourceFiles.size() != 1;
601
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);
608 }
609 for (const auto &Filename : UniqueFilenames)
610 SourceFiles.push_back(Filename);
611 }
612
613 for (const auto &SourceFile : SourceFiles) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000614 auto SourceBuffer = getSourceFile(SourceFile);
615 if (!SourceBuffer)
616 return 1;
Justin Bognerbc1dc572014-09-04 00:04:54 +0000617 SourceCoverageView mainView(SourceBuffer.get(), ViewOpts);
618 if (createSourceFileView(SourceFile, mainView, FunctionMappingRecords)) {
Alex Lorenze82d89c2014-08-22 22:56:03 +0000619 ViewOpts.colored_ostream(outs(), raw_ostream::RED)
620 << "warning: The file '" << SourceFile << "' isn't covered.";
621 outs() << "\n";
622 continue;
623 }
624
625 if (ShowFilenames) {
626 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
627 outs() << "\n";
628 }
Justin Bognerfe357c02014-09-17 18:23:47 +0000629 mainView.render(outs(), /*Wholefile=*/true);
Alex Lorenze82d89c2014-08-22 22:56:03 +0000630 if (SourceFiles.size() > 1)
631 outs() << "\n";
632 }
633
634 return 0;
635}
636
637int 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));
641
642 auto Err = commandLineParser(argc, argv);
643 if (Err)
644 return Err;
645
646 ViewOpts.Colors = !NoColors;
647
648 if (load())
649 return 1;
650
651 CoverageSummary Summarizer;
652 Summarizer.createSummaries(FunctionMappingRecords);
653 CoverageReport Report(ViewOpts, Summarizer);
654 if (SourceFiles.empty() && Filters.empty()) {
655 Report.renderFileReports(llvm::outs());
656 return 0;
657 }
658
659 Report.renderFunctionReports(llvm::outs());
660 return 0;
661}
662
663int show_main(int argc, const char **argv) {
664 CodeCoverageTool Tool;
665 return Tool.run(CodeCoverageTool::Show, argc, argv);
666}
667
668int report_main(int argc, const char **argv) {
669 CodeCoverageTool Tool;
670 return Tool.run(CodeCoverageTool::Report, argc, argv);
671}