1 //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // The 'CodeCoverageTool' class implements a command line tool to analyze and
10 // report coverage information using the profiling instrumentation and code
11 // coverage mapping.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "CoverageExporterJson.h"
16 #include "CoverageExporterLcov.h"
17 #include "CoverageFilters.h"
18 #include "CoverageReport.h"
19 #include "CoverageSummaryInfo.h"
20 #include "CoverageViewOptions.h"
21 #include "RenderingSupport.h"
22 #include "SourceCoverageView.h"
23 #include "llvm/ADT/SmallString.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/ADT/Triple.h"
26 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
27 #include "llvm/ProfileData/InstrProfReader.h"
28 #include "llvm/Support/CommandLine.h"
29 #include "llvm/Support/FileSystem.h"
30 #include "llvm/Support/Format.h"
31 #include "llvm/Support/MemoryBuffer.h"
32 #include "llvm/Support/Path.h"
33 #include "llvm/Support/Process.h"
34 #include "llvm/Support/Program.h"
35 #include "llvm/Support/ScopedPrinter.h"
36 #include "llvm/Support/SpecialCaseList.h"
37 #include "llvm/Support/ThreadPool.h"
38 #include "llvm/Support/Threading.h"
39 #include "llvm/Support/ToolOutputFile.h"
40 #include "llvm/Support/VirtualFileSystem.h"
41
42 #include <functional>
43 #include <map>
44 #include <system_error>
45
46 using namespace llvm;
47 using namespace coverage;
48
49 void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping,
50 const CoverageViewOptions &Options,
51 raw_ostream &OS);
52
53 namespace {
54 /// The implementation of the coverage tool.
55 class CodeCoverageTool {
56 public:
57 enum Command {
58 /// The show command.
59 Show,
60 /// The report command.
61 Report,
62 /// The export command.
63 Export
64 };
65
66 int run(Command Cmd, int argc, const char **argv);
67
68 private:
69 /// Print the error message to the error output stream.
70 void error(const Twine &Message, StringRef Whence = "");
71
72 /// Print the warning message to the error output stream.
73 void warning(const Twine &Message, StringRef Whence = "");
74
75 /// Convert \p Path into an absolute path and append it to the list
76 /// of collected paths.
77 void addCollectedPath(const std::string &Path);
78
79 /// If \p Path is a regular file, collect the path. If it's a
80 /// directory, recursively collect all of the paths within the directory.
81 void collectPaths(const std::string &Path);
82
83 /// Return a memory buffer for the given source file.
84 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
85
86 /// Create source views for the expansions of the view.
87 void attachExpansionSubViews(SourceCoverageView &View,
88 ArrayRef<ExpansionRecord> Expansions,
89 const CoverageMapping &Coverage);
90
91 /// Create source views for the branches of the view.
92 void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
93 ArrayRef<CountedRegion> Branches,
94 const MemoryBuffer &File,
95 CoverageData &CoverageInfo);
96
97 /// Create the source view of a particular function.
98 std::unique_ptr<SourceCoverageView>
99 createFunctionView(const FunctionRecord &Function,
100 const CoverageMapping &Coverage);
101
102 /// Create the main source view of a particular source file.
103 std::unique_ptr<SourceCoverageView>
104 createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
105
106 /// Load the coverage mapping data. Return nullptr if an error occurred.
107 std::unique_ptr<CoverageMapping> load();
108
109 /// Create a mapping from files in the Coverage data to local copies
110 /// (path-equivalence).
111 void remapPathNames(const CoverageMapping &Coverage);
112
113 /// Remove input source files which aren't mapped by \p Coverage.
114 void removeUnmappedInputs(const CoverageMapping &Coverage);
115
116 /// If a demangler is available, demangle all symbol names.
117 void demangleSymbols(const CoverageMapping &Coverage);
118
119 /// Write out a source file view to the filesystem.
120 void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
121 CoveragePrinter *Printer, bool ShowFilenames);
122
123 typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
124
125 int doShow(int argc, const char **argv,
126 CommandLineParserType commandLineParser);
127
128 int doReport(int argc, const char **argv,
129 CommandLineParserType commandLineParser);
130
131 int doExport(int argc, const char **argv,
132 CommandLineParserType commandLineParser);
133
134 std::vector<StringRef> ObjectFilenames;
135 CoverageViewOptions ViewOpts;
136 CoverageFiltersMatchAll Filters;
137 CoverageFilters IgnoreFilenameFilters;
138
139 /// True if InputSourceFiles are provided.
140 bool HadSourceFiles = false;
141
142 /// The path to the indexed profile.
143 std::string PGOFilename;
144
145 /// A list of input source files.
146 std::vector<std::string> SourceFiles;
147
148 /// In -path-equivalence mode, this maps the absolute paths from the coverage
149 /// mapping data to the input source files.
150 StringMap<std::string> RemappedFilenames;
151
152 /// The coverage data path to be remapped from, and the source path to be
153 /// remapped to, when using -path-equivalence.
154 Optional<std::pair<std::string, std::string>> PathRemapping;
155
156 /// The architecture the coverage mapping data targets.
157 std::vector<StringRef> CoverageArches;
158
159 /// A cache for demangled symbols.
160 DemangleCache DC;
161
162 /// A lock which guards printing to stderr.
163 std::mutex ErrsLock;
164
165 /// A container for input source file buffers.
166 std::mutex LoadedSourceFilesLock;
167 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
168 LoadedSourceFiles;
169
170 /// Whitelist from -name-whitelist to be used for filtering.
171 std::unique_ptr<SpecialCaseList> NameWhitelist;
172 };
173 }
174
getErrorString(const Twine & Message,StringRef Whence,bool Warning)175 static std::string getErrorString(const Twine &Message, StringRef Whence,
176 bool Warning) {
177 std::string Str = (Warning ? "warning" : "error");
178 Str += ": ";
179 if (!Whence.empty())
180 Str += Whence.str() + ": ";
181 Str += Message.str() + "\n";
182 return Str;
183 }
184
error(const Twine & Message,StringRef Whence)185 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
186 std::unique_lock<std::mutex> Guard{ErrsLock};
187 ViewOpts.colored_ostream(errs(), raw_ostream::RED)
188 << getErrorString(Message, Whence, false);
189 }
190
warning(const Twine & Message,StringRef Whence)191 void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
192 std::unique_lock<std::mutex> Guard{ErrsLock};
193 ViewOpts.colored_ostream(errs(), raw_ostream::RED)
194 << getErrorString(Message, Whence, true);
195 }
196
addCollectedPath(const std::string & Path)197 void CodeCoverageTool::addCollectedPath(const std::string &Path) {
198 SmallString<128> EffectivePath(Path);
199 if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
200 error(EC.message(), Path);
201 return;
202 }
203 sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true);
204 if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
205 SourceFiles.emplace_back(EffectivePath.str());
206 HadSourceFiles = !SourceFiles.empty();
207 }
208
collectPaths(const std::string & Path)209 void CodeCoverageTool::collectPaths(const std::string &Path) {
210 llvm::sys::fs::file_status Status;
211 llvm::sys::fs::status(Path, Status);
212 if (!llvm::sys::fs::exists(Status)) {
213 if (PathRemapping)
214 addCollectedPath(Path);
215 else
216 warning("Source file doesn't exist, proceeded by ignoring it.", Path);
217 return;
218 }
219
220 if (llvm::sys::fs::is_regular_file(Status)) {
221 addCollectedPath(Path);
222 return;
223 }
224
225 if (llvm::sys::fs::is_directory(Status)) {
226 std::error_code EC;
227 for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
228 F != E; F.increment(EC)) {
229
230 auto Status = F->status();
231 if (!Status) {
232 warning(Status.getError().message(), F->path());
233 continue;
234 }
235
236 if (Status->type() == llvm::sys::fs::file_type::regular_file)
237 addCollectedPath(F->path());
238 }
239 }
240 }
241
242 ErrorOr<const MemoryBuffer &>
getSourceFile(StringRef SourceFile)243 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
244 // If we've remapped filenames, look up the real location for this file.
245 std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
246 if (!RemappedFilenames.empty()) {
247 auto Loc = RemappedFilenames.find(SourceFile);
248 if (Loc != RemappedFilenames.end())
249 SourceFile = Loc->second;
250 }
251 for (const auto &Files : LoadedSourceFiles)
252 if (sys::fs::equivalent(SourceFile, Files.first))
253 return *Files.second;
254 auto Buffer = MemoryBuffer::getFile(SourceFile);
255 if (auto EC = Buffer.getError()) {
256 error(EC.message(), SourceFile);
257 return EC;
258 }
259 LoadedSourceFiles.emplace_back(std::string(SourceFile),
260 std::move(Buffer.get()));
261 return *LoadedSourceFiles.back().second;
262 }
263
attachExpansionSubViews(SourceCoverageView & View,ArrayRef<ExpansionRecord> Expansions,const CoverageMapping & Coverage)264 void CodeCoverageTool::attachExpansionSubViews(
265 SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
266 const CoverageMapping &Coverage) {
267 if (!ViewOpts.ShowExpandedRegions)
268 return;
269 for (const auto &Expansion : Expansions) {
270 auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
271 if (ExpansionCoverage.empty())
272 continue;
273 auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
274 if (!SourceBuffer)
275 continue;
276
277 auto SubViewBranches = ExpansionCoverage.getBranches();
278 auto SubViewExpansions = ExpansionCoverage.getExpansions();
279 auto SubView =
280 SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
281 ViewOpts, std::move(ExpansionCoverage));
282 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
283 attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
284 SourceBuffer.get(), ExpansionCoverage);
285 View.addExpansion(Expansion.Region, std::move(SubView));
286 }
287 }
288
attachBranchSubViews(SourceCoverageView & View,StringRef SourceName,ArrayRef<CountedRegion> Branches,const MemoryBuffer & File,CoverageData & CoverageInfo)289 void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
290 StringRef SourceName,
291 ArrayRef<CountedRegion> Branches,
292 const MemoryBuffer &File,
293 CoverageData &CoverageInfo) {
294 if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
295 return;
296
297 const auto *NextBranch = Branches.begin();
298 const auto *EndBranch = Branches.end();
299
300 // Group branches that have the same line number into the same subview.
301 while (NextBranch != EndBranch) {
302 std::vector<CountedRegion> ViewBranches;
303 unsigned CurrentLine = NextBranch->LineStart;
304
305 while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
306 ViewBranches.push_back(*NextBranch++);
307
308 if (!ViewBranches.empty()) {
309 auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
310 std::move(CoverageInfo));
311 View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
312 }
313 }
314 }
315
316 std::unique_ptr<SourceCoverageView>
createFunctionView(const FunctionRecord & Function,const CoverageMapping & Coverage)317 CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
318 const CoverageMapping &Coverage) {
319 auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
320 if (FunctionCoverage.empty())
321 return nullptr;
322 auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
323 if (!SourceBuffer)
324 return nullptr;
325
326 auto Branches = FunctionCoverage.getBranches();
327 auto Expansions = FunctionCoverage.getExpansions();
328 auto View = SourceCoverageView::create(DC.demangle(Function.Name),
329 SourceBuffer.get(), ViewOpts,
330 std::move(FunctionCoverage));
331 attachExpansionSubViews(*View, Expansions, Coverage);
332 attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
333 SourceBuffer.get(), FunctionCoverage);
334
335 return View;
336 }
337
338 std::unique_ptr<SourceCoverageView>
createSourceFileView(StringRef SourceFile,const CoverageMapping & Coverage)339 CodeCoverageTool::createSourceFileView(StringRef SourceFile,
340 const CoverageMapping &Coverage) {
341 auto SourceBuffer = getSourceFile(SourceFile);
342 if (!SourceBuffer)
343 return nullptr;
344 auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
345 if (FileCoverage.empty())
346 return nullptr;
347
348 auto Branches = FileCoverage.getBranches();
349 auto Expansions = FileCoverage.getExpansions();
350 auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
351 ViewOpts, std::move(FileCoverage));
352 attachExpansionSubViews(*View, Expansions, Coverage);
353 attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
354 FileCoverage);
355 if (!ViewOpts.ShowFunctionInstantiations)
356 return View;
357
358 for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) {
359 // Skip functions which have a single instantiation.
360 if (Group.size() < 2)
361 continue;
362
363 for (const FunctionRecord *Function : Group.getInstantiations()) {
364 std::unique_ptr<SourceCoverageView> SubView{nullptr};
365
366 StringRef Funcname = DC.demangle(Function->Name);
367
368 if (Function->ExecutionCount > 0) {
369 auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
370 auto SubViewExpansions = SubViewCoverage.getExpansions();
371 auto SubViewBranches = SubViewCoverage.getBranches();
372 SubView = SourceCoverageView::create(
373 Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
374 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
375 attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
376 SourceBuffer.get(), SubViewCoverage);
377 }
378
379 unsigned FileID = Function->CountedRegions.front().FileID;
380 unsigned Line = 0;
381 for (const auto &CR : Function->CountedRegions)
382 if (CR.FileID == FileID)
383 Line = std::max(CR.LineEnd, Line);
384 View->addInstantiation(Funcname, Line, std::move(SubView));
385 }
386 }
387 return View;
388 }
389
modifiedTimeGT(StringRef LHS,StringRef RHS)390 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
391 sys::fs::file_status Status;
392 if (sys::fs::status(LHS, Status))
393 return false;
394 auto LHSTime = Status.getLastModificationTime();
395 if (sys::fs::status(RHS, Status))
396 return false;
397 auto RHSTime = Status.getLastModificationTime();
398 return LHSTime > RHSTime;
399 }
400
load()401 std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
402 for (StringRef ObjectFilename : ObjectFilenames)
403 if (modifiedTimeGT(ObjectFilename, PGOFilename))
404 warning("profile data may be out of date - object is newer",
405 ObjectFilename);
406 auto CoverageOrErr =
407 CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches);
408 if (Error E = CoverageOrErr.takeError()) {
409 error("Failed to load coverage: " + toString(std::move(E)),
410 join(ObjectFilenames.begin(), ObjectFilenames.end(), ", "));
411 return nullptr;
412 }
413 auto Coverage = std::move(CoverageOrErr.get());
414 unsigned Mismatched = Coverage->getMismatchedCount();
415 if (Mismatched) {
416 warning(Twine(Mismatched) + " functions have mismatched data");
417
418 if (ViewOpts.Debug) {
419 for (const auto &HashMismatch : Coverage->getHashMismatches())
420 errs() << "hash-mismatch: "
421 << "No profile record found for '" << HashMismatch.first << "'"
422 << " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
423 << '\n';
424 }
425 }
426
427 remapPathNames(*Coverage);
428
429 if (!SourceFiles.empty())
430 removeUnmappedInputs(*Coverage);
431
432 demangleSymbols(*Coverage);
433
434 return Coverage;
435 }
436
remapPathNames(const CoverageMapping & Coverage)437 void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
438 if (!PathRemapping)
439 return;
440
441 // Convert remapping paths to native paths with trailing seperators.
442 auto nativeWithTrailing = [](StringRef Path) -> std::string {
443 if (Path.empty())
444 return "";
445 SmallString<128> NativePath;
446 sys::path::native(Path, NativePath);
447 sys::path::remove_dots(NativePath, true);
448 if (!sys::path::is_separator(NativePath.back()))
449 NativePath += sys::path::get_separator();
450 return NativePath.c_str();
451 };
452 std::string RemapFrom = nativeWithTrailing(PathRemapping->first);
453 std::string RemapTo = nativeWithTrailing(PathRemapping->second);
454
455 // Create a mapping from coverage data file paths to local paths.
456 for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
457 SmallString<128> NativeFilename;
458 sys::path::native(Filename, NativeFilename);
459 sys::path::remove_dots(NativeFilename, true);
460 if (NativeFilename.startswith(RemapFrom)) {
461 RemappedFilenames[Filename] =
462 RemapTo + NativeFilename.substr(RemapFrom.size()).str();
463 }
464 }
465
466 // Convert input files from local paths to coverage data file paths.
467 StringMap<std::string> InvRemappedFilenames;
468 for (const auto &RemappedFilename : RemappedFilenames)
469 InvRemappedFilenames[RemappedFilename.getValue()] =
470 std::string(RemappedFilename.getKey());
471
472 for (std::string &Filename : SourceFiles) {
473 SmallString<128> NativeFilename;
474 sys::path::native(Filename, NativeFilename);
475 auto CovFileName = InvRemappedFilenames.find(NativeFilename);
476 if (CovFileName != InvRemappedFilenames.end())
477 Filename = CovFileName->second;
478 }
479 }
480
removeUnmappedInputs(const CoverageMapping & Coverage)481 void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
482 std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
483
484 // The user may have specified source files which aren't in the coverage
485 // mapping. Filter these files away.
486 llvm::erase_if(SourceFiles, [&](const std::string &SF) {
487 return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF);
488 });
489 }
490
demangleSymbols(const CoverageMapping & Coverage)491 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
492 if (!ViewOpts.hasDemangler())
493 return;
494
495 // Pass function names to the demangler in a temporary file.
496 int InputFD;
497 SmallString<256> InputPath;
498 std::error_code EC =
499 sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
500 if (EC) {
501 error(InputPath, EC.message());
502 return;
503 }
504 ToolOutputFile InputTOF{InputPath, InputFD};
505
506 unsigned NumSymbols = 0;
507 for (const auto &Function : Coverage.getCoveredFunctions()) {
508 InputTOF.os() << Function.Name << '\n';
509 ++NumSymbols;
510 }
511 InputTOF.os().close();
512
513 // Use another temporary file to store the demangler's output.
514 int OutputFD;
515 SmallString<256> OutputPath;
516 EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
517 OutputPath);
518 if (EC) {
519 error(OutputPath, EC.message());
520 return;
521 }
522 ToolOutputFile OutputTOF{OutputPath, OutputFD};
523 OutputTOF.os().close();
524
525 // Invoke the demangler.
526 std::vector<StringRef> ArgsV;
527 for (StringRef Arg : ViewOpts.DemanglerOpts)
528 ArgsV.push_back(Arg);
529 Optional<StringRef> Redirects[] = {InputPath.str(), OutputPath.str(), {""}};
530 std::string ErrMsg;
531 int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
532 /*env=*/None, Redirects, /*secondsToWait=*/0,
533 /*memoryLimit=*/0, &ErrMsg);
534 if (RC) {
535 error(ErrMsg, ViewOpts.DemanglerOpts[0]);
536 return;
537 }
538
539 // Parse the demangler's output.
540 auto BufOrError = MemoryBuffer::getFile(OutputPath);
541 if (!BufOrError) {
542 error(OutputPath, BufOrError.getError().message());
543 return;
544 }
545
546 std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
547
548 SmallVector<StringRef, 8> Symbols;
549 StringRef DemanglerData = DemanglerBuf->getBuffer();
550 DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
551 /*KeepEmpty=*/false);
552 if (Symbols.size() != NumSymbols) {
553 error("Demangler did not provide expected number of symbols");
554 return;
555 }
556
557 // Cache the demangled names.
558 unsigned I = 0;
559 for (const auto &Function : Coverage.getCoveredFunctions())
560 // On Windows, lines in the demangler's output file end with "\r\n".
561 // Splitting by '\n' keeps '\r's, so cut them now.
562 DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
563 }
564
writeSourceFileView(StringRef SourceFile,CoverageMapping * Coverage,CoveragePrinter * Printer,bool ShowFilenames)565 void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
566 CoverageMapping *Coverage,
567 CoveragePrinter *Printer,
568 bool ShowFilenames) {
569 auto View = createSourceFileView(SourceFile, *Coverage);
570 if (!View) {
571 warning("The file '" + SourceFile + "' isn't covered.");
572 return;
573 }
574
575 auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
576 if (Error E = OSOrErr.takeError()) {
577 error("Could not create view file!", toString(std::move(E)));
578 return;
579 }
580 auto OS = std::move(OSOrErr.get());
581
582 View->print(*OS.get(), /*Wholefile=*/true,
583 /*ShowSourceName=*/ShowFilenames,
584 /*ShowTitle=*/ViewOpts.hasOutputDirectory());
585 Printer->closeViewFile(std::move(OS));
586 }
587
run(Command Cmd,int argc,const char ** argv)588 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
589 cl::opt<std::string> CovFilename(
590 cl::Positional, cl::desc("Covered executable or object file."));
591
592 cl::list<std::string> CovFilenames(
593 "object", cl::desc("Coverage executable or object file"), cl::ZeroOrMore);
594
595 cl::opt<bool> DebugDumpCollectedObjects(
596 "dump-collected-objects", cl::Optional, cl::Hidden,
597 cl::desc("Show the collected coverage object files"));
598
599 cl::list<std::string> InputSourceFiles(
600 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
601
602 cl::opt<bool> DebugDumpCollectedPaths(
603 "dump-collected-paths", cl::Optional, cl::Hidden,
604 cl::desc("Show the collected paths to source files"));
605
606 cl::opt<std::string, true> PGOFilename(
607 "instr-profile", cl::Required, cl::location(this->PGOFilename),
608 cl::desc(
609 "File with the profile data obtained after an instrumented run"));
610
611 cl::list<std::string> Arches(
612 "arch", cl::desc("architectures of the coverage mapping binaries"));
613
614 cl::opt<bool> DebugDump("dump", cl::Optional,
615 cl::desc("Show internal debug dump"));
616
617 cl::opt<CoverageViewOptions::OutputFormat> Format(
618 "format", cl::desc("Output format for line-based coverage reports"),
619 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
620 "Text output"),
621 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
622 "HTML output"),
623 clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
624 "lcov tracefile output")),
625 cl::init(CoverageViewOptions::OutputFormat::Text));
626
627 cl::opt<std::string> PathRemap(
628 "path-equivalence", cl::Optional,
629 cl::desc("<from>,<to> Map coverage data paths to local source file "
630 "paths"));
631
632 cl::OptionCategory FilteringCategory("Function filtering options");
633
634 cl::list<std::string> NameFilters(
635 "name", cl::Optional,
636 cl::desc("Show code coverage only for functions with the given name"),
637 cl::ZeroOrMore, cl::cat(FilteringCategory));
638
639 cl::list<std::string> NameFilterFiles(
640 "name-whitelist", cl::Optional,
641 cl::desc("Show code coverage only for functions listed in the given "
642 "file"),
643 cl::ZeroOrMore, cl::cat(FilteringCategory));
644
645 cl::list<std::string> NameRegexFilters(
646 "name-regex", cl::Optional,
647 cl::desc("Show code coverage only for functions that match the given "
648 "regular expression"),
649 cl::ZeroOrMore, cl::cat(FilteringCategory));
650
651 cl::list<std::string> IgnoreFilenameRegexFilters(
652 "ignore-filename-regex", cl::Optional,
653 cl::desc("Skip source code files with file paths that match the given "
654 "regular expression"),
655 cl::ZeroOrMore, cl::cat(FilteringCategory));
656
657 cl::opt<double> RegionCoverageLtFilter(
658 "region-coverage-lt", cl::Optional,
659 cl::desc("Show code coverage only for functions with region coverage "
660 "less than the given threshold"),
661 cl::cat(FilteringCategory));
662
663 cl::opt<double> RegionCoverageGtFilter(
664 "region-coverage-gt", cl::Optional,
665 cl::desc("Show code coverage only for functions with region coverage "
666 "greater than the given threshold"),
667 cl::cat(FilteringCategory));
668
669 cl::opt<double> LineCoverageLtFilter(
670 "line-coverage-lt", cl::Optional,
671 cl::desc("Show code coverage only for functions with line coverage less "
672 "than the given threshold"),
673 cl::cat(FilteringCategory));
674
675 cl::opt<double> LineCoverageGtFilter(
676 "line-coverage-gt", cl::Optional,
677 cl::desc("Show code coverage only for functions with line coverage "
678 "greater than the given threshold"),
679 cl::cat(FilteringCategory));
680
681 cl::opt<cl::boolOrDefault> UseColor(
682 "use-color", cl::desc("Emit colored output (default=autodetect)"),
683 cl::init(cl::BOU_UNSET));
684
685 cl::list<std::string> DemanglerOpts(
686 "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
687
688 cl::opt<bool> RegionSummary(
689 "show-region-summary", cl::Optional,
690 cl::desc("Show region statistics in summary table"),
691 cl::init(true));
692
693 cl::opt<bool> BranchSummary(
694 "show-branch-summary", cl::Optional,
695 cl::desc("Show branch condition statistics in summary table"),
696 cl::init(true));
697
698 cl::opt<bool> InstantiationSummary(
699 "show-instantiation-summary", cl::Optional,
700 cl::desc("Show instantiation statistics in summary table"));
701
702 cl::opt<bool> SummaryOnly(
703 "summary-only", cl::Optional,
704 cl::desc("Export only summary information for each source file"));
705
706 cl::opt<unsigned> NumThreads(
707 "num-threads", cl::init(0),
708 cl::desc("Number of merge threads to use (default: autodetect)"));
709 cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
710 cl::aliasopt(NumThreads));
711
712 auto commandLineParser = [&, this](int argc, const char **argv) -> int {
713 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
714 ViewOpts.Debug = DebugDump;
715
716 if (!CovFilename.empty())
717 ObjectFilenames.emplace_back(CovFilename);
718 for (const std::string &Filename : CovFilenames)
719 ObjectFilenames.emplace_back(Filename);
720 if (ObjectFilenames.empty()) {
721 errs() << "No filenames specified!\n";
722 ::exit(1);
723 }
724
725 if (DebugDumpCollectedObjects) {
726 for (StringRef OF : ObjectFilenames)
727 outs() << OF << '\n';
728 ::exit(0);
729 }
730
731 ViewOpts.Format = Format;
732 switch (ViewOpts.Format) {
733 case CoverageViewOptions::OutputFormat::Text:
734 ViewOpts.Colors = UseColor == cl::BOU_UNSET
735 ? sys::Process::StandardOutHasColors()
736 : UseColor == cl::BOU_TRUE;
737 break;
738 case CoverageViewOptions::OutputFormat::HTML:
739 if (UseColor == cl::BOU_FALSE)
740 errs() << "Color output cannot be disabled when generating html.\n";
741 ViewOpts.Colors = true;
742 break;
743 case CoverageViewOptions::OutputFormat::Lcov:
744 if (UseColor == cl::BOU_TRUE)
745 errs() << "Color output cannot be enabled when generating lcov.\n";
746 ViewOpts.Colors = false;
747 break;
748 }
749
750 // If path-equivalence was given and is a comma seperated pair then set
751 // PathRemapping.
752 auto EquivPair = StringRef(PathRemap).split(',');
753 if (!(EquivPair.first.empty() && EquivPair.second.empty()))
754 PathRemapping = {std::string(EquivPair.first),
755 std::string(EquivPair.second)};
756
757 // If a demangler is supplied, check if it exists and register it.
758 if (!DemanglerOpts.empty()) {
759 auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
760 if (!DemanglerPathOrErr) {
761 error("Could not find the demangler!",
762 DemanglerPathOrErr.getError().message());
763 return 1;
764 }
765 DemanglerOpts[0] = *DemanglerPathOrErr;
766 ViewOpts.DemanglerOpts.swap(DemanglerOpts);
767 }
768
769 // Read in -name-whitelist files.
770 if (!NameFilterFiles.empty()) {
771 std::string SpecialCaseListErr;
772 NameWhitelist = SpecialCaseList::create(
773 NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr);
774 if (!NameWhitelist)
775 error(SpecialCaseListErr);
776 }
777
778 // Create the function filters
779 if (!NameFilters.empty() || NameWhitelist || !NameRegexFilters.empty()) {
780 auto NameFilterer = std::make_unique<CoverageFilters>();
781 for (const auto &Name : NameFilters)
782 NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name));
783 if (NameWhitelist)
784 NameFilterer->push_back(
785 std::make_unique<NameWhitelistCoverageFilter>(*NameWhitelist));
786 for (const auto &Regex : NameRegexFilters)
787 NameFilterer->push_back(
788 std::make_unique<NameRegexCoverageFilter>(Regex));
789 Filters.push_back(std::move(NameFilterer));
790 }
791
792 if (RegionCoverageLtFilter.getNumOccurrences() ||
793 RegionCoverageGtFilter.getNumOccurrences() ||
794 LineCoverageLtFilter.getNumOccurrences() ||
795 LineCoverageGtFilter.getNumOccurrences()) {
796 auto StatFilterer = std::make_unique<CoverageFilters>();
797 if (RegionCoverageLtFilter.getNumOccurrences())
798 StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
799 RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
800 if (RegionCoverageGtFilter.getNumOccurrences())
801 StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
802 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
803 if (LineCoverageLtFilter.getNumOccurrences())
804 StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
805 LineCoverageFilter::LessThan, LineCoverageLtFilter));
806 if (LineCoverageGtFilter.getNumOccurrences())
807 StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
808 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
809 Filters.push_back(std::move(StatFilterer));
810 }
811
812 // Create the ignore filename filters.
813 for (const auto &RE : IgnoreFilenameRegexFilters)
814 IgnoreFilenameFilters.push_back(
815 std::make_unique<NameRegexCoverageFilter>(RE));
816
817 if (!Arches.empty()) {
818 for (const std::string &Arch : Arches) {
819 if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
820 error("Unknown architecture: " + Arch);
821 return 1;
822 }
823 CoverageArches.emplace_back(Arch);
824 }
825 if (CoverageArches.size() != ObjectFilenames.size()) {
826 error("Number of architectures doesn't match the number of objects");
827 return 1;
828 }
829 }
830
831 // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
832 for (const std::string &File : InputSourceFiles)
833 collectPaths(File);
834
835 if (DebugDumpCollectedPaths) {
836 for (const std::string &SF : SourceFiles)
837 outs() << SF << '\n';
838 ::exit(0);
839 }
840
841 ViewOpts.ShowBranchSummary = BranchSummary;
842 ViewOpts.ShowRegionSummary = RegionSummary;
843 ViewOpts.ShowInstantiationSummary = InstantiationSummary;
844 ViewOpts.ExportSummaryOnly = SummaryOnly;
845 ViewOpts.NumThreads = NumThreads;
846
847 return 0;
848 };
849
850 switch (Cmd) {
851 case Show:
852 return doShow(argc, argv, commandLineParser);
853 case Report:
854 return doReport(argc, argv, commandLineParser);
855 case Export:
856 return doExport(argc, argv, commandLineParser);
857 }
858 return 0;
859 }
860
doShow(int argc,const char ** argv,CommandLineParserType commandLineParser)861 int CodeCoverageTool::doShow(int argc, const char **argv,
862 CommandLineParserType commandLineParser) {
863
864 cl::OptionCategory ViewCategory("Viewing options");
865
866 cl::opt<bool> ShowLineExecutionCounts(
867 "show-line-counts", cl::Optional,
868 cl::desc("Show the execution counts for each line"), cl::init(true),
869 cl::cat(ViewCategory));
870
871 cl::opt<bool> ShowRegions(
872 "show-regions", cl::Optional,
873 cl::desc("Show the execution counts for each region"),
874 cl::cat(ViewCategory));
875
876 cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
877 "show-branches", cl::Optional,
878 cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
879 cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
880 "count", "Show True/False counts"),
881 clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
882 "percent", "Show True/False percent")),
883 cl::init(CoverageViewOptions::BranchOutputType::Off));
884
885 cl::opt<bool> ShowBestLineRegionsCounts(
886 "show-line-counts-or-regions", cl::Optional,
887 cl::desc("Show the execution counts for each line, or the execution "
888 "counts for each region on lines that have multiple regions"),
889 cl::cat(ViewCategory));
890
891 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
892 cl::desc("Show expanded source regions"),
893 cl::cat(ViewCategory));
894
895 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
896 cl::desc("Show function instantiations"),
897 cl::init(true), cl::cat(ViewCategory));
898
899 cl::opt<std::string> ShowOutputDirectory(
900 "output-dir", cl::init(""),
901 cl::desc("Directory in which coverage information is written out"));
902 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
903 cl::aliasopt(ShowOutputDirectory));
904
905 cl::opt<uint32_t> TabSize(
906 "tab-size", cl::init(2),
907 cl::desc(
908 "Set tab expansion size for html coverage reports (default = 2)"));
909
910 cl::opt<std::string> ProjectTitle(
911 "project-title", cl::Optional,
912 cl::desc("Set project title for the coverage report"));
913
914 auto Err = commandLineParser(argc, argv);
915 if (Err)
916 return Err;
917
918 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
919 error("Lcov format should be used with 'llvm-cov export'.");
920 return 1;
921 }
922
923 ViewOpts.ShowLineNumbers = true;
924 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
925 !ShowRegions || ShowBestLineRegionsCounts;
926 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
927 ViewOpts.ShowExpandedRegions = ShowExpansions;
928 ViewOpts.ShowBranchCounts =
929 ShowBranches == CoverageViewOptions::BranchOutputType::Count;
930 ViewOpts.ShowBranchPercents =
931 ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
932 ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
933 ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
934 ViewOpts.TabSize = TabSize;
935 ViewOpts.ProjectTitle = ProjectTitle;
936
937 if (ViewOpts.hasOutputDirectory()) {
938 if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
939 error("Could not create output directory!", E.message());
940 return 1;
941 }
942 }
943
944 sys::fs::file_status Status;
945 if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
946 error("Could not read profile data!", EC.message());
947 return 1;
948 }
949
950 auto ModifiedTime = Status.getLastModificationTime();
951 std::string ModifiedTimeStr = to_string(ModifiedTime);
952 size_t found = ModifiedTimeStr.rfind(':');
953 ViewOpts.CreatedTimeStr = (found != std::string::npos)
954 ? "Created: " + ModifiedTimeStr.substr(0, found)
955 : "Created: " + ModifiedTimeStr;
956
957 auto Coverage = load();
958 if (!Coverage)
959 return 1;
960
961 auto Printer = CoveragePrinter::create(ViewOpts);
962
963 if (SourceFiles.empty() && !HadSourceFiles)
964 // Get the source files from the function coverage mapping.
965 for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
966 if (!IgnoreFilenameFilters.matchesFilename(Filename))
967 SourceFiles.push_back(std::string(Filename));
968 }
969
970 // Create an index out of the source files.
971 if (ViewOpts.hasOutputDirectory()) {
972 if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) {
973 error("Could not create index file!", toString(std::move(E)));
974 return 1;
975 }
976 }
977
978 if (!Filters.empty()) {
979 // Build the map of filenames to functions.
980 std::map<llvm::StringRef, std::vector<const FunctionRecord *>>
981 FilenameFunctionMap;
982 for (const auto &SourceFile : SourceFiles)
983 for (const auto &Function : Coverage->getCoveredFunctions(SourceFile))
984 if (Filters.matches(*Coverage.get(), Function))
985 FilenameFunctionMap[SourceFile].push_back(&Function);
986
987 // Only print filter matching functions for each file.
988 for (const auto &FileFunc : FilenameFunctionMap) {
989 StringRef File = FileFunc.first;
990 const auto &Functions = FileFunc.second;
991
992 auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false);
993 if (Error E = OSOrErr.takeError()) {
994 error("Could not create view file!", toString(std::move(E)));
995 return 1;
996 }
997 auto OS = std::move(OSOrErr.get());
998
999 bool ShowTitle = ViewOpts.hasOutputDirectory();
1000 for (const auto *Function : Functions) {
1001 auto FunctionView = createFunctionView(*Function, *Coverage);
1002 if (!FunctionView) {
1003 warning("Could not read coverage for '" + Function->Name + "'.");
1004 continue;
1005 }
1006 FunctionView->print(*OS.get(), /*WholeFile=*/false,
1007 /*ShowSourceName=*/true, ShowTitle);
1008 ShowTitle = false;
1009 }
1010
1011 Printer->closeViewFile(std::move(OS));
1012 }
1013 return 0;
1014 }
1015
1016 // Show files
1017 bool ShowFilenames =
1018 (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
1019 (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
1020
1021 ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
1022 if (ViewOpts.NumThreads == 0) {
1023 // If NumThreads is not specified, create one thread for each input, up to
1024 // the number of hardware cores.
1025 S = heavyweight_hardware_concurrency(SourceFiles.size());
1026 S.Limit = true;
1027 }
1028
1029 if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
1030 for (const std::string &SourceFile : SourceFiles)
1031 writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
1032 ShowFilenames);
1033 } else {
1034 // In -output-dir mode, it's safe to use multiple threads to print files.
1035 ThreadPool Pool(S);
1036 for (const std::string &SourceFile : SourceFiles)
1037 Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
1038 Coverage.get(), Printer.get(), ShowFilenames);
1039 Pool.wait();
1040 }
1041
1042 return 0;
1043 }
1044
doReport(int argc,const char ** argv,CommandLineParserType commandLineParser)1045 int CodeCoverageTool::doReport(int argc, const char **argv,
1046 CommandLineParserType commandLineParser) {
1047 cl::opt<bool> ShowFunctionSummaries(
1048 "show-functions", cl::Optional, cl::init(false),
1049 cl::desc("Show coverage summaries for each function"));
1050
1051 auto Err = commandLineParser(argc, argv);
1052 if (Err)
1053 return Err;
1054
1055 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) {
1056 error("HTML output for summary reports is not yet supported.");
1057 return 1;
1058 } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
1059 error("Lcov format should be used with 'llvm-cov export'.");
1060 return 1;
1061 }
1062
1063 auto Coverage = load();
1064 if (!Coverage)
1065 return 1;
1066
1067 CoverageReport Report(ViewOpts, *Coverage.get());
1068 if (!ShowFunctionSummaries) {
1069 if (SourceFiles.empty())
1070 Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
1071 else
1072 Report.renderFileReports(llvm::outs(), SourceFiles);
1073 } else {
1074 if (SourceFiles.empty()) {
1075 error("Source files must be specified when -show-functions=true is "
1076 "specified");
1077 return 1;
1078 }
1079
1080 Report.renderFunctionReports(SourceFiles, DC, llvm::outs());
1081 }
1082 return 0;
1083 }
1084
doExport(int argc,const char ** argv,CommandLineParserType commandLineParser)1085 int CodeCoverageTool::doExport(int argc, const char **argv,
1086 CommandLineParserType commandLineParser) {
1087
1088 cl::OptionCategory ExportCategory("Exporting options");
1089
1090 cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional,
1091 cl::desc("Don't export expanded source regions"),
1092 cl::cat(ExportCategory));
1093
1094 cl::opt<bool> SkipFunctions("skip-functions", cl::Optional,
1095 cl::desc("Don't export per-function data"),
1096 cl::cat(ExportCategory));
1097
1098 auto Err = commandLineParser(argc, argv);
1099 if (Err)
1100 return Err;
1101
1102 ViewOpts.SkipExpansions = SkipExpansions;
1103 ViewOpts.SkipFunctions = SkipFunctions;
1104
1105 if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
1106 ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
1107 error("Coverage data can only be exported as textual JSON or an "
1108 "lcov tracefile.");
1109 return 1;
1110 }
1111
1112 auto Coverage = load();
1113 if (!Coverage) {
1114 error("Could not load coverage information");
1115 return 1;
1116 }
1117
1118 std::unique_ptr<CoverageExporter> Exporter;
1119
1120 switch (ViewOpts.Format) {
1121 case CoverageViewOptions::OutputFormat::Text:
1122 Exporter = std::make_unique<CoverageExporterJson>(*Coverage.get(),
1123 ViewOpts, outs());
1124 break;
1125 case CoverageViewOptions::OutputFormat::HTML:
1126 // Unreachable because we should have gracefully terminated with an error
1127 // above.
1128 llvm_unreachable("Export in HTML is not supported!");
1129 case CoverageViewOptions::OutputFormat::Lcov:
1130 Exporter = std::make_unique<CoverageExporterLcov>(*Coverage.get(),
1131 ViewOpts, outs());
1132 break;
1133 }
1134
1135 if (SourceFiles.empty())
1136 Exporter->renderRoot(IgnoreFilenameFilters);
1137 else
1138 Exporter->renderRoot(SourceFiles);
1139
1140 return 0;
1141 }
1142
showMain(int argc,const char * argv[])1143 int showMain(int argc, const char *argv[]) {
1144 CodeCoverageTool Tool;
1145 return Tool.run(CodeCoverageTool::Show, argc, argv);
1146 }
1147
reportMain(int argc,const char * argv[])1148 int reportMain(int argc, const char *argv[]) {
1149 CodeCoverageTool Tool;
1150 return Tool.run(CodeCoverageTool::Report, argc, argv);
1151 }
1152
exportMain(int argc,const char * argv[])1153 int exportMain(int argc, const char *argv[]) {
1154 CodeCoverageTool Tool;
1155 return Tool.run(CodeCoverageTool::Export, argc, argv);
1156 }
1157