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