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