Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / llvm / tools / llvm-cov / CodeCoverage.cpp
blobb5d763d8643cd7d84c03431be60e984da26634a2
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.
13 //===----------------------------------------------------------------------===//
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/Debuginfod/BuildIDFetcher.h"
26 #include "llvm/Debuginfod/Debuginfod.h"
27 #include "llvm/Debuginfod/HTTPClient.h"
28 #include "llvm/Object/BuildID.h"
29 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
30 #include "llvm/ProfileData/InstrProfReader.h"
31 #include "llvm/Support/CommandLine.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/Format.h"
34 #include "llvm/Support/MemoryBuffer.h"
35 #include "llvm/Support/Path.h"
36 #include "llvm/Support/Process.h"
37 #include "llvm/Support/Program.h"
38 #include "llvm/Support/ScopedPrinter.h"
39 #include "llvm/Support/SpecialCaseList.h"
40 #include "llvm/Support/ThreadPool.h"
41 #include "llvm/Support/Threading.h"
42 #include "llvm/Support/ToolOutputFile.h"
43 #include "llvm/Support/VirtualFileSystem.h"
44 #include "llvm/TargetParser/Triple.h"
46 #include <functional>
47 #include <map>
48 #include <optional>
49 #include <system_error>
51 using namespace llvm;
52 using namespace coverage;
54 void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping,
55 const CoverageViewOptions &Options,
56 raw_ostream &OS);
58 namespace {
59 /// The implementation of the coverage tool.
60 class CodeCoverageTool {
61 public:
62 enum Command {
63 /// The show command.
64 Show,
65 /// The report command.
66 Report,
67 /// The export command.
68 Export
71 int run(Command Cmd, int argc, const char **argv);
73 private:
74 /// Print the error message to the error output stream.
75 void error(const Twine &Message, StringRef Whence = "");
77 /// Print the warning message to the error output stream.
78 void warning(const Twine &Message, StringRef Whence = "");
80 /// Convert \p Path into an absolute path and append it to the list
81 /// of collected paths.
82 void addCollectedPath(const std::string &Path);
84 /// If \p Path is a regular file, collect the path. If it's a
85 /// directory, recursively collect all of the paths within the directory.
86 void collectPaths(const std::string &Path);
88 /// Check if the two given files are the same file.
89 bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2);
91 /// Retrieve a file status with a cache.
92 std::optional<sys::fs::file_status> getFileStatus(StringRef FilePath);
94 /// Return a memory buffer for the given source file.
95 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
97 /// Create source views for the expansions of the view.
98 void attachExpansionSubViews(SourceCoverageView &View,
99 ArrayRef<ExpansionRecord> Expansions,
100 const CoverageMapping &Coverage);
102 /// Create source views for the branches of the view.
103 void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName,
104 ArrayRef<CountedRegion> Branches,
105 const MemoryBuffer &File,
106 CoverageData &CoverageInfo);
108 /// Create the source view of a particular function.
109 std::unique_ptr<SourceCoverageView>
110 createFunctionView(const FunctionRecord &Function,
111 const CoverageMapping &Coverage);
113 /// Create the main source view of a particular source file.
114 std::unique_ptr<SourceCoverageView>
115 createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
117 /// Load the coverage mapping data. Return nullptr if an error occurred.
118 std::unique_ptr<CoverageMapping> load();
120 /// Create a mapping from files in the Coverage data to local copies
121 /// (path-equivalence).
122 void remapPathNames(const CoverageMapping &Coverage);
124 /// Remove input source files which aren't mapped by \p Coverage.
125 void removeUnmappedInputs(const CoverageMapping &Coverage);
127 /// If a demangler is available, demangle all symbol names.
128 void demangleSymbols(const CoverageMapping &Coverage);
130 /// Write out a source file view to the filesystem.
131 void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
132 CoveragePrinter *Printer, bool ShowFilenames);
134 typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
136 int doShow(int argc, const char **argv,
137 CommandLineParserType commandLineParser);
139 int doReport(int argc, const char **argv,
140 CommandLineParserType commandLineParser);
142 int doExport(int argc, const char **argv,
143 CommandLineParserType commandLineParser);
145 std::vector<StringRef> ObjectFilenames;
146 CoverageViewOptions ViewOpts;
147 CoverageFiltersMatchAll Filters;
148 CoverageFilters IgnoreFilenameFilters;
150 /// True if InputSourceFiles are provided.
151 bool HadSourceFiles = false;
153 /// The path to the indexed profile.
154 std::string PGOFilename;
156 /// A list of input source files.
157 std::vector<std::string> SourceFiles;
159 /// In -path-equivalence mode, this maps the absolute paths from the coverage
160 /// mapping data to the input source files.
161 StringMap<std::string> RemappedFilenames;
163 /// The coverage data path to be remapped from, and the source path to be
164 /// remapped to, when using -path-equivalence.
165 std::optional<std::vector<std::pair<std::string, std::string>>>
166 PathRemappings;
168 /// File status cache used when finding the same file.
169 StringMap<std::optional<sys::fs::file_status>> FileStatusCache;
171 /// The architecture the coverage mapping data targets.
172 std::vector<StringRef> CoverageArches;
174 /// A cache for demangled symbols.
175 DemangleCache DC;
177 /// A lock which guards printing to stderr.
178 std::mutex ErrsLock;
180 /// A container for input source file buffers.
181 std::mutex LoadedSourceFilesLock;
182 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
183 LoadedSourceFiles;
185 /// Allowlist from -name-allowlist to be used for filtering.
186 std::unique_ptr<SpecialCaseList> NameAllowlist;
188 std::unique_ptr<object::BuildIDFetcher> BIDFetcher;
190 bool CheckBinaryIDs;
194 static std::string getErrorString(const Twine &Message, StringRef Whence,
195 bool Warning) {
196 std::string Str = (Warning ? "warning" : "error");
197 Str += ": ";
198 if (!Whence.empty())
199 Str += Whence.str() + ": ";
200 Str += Message.str() + "\n";
201 return Str;
204 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
205 std::unique_lock<std::mutex> Guard{ErrsLock};
206 ViewOpts.colored_ostream(errs(), raw_ostream::RED)
207 << getErrorString(Message, Whence, false);
210 void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) {
211 std::unique_lock<std::mutex> Guard{ErrsLock};
212 ViewOpts.colored_ostream(errs(), raw_ostream::RED)
213 << getErrorString(Message, Whence, true);
216 void CodeCoverageTool::addCollectedPath(const std::string &Path) {
217 SmallString<128> EffectivePath(Path);
218 if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) {
219 error(EC.message(), Path);
220 return;
222 sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true);
223 if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
224 SourceFiles.emplace_back(EffectivePath.str());
225 HadSourceFiles = !SourceFiles.empty();
228 void CodeCoverageTool::collectPaths(const std::string &Path) {
229 llvm::sys::fs::file_status Status;
230 llvm::sys::fs::status(Path, Status);
231 if (!llvm::sys::fs::exists(Status)) {
232 if (PathRemappings)
233 addCollectedPath(Path);
234 else
235 warning("Source file doesn't exist, proceeded by ignoring it.", Path);
236 return;
239 if (llvm::sys::fs::is_regular_file(Status)) {
240 addCollectedPath(Path);
241 return;
244 if (llvm::sys::fs::is_directory(Status)) {
245 std::error_code EC;
246 for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
247 F != E; F.increment(EC)) {
249 auto Status = F->status();
250 if (!Status) {
251 warning(Status.getError().message(), F->path());
252 continue;
255 if (Status->type() == llvm::sys::fs::file_type::regular_file)
256 addCollectedPath(F->path());
261 std::optional<sys::fs::file_status>
262 CodeCoverageTool::getFileStatus(StringRef FilePath) {
263 auto It = FileStatusCache.try_emplace(FilePath);
264 auto &CachedStatus = It.first->getValue();
265 if (!It.second)
266 return CachedStatus;
268 sys::fs::file_status Status;
269 if (!sys::fs::status(FilePath, Status))
270 CachedStatus = Status;
271 return CachedStatus;
274 bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1,
275 StringRef FilePath2) {
276 auto Status1 = getFileStatus(FilePath1);
277 auto Status2 = getFileStatus(FilePath2);
278 return Status1 && Status2 && sys::fs::equivalent(*Status1, *Status2);
281 ErrorOr<const MemoryBuffer &>
282 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
283 // If we've remapped filenames, look up the real location for this file.
284 std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
285 if (!RemappedFilenames.empty()) {
286 auto Loc = RemappedFilenames.find(SourceFile);
287 if (Loc != RemappedFilenames.end())
288 SourceFile = Loc->second;
290 for (const auto &Files : LoadedSourceFiles)
291 if (isEquivalentFile(SourceFile, Files.first))
292 return *Files.second;
293 auto Buffer = MemoryBuffer::getFile(SourceFile);
294 if (auto EC = Buffer.getError()) {
295 error(EC.message(), SourceFile);
296 return EC;
298 LoadedSourceFiles.emplace_back(std::string(SourceFile),
299 std::move(Buffer.get()));
300 return *LoadedSourceFiles.back().second;
303 void CodeCoverageTool::attachExpansionSubViews(
304 SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
305 const CoverageMapping &Coverage) {
306 if (!ViewOpts.ShowExpandedRegions)
307 return;
308 for (const auto &Expansion : Expansions) {
309 auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
310 if (ExpansionCoverage.empty())
311 continue;
312 auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
313 if (!SourceBuffer)
314 continue;
316 auto SubViewBranches = ExpansionCoverage.getBranches();
317 auto SubViewExpansions = ExpansionCoverage.getExpansions();
318 auto SubView =
319 SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
320 ViewOpts, std::move(ExpansionCoverage));
321 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
322 attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches,
323 SourceBuffer.get(), ExpansionCoverage);
324 View.addExpansion(Expansion.Region, std::move(SubView));
328 void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View,
329 StringRef SourceName,
330 ArrayRef<CountedRegion> Branches,
331 const MemoryBuffer &File,
332 CoverageData &CoverageInfo) {
333 if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents)
334 return;
336 const auto *NextBranch = Branches.begin();
337 const auto *EndBranch = Branches.end();
339 // Group branches that have the same line number into the same subview.
340 while (NextBranch != EndBranch) {
341 std::vector<CountedRegion> ViewBranches;
342 unsigned CurrentLine = NextBranch->LineStart;
344 while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart)
345 ViewBranches.push_back(*NextBranch++);
347 if (!ViewBranches.empty()) {
348 auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts,
349 std::move(CoverageInfo));
350 View.addBranch(CurrentLine, ViewBranches, std::move(SubView));
355 std::unique_ptr<SourceCoverageView>
356 CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
357 const CoverageMapping &Coverage) {
358 auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
359 if (FunctionCoverage.empty())
360 return nullptr;
361 auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
362 if (!SourceBuffer)
363 return nullptr;
365 auto Branches = FunctionCoverage.getBranches();
366 auto Expansions = FunctionCoverage.getExpansions();
367 auto View = SourceCoverageView::create(DC.demangle(Function.Name),
368 SourceBuffer.get(), ViewOpts,
369 std::move(FunctionCoverage));
370 attachExpansionSubViews(*View, Expansions, Coverage);
371 attachBranchSubViews(*View, DC.demangle(Function.Name), Branches,
372 SourceBuffer.get(), FunctionCoverage);
374 return View;
377 std::unique_ptr<SourceCoverageView>
378 CodeCoverageTool::createSourceFileView(StringRef SourceFile,
379 const CoverageMapping &Coverage) {
380 auto SourceBuffer = getSourceFile(SourceFile);
381 if (!SourceBuffer)
382 return nullptr;
383 auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
384 if (FileCoverage.empty())
385 return nullptr;
387 auto Branches = FileCoverage.getBranches();
388 auto Expansions = FileCoverage.getExpansions();
389 auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
390 ViewOpts, std::move(FileCoverage));
391 attachExpansionSubViews(*View, Expansions, Coverage);
392 attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(),
393 FileCoverage);
394 if (!ViewOpts.ShowFunctionInstantiations)
395 return View;
397 for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) {
398 // Skip functions which have a single instantiation.
399 if (Group.size() < 2)
400 continue;
402 for (const FunctionRecord *Function : Group.getInstantiations()) {
403 std::unique_ptr<SourceCoverageView> SubView{nullptr};
405 StringRef Funcname = DC.demangle(Function->Name);
407 if (Function->ExecutionCount > 0) {
408 auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
409 auto SubViewExpansions = SubViewCoverage.getExpansions();
410 auto SubViewBranches = SubViewCoverage.getBranches();
411 SubView = SourceCoverageView::create(
412 Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage));
413 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
414 attachBranchSubViews(*SubView, SourceFile, SubViewBranches,
415 SourceBuffer.get(), SubViewCoverage);
418 unsigned FileID = Function->CountedRegions.front().FileID;
419 unsigned Line = 0;
420 for (const auto &CR : Function->CountedRegions)
421 if (CR.FileID == FileID)
422 Line = std::max(CR.LineEnd, Line);
423 View->addInstantiation(Funcname, Line, std::move(SubView));
426 return View;
429 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
430 sys::fs::file_status Status;
431 if (sys::fs::status(LHS, Status))
432 return false;
433 auto LHSTime = Status.getLastModificationTime();
434 if (sys::fs::status(RHS, Status))
435 return false;
436 auto RHSTime = Status.getLastModificationTime();
437 return LHSTime > RHSTime;
440 std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
441 for (StringRef ObjectFilename : ObjectFilenames)
442 if (modifiedTimeGT(ObjectFilename, PGOFilename))
443 warning("profile data may be out of date - object is newer",
444 ObjectFilename);
445 auto FS = vfs::getRealFileSystem();
446 auto CoverageOrErr = CoverageMapping::load(
447 ObjectFilenames, PGOFilename, *FS, CoverageArches,
448 ViewOpts.CompilationDirectory, BIDFetcher.get(), CheckBinaryIDs);
449 if (Error E = CoverageOrErr.takeError()) {
450 error("failed to load coverage: " + toString(std::move(E)));
451 return nullptr;
453 auto Coverage = std::move(CoverageOrErr.get());
454 unsigned Mismatched = Coverage->getMismatchedCount();
455 if (Mismatched) {
456 warning(Twine(Mismatched) + " functions have mismatched data");
458 if (ViewOpts.Debug) {
459 for (const auto &HashMismatch : Coverage->getHashMismatches())
460 errs() << "hash-mismatch: "
461 << "No profile record found for '" << HashMismatch.first << "'"
462 << " with hash = 0x" << Twine::utohexstr(HashMismatch.second)
463 << '\n';
467 remapPathNames(*Coverage);
469 if (!SourceFiles.empty())
470 removeUnmappedInputs(*Coverage);
472 demangleSymbols(*Coverage);
474 return Coverage;
477 void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) {
478 if (!PathRemappings)
479 return;
481 // Convert remapping paths to native paths with trailing seperators.
482 auto nativeWithTrailing = [](StringRef Path) -> std::string {
483 if (Path.empty())
484 return "";
485 SmallString<128> NativePath;
486 sys::path::native(Path, NativePath);
487 sys::path::remove_dots(NativePath, true);
488 if (!NativePath.empty() && !sys::path::is_separator(NativePath.back()))
489 NativePath += sys::path::get_separator();
490 return NativePath.c_str();
493 for (std::pair<std::string, std::string> &PathRemapping : *PathRemappings) {
494 std::string RemapFrom = nativeWithTrailing(PathRemapping.first);
495 std::string RemapTo = nativeWithTrailing(PathRemapping.second);
497 // Create a mapping from coverage data file paths to local paths.
498 for (StringRef Filename : Coverage.getUniqueSourceFiles()) {
499 if (RemappedFilenames.count(Filename) == 1)
500 continue;
502 SmallString<128> NativeFilename;
503 sys::path::native(Filename, NativeFilename);
504 sys::path::remove_dots(NativeFilename, true);
505 if (NativeFilename.startswith(RemapFrom)) {
506 RemappedFilenames[Filename] =
507 RemapTo + NativeFilename.substr(RemapFrom.size()).str();
512 // Convert input files from local paths to coverage data file paths.
513 StringMap<std::string> InvRemappedFilenames;
514 for (const auto &RemappedFilename : RemappedFilenames)
515 InvRemappedFilenames[RemappedFilename.getValue()] =
516 std::string(RemappedFilename.getKey());
518 for (std::string &Filename : SourceFiles) {
519 SmallString<128> NativeFilename;
520 sys::path::native(Filename, NativeFilename);
521 auto CovFileName = InvRemappedFilenames.find(NativeFilename);
522 if (CovFileName != InvRemappedFilenames.end())
523 Filename = CovFileName->second;
527 void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) {
528 std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles();
530 // The user may have specified source files which aren't in the coverage
531 // mapping. Filter these files away.
532 llvm::erase_if(SourceFiles, [&](const std::string &SF) {
533 return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF);
537 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
538 if (!ViewOpts.hasDemangler())
539 return;
541 // Pass function names to the demangler in a temporary file.
542 int InputFD;
543 SmallString<256> InputPath;
544 std::error_code EC =
545 sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
546 if (EC) {
547 error(InputPath, EC.message());
548 return;
550 ToolOutputFile InputTOF{InputPath, InputFD};
552 unsigned NumSymbols = 0;
553 for (const auto &Function : Coverage.getCoveredFunctions()) {
554 InputTOF.os() << Function.Name << '\n';
555 ++NumSymbols;
557 InputTOF.os().close();
559 // Use another temporary file to store the demangler's output.
560 int OutputFD;
561 SmallString<256> OutputPath;
562 EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
563 OutputPath);
564 if (EC) {
565 error(OutputPath, EC.message());
566 return;
568 ToolOutputFile OutputTOF{OutputPath, OutputFD};
569 OutputTOF.os().close();
571 // Invoke the demangler.
572 std::vector<StringRef> ArgsV;
573 ArgsV.reserve(ViewOpts.DemanglerOpts.size());
574 for (StringRef Arg : ViewOpts.DemanglerOpts)
575 ArgsV.push_back(Arg);
576 std::optional<StringRef> Redirects[] = {
577 InputPath.str(), OutputPath.str(), {""}};
578 std::string ErrMsg;
579 int RC =
580 sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
581 /*env=*/std::nullopt, Redirects, /*secondsToWait=*/0,
582 /*memoryLimit=*/0, &ErrMsg);
583 if (RC) {
584 error(ErrMsg, ViewOpts.DemanglerOpts[0]);
585 return;
588 // Parse the demangler's output.
589 auto BufOrError = MemoryBuffer::getFile(OutputPath);
590 if (!BufOrError) {
591 error(OutputPath, BufOrError.getError().message());
592 return;
595 std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
597 SmallVector<StringRef, 8> Symbols;
598 StringRef DemanglerData = DemanglerBuf->getBuffer();
599 DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
600 /*KeepEmpty=*/false);
601 if (Symbols.size() != NumSymbols) {
602 error("demangler did not provide expected number of symbols");
603 return;
606 // Cache the demangled names.
607 unsigned I = 0;
608 for (const auto &Function : Coverage.getCoveredFunctions())
609 // On Windows, lines in the demangler's output file end with "\r\n".
610 // Splitting by '\n' keeps '\r's, so cut them now.
611 DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim());
614 void CodeCoverageTool::writeSourceFileView(StringRef SourceFile,
615 CoverageMapping *Coverage,
616 CoveragePrinter *Printer,
617 bool ShowFilenames) {
618 auto View = createSourceFileView(SourceFile, *Coverage);
619 if (!View) {
620 warning("The file '" + SourceFile + "' isn't covered.");
621 return;
624 auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
625 if (Error E = OSOrErr.takeError()) {
626 error("could not create view file!", toString(std::move(E)));
627 return;
629 auto OS = std::move(OSOrErr.get());
631 View->print(*OS.get(), /*Wholefile=*/true,
632 /*ShowSourceName=*/ShowFilenames,
633 /*ShowTitle=*/ViewOpts.hasOutputDirectory());
634 Printer->closeViewFile(std::move(OS));
637 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
638 cl::opt<std::string> CovFilename(
639 cl::Positional, cl::desc("Covered executable or object file."));
641 cl::list<std::string> CovFilenames(
642 "object", cl::desc("Coverage executable or object file"));
644 cl::opt<bool> DebugDumpCollectedObjects(
645 "dump-collected-objects", cl::Optional, cl::Hidden,
646 cl::desc("Show the collected coverage object files"));
648 cl::list<std::string> InputSourceFiles("sources", cl::Positional,
649 cl::desc("<Source files>"));
651 cl::opt<bool> DebugDumpCollectedPaths(
652 "dump-collected-paths", cl::Optional, cl::Hidden,
653 cl::desc("Show the collected paths to source files"));
655 cl::opt<std::string, true> PGOFilename(
656 "instr-profile", cl::Required, cl::location(this->PGOFilename),
657 cl::desc(
658 "File with the profile data obtained after an instrumented run"));
660 cl::list<std::string> Arches(
661 "arch", cl::desc("architectures of the coverage mapping binaries"));
663 cl::opt<bool> DebugDump("dump", cl::Optional,
664 cl::desc("Show internal debug dump"));
666 cl::list<std::string> DebugFileDirectory(
667 "debug-file-directory",
668 cl::desc("Directories to search for object files by build ID"));
669 cl::opt<bool> Debuginfod(
670 "debuginfod", cl::ZeroOrMore,
671 cl::desc("Use debuginfod to look up object files from profile"),
672 cl::init(canUseDebuginfod()));
674 cl::opt<CoverageViewOptions::OutputFormat> Format(
675 "format", cl::desc("Output format for line-based coverage reports"),
676 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
677 "Text output"),
678 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
679 "HTML output"),
680 clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov",
681 "lcov tracefile output")),
682 cl::init(CoverageViewOptions::OutputFormat::Text));
684 cl::list<std::string> PathRemaps(
685 "path-equivalence", cl::Optional,
686 cl::desc("<from>,<to> Map coverage data paths to local source file "
687 "paths"));
689 cl::OptionCategory FilteringCategory("Function filtering options");
691 cl::list<std::string> NameFilters(
692 "name", cl::Optional,
693 cl::desc("Show code coverage only for functions with the given name"),
694 cl::cat(FilteringCategory));
696 cl::list<std::string> NameFilterFiles(
697 "name-allowlist", cl::Optional,
698 cl::desc("Show code coverage only for functions listed in the given "
699 "file"),
700 cl::cat(FilteringCategory));
702 cl::list<std::string> NameRegexFilters(
703 "name-regex", cl::Optional,
704 cl::desc("Show code coverage only for functions that match the given "
705 "regular expression"),
706 cl::cat(FilteringCategory));
708 cl::list<std::string> IgnoreFilenameRegexFilters(
709 "ignore-filename-regex", cl::Optional,
710 cl::desc("Skip source code files with file paths that match the given "
711 "regular expression"),
712 cl::cat(FilteringCategory));
714 cl::opt<double> RegionCoverageLtFilter(
715 "region-coverage-lt", cl::Optional,
716 cl::desc("Show code coverage only for functions with region coverage "
717 "less than the given threshold"),
718 cl::cat(FilteringCategory));
720 cl::opt<double> RegionCoverageGtFilter(
721 "region-coverage-gt", cl::Optional,
722 cl::desc("Show code coverage only for functions with region coverage "
723 "greater than the given threshold"),
724 cl::cat(FilteringCategory));
726 cl::opt<double> LineCoverageLtFilter(
727 "line-coverage-lt", cl::Optional,
728 cl::desc("Show code coverage only for functions with line coverage less "
729 "than the given threshold"),
730 cl::cat(FilteringCategory));
732 cl::opt<double> LineCoverageGtFilter(
733 "line-coverage-gt", cl::Optional,
734 cl::desc("Show code coverage only for functions with line coverage "
735 "greater than the given threshold"),
736 cl::cat(FilteringCategory));
738 cl::opt<cl::boolOrDefault> UseColor(
739 "use-color", cl::desc("Emit colored output (default=autodetect)"),
740 cl::init(cl::BOU_UNSET));
742 cl::list<std::string> DemanglerOpts(
743 "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
745 cl::opt<bool> RegionSummary(
746 "show-region-summary", cl::Optional,
747 cl::desc("Show region statistics in summary table"),
748 cl::init(true));
750 cl::opt<bool> BranchSummary(
751 "show-branch-summary", cl::Optional,
752 cl::desc("Show branch condition statistics in summary table"),
753 cl::init(true));
755 cl::opt<bool> InstantiationSummary(
756 "show-instantiation-summary", cl::Optional,
757 cl::desc("Show instantiation statistics in summary table"));
759 cl::opt<bool> SummaryOnly(
760 "summary-only", cl::Optional,
761 cl::desc("Export only summary information for each source file"));
763 cl::opt<unsigned> NumThreads(
764 "num-threads", cl::init(0),
765 cl::desc("Number of merge threads to use (default: autodetect)"));
766 cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
767 cl::aliasopt(NumThreads));
769 cl::opt<std::string> CompilationDirectory(
770 "compilation-dir", cl::init(""),
771 cl::desc("Directory used as a base for relative coverage mapping paths"));
773 cl::opt<bool> CheckBinaryIDs(
774 "check-binary-ids", cl::desc("Fail if an object couldn't be found for a "
775 "binary ID in the profile"));
777 auto commandLineParser = [&, this](int argc, const char **argv) -> int {
778 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
779 ViewOpts.Debug = DebugDump;
780 if (Debuginfod) {
781 HTTPClient::initialize();
782 BIDFetcher = std::make_unique<DebuginfodFetcher>(DebugFileDirectory);
783 } else {
784 BIDFetcher = std::make_unique<object::BuildIDFetcher>(DebugFileDirectory);
786 this->CheckBinaryIDs = CheckBinaryIDs;
788 if (!CovFilename.empty())
789 ObjectFilenames.emplace_back(CovFilename);
790 for (const std::string &Filename : CovFilenames)
791 ObjectFilenames.emplace_back(Filename);
792 if (ObjectFilenames.empty() && !Debuginfod && DebugFileDirectory.empty()) {
793 errs() << "No filenames specified!\n";
794 ::exit(1);
797 if (DebugDumpCollectedObjects) {
798 for (StringRef OF : ObjectFilenames)
799 outs() << OF << '\n';
800 ::exit(0);
803 ViewOpts.Format = Format;
804 switch (ViewOpts.Format) {
805 case CoverageViewOptions::OutputFormat::Text:
806 ViewOpts.Colors = UseColor == cl::BOU_UNSET
807 ? sys::Process::StandardOutHasColors()
808 : UseColor == cl::BOU_TRUE;
809 break;
810 case CoverageViewOptions::OutputFormat::HTML:
811 if (UseColor == cl::BOU_FALSE)
812 errs() << "Color output cannot be disabled when generating html.\n";
813 ViewOpts.Colors = true;
814 break;
815 case CoverageViewOptions::OutputFormat::Lcov:
816 if (UseColor == cl::BOU_TRUE)
817 errs() << "Color output cannot be enabled when generating lcov.\n";
818 ViewOpts.Colors = false;
819 break;
822 if (!PathRemaps.empty()) {
823 std::vector<std::pair<std::string, std::string>> Remappings;
825 for (const std::string &PathRemap : PathRemaps) {
826 auto EquivPair = StringRef(PathRemap).split(',');
827 if (EquivPair.first.empty() || EquivPair.second.empty()) {
828 error("invalid argument '" + PathRemap +
829 "', must be in format 'from,to'",
830 "-path-equivalence");
831 return 1;
834 Remappings.push_back(
835 {std::string(EquivPair.first), std::string(EquivPair.second)});
838 PathRemappings = Remappings;
841 // If a demangler is supplied, check if it exists and register it.
842 if (!DemanglerOpts.empty()) {
843 auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
844 if (!DemanglerPathOrErr) {
845 error("could not find the demangler!",
846 DemanglerPathOrErr.getError().message());
847 return 1;
849 DemanglerOpts[0] = *DemanglerPathOrErr;
850 ViewOpts.DemanglerOpts.swap(DemanglerOpts);
853 // Read in -name-allowlist files.
854 if (!NameFilterFiles.empty()) {
855 std::string SpecialCaseListErr;
856 NameAllowlist = SpecialCaseList::create(
857 NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr);
858 if (!NameAllowlist)
859 error(SpecialCaseListErr);
862 // Create the function filters
863 if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) {
864 auto NameFilterer = std::make_unique<CoverageFilters>();
865 for (const auto &Name : NameFilters)
866 NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name));
867 if (NameAllowlist && !NameFilterFiles.empty())
868 NameFilterer->push_back(
869 std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist));
870 for (const auto &Regex : NameRegexFilters)
871 NameFilterer->push_back(
872 std::make_unique<NameRegexCoverageFilter>(Regex));
873 Filters.push_back(std::move(NameFilterer));
876 if (RegionCoverageLtFilter.getNumOccurrences() ||
877 RegionCoverageGtFilter.getNumOccurrences() ||
878 LineCoverageLtFilter.getNumOccurrences() ||
879 LineCoverageGtFilter.getNumOccurrences()) {
880 auto StatFilterer = std::make_unique<CoverageFilters>();
881 if (RegionCoverageLtFilter.getNumOccurrences())
882 StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
883 RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
884 if (RegionCoverageGtFilter.getNumOccurrences())
885 StatFilterer->push_back(std::make_unique<RegionCoverageFilter>(
886 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
887 if (LineCoverageLtFilter.getNumOccurrences())
888 StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
889 LineCoverageFilter::LessThan, LineCoverageLtFilter));
890 if (LineCoverageGtFilter.getNumOccurrences())
891 StatFilterer->push_back(std::make_unique<LineCoverageFilter>(
892 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
893 Filters.push_back(std::move(StatFilterer));
896 // Create the ignore filename filters.
897 for (const auto &RE : IgnoreFilenameRegexFilters)
898 IgnoreFilenameFilters.push_back(
899 std::make_unique<NameRegexCoverageFilter>(RE));
901 if (!Arches.empty()) {
902 for (const std::string &Arch : Arches) {
903 if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
904 error("unknown architecture: " + Arch);
905 return 1;
907 CoverageArches.emplace_back(Arch);
909 if (CoverageArches.size() != 1 &&
910 CoverageArches.size() != ObjectFilenames.size()) {
911 error("number of architectures doesn't match the number of objects");
912 return 1;
916 // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
917 for (const std::string &File : InputSourceFiles)
918 collectPaths(File);
920 if (DebugDumpCollectedPaths) {
921 for (const std::string &SF : SourceFiles)
922 outs() << SF << '\n';
923 ::exit(0);
926 ViewOpts.ShowBranchSummary = BranchSummary;
927 ViewOpts.ShowRegionSummary = RegionSummary;
928 ViewOpts.ShowInstantiationSummary = InstantiationSummary;
929 ViewOpts.ExportSummaryOnly = SummaryOnly;
930 ViewOpts.NumThreads = NumThreads;
931 ViewOpts.CompilationDirectory = CompilationDirectory;
933 return 0;
936 switch (Cmd) {
937 case Show:
938 return doShow(argc, argv, commandLineParser);
939 case Report:
940 return doReport(argc, argv, commandLineParser);
941 case Export:
942 return doExport(argc, argv, commandLineParser);
944 return 0;
947 int CodeCoverageTool::doShow(int argc, const char **argv,
948 CommandLineParserType commandLineParser) {
950 cl::OptionCategory ViewCategory("Viewing options");
952 cl::opt<bool> ShowLineExecutionCounts(
953 "show-line-counts", cl::Optional,
954 cl::desc("Show the execution counts for each line"), cl::init(true),
955 cl::cat(ViewCategory));
957 cl::opt<bool> ShowRegions(
958 "show-regions", cl::Optional,
959 cl::desc("Show the execution counts for each region"),
960 cl::cat(ViewCategory));
962 cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches(
963 "show-branches", cl::Optional,
964 cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory),
965 cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count,
966 "count", "Show True/False counts"),
967 clEnumValN(CoverageViewOptions::BranchOutputType::Percent,
968 "percent", "Show True/False percent")),
969 cl::init(CoverageViewOptions::BranchOutputType::Off));
971 cl::opt<bool> ShowBestLineRegionsCounts(
972 "show-line-counts-or-regions", cl::Optional,
973 cl::desc("Show the execution counts for each line, or the execution "
974 "counts for each region on lines that have multiple regions"),
975 cl::cat(ViewCategory));
977 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
978 cl::desc("Show expanded source regions"),
979 cl::cat(ViewCategory));
981 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
982 cl::desc("Show function instantiations"),
983 cl::init(true), cl::cat(ViewCategory));
985 cl::opt<bool> ShowDirectoryCoverage("show-directory-coverage", cl::Optional,
986 cl::desc("Show directory coverage"),
987 cl::cat(ViewCategory));
989 cl::opt<std::string> ShowOutputDirectory(
990 "output-dir", cl::init(""),
991 cl::desc("Directory in which coverage information is written out"));
992 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
993 cl::aliasopt(ShowOutputDirectory));
995 cl::opt<uint32_t> TabSize(
996 "tab-size", cl::init(2),
997 cl::desc(
998 "Set tab expansion size for html coverage reports (default = 2)"));
1000 cl::opt<std::string> ProjectTitle(
1001 "project-title", cl::Optional,
1002 cl::desc("Set project title for the coverage report"));
1004 cl::opt<std::string> CovWatermark(
1005 "coverage-watermark", cl::Optional,
1006 cl::desc("<high>,<low> value indicate thresholds for high and low"
1007 "coverage watermark"));
1009 auto Err = commandLineParser(argc, argv);
1010 if (Err)
1011 return Err;
1013 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
1014 error("lcov format should be used with 'llvm-cov export'.");
1015 return 1;
1018 ViewOpts.HighCovWatermark = 100.0;
1019 ViewOpts.LowCovWatermark = 80.0;
1020 if (!CovWatermark.empty()) {
1021 auto WaterMarkPair = StringRef(CovWatermark).split(',');
1022 if (WaterMarkPair.first.empty() || WaterMarkPair.second.empty()) {
1023 error("invalid argument '" + CovWatermark +
1024 "', must be in format 'high,low'",
1025 "-coverage-watermark");
1026 return 1;
1029 char *EndPointer = nullptr;
1030 ViewOpts.HighCovWatermark =
1031 strtod(WaterMarkPair.first.begin(), &EndPointer);
1032 if (EndPointer != WaterMarkPair.first.end()) {
1033 error("invalid number '" + WaterMarkPair.first +
1034 "', invalid value for 'high'",
1035 "-coverage-watermark");
1036 return 1;
1039 ViewOpts.LowCovWatermark =
1040 strtod(WaterMarkPair.second.begin(), &EndPointer);
1041 if (EndPointer != WaterMarkPair.second.end()) {
1042 error("invalid number '" + WaterMarkPair.second +
1043 "', invalid value for 'low'",
1044 "-coverage-watermark");
1045 return 1;
1048 if (ViewOpts.HighCovWatermark > 100 || ViewOpts.LowCovWatermark < 0 ||
1049 ViewOpts.HighCovWatermark <= ViewOpts.LowCovWatermark) {
1050 error(
1051 "invalid number range '" + CovWatermark +
1052 "', must be both high and low should be between 0-100, and high "
1053 "> low",
1054 "-coverage-watermark");
1055 return 1;
1059 ViewOpts.ShowLineNumbers = true;
1060 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
1061 !ShowRegions || ShowBestLineRegionsCounts;
1062 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
1063 ViewOpts.ShowExpandedRegions = ShowExpansions;
1064 ViewOpts.ShowBranchCounts =
1065 ShowBranches == CoverageViewOptions::BranchOutputType::Count;
1066 ViewOpts.ShowBranchPercents =
1067 ShowBranches == CoverageViewOptions::BranchOutputType::Percent;
1068 ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
1069 ViewOpts.ShowDirectoryCoverage = ShowDirectoryCoverage;
1070 ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
1071 ViewOpts.TabSize = TabSize;
1072 ViewOpts.ProjectTitle = ProjectTitle;
1074 if (ViewOpts.hasOutputDirectory()) {
1075 if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
1076 error("could not create output directory!", E.message());
1077 return 1;
1081 sys::fs::file_status Status;
1082 if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1083 error("could not read profile data!" + EC.message(), PGOFilename);
1084 return 1;
1087 auto ModifiedTime = Status.getLastModificationTime();
1088 std::string ModifiedTimeStr = to_string(ModifiedTime);
1089 size_t found = ModifiedTimeStr.rfind(':');
1090 ViewOpts.CreatedTimeStr = (found != std::string::npos)
1091 ? "Created: " + ModifiedTimeStr.substr(0, found)
1092 : "Created: " + ModifiedTimeStr;
1094 auto Coverage = load();
1095 if (!Coverage)
1096 return 1;
1098 auto Printer = CoveragePrinter::create(ViewOpts);
1100 if (SourceFiles.empty() && !HadSourceFiles)
1101 // Get the source files from the function coverage mapping.
1102 for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
1103 if (!IgnoreFilenameFilters.matchesFilename(Filename))
1104 SourceFiles.push_back(std::string(Filename));
1107 // Create an index out of the source files.
1108 if (ViewOpts.hasOutputDirectory()) {
1109 if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) {
1110 error("could not create index file!", toString(std::move(E)));
1111 return 1;
1115 if (!Filters.empty()) {
1116 // Build the map of filenames to functions.
1117 std::map<llvm::StringRef, std::vector<const FunctionRecord *>>
1118 FilenameFunctionMap;
1119 for (const auto &SourceFile : SourceFiles)
1120 for (const auto &Function : Coverage->getCoveredFunctions(SourceFile))
1121 if (Filters.matches(*Coverage, Function))
1122 FilenameFunctionMap[SourceFile].push_back(&Function);
1124 // Only print filter matching functions for each file.
1125 for (const auto &FileFunc : FilenameFunctionMap) {
1126 StringRef File = FileFunc.first;
1127 const auto &Functions = FileFunc.second;
1129 auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false);
1130 if (Error E = OSOrErr.takeError()) {
1131 error("could not create view file!", toString(std::move(E)));
1132 return 1;
1134 auto OS = std::move(OSOrErr.get());
1136 bool ShowTitle = ViewOpts.hasOutputDirectory();
1137 for (const auto *Function : Functions) {
1138 auto FunctionView = createFunctionView(*Function, *Coverage);
1139 if (!FunctionView) {
1140 warning("Could not read coverage for '" + Function->Name + "'.");
1141 continue;
1143 FunctionView->print(*OS.get(), /*WholeFile=*/false,
1144 /*ShowSourceName=*/true, ShowTitle);
1145 ShowTitle = false;
1148 Printer->closeViewFile(std::move(OS));
1150 return 0;
1153 // Show files
1154 bool ShowFilenames =
1155 (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
1156 (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
1158 ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads);
1159 if (ViewOpts.NumThreads == 0) {
1160 // If NumThreads is not specified, create one thread for each input, up to
1161 // the number of hardware cores.
1162 S = heavyweight_hardware_concurrency(SourceFiles.size());
1163 S.Limit = true;
1166 if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) {
1167 for (const std::string &SourceFile : SourceFiles)
1168 writeSourceFileView(SourceFile, Coverage.get(), Printer.get(),
1169 ShowFilenames);
1170 } else {
1171 // In -output-dir mode, it's safe to use multiple threads to print files.
1172 ThreadPool Pool(S);
1173 for (const std::string &SourceFile : SourceFiles)
1174 Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile,
1175 Coverage.get(), Printer.get(), ShowFilenames);
1176 Pool.wait();
1179 return 0;
1182 int CodeCoverageTool::doReport(int argc, const char **argv,
1183 CommandLineParserType commandLineParser) {
1184 cl::opt<bool> ShowFunctionSummaries(
1185 "show-functions", cl::Optional, cl::init(false),
1186 cl::desc("Show coverage summaries for each function"));
1188 auto Err = commandLineParser(argc, argv);
1189 if (Err)
1190 return Err;
1192 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) {
1193 error("HTML output for summary reports is not yet supported.");
1194 return 1;
1195 } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) {
1196 error("lcov format should be used with 'llvm-cov export'.");
1197 return 1;
1200 sys::fs::file_status Status;
1201 if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1202 error("could not read profile data!" + EC.message(), PGOFilename);
1203 return 1;
1206 auto Coverage = load();
1207 if (!Coverage)
1208 return 1;
1210 CoverageReport Report(ViewOpts, *Coverage);
1211 if (!ShowFunctionSummaries) {
1212 if (SourceFiles.empty())
1213 Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
1214 else
1215 Report.renderFileReports(llvm::outs(), SourceFiles);
1216 } else {
1217 if (SourceFiles.empty()) {
1218 error("source files must be specified when -show-functions=true is "
1219 "specified");
1220 return 1;
1223 Report.renderFunctionReports(SourceFiles, DC, llvm::outs());
1225 return 0;
1228 int CodeCoverageTool::doExport(int argc, const char **argv,
1229 CommandLineParserType commandLineParser) {
1231 cl::OptionCategory ExportCategory("Exporting options");
1233 cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional,
1234 cl::desc("Don't export expanded source regions"),
1235 cl::cat(ExportCategory));
1237 cl::opt<bool> SkipFunctions("skip-functions", cl::Optional,
1238 cl::desc("Don't export per-function data"),
1239 cl::cat(ExportCategory));
1241 cl::opt<bool> SkipBranches("skip-branches", cl::Optional,
1242 cl::desc("Don't export branch data (LCOV)"),
1243 cl::cat(ExportCategory));
1245 auto Err = commandLineParser(argc, argv);
1246 if (Err)
1247 return Err;
1249 ViewOpts.SkipExpansions = SkipExpansions;
1250 ViewOpts.SkipFunctions = SkipFunctions;
1251 ViewOpts.SkipBranches = SkipBranches;
1253 if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text &&
1254 ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) {
1255 error("coverage data can only be exported as textual JSON or an "
1256 "lcov tracefile.");
1257 return 1;
1260 sys::fs::file_status Status;
1261 if (std::error_code EC = sys::fs::status(PGOFilename, Status)) {
1262 error("could not read profile data!" + EC.message(), PGOFilename);
1263 return 1;
1266 auto Coverage = load();
1267 if (!Coverage) {
1268 error("could not load coverage information");
1269 return 1;
1272 std::unique_ptr<CoverageExporter> Exporter;
1274 switch (ViewOpts.Format) {
1275 case CoverageViewOptions::OutputFormat::Text:
1276 Exporter =
1277 std::make_unique<CoverageExporterJson>(*Coverage, ViewOpts, outs());
1278 break;
1279 case CoverageViewOptions::OutputFormat::HTML:
1280 // Unreachable because we should have gracefully terminated with an error
1281 // above.
1282 llvm_unreachable("Export in HTML is not supported!");
1283 case CoverageViewOptions::OutputFormat::Lcov:
1284 Exporter =
1285 std::make_unique<CoverageExporterLcov>(*Coverage, ViewOpts, outs());
1286 break;
1289 if (SourceFiles.empty())
1290 Exporter->renderRoot(IgnoreFilenameFilters);
1291 else
1292 Exporter->renderRoot(SourceFiles);
1294 return 0;
1297 int showMain(int argc, const char *argv[]) {
1298 CodeCoverageTool Tool;
1299 return Tool.run(CodeCoverageTool::Show, argc, argv);
1302 int reportMain(int argc, const char *argv[]) {
1303 CodeCoverageTool Tool;
1304 return Tool.run(CodeCoverageTool::Report, argc, argv);
1307 int exportMain(int argc, const char *argv[]) {
1308 CodeCoverageTool Tool;
1309 return Tool.run(CodeCoverageTool::Export, argc, argv);