1 //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // The 'CodeCoverageTool' class implements a command line tool to analyze and
11 // report coverage information using the profiling instrumentation and code
14 //===----------------------------------------------------------------------===//
16 #include "CoverageFilters.h"
17 #include "CoverageReport.h"
18 #include "CoverageViewOptions.h"
19 #include "RenderingSupport.h"
20 #include "SourceCoverageView.h"
21 #include "llvm/ADT/SmallString.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/Triple.h"
24 #include "llvm/ProfileData/Coverage/CoverageMapping.h"
25 #include "llvm/ProfileData/InstrProfReader.h"
26 #include "llvm/Support/CommandLine.h"
27 #include "llvm/Support/FileSystem.h"
28 #include "llvm/Support/Format.h"
29 #include "llvm/Support/MemoryBuffer.h"
30 #include "llvm/Support/Path.h"
31 #include "llvm/Support/Process.h"
32 #include "llvm/Support/Program.h"
33 #include "llvm/Support/ThreadPool.h"
34 #include "llvm/Support/ToolOutputFile.h"
36 #include <system_error>
39 using namespace coverage;
42 /// \brief The implementation of the coverage tool.
43 class CodeCoverageTool {
46 /// \brief The show command.
48 /// \brief The report command.
52 /// \brief Print the error message to the error output stream.
53 void error(const Twine &Message, StringRef Whence = "");
55 /// \brief Record (but do not print) an error message in a thread-safe way.
56 void deferError(const Twine &Message, StringRef Whence = "");
58 /// \brief Record (but do not print) a warning message in a thread-safe way.
59 void deferWarning(const Twine &Message, StringRef Whence = "");
61 /// \brief Print (and then clear) all deferred error and warning messages.
62 void consumeDeferredMessages();
64 /// \brief Append a reference to a private copy of \p Path into SourceFiles.
65 void addCollectedPath(const std::string &Path);
67 /// \brief Return a memory buffer for the given source file.
68 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
70 /// \brief Create source views for the expansions of the view.
71 void attachExpansionSubViews(SourceCoverageView &View,
72 ArrayRef<ExpansionRecord> Expansions,
73 const CoverageMapping &Coverage);
75 /// \brief Create the source view of a particular function.
76 std::unique_ptr<SourceCoverageView>
77 createFunctionView(const FunctionRecord &Function,
78 const CoverageMapping &Coverage);
80 /// \brief Create the main source view of a particular source file.
81 std::unique_ptr<SourceCoverageView>
82 createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
84 /// \brief Load the coverage mapping data. Return nullptr if an error occured.
85 std::unique_ptr<CoverageMapping> load();
87 /// \brief If a demangler is available, demangle all symbol names.
88 void demangleSymbols(const CoverageMapping &Coverage);
90 /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym.
91 StringRef getSymbolForHumans(StringRef Sym) const;
93 int run(Command Cmd, int argc, const char **argv);
95 typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
97 int show(int argc, const char **argv,
98 CommandLineParserType commandLineParser);
100 int report(int argc, const char **argv,
101 CommandLineParserType commandLineParser);
103 std::string ObjectFilename;
104 CoverageViewOptions ViewOpts;
105 std::string PGOFilename;
106 CoverageFiltersMatchAll Filters;
107 std::vector<StringRef> SourceFiles;
108 bool CompareFilenamesOnly;
109 StringMap<std::string> RemappedFilenames;
110 std::string CoverageArch;
113 /// A cache for demangled symbol names.
114 StringMap<std::string> DemangledNames;
116 /// File paths (absolute, or otherwise) to input source files.
117 std::vector<std::string> CollectedPaths;
119 /// Errors and warnings which have not been printed.
120 std::mutex DeferredMessagesLock;
121 std::vector<std::string> DeferredMessages;
123 /// A container for input source file buffers.
124 std::mutex LoadedSourceFilesLock;
125 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
130 static std::string getErrorString(const Twine &Message, StringRef Whence,
132 std::string Str = (Warning ? "warning" : "error");
135 Str += Whence.str() + ": ";
136 Str += Message.str() + "\n";
140 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
141 errs() << getErrorString(Message, Whence, false);
144 void CodeCoverageTool::deferError(const Twine &Message, StringRef Whence) {
145 std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
146 DeferredMessages.emplace_back(getErrorString(Message, Whence, false));
149 void CodeCoverageTool::deferWarning(const Twine &Message, StringRef Whence) {
150 std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
151 DeferredMessages.emplace_back(getErrorString(Message, Whence, true));
154 void CodeCoverageTool::consumeDeferredMessages() {
155 std::unique_lock<std::mutex> Guard{DeferredMessagesLock};
156 for (const std::string &Message : DeferredMessages)
157 ViewOpts.colored_ostream(errs(), raw_ostream::RED) << Message;
158 DeferredMessages.clear();
161 void CodeCoverageTool::addCollectedPath(const std::string &Path) {
162 CollectedPaths.push_back(Path);
163 SourceFiles.emplace_back(CollectedPaths.back());
166 ErrorOr<const MemoryBuffer &>
167 CodeCoverageTool::getSourceFile(StringRef SourceFile) {
168 // If we've remapped filenames, look up the real location for this file.
169 std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock};
170 if (!RemappedFilenames.empty()) {
171 auto Loc = RemappedFilenames.find(SourceFile);
172 if (Loc != RemappedFilenames.end())
173 SourceFile = Loc->second;
175 for (const auto &Files : LoadedSourceFiles)
176 if (sys::fs::equivalent(SourceFile, Files.first))
177 return *Files.second;
178 auto Buffer = MemoryBuffer::getFile(SourceFile);
179 if (auto EC = Buffer.getError()) {
180 deferError(EC.message(), SourceFile);
183 LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get()));
184 return *LoadedSourceFiles.back().second;
187 void CodeCoverageTool::attachExpansionSubViews(
188 SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions,
189 const CoverageMapping &Coverage) {
190 if (!ViewOpts.ShowExpandedRegions)
192 for (const auto &Expansion : Expansions) {
193 auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion);
194 if (ExpansionCoverage.empty())
196 auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename());
200 auto SubViewExpansions = ExpansionCoverage.getExpansions();
202 SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(),
203 ViewOpts, std::move(ExpansionCoverage));
204 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
205 View.addExpansion(Expansion.Region, std::move(SubView));
209 std::unique_ptr<SourceCoverageView>
210 CodeCoverageTool::createFunctionView(const FunctionRecord &Function,
211 const CoverageMapping &Coverage) {
212 auto FunctionCoverage = Coverage.getCoverageForFunction(Function);
213 if (FunctionCoverage.empty())
215 auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename());
219 auto Expansions = FunctionCoverage.getExpansions();
220 auto View = SourceCoverageView::create(getSymbolForHumans(Function.Name),
221 SourceBuffer.get(), ViewOpts,
222 std::move(FunctionCoverage));
223 attachExpansionSubViews(*View, Expansions, Coverage);
228 std::unique_ptr<SourceCoverageView>
229 CodeCoverageTool::createSourceFileView(StringRef SourceFile,
230 const CoverageMapping &Coverage) {
231 auto SourceBuffer = getSourceFile(SourceFile);
234 auto FileCoverage = Coverage.getCoverageForFile(SourceFile);
235 if (FileCoverage.empty())
238 auto Expansions = FileCoverage.getExpansions();
239 auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(),
240 ViewOpts, std::move(FileCoverage));
241 attachExpansionSubViews(*View, Expansions, Coverage);
243 for (const auto *Function : Coverage.getInstantiations(SourceFile)) {
244 auto SubViewCoverage = Coverage.getCoverageForFunction(*Function);
245 auto SubViewExpansions = SubViewCoverage.getExpansions();
246 auto SubView = SourceCoverageView::create(
247 getSymbolForHumans(Function->Name), SourceBuffer.get(), ViewOpts,
248 std::move(SubViewCoverage));
249 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage);
252 unsigned FileID = Function->CountedRegions.front().FileID;
254 for (const auto &CR : Function->CountedRegions)
255 if (CR.FileID == FileID)
256 Line = std::max(CR.LineEnd, Line);
257 View->addInstantiation(Function->Name, Line, std::move(SubView));
263 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) {
264 sys::fs::file_status Status;
265 if (sys::fs::status(LHS, Status))
267 auto LHSTime = Status.getLastModificationTime();
268 if (sys::fs::status(RHS, Status))
270 auto RHSTime = Status.getLastModificationTime();
271 return LHSTime > RHSTime;
274 std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
275 if (modifiedTimeGT(ObjectFilename, PGOFilename))
276 errs() << "warning: profile data may be out of date - object is newer\n";
277 auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename,
279 if (Error E = CoverageOrErr.takeError()) {
280 colored_ostream(errs(), raw_ostream::RED)
281 << "error: Failed to load coverage: " << toString(std::move(E)) << "\n";
284 auto Coverage = std::move(CoverageOrErr.get());
285 unsigned Mismatched = Coverage->getMismatchedCount();
287 colored_ostream(errs(), raw_ostream::RED)
288 << "warning: " << Mismatched << " functions have mismatched data. ";
292 if (CompareFilenamesOnly) {
293 auto CoveredFiles = Coverage.get()->getUniqueSourceFiles();
294 for (auto &SF : SourceFiles) {
295 StringRef SFBase = sys::path::filename(SF);
296 for (const auto &CF : CoveredFiles)
297 if (SFBase == sys::path::filename(CF)) {
298 RemappedFilenames[CF] = SF;
305 demangleSymbols(*Coverage);
310 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
311 if (!ViewOpts.hasDemangler())
314 // Pass function names to the demangler in a temporary file.
316 SmallString<256> InputPath;
318 sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath);
320 error(InputPath, EC.message());
323 tool_output_file InputTOF{InputPath, InputFD};
325 unsigned NumSymbols = 0;
326 for (const auto &Function : Coverage.getCoveredFunctions()) {
327 InputTOF.os() << Function.Name << '\n';
330 InputTOF.os().close();
332 // Use another temporary file to store the demangler's output.
334 SmallString<256> OutputPath;
335 EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD,
338 error(OutputPath, EC.message());
341 tool_output_file OutputTOF{OutputPath, OutputFD};
342 OutputTOF.os().close();
344 // Invoke the demangler.
345 std::vector<const char *> ArgsV;
346 for (const std::string &Arg : ViewOpts.DemanglerOpts)
347 ArgsV.push_back(Arg.c_str());
348 ArgsV.push_back(nullptr);
349 StringRef InputPathRef = InputPath.str();
350 StringRef OutputPathRef = OutputPath.str();
352 const StringRef *Redirects[] = {&InputPathRef, &OutputPathRef, &StderrRef};
354 int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV.data(),
355 /*env=*/nullptr, Redirects, /*secondsToWait=*/0,
356 /*memoryLimit=*/0, &ErrMsg);
358 error(ErrMsg, ViewOpts.DemanglerOpts[0]);
362 // Parse the demangler's output.
363 auto BufOrError = MemoryBuffer::getFile(OutputPath);
365 error(OutputPath, BufOrError.getError().message());
369 std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError);
371 SmallVector<StringRef, 8> Symbols;
372 StringRef DemanglerData = DemanglerBuf->getBuffer();
373 DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols,
374 /*KeepEmpty=*/false);
375 if (Symbols.size() != NumSymbols) {
376 error("Demangler did not provide expected number of symbols");
380 // Cache the demangled names.
382 for (const auto &Function : Coverage.getCoveredFunctions())
383 DemangledNames[Function.Name] = Symbols[I++];
386 StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const {
387 const auto DemangledName = DemangledNames.find(Sym);
388 if (DemangledName == DemangledNames.end())
390 return DemangledName->getValue();
393 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
394 cl::opt<std::string, true> ObjectFilename(
395 cl::Positional, cl::Required, cl::location(this->ObjectFilename),
396 cl::desc("Covered executable or object file."));
398 cl::list<std::string> InputSourceFiles(
399 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
401 cl::opt<std::string, true> PGOFilename(
402 "instr-profile", cl::Required, cl::location(this->PGOFilename),
404 "File with the profile data obtained after an instrumented run"));
406 cl::opt<std::string> Arch(
407 "arch", cl::desc("architecture of the coverage mapping binary"));
409 cl::opt<bool> DebugDump("dump", cl::Optional,
410 cl::desc("Show internal debug dump"));
412 cl::opt<CoverageViewOptions::OutputFormat> Format(
413 "format", cl::desc("Output format for line-based coverage reports"),
414 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
416 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html",
419 cl::init(CoverageViewOptions::OutputFormat::Text));
421 cl::opt<bool> FilenameEquivalence(
422 "filename-equivalence", cl::Optional,
423 cl::desc("Treat source files as equivalent to paths in the coverage data "
424 "when the file names match, even if the full paths do not"));
426 cl::OptionCategory FilteringCategory("Function filtering options");
428 cl::list<std::string> NameFilters(
429 "name", cl::Optional,
430 cl::desc("Show code coverage only for functions with the given name"),
431 cl::ZeroOrMore, cl::cat(FilteringCategory));
433 cl::list<std::string> NameRegexFilters(
434 "name-regex", cl::Optional,
435 cl::desc("Show code coverage only for functions that match the given "
436 "regular expression"),
437 cl::ZeroOrMore, cl::cat(FilteringCategory));
439 cl::opt<double> RegionCoverageLtFilter(
440 "region-coverage-lt", cl::Optional,
441 cl::desc("Show code coverage only for functions with region coverage "
442 "less than the given threshold"),
443 cl::cat(FilteringCategory));
445 cl::opt<double> RegionCoverageGtFilter(
446 "region-coverage-gt", cl::Optional,
447 cl::desc("Show code coverage only for functions with region coverage "
448 "greater than the given threshold"),
449 cl::cat(FilteringCategory));
451 cl::opt<double> LineCoverageLtFilter(
452 "line-coverage-lt", cl::Optional,
453 cl::desc("Show code coverage only for functions with line coverage less "
454 "than the given threshold"),
455 cl::cat(FilteringCategory));
457 cl::opt<double> LineCoverageGtFilter(
458 "line-coverage-gt", cl::Optional,
459 cl::desc("Show code coverage only for functions with line coverage "
460 "greater than the given threshold"),
461 cl::cat(FilteringCategory));
463 cl::opt<cl::boolOrDefault> UseColor(
464 "use-color", cl::desc("Emit colored output (default=autodetect)"),
465 cl::init(cl::BOU_UNSET));
467 cl::list<std::string> DemanglerOpts(
468 "Xdemangler", cl::desc("<demangler-path>|<demangler-option>"));
470 auto commandLineParser = [&, this](int argc, const char **argv) -> int {
471 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
472 ViewOpts.Debug = DebugDump;
473 CompareFilenamesOnly = FilenameEquivalence;
475 ViewOpts.Format = Format;
476 switch (ViewOpts.Format) {
477 case CoverageViewOptions::OutputFormat::Text:
478 ViewOpts.Colors = UseColor == cl::BOU_UNSET
479 ? sys::Process::StandardOutHasColors()
480 : UseColor == cl::BOU_TRUE;
482 case CoverageViewOptions::OutputFormat::HTML:
483 if (UseColor == cl::BOU_FALSE)
484 error("Color output cannot be disabled when generating html.");
485 ViewOpts.Colors = true;
489 // If a demangler is supplied, check if it exists and register it.
490 if (DemanglerOpts.size()) {
491 auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]);
492 if (!DemanglerPathOrErr) {
493 error("Could not find the demangler!",
494 DemanglerPathOrErr.getError().message());
497 DemanglerOpts[0] = *DemanglerPathOrErr;
498 ViewOpts.DemanglerOpts.swap(DemanglerOpts);
501 // Create the function filters
502 if (!NameFilters.empty() || !NameRegexFilters.empty()) {
503 auto NameFilterer = new CoverageFilters;
504 for (const auto &Name : NameFilters)
505 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
506 for (const auto &Regex : NameRegexFilters)
507 NameFilterer->push_back(
508 llvm::make_unique<NameRegexCoverageFilter>(Regex));
509 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
511 if (RegionCoverageLtFilter.getNumOccurrences() ||
512 RegionCoverageGtFilter.getNumOccurrences() ||
513 LineCoverageLtFilter.getNumOccurrences() ||
514 LineCoverageGtFilter.getNumOccurrences()) {
515 auto StatFilterer = new CoverageFilters;
516 if (RegionCoverageLtFilter.getNumOccurrences())
517 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
518 RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
519 if (RegionCoverageGtFilter.getNumOccurrences())
520 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
521 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
522 if (LineCoverageLtFilter.getNumOccurrences())
523 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
524 LineCoverageFilter::LessThan, LineCoverageLtFilter));
525 if (LineCoverageGtFilter.getNumOccurrences())
526 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
527 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
528 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
532 Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
533 errs() << "error: Unknown architecture: " << Arch << "\n";
538 for (const auto &File : InputSourceFiles) {
539 SmallString<128> Path(File);
540 if (!CompareFilenamesOnly)
541 if (std::error_code EC = sys::fs::make_absolute(Path)) {
542 errs() << "error: " << File << ": " << EC.message();
545 addCollectedPath(Path.str());
552 return show(argc, argv, commandLineParser);
554 return report(argc, argv, commandLineParser);
559 int CodeCoverageTool::show(int argc, const char **argv,
560 CommandLineParserType commandLineParser) {
562 cl::OptionCategory ViewCategory("Viewing options");
564 cl::opt<bool> ShowLineExecutionCounts(
565 "show-line-counts", cl::Optional,
566 cl::desc("Show the execution counts for each line"), cl::init(true),
567 cl::cat(ViewCategory));
569 cl::opt<bool> ShowRegions(
570 "show-regions", cl::Optional,
571 cl::desc("Show the execution counts for each region"),
572 cl::cat(ViewCategory));
574 cl::opt<bool> ShowBestLineRegionsCounts(
575 "show-line-counts-or-regions", cl::Optional,
576 cl::desc("Show the execution counts for each line, or the execution "
577 "counts for each region on lines that have multiple regions"),
578 cl::cat(ViewCategory));
580 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
581 cl::desc("Show expanded source regions"),
582 cl::cat(ViewCategory));
584 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
585 cl::desc("Show function instantiations"),
586 cl::cat(ViewCategory));
588 cl::opt<std::string> ShowOutputDirectory(
589 "output-dir", cl::init(""),
590 cl::desc("Directory in which coverage information is written out"));
591 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"),
592 cl::aliasopt(ShowOutputDirectory));
594 auto Err = commandLineParser(argc, argv);
598 ViewOpts.ShowLineNumbers = true;
599 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
600 !ShowRegions || ShowBestLineRegionsCounts;
601 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
602 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
603 ViewOpts.ShowExpandedRegions = ShowExpansions;
604 ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
605 ViewOpts.ShowOutputDirectory = ShowOutputDirectory;
607 if (ViewOpts.hasOutputDirectory()) {
608 if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) {
609 error("Could not create output directory!", E.message());
614 auto Coverage = load();
618 auto Printer = CoveragePrinter::create(ViewOpts);
620 if (!Filters.empty()) {
621 auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true);
622 if (Error E = OSOrErr.takeError()) {
623 error("Could not create view file!", toString(std::move(E)));
626 auto OS = std::move(OSOrErr.get());
629 for (const auto &Function : Coverage->getCoveredFunctions()) {
630 if (!Filters.matches(Function))
633 auto mainView = createFunctionView(Function, *Coverage);
635 ViewOpts.colored_ostream(errs(), raw_ostream::RED)
636 << "warning: Could not read coverage for '" << Function.Name << "'."
641 mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true);
644 Printer->closeViewFile(std::move(OS));
649 bool ShowFilenames = SourceFiles.size() != 1;
651 if (SourceFiles.empty())
652 // Get the source files from the function coverage mapping.
653 for (StringRef Filename : Coverage->getUniqueSourceFiles())
654 SourceFiles.push_back(Filename);
656 // Create an index out of the source files.
657 if (ViewOpts.hasOutputDirectory()) {
658 if (Error E = Printer->createIndexFile(SourceFiles)) {
659 error("Could not create index file!", toString(std::move(E)));
664 // In -output-dir mode, it's safe to use multiple threads to print files.
665 unsigned ThreadCount = 1;
666 if (ViewOpts.hasOutputDirectory())
667 ThreadCount = std::thread::hardware_concurrency();
668 ThreadPool Pool(ThreadCount);
670 for (StringRef SourceFile : SourceFiles) {
671 Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames] {
672 auto View = createSourceFileView(SourceFile, *Coverage);
674 deferWarning("The file '" + SourceFile.str() + "' isn't covered.");
678 auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false);
679 if (Error E = OSOrErr.takeError()) {
680 deferError("Could not create view file!", toString(std::move(E)));
683 auto OS = std::move(OSOrErr.get());
685 View->print(*OS.get(), /*Wholefile=*/true,
686 /*ShowSourceName=*/ShowFilenames);
687 Printer->closeViewFile(std::move(OS));
693 consumeDeferredMessages();
698 int CodeCoverageTool::report(int argc, const char **argv,
699 CommandLineParserType commandLineParser) {
700 auto Err = commandLineParser(argc, argv);
704 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML)
705 error("HTML output for summary reports is not yet supported.");
707 auto Coverage = load();
711 CoverageReport Report(ViewOpts, std::move(Coverage));
712 if (SourceFiles.empty())
713 Report.renderFileReports(llvm::outs());
715 Report.renderFunctionReports(SourceFiles, llvm::outs());
719 int showMain(int argc, const char *argv[]) {
720 CodeCoverageTool Tool;
721 return Tool.run(CodeCoverageTool::Show, argc, argv);
724 int reportMain(int argc, const char *argv[]) {
725 CodeCoverageTool Tool;
726 return Tool.run(CodeCoverageTool::Report, argc, argv);