1 //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
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
7 //===----------------------------------------------------------------------===//
9 // The 'CodeCoverageTool' class implements a command line tool to analyze and
10 // report coverage information using the profiling instrumentation and code
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"
49 #include <system_error>
52 using namespace coverage
;
54 void exportCoverageDataToJson(const coverage::CoverageMapping
&CoverageMapping
,
55 const CoverageViewOptions
&Options
,
59 /// The implementation of the coverage tool.
60 class CodeCoverageTool
{
65 /// The report command.
67 /// The export command.
71 int run(Command Cmd
, int argc
, const char **argv
);
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
>>>
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.
177 /// A lock which guards printing to stderr.
180 /// A container for input source file buffers.
181 std::mutex LoadedSourceFilesLock
;
182 std::vector
<std::pair
<std::string
, std::unique_ptr
<MemoryBuffer
>>>
185 /// Allowlist from -name-allowlist to be used for filtering.
186 std::unique_ptr
<SpecialCaseList
> NameAllowlist
;
188 std::unique_ptr
<object::BuildIDFetcher
> BIDFetcher
;
194 static std::string
getErrorString(const Twine
&Message
, StringRef Whence
,
196 std::string Str
= (Warning
? "warning" : "error");
199 Str
+= Whence
.str() + ": ";
200 Str
+= Message
.str() + "\n";
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
);
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
)) {
233 addCollectedPath(Path
);
235 warning("Source file doesn't exist, proceeded by ignoring it.", Path
);
239 if (llvm::sys::fs::is_regular_file(Status
)) {
240 addCollectedPath(Path
);
244 if (llvm::sys::fs::is_directory(Status
)) {
246 for (llvm::sys::fs::recursive_directory_iterator
F(Path
, EC
), E
;
247 F
!= E
; F
.increment(EC
)) {
249 auto Status
= F
->status();
251 warning(Status
.getError().message(), F
->path());
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();
268 sys::fs::file_status Status
;
269 if (!sys::fs::status(FilePath
, Status
))
270 CachedStatus
= Status
;
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
);
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
)
308 for (const auto &Expansion
: Expansions
) {
309 auto ExpansionCoverage
= Coverage
.getCoverageForExpansion(Expansion
);
310 if (ExpansionCoverage
.empty())
312 auto SourceBuffer
= getSourceFile(ExpansionCoverage
.getFilename());
316 auto SubViewBranches
= ExpansionCoverage
.getBranches();
317 auto SubViewExpansions
= ExpansionCoverage
.getExpansions();
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
)
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())
361 auto SourceBuffer
= getSourceFile(FunctionCoverage
.getFilename());
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
);
377 std::unique_ptr
<SourceCoverageView
>
378 CodeCoverageTool::createSourceFileView(StringRef SourceFile
,
379 const CoverageMapping
&Coverage
) {
380 auto SourceBuffer
= getSourceFile(SourceFile
);
383 auto FileCoverage
= Coverage
.getCoverageForFile(SourceFile
);
384 if (FileCoverage
.empty())
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(),
394 if (!ViewOpts
.ShowFunctionInstantiations
)
397 for (const auto &Group
: Coverage
.getInstantiationGroups(SourceFile
)) {
398 // Skip functions which have a single instantiation.
399 if (Group
.size() < 2)
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
;
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
));
429 static bool modifiedTimeGT(StringRef LHS
, StringRef RHS
) {
430 sys::fs::file_status Status
;
431 if (sys::fs::status(LHS
, Status
))
433 auto LHSTime
= Status
.getLastModificationTime();
434 if (sys::fs::status(RHS
, Status
))
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",
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
)));
453 auto Coverage
= std::move(CoverageOrErr
.get());
454 unsigned Mismatched
= Coverage
->getMismatchedCount();
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
)
467 remapPathNames(*Coverage
);
469 if (!SourceFiles
.empty())
470 removeUnmappedInputs(*Coverage
);
472 demangleSymbols(*Coverage
);
477 void CodeCoverageTool::remapPathNames(const CoverageMapping
&Coverage
) {
481 // Convert remapping paths to native paths with trailing seperators.
482 auto nativeWithTrailing
= [](StringRef Path
) -> std::string
{
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)
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())
541 // Pass function names to the demangler in a temporary file.
543 SmallString
<256> InputPath
;
545 sys::fs::createTemporaryFile("demangle-in", "list", InputFD
, InputPath
);
547 error(InputPath
, EC
.message());
550 ToolOutputFile InputTOF
{InputPath
, InputFD
};
552 unsigned NumSymbols
= 0;
553 for (const auto &Function
: Coverage
.getCoveredFunctions()) {
554 InputTOF
.os() << Function
.Name
<< '\n';
557 InputTOF
.os().close();
559 // Use another temporary file to store the demangler's output.
561 SmallString
<256> OutputPath
;
562 EC
= sys::fs::createTemporaryFile("demangle-out", "list", OutputFD
,
565 error(OutputPath
, EC
.message());
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(), {""}};
580 sys::ExecuteAndWait(ViewOpts
.DemanglerOpts
[0], ArgsV
,
581 /*env=*/std::nullopt
, Redirects
, /*secondsToWait=*/0,
582 /*memoryLimit=*/0, &ErrMsg
);
584 error(ErrMsg
, ViewOpts
.DemanglerOpts
[0]);
588 // Parse the demangler's output.
589 auto BufOrError
= MemoryBuffer::getFile(OutputPath
);
591 error(OutputPath
, BufOrError
.getError().message());
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");
606 // Cache the demangled names.
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
);
620 warning("The file '" + SourceFile
+ "' isn't covered.");
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
)));
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
),
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",
678 clEnumValN(CoverageViewOptions::OutputFormat::HTML
, "html",
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 "
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 "
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"),
750 cl::opt
<bool> BranchSummary(
751 "show-branch-summary", cl::Optional
,
752 cl::desc("Show branch condition statistics in summary table"),
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
;
781 HTTPClient::initialize();
782 BIDFetcher
= std::make_unique
<DebuginfodFetcher
>(DebugFileDirectory
);
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";
797 if (DebugDumpCollectedObjects
) {
798 for (StringRef OF
: ObjectFilenames
)
799 outs() << OF
<< '\n';
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
;
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;
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;
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");
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());
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
);
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
);
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");
916 // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
917 for (const std::string
&File
: InputSourceFiles
)
920 if (DebugDumpCollectedPaths
) {
921 for (const std::string
&SF
: SourceFiles
)
922 outs() << SF
<< '\n';
926 ViewOpts
.ShowBranchSummary
= BranchSummary
;
927 ViewOpts
.ShowRegionSummary
= RegionSummary
;
928 ViewOpts
.ShowInstantiationSummary
= InstantiationSummary
;
929 ViewOpts
.ExportSummaryOnly
= SummaryOnly
;
930 ViewOpts
.NumThreads
= NumThreads
;
931 ViewOpts
.CompilationDirectory
= CompilationDirectory
;
938 return doShow(argc
, argv
, commandLineParser
);
940 return doReport(argc
, argv
, commandLineParser
);
942 return doExport(argc
, argv
, commandLineParser
);
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),
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
);
1013 if (ViewOpts
.Format
== CoverageViewOptions::OutputFormat::Lcov
) {
1014 error("lcov format should be used with 'llvm-cov export'.");
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");
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");
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");
1048 if (ViewOpts
.HighCovWatermark
> 100 || ViewOpts
.LowCovWatermark
< 0 ||
1049 ViewOpts
.HighCovWatermark
<= ViewOpts
.LowCovWatermark
) {
1051 "invalid number range '" + CovWatermark
+
1052 "', must be both high and low should be between 0-100, and high "
1054 "-coverage-watermark");
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());
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
);
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();
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
)));
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
)));
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
+ "'.");
1143 FunctionView
->print(*OS
.get(), /*WholeFile=*/false,
1144 /*ShowSourceName=*/true, ShowTitle
);
1148 Printer
->closeViewFile(std::move(OS
));
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());
1166 if (!ViewOpts
.hasOutputDirectory() || S
.ThreadsRequested
== 1) {
1167 for (const std::string
&SourceFile
: SourceFiles
)
1168 writeSourceFileView(SourceFile
, Coverage
.get(), Printer
.get(),
1171 // In -output-dir mode, it's safe to use multiple threads to print files.
1173 for (const std::string
&SourceFile
: SourceFiles
)
1174 Pool
.async(&CodeCoverageTool::writeSourceFileView
, this, SourceFile
,
1175 Coverage
.get(), Printer
.get(), ShowFilenames
);
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
);
1192 if (ViewOpts
.Format
== CoverageViewOptions::OutputFormat::HTML
) {
1193 error("HTML output for summary reports is not yet supported.");
1195 } else if (ViewOpts
.Format
== CoverageViewOptions::OutputFormat::Lcov
) {
1196 error("lcov format should be used with 'llvm-cov export'.");
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
);
1206 auto Coverage
= load();
1210 CoverageReport
Report(ViewOpts
, *Coverage
);
1211 if (!ShowFunctionSummaries
) {
1212 if (SourceFiles
.empty())
1213 Report
.renderFileReports(llvm::outs(), IgnoreFilenameFilters
);
1215 Report
.renderFileReports(llvm::outs(), SourceFiles
);
1217 if (SourceFiles
.empty()) {
1218 error("source files must be specified when -show-functions=true is "
1223 Report
.renderFunctionReports(SourceFiles
, DC
, llvm::outs());
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
);
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 "
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
);
1266 auto Coverage
= load();
1268 error("could not load coverage information");
1272 std::unique_ptr
<CoverageExporter
> Exporter
;
1274 switch (ViewOpts
.Format
) {
1275 case CoverageViewOptions::OutputFormat::Text
:
1277 std::make_unique
<CoverageExporterJson
>(*Coverage
, ViewOpts
, outs());
1279 case CoverageViewOptions::OutputFormat::HTML
:
1280 // Unreachable because we should have gracefully terminated with an error
1282 llvm_unreachable("Export in HTML is not supported!");
1283 case CoverageViewOptions::OutputFormat::Lcov
:
1285 std::make_unique
<CoverageExporterLcov
>(*Coverage
, ViewOpts
, outs());
1289 if (SourceFiles
.empty())
1290 Exporter
->renderRoot(IgnoreFilenameFilters
);
1292 Exporter
->renderRoot(SourceFiles
);
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
);