10b57cec5SDimitry Andric //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // The 'CodeCoverageTool' class implements a command line tool to analyze and 100b57cec5SDimitry Andric // report coverage information using the profiling instrumentation and code 110b57cec5SDimitry Andric // coverage mapping. 120b57cec5SDimitry Andric // 130b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "CoverageExporterJson.h" 160b57cec5SDimitry Andric #include "CoverageExporterLcov.h" 170b57cec5SDimitry Andric #include "CoverageFilters.h" 180b57cec5SDimitry Andric #include "CoverageReport.h" 190b57cec5SDimitry Andric #include "CoverageSummaryInfo.h" 200b57cec5SDimitry Andric #include "CoverageViewOptions.h" 210b57cec5SDimitry Andric #include "RenderingSupport.h" 220b57cec5SDimitry Andric #include "SourceCoverageView.h" 230b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 240b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 250b57cec5SDimitry Andric #include "llvm/ADT/Triple.h" 26*1ac55f4cSDimitry Andric #include "llvm/Debuginfod/BuildIDFetcher.h" 27*1ac55f4cSDimitry Andric #include "llvm/Debuginfod/Debuginfod.h" 28*1ac55f4cSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h" 29*1ac55f4cSDimitry Andric #include "llvm/Object/BuildID.h" 300b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMapping.h" 310b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h" 320b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 330b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 340b57cec5SDimitry Andric #include "llvm/Support/Format.h" 350b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 360b57cec5SDimitry Andric #include "llvm/Support/Path.h" 370b57cec5SDimitry Andric #include "llvm/Support/Process.h" 380b57cec5SDimitry Andric #include "llvm/Support/Program.h" 390b57cec5SDimitry Andric #include "llvm/Support/ScopedPrinter.h" 405ffd83dbSDimitry Andric #include "llvm/Support/SpecialCaseList.h" 410b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h" 420b57cec5SDimitry Andric #include "llvm/Support/Threading.h" 430b57cec5SDimitry Andric #include "llvm/Support/ToolOutputFile.h" 44480093f4SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 450b57cec5SDimitry Andric 460b57cec5SDimitry Andric #include <functional> 470b57cec5SDimitry Andric #include <map> 48bdd1243dSDimitry Andric #include <optional> 490b57cec5SDimitry Andric #include <system_error> 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric using namespace llvm; 520b57cec5SDimitry Andric using namespace coverage; 530b57cec5SDimitry Andric 540b57cec5SDimitry Andric void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping, 550b57cec5SDimitry Andric const CoverageViewOptions &Options, 560b57cec5SDimitry Andric raw_ostream &OS); 570b57cec5SDimitry Andric 580b57cec5SDimitry Andric namespace { 590b57cec5SDimitry Andric /// The implementation of the coverage tool. 600b57cec5SDimitry Andric class CodeCoverageTool { 610b57cec5SDimitry Andric public: 620b57cec5SDimitry Andric enum Command { 630b57cec5SDimitry Andric /// The show command. 640b57cec5SDimitry Andric Show, 650b57cec5SDimitry Andric /// The report command. 660b57cec5SDimitry Andric Report, 670b57cec5SDimitry Andric /// The export command. 680b57cec5SDimitry Andric Export 690b57cec5SDimitry Andric }; 700b57cec5SDimitry Andric 710b57cec5SDimitry Andric int run(Command Cmd, int argc, const char **argv); 720b57cec5SDimitry Andric 730b57cec5SDimitry Andric private: 740b57cec5SDimitry Andric /// Print the error message to the error output stream. 750b57cec5SDimitry Andric void error(const Twine &Message, StringRef Whence = ""); 760b57cec5SDimitry Andric 770b57cec5SDimitry Andric /// Print the warning message to the error output stream. 780b57cec5SDimitry Andric void warning(const Twine &Message, StringRef Whence = ""); 790b57cec5SDimitry Andric 800b57cec5SDimitry Andric /// Convert \p Path into an absolute path and append it to the list 810b57cec5SDimitry Andric /// of collected paths. 820b57cec5SDimitry Andric void addCollectedPath(const std::string &Path); 830b57cec5SDimitry Andric 840b57cec5SDimitry Andric /// If \p Path is a regular file, collect the path. If it's a 850b57cec5SDimitry Andric /// directory, recursively collect all of the paths within the directory. 860b57cec5SDimitry Andric void collectPaths(const std::string &Path); 870b57cec5SDimitry Andric 88fe6060f1SDimitry Andric /// Check if the two given files are the same file. 89fe6060f1SDimitry Andric bool isEquivalentFile(StringRef FilePath1, StringRef FilePath2); 90fe6060f1SDimitry Andric 91fe6060f1SDimitry Andric /// Retrieve a file status with a cache. 92bdd1243dSDimitry Andric std::optional<sys::fs::file_status> getFileStatus(StringRef FilePath); 93fe6060f1SDimitry Andric 940b57cec5SDimitry Andric /// Return a memory buffer for the given source file. 950b57cec5SDimitry Andric ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric /// Create source views for the expansions of the view. 980b57cec5SDimitry Andric void attachExpansionSubViews(SourceCoverageView &View, 990b57cec5SDimitry Andric ArrayRef<ExpansionRecord> Expansions, 1000b57cec5SDimitry Andric const CoverageMapping &Coverage); 1010b57cec5SDimitry Andric 102e8d8bef9SDimitry Andric /// Create source views for the branches of the view. 103e8d8bef9SDimitry Andric void attachBranchSubViews(SourceCoverageView &View, StringRef SourceName, 104e8d8bef9SDimitry Andric ArrayRef<CountedRegion> Branches, 105e8d8bef9SDimitry Andric const MemoryBuffer &File, 106e8d8bef9SDimitry Andric CoverageData &CoverageInfo); 107e8d8bef9SDimitry Andric 1080b57cec5SDimitry Andric /// Create the source view of a particular function. 1090b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 1100b57cec5SDimitry Andric createFunctionView(const FunctionRecord &Function, 1110b57cec5SDimitry Andric const CoverageMapping &Coverage); 1120b57cec5SDimitry Andric 1130b57cec5SDimitry Andric /// Create the main source view of a particular source file. 1140b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 1150b57cec5SDimitry Andric createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric /// Load the coverage mapping data. Return nullptr if an error occurred. 1180b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> load(); 1190b57cec5SDimitry Andric 1200b57cec5SDimitry Andric /// Create a mapping from files in the Coverage data to local copies 1210b57cec5SDimitry Andric /// (path-equivalence). 1220b57cec5SDimitry Andric void remapPathNames(const CoverageMapping &Coverage); 1230b57cec5SDimitry Andric 1240b57cec5SDimitry Andric /// Remove input source files which aren't mapped by \p Coverage. 1250b57cec5SDimitry Andric void removeUnmappedInputs(const CoverageMapping &Coverage); 1260b57cec5SDimitry Andric 1270b57cec5SDimitry Andric /// If a demangler is available, demangle all symbol names. 1280b57cec5SDimitry Andric void demangleSymbols(const CoverageMapping &Coverage); 1290b57cec5SDimitry Andric 1300b57cec5SDimitry Andric /// Write out a source file view to the filesystem. 1310b57cec5SDimitry Andric void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage, 1320b57cec5SDimitry Andric CoveragePrinter *Printer, bool ShowFilenames); 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric int doShow(int argc, const char **argv, 1370b57cec5SDimitry Andric CommandLineParserType commandLineParser); 1380b57cec5SDimitry Andric 1390b57cec5SDimitry Andric int doReport(int argc, const char **argv, 1400b57cec5SDimitry Andric CommandLineParserType commandLineParser); 1410b57cec5SDimitry Andric 1420b57cec5SDimitry Andric int doExport(int argc, const char **argv, 1430b57cec5SDimitry Andric CommandLineParserType commandLineParser); 1440b57cec5SDimitry Andric 1450b57cec5SDimitry Andric std::vector<StringRef> ObjectFilenames; 1460b57cec5SDimitry Andric CoverageViewOptions ViewOpts; 1470b57cec5SDimitry Andric CoverageFiltersMatchAll Filters; 1480b57cec5SDimitry Andric CoverageFilters IgnoreFilenameFilters; 1490b57cec5SDimitry Andric 150e8d8bef9SDimitry Andric /// True if InputSourceFiles are provided. 151e8d8bef9SDimitry Andric bool HadSourceFiles = false; 152e8d8bef9SDimitry Andric 1530b57cec5SDimitry Andric /// The path to the indexed profile. 1540b57cec5SDimitry Andric std::string PGOFilename; 1550b57cec5SDimitry Andric 1560b57cec5SDimitry Andric /// A list of input source files. 1570b57cec5SDimitry Andric std::vector<std::string> SourceFiles; 1580b57cec5SDimitry Andric 1590b57cec5SDimitry Andric /// In -path-equivalence mode, this maps the absolute paths from the coverage 1600b57cec5SDimitry Andric /// mapping data to the input source files. 1610b57cec5SDimitry Andric StringMap<std::string> RemappedFilenames; 1620b57cec5SDimitry Andric 1630b57cec5SDimitry Andric /// The coverage data path to be remapped from, and the source path to be 1640b57cec5SDimitry Andric /// remapped to, when using -path-equivalence. 165bdd1243dSDimitry Andric std::optional<std::pair<std::string, std::string>> PathRemapping; 1660b57cec5SDimitry Andric 167fe6060f1SDimitry Andric /// File status cache used when finding the same file. 168bdd1243dSDimitry Andric StringMap<std::optional<sys::fs::file_status>> FileStatusCache; 169fe6060f1SDimitry Andric 1700b57cec5SDimitry Andric /// The architecture the coverage mapping data targets. 1710b57cec5SDimitry Andric std::vector<StringRef> CoverageArches; 1720b57cec5SDimitry Andric 1730b57cec5SDimitry Andric /// A cache for demangled symbols. 1740b57cec5SDimitry Andric DemangleCache DC; 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric /// A lock which guards printing to stderr. 1770b57cec5SDimitry Andric std::mutex ErrsLock; 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric /// A container for input source file buffers. 1800b57cec5SDimitry Andric std::mutex LoadedSourceFilesLock; 1810b57cec5SDimitry Andric std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> 1820b57cec5SDimitry Andric LoadedSourceFiles; 1830b57cec5SDimitry Andric 1844824e7fdSDimitry Andric /// Allowlist from -name-allowlist to be used for filtering. 1854824e7fdSDimitry Andric std::unique_ptr<SpecialCaseList> NameAllowlist; 186*1ac55f4cSDimitry Andric 187*1ac55f4cSDimitry Andric std::unique_ptr<object::BuildIDFetcher> BIDFetcher; 1880b57cec5SDimitry Andric }; 1890b57cec5SDimitry Andric } 1900b57cec5SDimitry Andric 1910b57cec5SDimitry Andric static std::string getErrorString(const Twine &Message, StringRef Whence, 1920b57cec5SDimitry Andric bool Warning) { 1930b57cec5SDimitry Andric std::string Str = (Warning ? "warning" : "error"); 1940b57cec5SDimitry Andric Str += ": "; 1950b57cec5SDimitry Andric if (!Whence.empty()) 1960b57cec5SDimitry Andric Str += Whence.str() + ": "; 1970b57cec5SDimitry Andric Str += Message.str() + "\n"; 1980b57cec5SDimitry Andric return Str; 1990b57cec5SDimitry Andric } 2000b57cec5SDimitry Andric 2010b57cec5SDimitry Andric void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { 2020b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{ErrsLock}; 2030b57cec5SDimitry Andric ViewOpts.colored_ostream(errs(), raw_ostream::RED) 2040b57cec5SDimitry Andric << getErrorString(Message, Whence, false); 2050b57cec5SDimitry Andric } 2060b57cec5SDimitry Andric 2070b57cec5SDimitry Andric void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { 2080b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{ErrsLock}; 2090b57cec5SDimitry Andric ViewOpts.colored_ostream(errs(), raw_ostream::RED) 2100b57cec5SDimitry Andric << getErrorString(Message, Whence, true); 2110b57cec5SDimitry Andric } 2120b57cec5SDimitry Andric 2130b57cec5SDimitry Andric void CodeCoverageTool::addCollectedPath(const std::string &Path) { 2140b57cec5SDimitry Andric SmallString<128> EffectivePath(Path); 2150b57cec5SDimitry Andric if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) { 2160b57cec5SDimitry Andric error(EC.message(), Path); 2170b57cec5SDimitry Andric return; 2180b57cec5SDimitry Andric } 21904eeddc0SDimitry Andric sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true); 2200b57cec5SDimitry Andric if (!IgnoreFilenameFilters.matchesFilename(EffectivePath)) 2210b57cec5SDimitry Andric SourceFiles.emplace_back(EffectivePath.str()); 222e8d8bef9SDimitry Andric HadSourceFiles = !SourceFiles.empty(); 2230b57cec5SDimitry Andric } 2240b57cec5SDimitry Andric 2250b57cec5SDimitry Andric void CodeCoverageTool::collectPaths(const std::string &Path) { 2260b57cec5SDimitry Andric llvm::sys::fs::file_status Status; 2270b57cec5SDimitry Andric llvm::sys::fs::status(Path, Status); 2280b57cec5SDimitry Andric if (!llvm::sys::fs::exists(Status)) { 2290b57cec5SDimitry Andric if (PathRemapping) 2300b57cec5SDimitry Andric addCollectedPath(Path); 2310b57cec5SDimitry Andric else 2320b57cec5SDimitry Andric warning("Source file doesn't exist, proceeded by ignoring it.", Path); 2330b57cec5SDimitry Andric return; 2340b57cec5SDimitry Andric } 2350b57cec5SDimitry Andric 2360b57cec5SDimitry Andric if (llvm::sys::fs::is_regular_file(Status)) { 2370b57cec5SDimitry Andric addCollectedPath(Path); 2380b57cec5SDimitry Andric return; 2390b57cec5SDimitry Andric } 2400b57cec5SDimitry Andric 2410b57cec5SDimitry Andric if (llvm::sys::fs::is_directory(Status)) { 2420b57cec5SDimitry Andric std::error_code EC; 2430b57cec5SDimitry Andric for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; 2440b57cec5SDimitry Andric F != E; F.increment(EC)) { 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric auto Status = F->status(); 2470b57cec5SDimitry Andric if (!Status) { 2480b57cec5SDimitry Andric warning(Status.getError().message(), F->path()); 2490b57cec5SDimitry Andric continue; 2500b57cec5SDimitry Andric } 2510b57cec5SDimitry Andric 2520b57cec5SDimitry Andric if (Status->type() == llvm::sys::fs::file_type::regular_file) 2530b57cec5SDimitry Andric addCollectedPath(F->path()); 2540b57cec5SDimitry Andric } 2550b57cec5SDimitry Andric } 2560b57cec5SDimitry Andric } 2570b57cec5SDimitry Andric 258bdd1243dSDimitry Andric std::optional<sys::fs::file_status> 259fe6060f1SDimitry Andric CodeCoverageTool::getFileStatus(StringRef FilePath) { 260fe6060f1SDimitry Andric auto It = FileStatusCache.try_emplace(FilePath); 261fe6060f1SDimitry Andric auto &CachedStatus = It.first->getValue(); 262fe6060f1SDimitry Andric if (!It.second) 263fe6060f1SDimitry Andric return CachedStatus; 264fe6060f1SDimitry Andric 265fe6060f1SDimitry Andric sys::fs::file_status Status; 266fe6060f1SDimitry Andric if (!sys::fs::status(FilePath, Status)) 267fe6060f1SDimitry Andric CachedStatus = Status; 268fe6060f1SDimitry Andric return CachedStatus; 269fe6060f1SDimitry Andric } 270fe6060f1SDimitry Andric 271fe6060f1SDimitry Andric bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1, 272fe6060f1SDimitry Andric StringRef FilePath2) { 273fe6060f1SDimitry Andric auto Status1 = getFileStatus(FilePath1); 274fe6060f1SDimitry Andric auto Status2 = getFileStatus(FilePath2); 27581ad6265SDimitry Andric return Status1 && Status2 && sys::fs::equivalent(*Status1, *Status2); 276fe6060f1SDimitry Andric } 277fe6060f1SDimitry Andric 2780b57cec5SDimitry Andric ErrorOr<const MemoryBuffer &> 2790b57cec5SDimitry Andric CodeCoverageTool::getSourceFile(StringRef SourceFile) { 2800b57cec5SDimitry Andric // If we've remapped filenames, look up the real location for this file. 2810b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock}; 2820b57cec5SDimitry Andric if (!RemappedFilenames.empty()) { 2830b57cec5SDimitry Andric auto Loc = RemappedFilenames.find(SourceFile); 2840b57cec5SDimitry Andric if (Loc != RemappedFilenames.end()) 2850b57cec5SDimitry Andric SourceFile = Loc->second; 2860b57cec5SDimitry Andric } 2870b57cec5SDimitry Andric for (const auto &Files : LoadedSourceFiles) 288fe6060f1SDimitry Andric if (isEquivalentFile(SourceFile, Files.first)) 2890b57cec5SDimitry Andric return *Files.second; 2900b57cec5SDimitry Andric auto Buffer = MemoryBuffer::getFile(SourceFile); 2910b57cec5SDimitry Andric if (auto EC = Buffer.getError()) { 2920b57cec5SDimitry Andric error(EC.message(), SourceFile); 2930b57cec5SDimitry Andric return EC; 2940b57cec5SDimitry Andric } 2955ffd83dbSDimitry Andric LoadedSourceFiles.emplace_back(std::string(SourceFile), 2965ffd83dbSDimitry Andric std::move(Buffer.get())); 2970b57cec5SDimitry Andric return *LoadedSourceFiles.back().second; 2980b57cec5SDimitry Andric } 2990b57cec5SDimitry Andric 3000b57cec5SDimitry Andric void CodeCoverageTool::attachExpansionSubViews( 3010b57cec5SDimitry Andric SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions, 3020b57cec5SDimitry Andric const CoverageMapping &Coverage) { 3030b57cec5SDimitry Andric if (!ViewOpts.ShowExpandedRegions) 3040b57cec5SDimitry Andric return; 3050b57cec5SDimitry Andric for (const auto &Expansion : Expansions) { 3060b57cec5SDimitry Andric auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); 3070b57cec5SDimitry Andric if (ExpansionCoverage.empty()) 3080b57cec5SDimitry Andric continue; 3090b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename()); 3100b57cec5SDimitry Andric if (!SourceBuffer) 3110b57cec5SDimitry Andric continue; 3120b57cec5SDimitry Andric 313e8d8bef9SDimitry Andric auto SubViewBranches = ExpansionCoverage.getBranches(); 3140b57cec5SDimitry Andric auto SubViewExpansions = ExpansionCoverage.getExpansions(); 3150b57cec5SDimitry Andric auto SubView = 3160b57cec5SDimitry Andric SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), 3170b57cec5SDimitry Andric ViewOpts, std::move(ExpansionCoverage)); 3180b57cec5SDimitry Andric attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 319e8d8bef9SDimitry Andric attachBranchSubViews(*SubView, Expansion.Function.Name, SubViewBranches, 320e8d8bef9SDimitry Andric SourceBuffer.get(), ExpansionCoverage); 3210b57cec5SDimitry Andric View.addExpansion(Expansion.Region, std::move(SubView)); 3220b57cec5SDimitry Andric } 3230b57cec5SDimitry Andric } 3240b57cec5SDimitry Andric 325e8d8bef9SDimitry Andric void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View, 326e8d8bef9SDimitry Andric StringRef SourceName, 327e8d8bef9SDimitry Andric ArrayRef<CountedRegion> Branches, 328e8d8bef9SDimitry Andric const MemoryBuffer &File, 329e8d8bef9SDimitry Andric CoverageData &CoverageInfo) { 330e8d8bef9SDimitry Andric if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents) 331e8d8bef9SDimitry Andric return; 332e8d8bef9SDimitry Andric 333e8d8bef9SDimitry Andric const auto *NextBranch = Branches.begin(); 334e8d8bef9SDimitry Andric const auto *EndBranch = Branches.end(); 335e8d8bef9SDimitry Andric 336e8d8bef9SDimitry Andric // Group branches that have the same line number into the same subview. 337e8d8bef9SDimitry Andric while (NextBranch != EndBranch) { 338e8d8bef9SDimitry Andric std::vector<CountedRegion> ViewBranches; 339e8d8bef9SDimitry Andric unsigned CurrentLine = NextBranch->LineStart; 340e8d8bef9SDimitry Andric 341e8d8bef9SDimitry Andric while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) 342e8d8bef9SDimitry Andric ViewBranches.push_back(*NextBranch++); 343e8d8bef9SDimitry Andric 344e8d8bef9SDimitry Andric if (!ViewBranches.empty()) { 345e8d8bef9SDimitry Andric auto SubView = SourceCoverageView::create(SourceName, File, ViewOpts, 346e8d8bef9SDimitry Andric std::move(CoverageInfo)); 347e8d8bef9SDimitry Andric View.addBranch(CurrentLine, ViewBranches, std::move(SubView)); 348e8d8bef9SDimitry Andric } 349e8d8bef9SDimitry Andric } 350e8d8bef9SDimitry Andric } 351e8d8bef9SDimitry Andric 3520b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 3530b57cec5SDimitry Andric CodeCoverageTool::createFunctionView(const FunctionRecord &Function, 3540b57cec5SDimitry Andric const CoverageMapping &Coverage) { 3550b57cec5SDimitry Andric auto FunctionCoverage = Coverage.getCoverageForFunction(Function); 3560b57cec5SDimitry Andric if (FunctionCoverage.empty()) 3570b57cec5SDimitry Andric return nullptr; 3580b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); 3590b57cec5SDimitry Andric if (!SourceBuffer) 3600b57cec5SDimitry Andric return nullptr; 3610b57cec5SDimitry Andric 362e8d8bef9SDimitry Andric auto Branches = FunctionCoverage.getBranches(); 3630b57cec5SDimitry Andric auto Expansions = FunctionCoverage.getExpansions(); 3640b57cec5SDimitry Andric auto View = SourceCoverageView::create(DC.demangle(Function.Name), 3650b57cec5SDimitry Andric SourceBuffer.get(), ViewOpts, 3660b57cec5SDimitry Andric std::move(FunctionCoverage)); 3670b57cec5SDimitry Andric attachExpansionSubViews(*View, Expansions, Coverage); 368e8d8bef9SDimitry Andric attachBranchSubViews(*View, DC.demangle(Function.Name), Branches, 369e8d8bef9SDimitry Andric SourceBuffer.get(), FunctionCoverage); 3700b57cec5SDimitry Andric 3710b57cec5SDimitry Andric return View; 3720b57cec5SDimitry Andric } 3730b57cec5SDimitry Andric 3740b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 3750b57cec5SDimitry Andric CodeCoverageTool::createSourceFileView(StringRef SourceFile, 3760b57cec5SDimitry Andric const CoverageMapping &Coverage) { 3770b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(SourceFile); 3780b57cec5SDimitry Andric if (!SourceBuffer) 3790b57cec5SDimitry Andric return nullptr; 3800b57cec5SDimitry Andric auto FileCoverage = Coverage.getCoverageForFile(SourceFile); 3810b57cec5SDimitry Andric if (FileCoverage.empty()) 3820b57cec5SDimitry Andric return nullptr; 3830b57cec5SDimitry Andric 384e8d8bef9SDimitry Andric auto Branches = FileCoverage.getBranches(); 3850b57cec5SDimitry Andric auto Expansions = FileCoverage.getExpansions(); 3860b57cec5SDimitry Andric auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), 3870b57cec5SDimitry Andric ViewOpts, std::move(FileCoverage)); 3880b57cec5SDimitry Andric attachExpansionSubViews(*View, Expansions, Coverage); 389e8d8bef9SDimitry Andric attachBranchSubViews(*View, SourceFile, Branches, SourceBuffer.get(), 390e8d8bef9SDimitry Andric FileCoverage); 3910b57cec5SDimitry Andric if (!ViewOpts.ShowFunctionInstantiations) 3920b57cec5SDimitry Andric return View; 3930b57cec5SDimitry Andric 3940b57cec5SDimitry Andric for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) { 3950b57cec5SDimitry Andric // Skip functions which have a single instantiation. 3960b57cec5SDimitry Andric if (Group.size() < 2) 3970b57cec5SDimitry Andric continue; 3980b57cec5SDimitry Andric 3990b57cec5SDimitry Andric for (const FunctionRecord *Function : Group.getInstantiations()) { 4000b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> SubView{nullptr}; 4010b57cec5SDimitry Andric 4020b57cec5SDimitry Andric StringRef Funcname = DC.demangle(Function->Name); 4030b57cec5SDimitry Andric 4040b57cec5SDimitry Andric if (Function->ExecutionCount > 0) { 4050b57cec5SDimitry Andric auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); 4060b57cec5SDimitry Andric auto SubViewExpansions = SubViewCoverage.getExpansions(); 407e8d8bef9SDimitry Andric auto SubViewBranches = SubViewCoverage.getBranches(); 4080b57cec5SDimitry Andric SubView = SourceCoverageView::create( 4090b57cec5SDimitry Andric Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); 4100b57cec5SDimitry Andric attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 411e8d8bef9SDimitry Andric attachBranchSubViews(*SubView, SourceFile, SubViewBranches, 412e8d8bef9SDimitry Andric SourceBuffer.get(), SubViewCoverage); 4130b57cec5SDimitry Andric } 4140b57cec5SDimitry Andric 4150b57cec5SDimitry Andric unsigned FileID = Function->CountedRegions.front().FileID; 4160b57cec5SDimitry Andric unsigned Line = 0; 4170b57cec5SDimitry Andric for (const auto &CR : Function->CountedRegions) 4180b57cec5SDimitry Andric if (CR.FileID == FileID) 4190b57cec5SDimitry Andric Line = std::max(CR.LineEnd, Line); 4200b57cec5SDimitry Andric View->addInstantiation(Funcname, Line, std::move(SubView)); 4210b57cec5SDimitry Andric } 4220b57cec5SDimitry Andric } 4230b57cec5SDimitry Andric return View; 4240b57cec5SDimitry Andric } 4250b57cec5SDimitry Andric 4260b57cec5SDimitry Andric static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { 4270b57cec5SDimitry Andric sys::fs::file_status Status; 4280b57cec5SDimitry Andric if (sys::fs::status(LHS, Status)) 4290b57cec5SDimitry Andric return false; 4300b57cec5SDimitry Andric auto LHSTime = Status.getLastModificationTime(); 4310b57cec5SDimitry Andric if (sys::fs::status(RHS, Status)) 4320b57cec5SDimitry Andric return false; 4330b57cec5SDimitry Andric auto RHSTime = Status.getLastModificationTime(); 4340b57cec5SDimitry Andric return LHSTime > RHSTime; 4350b57cec5SDimitry Andric } 4360b57cec5SDimitry Andric 4370b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { 4380b57cec5SDimitry Andric for (StringRef ObjectFilename : ObjectFilenames) 4390b57cec5SDimitry Andric if (modifiedTimeGT(ObjectFilename, PGOFilename)) 4400b57cec5SDimitry Andric warning("profile data may be out of date - object is newer", 4410b57cec5SDimitry Andric ObjectFilename); 4420b57cec5SDimitry Andric auto CoverageOrErr = 443fe6060f1SDimitry Andric CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches, 444*1ac55f4cSDimitry Andric ViewOpts.CompilationDirectory, BIDFetcher.get()); 4450b57cec5SDimitry Andric if (Error E = CoverageOrErr.takeError()) { 446fcaf7f86SDimitry Andric error("Failed to load coverage: " + toString(std::move(E))); 4470b57cec5SDimitry Andric return nullptr; 4480b57cec5SDimitry Andric } 4490b57cec5SDimitry Andric auto Coverage = std::move(CoverageOrErr.get()); 4500b57cec5SDimitry Andric unsigned Mismatched = Coverage->getMismatchedCount(); 4510b57cec5SDimitry Andric if (Mismatched) { 4520b57cec5SDimitry Andric warning(Twine(Mismatched) + " functions have mismatched data"); 4530b57cec5SDimitry Andric 4540b57cec5SDimitry Andric if (ViewOpts.Debug) { 4550b57cec5SDimitry Andric for (const auto &HashMismatch : Coverage->getHashMismatches()) 4560b57cec5SDimitry Andric errs() << "hash-mismatch: " 4570b57cec5SDimitry Andric << "No profile record found for '" << HashMismatch.first << "'" 4580b57cec5SDimitry Andric << " with hash = 0x" << Twine::utohexstr(HashMismatch.second) 4590b57cec5SDimitry Andric << '\n'; 4600b57cec5SDimitry Andric } 4610b57cec5SDimitry Andric } 4620b57cec5SDimitry Andric 4630b57cec5SDimitry Andric remapPathNames(*Coverage); 4640b57cec5SDimitry Andric 4650b57cec5SDimitry Andric if (!SourceFiles.empty()) 4660b57cec5SDimitry Andric removeUnmappedInputs(*Coverage); 4670b57cec5SDimitry Andric 4680b57cec5SDimitry Andric demangleSymbols(*Coverage); 4690b57cec5SDimitry Andric 4700b57cec5SDimitry Andric return Coverage; 4710b57cec5SDimitry Andric } 4720b57cec5SDimitry Andric 4730b57cec5SDimitry Andric void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { 4740b57cec5SDimitry Andric if (!PathRemapping) 4750b57cec5SDimitry Andric return; 4760b57cec5SDimitry Andric 4770b57cec5SDimitry Andric // Convert remapping paths to native paths with trailing seperators. 4780b57cec5SDimitry Andric auto nativeWithTrailing = [](StringRef Path) -> std::string { 4790b57cec5SDimitry Andric if (Path.empty()) 4800b57cec5SDimitry Andric return ""; 4810b57cec5SDimitry Andric SmallString<128> NativePath; 4820b57cec5SDimitry Andric sys::path::native(Path, NativePath); 483e8d8bef9SDimitry Andric sys::path::remove_dots(NativePath, true); 484fe6060f1SDimitry Andric if (!NativePath.empty() && !sys::path::is_separator(NativePath.back())) 4850b57cec5SDimitry Andric NativePath += sys::path::get_separator(); 4860b57cec5SDimitry Andric return NativePath.c_str(); 4870b57cec5SDimitry Andric }; 4880b57cec5SDimitry Andric std::string RemapFrom = nativeWithTrailing(PathRemapping->first); 4890b57cec5SDimitry Andric std::string RemapTo = nativeWithTrailing(PathRemapping->second); 4900b57cec5SDimitry Andric 4910b57cec5SDimitry Andric // Create a mapping from coverage data file paths to local paths. 4920b57cec5SDimitry Andric for (StringRef Filename : Coverage.getUniqueSourceFiles()) { 4930b57cec5SDimitry Andric SmallString<128> NativeFilename; 4940b57cec5SDimitry Andric sys::path::native(Filename, NativeFilename); 495e8d8bef9SDimitry Andric sys::path::remove_dots(NativeFilename, true); 4960b57cec5SDimitry Andric if (NativeFilename.startswith(RemapFrom)) { 4970b57cec5SDimitry Andric RemappedFilenames[Filename] = 4980b57cec5SDimitry Andric RemapTo + NativeFilename.substr(RemapFrom.size()).str(); 4990b57cec5SDimitry Andric } 5000b57cec5SDimitry Andric } 5010b57cec5SDimitry Andric 5020b57cec5SDimitry Andric // Convert input files from local paths to coverage data file paths. 5030b57cec5SDimitry Andric StringMap<std::string> InvRemappedFilenames; 5040b57cec5SDimitry Andric for (const auto &RemappedFilename : RemappedFilenames) 5055ffd83dbSDimitry Andric InvRemappedFilenames[RemappedFilename.getValue()] = 5065ffd83dbSDimitry Andric std::string(RemappedFilename.getKey()); 5070b57cec5SDimitry Andric 5080b57cec5SDimitry Andric for (std::string &Filename : SourceFiles) { 5090b57cec5SDimitry Andric SmallString<128> NativeFilename; 5100b57cec5SDimitry Andric sys::path::native(Filename, NativeFilename); 5110b57cec5SDimitry Andric auto CovFileName = InvRemappedFilenames.find(NativeFilename); 5120b57cec5SDimitry Andric if (CovFileName != InvRemappedFilenames.end()) 5130b57cec5SDimitry Andric Filename = CovFileName->second; 5140b57cec5SDimitry Andric } 5150b57cec5SDimitry Andric } 5160b57cec5SDimitry Andric 5170b57cec5SDimitry Andric void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { 5180b57cec5SDimitry Andric std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); 5190b57cec5SDimitry Andric 5200b57cec5SDimitry Andric // The user may have specified source files which aren't in the coverage 5210b57cec5SDimitry Andric // mapping. Filter these files away. 522e8d8bef9SDimitry Andric llvm::erase_if(SourceFiles, [&](const std::string &SF) { 523e8d8bef9SDimitry Andric return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF); 5240b57cec5SDimitry Andric }); 5250b57cec5SDimitry Andric } 5260b57cec5SDimitry Andric 5270b57cec5SDimitry Andric void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { 5280b57cec5SDimitry Andric if (!ViewOpts.hasDemangler()) 5290b57cec5SDimitry Andric return; 5300b57cec5SDimitry Andric 5310b57cec5SDimitry Andric // Pass function names to the demangler in a temporary file. 5320b57cec5SDimitry Andric int InputFD; 5330b57cec5SDimitry Andric SmallString<256> InputPath; 5340b57cec5SDimitry Andric std::error_code EC = 5350b57cec5SDimitry Andric sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath); 5360b57cec5SDimitry Andric if (EC) { 5370b57cec5SDimitry Andric error(InputPath, EC.message()); 5380b57cec5SDimitry Andric return; 5390b57cec5SDimitry Andric } 5400b57cec5SDimitry Andric ToolOutputFile InputTOF{InputPath, InputFD}; 5410b57cec5SDimitry Andric 5420b57cec5SDimitry Andric unsigned NumSymbols = 0; 5430b57cec5SDimitry Andric for (const auto &Function : Coverage.getCoveredFunctions()) { 5440b57cec5SDimitry Andric InputTOF.os() << Function.Name << '\n'; 5450b57cec5SDimitry Andric ++NumSymbols; 5460b57cec5SDimitry Andric } 5470b57cec5SDimitry Andric InputTOF.os().close(); 5480b57cec5SDimitry Andric 5490b57cec5SDimitry Andric // Use another temporary file to store the demangler's output. 5500b57cec5SDimitry Andric int OutputFD; 5510b57cec5SDimitry Andric SmallString<256> OutputPath; 5520b57cec5SDimitry Andric EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD, 5530b57cec5SDimitry Andric OutputPath); 5540b57cec5SDimitry Andric if (EC) { 5550b57cec5SDimitry Andric error(OutputPath, EC.message()); 5560b57cec5SDimitry Andric return; 5570b57cec5SDimitry Andric } 5580b57cec5SDimitry Andric ToolOutputFile OutputTOF{OutputPath, OutputFD}; 5590b57cec5SDimitry Andric OutputTOF.os().close(); 5600b57cec5SDimitry Andric 5610b57cec5SDimitry Andric // Invoke the demangler. 5620b57cec5SDimitry Andric std::vector<StringRef> ArgsV; 563bdd1243dSDimitry Andric ArgsV.reserve(ViewOpts.DemanglerOpts.size()); 5640b57cec5SDimitry Andric for (StringRef Arg : ViewOpts.DemanglerOpts) 5650b57cec5SDimitry Andric ArgsV.push_back(Arg); 566bdd1243dSDimitry Andric std::optional<StringRef> Redirects[] = { 567bdd1243dSDimitry Andric InputPath.str(), OutputPath.str(), {""}}; 5680b57cec5SDimitry Andric std::string ErrMsg; 569bdd1243dSDimitry Andric int RC = 570bdd1243dSDimitry Andric sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV, 571bdd1243dSDimitry Andric /*env=*/std::nullopt, Redirects, /*secondsToWait=*/0, 5720b57cec5SDimitry Andric /*memoryLimit=*/0, &ErrMsg); 5730b57cec5SDimitry Andric if (RC) { 5740b57cec5SDimitry Andric error(ErrMsg, ViewOpts.DemanglerOpts[0]); 5750b57cec5SDimitry Andric return; 5760b57cec5SDimitry Andric } 5770b57cec5SDimitry Andric 5780b57cec5SDimitry Andric // Parse the demangler's output. 5790b57cec5SDimitry Andric auto BufOrError = MemoryBuffer::getFile(OutputPath); 5800b57cec5SDimitry Andric if (!BufOrError) { 5810b57cec5SDimitry Andric error(OutputPath, BufOrError.getError().message()); 5820b57cec5SDimitry Andric return; 5830b57cec5SDimitry Andric } 5840b57cec5SDimitry Andric 5850b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError); 5860b57cec5SDimitry Andric 5870b57cec5SDimitry Andric SmallVector<StringRef, 8> Symbols; 5880b57cec5SDimitry Andric StringRef DemanglerData = DemanglerBuf->getBuffer(); 5890b57cec5SDimitry Andric DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols, 5900b57cec5SDimitry Andric /*KeepEmpty=*/false); 5910b57cec5SDimitry Andric if (Symbols.size() != NumSymbols) { 5920b57cec5SDimitry Andric error("Demangler did not provide expected number of symbols"); 5930b57cec5SDimitry Andric return; 5940b57cec5SDimitry Andric } 5950b57cec5SDimitry Andric 5960b57cec5SDimitry Andric // Cache the demangled names. 5970b57cec5SDimitry Andric unsigned I = 0; 5980b57cec5SDimitry Andric for (const auto &Function : Coverage.getCoveredFunctions()) 5990b57cec5SDimitry Andric // On Windows, lines in the demangler's output file end with "\r\n". 6000b57cec5SDimitry Andric // Splitting by '\n' keeps '\r's, so cut them now. 6015ffd83dbSDimitry Andric DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim()); 6020b57cec5SDimitry Andric } 6030b57cec5SDimitry Andric 6040b57cec5SDimitry Andric void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, 6050b57cec5SDimitry Andric CoverageMapping *Coverage, 6060b57cec5SDimitry Andric CoveragePrinter *Printer, 6070b57cec5SDimitry Andric bool ShowFilenames) { 6080b57cec5SDimitry Andric auto View = createSourceFileView(SourceFile, *Coverage); 6090b57cec5SDimitry Andric if (!View) { 6100b57cec5SDimitry Andric warning("The file '" + SourceFile + "' isn't covered."); 6110b57cec5SDimitry Andric return; 6120b57cec5SDimitry Andric } 6130b57cec5SDimitry Andric 6140b57cec5SDimitry Andric auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); 6150b57cec5SDimitry Andric if (Error E = OSOrErr.takeError()) { 6160b57cec5SDimitry Andric error("Could not create view file!", toString(std::move(E))); 6170b57cec5SDimitry Andric return; 6180b57cec5SDimitry Andric } 6190b57cec5SDimitry Andric auto OS = std::move(OSOrErr.get()); 6200b57cec5SDimitry Andric 6210b57cec5SDimitry Andric View->print(*OS.get(), /*Wholefile=*/true, 6220b57cec5SDimitry Andric /*ShowSourceName=*/ShowFilenames, 6230b57cec5SDimitry Andric /*ShowTitle=*/ViewOpts.hasOutputDirectory()); 6240b57cec5SDimitry Andric Printer->closeViewFile(std::move(OS)); 6250b57cec5SDimitry Andric } 6260b57cec5SDimitry Andric 6270b57cec5SDimitry Andric int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 6280b57cec5SDimitry Andric cl::opt<std::string> CovFilename( 6290b57cec5SDimitry Andric cl::Positional, cl::desc("Covered executable or object file.")); 6300b57cec5SDimitry Andric 6310b57cec5SDimitry Andric cl::list<std::string> CovFilenames( 63281ad6265SDimitry Andric "object", cl::desc("Coverage executable or object file")); 633e8d8bef9SDimitry Andric 634e8d8bef9SDimitry Andric cl::opt<bool> DebugDumpCollectedObjects( 635e8d8bef9SDimitry Andric "dump-collected-objects", cl::Optional, cl::Hidden, 636e8d8bef9SDimitry Andric cl::desc("Show the collected coverage object files")); 6370b57cec5SDimitry Andric 638*1ac55f4cSDimitry Andric cl::list<std::string> InputSourceFiles("sources", cl::Positional, 63981ad6265SDimitry Andric cl::desc("<Source files>")); 6400b57cec5SDimitry Andric 6410b57cec5SDimitry Andric cl::opt<bool> DebugDumpCollectedPaths( 6420b57cec5SDimitry Andric "dump-collected-paths", cl::Optional, cl::Hidden, 6430b57cec5SDimitry Andric cl::desc("Show the collected paths to source files")); 6440b57cec5SDimitry Andric 6450b57cec5SDimitry Andric cl::opt<std::string, true> PGOFilename( 6460b57cec5SDimitry Andric "instr-profile", cl::Required, cl::location(this->PGOFilename), 6470b57cec5SDimitry Andric cl::desc( 6480b57cec5SDimitry Andric "File with the profile data obtained after an instrumented run")); 6490b57cec5SDimitry Andric 6500b57cec5SDimitry Andric cl::list<std::string> Arches( 6510b57cec5SDimitry Andric "arch", cl::desc("architectures of the coverage mapping binaries")); 6520b57cec5SDimitry Andric 6530b57cec5SDimitry Andric cl::opt<bool> DebugDump("dump", cl::Optional, 6540b57cec5SDimitry Andric cl::desc("Show internal debug dump")); 6550b57cec5SDimitry Andric 656*1ac55f4cSDimitry Andric cl::list<std::string> DebugFileDirectory( 657*1ac55f4cSDimitry Andric "debug-file-directory", 658*1ac55f4cSDimitry Andric cl::desc("Directories to search for object files by build ID")); 659*1ac55f4cSDimitry Andric cl::opt<bool> Debuginfod( 660*1ac55f4cSDimitry Andric "debuginfod", cl::ZeroOrMore, 661*1ac55f4cSDimitry Andric cl::desc("Use debuginfod to look up object files from profile"), 662*1ac55f4cSDimitry Andric cl::init(canUseDebuginfod())); 663*1ac55f4cSDimitry Andric 6640b57cec5SDimitry Andric cl::opt<CoverageViewOptions::OutputFormat> Format( 6650b57cec5SDimitry Andric "format", cl::desc("Output format for line-based coverage reports"), 6660b57cec5SDimitry Andric cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", 6670b57cec5SDimitry Andric "Text output"), 6680b57cec5SDimitry Andric clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", 6690b57cec5SDimitry Andric "HTML output"), 6700b57cec5SDimitry Andric clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov", 6710b57cec5SDimitry Andric "lcov tracefile output")), 6720b57cec5SDimitry Andric cl::init(CoverageViewOptions::OutputFormat::Text)); 6730b57cec5SDimitry Andric 6740b57cec5SDimitry Andric cl::opt<std::string> PathRemap( 6750b57cec5SDimitry Andric "path-equivalence", cl::Optional, 6760b57cec5SDimitry Andric cl::desc("<from>,<to> Map coverage data paths to local source file " 6770b57cec5SDimitry Andric "paths")); 6780b57cec5SDimitry Andric 6790b57cec5SDimitry Andric cl::OptionCategory FilteringCategory("Function filtering options"); 6800b57cec5SDimitry Andric 6810b57cec5SDimitry Andric cl::list<std::string> NameFilters( 6820b57cec5SDimitry Andric "name", cl::Optional, 6830b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with the given name"), 68481ad6265SDimitry Andric cl::cat(FilteringCategory)); 6850b57cec5SDimitry Andric 6860b57cec5SDimitry Andric cl::list<std::string> NameFilterFiles( 6874824e7fdSDimitry Andric "name-allowlist", cl::Optional, 6880b57cec5SDimitry Andric cl::desc("Show code coverage only for functions listed in the given " 6890b57cec5SDimitry Andric "file"), 69081ad6265SDimitry Andric cl::cat(FilteringCategory)); 6910b57cec5SDimitry Andric 6920b57cec5SDimitry Andric cl::list<std::string> NameRegexFilters( 6930b57cec5SDimitry Andric "name-regex", cl::Optional, 6940b57cec5SDimitry Andric cl::desc("Show code coverage only for functions that match the given " 6950b57cec5SDimitry Andric "regular expression"), 69681ad6265SDimitry Andric cl::cat(FilteringCategory)); 6970b57cec5SDimitry Andric 6980b57cec5SDimitry Andric cl::list<std::string> IgnoreFilenameRegexFilters( 6990b57cec5SDimitry Andric "ignore-filename-regex", cl::Optional, 7000b57cec5SDimitry Andric cl::desc("Skip source code files with file paths that match the given " 7010b57cec5SDimitry Andric "regular expression"), 70281ad6265SDimitry Andric cl::cat(FilteringCategory)); 7030b57cec5SDimitry Andric 7040b57cec5SDimitry Andric cl::opt<double> RegionCoverageLtFilter( 7050b57cec5SDimitry Andric "region-coverage-lt", cl::Optional, 7060b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with region coverage " 7070b57cec5SDimitry Andric "less than the given threshold"), 7080b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7090b57cec5SDimitry Andric 7100b57cec5SDimitry Andric cl::opt<double> RegionCoverageGtFilter( 7110b57cec5SDimitry Andric "region-coverage-gt", cl::Optional, 7120b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with region coverage " 7130b57cec5SDimitry Andric "greater than the given threshold"), 7140b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7150b57cec5SDimitry Andric 7160b57cec5SDimitry Andric cl::opt<double> LineCoverageLtFilter( 7170b57cec5SDimitry Andric "line-coverage-lt", cl::Optional, 7180b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with line coverage less " 7190b57cec5SDimitry Andric "than the given threshold"), 7200b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7210b57cec5SDimitry Andric 7220b57cec5SDimitry Andric cl::opt<double> LineCoverageGtFilter( 7230b57cec5SDimitry Andric "line-coverage-gt", cl::Optional, 7240b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with line coverage " 7250b57cec5SDimitry Andric "greater than the given threshold"), 7260b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7270b57cec5SDimitry Andric 7280b57cec5SDimitry Andric cl::opt<cl::boolOrDefault> UseColor( 7290b57cec5SDimitry Andric "use-color", cl::desc("Emit colored output (default=autodetect)"), 7300b57cec5SDimitry Andric cl::init(cl::BOU_UNSET)); 7310b57cec5SDimitry Andric 7320b57cec5SDimitry Andric cl::list<std::string> DemanglerOpts( 7330b57cec5SDimitry Andric "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); 7340b57cec5SDimitry Andric 7350b57cec5SDimitry Andric cl::opt<bool> RegionSummary( 7360b57cec5SDimitry Andric "show-region-summary", cl::Optional, 7370b57cec5SDimitry Andric cl::desc("Show region statistics in summary table"), 7380b57cec5SDimitry Andric cl::init(true)); 7390b57cec5SDimitry Andric 740e8d8bef9SDimitry Andric cl::opt<bool> BranchSummary( 741e8d8bef9SDimitry Andric "show-branch-summary", cl::Optional, 742e8d8bef9SDimitry Andric cl::desc("Show branch condition statistics in summary table"), 743e8d8bef9SDimitry Andric cl::init(true)); 744e8d8bef9SDimitry Andric 7450b57cec5SDimitry Andric cl::opt<bool> InstantiationSummary( 7460b57cec5SDimitry Andric "show-instantiation-summary", cl::Optional, 7470b57cec5SDimitry Andric cl::desc("Show instantiation statistics in summary table")); 7480b57cec5SDimitry Andric 7490b57cec5SDimitry Andric cl::opt<bool> SummaryOnly( 7500b57cec5SDimitry Andric "summary-only", cl::Optional, 7510b57cec5SDimitry Andric cl::desc("Export only summary information for each source file")); 7520b57cec5SDimitry Andric 7530b57cec5SDimitry Andric cl::opt<unsigned> NumThreads( 7540b57cec5SDimitry Andric "num-threads", cl::init(0), 7550b57cec5SDimitry Andric cl::desc("Number of merge threads to use (default: autodetect)")); 7560b57cec5SDimitry Andric cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), 7570b57cec5SDimitry Andric cl::aliasopt(NumThreads)); 7580b57cec5SDimitry Andric 759fe6060f1SDimitry Andric cl::opt<std::string> CompilationDirectory( 760fe6060f1SDimitry Andric "compilation-dir", cl::init(""), 761fe6060f1SDimitry Andric cl::desc("Directory used as a base for relative coverage mapping paths")); 762fe6060f1SDimitry Andric 7630b57cec5SDimitry Andric auto commandLineParser = [&, this](int argc, const char **argv) -> int { 7640b57cec5SDimitry Andric cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 7650b57cec5SDimitry Andric ViewOpts.Debug = DebugDump; 766*1ac55f4cSDimitry Andric if (Debuginfod) { 767*1ac55f4cSDimitry Andric HTTPClient::initialize(); 768*1ac55f4cSDimitry Andric BIDFetcher = std::make_unique<DebuginfodFetcher>(DebugFileDirectory); 769*1ac55f4cSDimitry Andric } else { 770*1ac55f4cSDimitry Andric BIDFetcher = std::make_unique<object::BuildIDFetcher>(DebugFileDirectory); 771*1ac55f4cSDimitry Andric } 7720b57cec5SDimitry Andric 7730b57cec5SDimitry Andric if (!CovFilename.empty()) 7740b57cec5SDimitry Andric ObjectFilenames.emplace_back(CovFilename); 7750b57cec5SDimitry Andric for (const std::string &Filename : CovFilenames) 7760b57cec5SDimitry Andric ObjectFilenames.emplace_back(Filename); 777*1ac55f4cSDimitry Andric if (ObjectFilenames.empty() && !Debuginfod && DebugFileDirectory.empty()) { 7780b57cec5SDimitry Andric errs() << "No filenames specified!\n"; 7790b57cec5SDimitry Andric ::exit(1); 7800b57cec5SDimitry Andric } 7810b57cec5SDimitry Andric 782e8d8bef9SDimitry Andric if (DebugDumpCollectedObjects) { 783e8d8bef9SDimitry Andric for (StringRef OF : ObjectFilenames) 784e8d8bef9SDimitry Andric outs() << OF << '\n'; 785e8d8bef9SDimitry Andric ::exit(0); 786e8d8bef9SDimitry Andric } 787e8d8bef9SDimitry Andric 7880b57cec5SDimitry Andric ViewOpts.Format = Format; 7890b57cec5SDimitry Andric switch (ViewOpts.Format) { 7900b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 7910b57cec5SDimitry Andric ViewOpts.Colors = UseColor == cl::BOU_UNSET 7920b57cec5SDimitry Andric ? sys::Process::StandardOutHasColors() 7930b57cec5SDimitry Andric : UseColor == cl::BOU_TRUE; 7940b57cec5SDimitry Andric break; 7950b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 7960b57cec5SDimitry Andric if (UseColor == cl::BOU_FALSE) 7970b57cec5SDimitry Andric errs() << "Color output cannot be disabled when generating html.\n"; 7980b57cec5SDimitry Andric ViewOpts.Colors = true; 7990b57cec5SDimitry Andric break; 8000b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 8010b57cec5SDimitry Andric if (UseColor == cl::BOU_TRUE) 8020b57cec5SDimitry Andric errs() << "Color output cannot be enabled when generating lcov.\n"; 8030b57cec5SDimitry Andric ViewOpts.Colors = false; 8040b57cec5SDimitry Andric break; 8050b57cec5SDimitry Andric } 8060b57cec5SDimitry Andric 8070b57cec5SDimitry Andric // If path-equivalence was given and is a comma seperated pair then set 8080b57cec5SDimitry Andric // PathRemapping. 809349cc55cSDimitry Andric if (!PathRemap.empty()) { 8100b57cec5SDimitry Andric auto EquivPair = StringRef(PathRemap).split(','); 811349cc55cSDimitry Andric if (EquivPair.first.empty() || EquivPair.second.empty()) { 812349cc55cSDimitry Andric error("invalid argument '" + PathRemap + 813349cc55cSDimitry Andric "', must be in format 'from,to'", 814349cc55cSDimitry Andric "-path-equivalence"); 815349cc55cSDimitry Andric return 1; 816349cc55cSDimitry Andric } 817349cc55cSDimitry Andric 8185ffd83dbSDimitry Andric PathRemapping = {std::string(EquivPair.first), 8195ffd83dbSDimitry Andric std::string(EquivPair.second)}; 820349cc55cSDimitry Andric } 8210b57cec5SDimitry Andric 8220b57cec5SDimitry Andric // If a demangler is supplied, check if it exists and register it. 8230b57cec5SDimitry Andric if (!DemanglerOpts.empty()) { 8240b57cec5SDimitry Andric auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); 8250b57cec5SDimitry Andric if (!DemanglerPathOrErr) { 8260b57cec5SDimitry Andric error("Could not find the demangler!", 8270b57cec5SDimitry Andric DemanglerPathOrErr.getError().message()); 8280b57cec5SDimitry Andric return 1; 8290b57cec5SDimitry Andric } 8300b57cec5SDimitry Andric DemanglerOpts[0] = *DemanglerPathOrErr; 8310b57cec5SDimitry Andric ViewOpts.DemanglerOpts.swap(DemanglerOpts); 8320b57cec5SDimitry Andric } 8330b57cec5SDimitry Andric 8344824e7fdSDimitry Andric // Read in -name-allowlist files. 835bdd1243dSDimitry Andric if (!NameFilterFiles.empty()) { 8360b57cec5SDimitry Andric std::string SpecialCaseListErr; 8374824e7fdSDimitry Andric NameAllowlist = SpecialCaseList::create( 838480093f4SDimitry Andric NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr); 8394824e7fdSDimitry Andric if (!NameAllowlist) 8400b57cec5SDimitry Andric error(SpecialCaseListErr); 8410b57cec5SDimitry Andric } 8420b57cec5SDimitry Andric 8430b57cec5SDimitry Andric // Create the function filters 8444824e7fdSDimitry Andric if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) { 8458bcb0991SDimitry Andric auto NameFilterer = std::make_unique<CoverageFilters>(); 8460b57cec5SDimitry Andric for (const auto &Name : NameFilters) 8478bcb0991SDimitry Andric NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name)); 848bdd1243dSDimitry Andric if (NameAllowlist && !NameFilterFiles.empty()) 8490b57cec5SDimitry Andric NameFilterer->push_back( 8504824e7fdSDimitry Andric std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist)); 8510b57cec5SDimitry Andric for (const auto &Regex : NameRegexFilters) 8520b57cec5SDimitry Andric NameFilterer->push_back( 8538bcb0991SDimitry Andric std::make_unique<NameRegexCoverageFilter>(Regex)); 8540b57cec5SDimitry Andric Filters.push_back(std::move(NameFilterer)); 8550b57cec5SDimitry Andric } 8560b57cec5SDimitry Andric 8570b57cec5SDimitry Andric if (RegionCoverageLtFilter.getNumOccurrences() || 8580b57cec5SDimitry Andric RegionCoverageGtFilter.getNumOccurrences() || 8590b57cec5SDimitry Andric LineCoverageLtFilter.getNumOccurrences() || 8600b57cec5SDimitry Andric LineCoverageGtFilter.getNumOccurrences()) { 8618bcb0991SDimitry Andric auto StatFilterer = std::make_unique<CoverageFilters>(); 8620b57cec5SDimitry Andric if (RegionCoverageLtFilter.getNumOccurrences()) 8638bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( 8640b57cec5SDimitry Andric RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 8650b57cec5SDimitry Andric if (RegionCoverageGtFilter.getNumOccurrences()) 8668bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( 8670b57cec5SDimitry Andric RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 8680b57cec5SDimitry Andric if (LineCoverageLtFilter.getNumOccurrences()) 8698bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<LineCoverageFilter>( 8700b57cec5SDimitry Andric LineCoverageFilter::LessThan, LineCoverageLtFilter)); 8710b57cec5SDimitry Andric if (LineCoverageGtFilter.getNumOccurrences()) 8728bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<LineCoverageFilter>( 8730b57cec5SDimitry Andric RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 8740b57cec5SDimitry Andric Filters.push_back(std::move(StatFilterer)); 8750b57cec5SDimitry Andric } 8760b57cec5SDimitry Andric 8770b57cec5SDimitry Andric // Create the ignore filename filters. 8780b57cec5SDimitry Andric for (const auto &RE : IgnoreFilenameRegexFilters) 8790b57cec5SDimitry Andric IgnoreFilenameFilters.push_back( 8808bcb0991SDimitry Andric std::make_unique<NameRegexCoverageFilter>(RE)); 8810b57cec5SDimitry Andric 8820b57cec5SDimitry Andric if (!Arches.empty()) { 8830b57cec5SDimitry Andric for (const std::string &Arch : Arches) { 8840b57cec5SDimitry Andric if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { 8850b57cec5SDimitry Andric error("Unknown architecture: " + Arch); 8860b57cec5SDimitry Andric return 1; 8870b57cec5SDimitry Andric } 8880b57cec5SDimitry Andric CoverageArches.emplace_back(Arch); 8890b57cec5SDimitry Andric } 890*1ac55f4cSDimitry Andric if (CoverageArches.size() != 1 && 891*1ac55f4cSDimitry Andric CoverageArches.size() != ObjectFilenames.size()) { 8920b57cec5SDimitry Andric error("Number of architectures doesn't match the number of objects"); 8930b57cec5SDimitry Andric return 1; 8940b57cec5SDimitry Andric } 8950b57cec5SDimitry Andric } 8960b57cec5SDimitry Andric 8970b57cec5SDimitry Andric // IgnoreFilenameFilters are applied even when InputSourceFiles specified. 8980b57cec5SDimitry Andric for (const std::string &File : InputSourceFiles) 8990b57cec5SDimitry Andric collectPaths(File); 9000b57cec5SDimitry Andric 9010b57cec5SDimitry Andric if (DebugDumpCollectedPaths) { 9020b57cec5SDimitry Andric for (const std::string &SF : SourceFiles) 9030b57cec5SDimitry Andric outs() << SF << '\n'; 9040b57cec5SDimitry Andric ::exit(0); 9050b57cec5SDimitry Andric } 9060b57cec5SDimitry Andric 907e8d8bef9SDimitry Andric ViewOpts.ShowBranchSummary = BranchSummary; 9080b57cec5SDimitry Andric ViewOpts.ShowRegionSummary = RegionSummary; 9090b57cec5SDimitry Andric ViewOpts.ShowInstantiationSummary = InstantiationSummary; 9100b57cec5SDimitry Andric ViewOpts.ExportSummaryOnly = SummaryOnly; 9110b57cec5SDimitry Andric ViewOpts.NumThreads = NumThreads; 912fe6060f1SDimitry Andric ViewOpts.CompilationDirectory = CompilationDirectory; 9130b57cec5SDimitry Andric 9140b57cec5SDimitry Andric return 0; 9150b57cec5SDimitry Andric }; 9160b57cec5SDimitry Andric 9170b57cec5SDimitry Andric switch (Cmd) { 9180b57cec5SDimitry Andric case Show: 9190b57cec5SDimitry Andric return doShow(argc, argv, commandLineParser); 9200b57cec5SDimitry Andric case Report: 9210b57cec5SDimitry Andric return doReport(argc, argv, commandLineParser); 9220b57cec5SDimitry Andric case Export: 9230b57cec5SDimitry Andric return doExport(argc, argv, commandLineParser); 9240b57cec5SDimitry Andric } 9250b57cec5SDimitry Andric return 0; 9260b57cec5SDimitry Andric } 9270b57cec5SDimitry Andric 9280b57cec5SDimitry Andric int CodeCoverageTool::doShow(int argc, const char **argv, 9290b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 9300b57cec5SDimitry Andric 9310b57cec5SDimitry Andric cl::OptionCategory ViewCategory("Viewing options"); 9320b57cec5SDimitry Andric 9330b57cec5SDimitry Andric cl::opt<bool> ShowLineExecutionCounts( 9340b57cec5SDimitry Andric "show-line-counts", cl::Optional, 9350b57cec5SDimitry Andric cl::desc("Show the execution counts for each line"), cl::init(true), 9360b57cec5SDimitry Andric cl::cat(ViewCategory)); 9370b57cec5SDimitry Andric 9380b57cec5SDimitry Andric cl::opt<bool> ShowRegions( 9390b57cec5SDimitry Andric "show-regions", cl::Optional, 9400b57cec5SDimitry Andric cl::desc("Show the execution counts for each region"), 9410b57cec5SDimitry Andric cl::cat(ViewCategory)); 9420b57cec5SDimitry Andric 943e8d8bef9SDimitry Andric cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches( 944e8d8bef9SDimitry Andric "show-branches", cl::Optional, 945e8d8bef9SDimitry Andric cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory), 946e8d8bef9SDimitry Andric cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count, 947e8d8bef9SDimitry Andric "count", "Show True/False counts"), 948e8d8bef9SDimitry Andric clEnumValN(CoverageViewOptions::BranchOutputType::Percent, 949e8d8bef9SDimitry Andric "percent", "Show True/False percent")), 950e8d8bef9SDimitry Andric cl::init(CoverageViewOptions::BranchOutputType::Off)); 951e8d8bef9SDimitry Andric 9520b57cec5SDimitry Andric cl::opt<bool> ShowBestLineRegionsCounts( 9530b57cec5SDimitry Andric "show-line-counts-or-regions", cl::Optional, 9540b57cec5SDimitry Andric cl::desc("Show the execution counts for each line, or the execution " 9550b57cec5SDimitry Andric "counts for each region on lines that have multiple regions"), 9560b57cec5SDimitry Andric cl::cat(ViewCategory)); 9570b57cec5SDimitry Andric 9580b57cec5SDimitry Andric cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 9590b57cec5SDimitry Andric cl::desc("Show expanded source regions"), 9600b57cec5SDimitry Andric cl::cat(ViewCategory)); 9610b57cec5SDimitry Andric 9620b57cec5SDimitry Andric cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 9630b57cec5SDimitry Andric cl::desc("Show function instantiations"), 9640b57cec5SDimitry Andric cl::init(true), cl::cat(ViewCategory)); 9650b57cec5SDimitry Andric 9660b57cec5SDimitry Andric cl::opt<std::string> ShowOutputDirectory( 9670b57cec5SDimitry Andric "output-dir", cl::init(""), 9680b57cec5SDimitry Andric cl::desc("Directory in which coverage information is written out")); 9690b57cec5SDimitry Andric cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), 9700b57cec5SDimitry Andric cl::aliasopt(ShowOutputDirectory)); 9710b57cec5SDimitry Andric 9720b57cec5SDimitry Andric cl::opt<uint32_t> TabSize( 9730b57cec5SDimitry Andric "tab-size", cl::init(2), 9740b57cec5SDimitry Andric cl::desc( 9750b57cec5SDimitry Andric "Set tab expansion size for html coverage reports (default = 2)")); 9760b57cec5SDimitry Andric 9770b57cec5SDimitry Andric cl::opt<std::string> ProjectTitle( 9780b57cec5SDimitry Andric "project-title", cl::Optional, 9790b57cec5SDimitry Andric cl::desc("Set project title for the coverage report")); 9800b57cec5SDimitry Andric 98181ad6265SDimitry Andric cl::opt<std::string> CovWatermark( 98281ad6265SDimitry Andric "coverage-watermark", cl::Optional, 98381ad6265SDimitry Andric cl::desc("<high>,<low> value indicate thresholds for high and low" 98481ad6265SDimitry Andric "coverage watermark")); 98581ad6265SDimitry Andric 9860b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 9870b57cec5SDimitry Andric if (Err) 9880b57cec5SDimitry Andric return Err; 9890b57cec5SDimitry Andric 9900b57cec5SDimitry Andric if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 9910b57cec5SDimitry Andric error("Lcov format should be used with 'llvm-cov export'."); 9920b57cec5SDimitry Andric return 1; 9930b57cec5SDimitry Andric } 9940b57cec5SDimitry Andric 99581ad6265SDimitry Andric ViewOpts.HighCovWatermark = 100.0; 99681ad6265SDimitry Andric ViewOpts.LowCovWatermark = 80.0; 99781ad6265SDimitry Andric if (!CovWatermark.empty()) { 99881ad6265SDimitry Andric auto WaterMarkPair = StringRef(CovWatermark).split(','); 99981ad6265SDimitry Andric if (WaterMarkPair.first.empty() || WaterMarkPair.second.empty()) { 100081ad6265SDimitry Andric error("invalid argument '" + CovWatermark + 100181ad6265SDimitry Andric "', must be in format 'high,low'", 100281ad6265SDimitry Andric "-coverage-watermark"); 100381ad6265SDimitry Andric return 1; 100481ad6265SDimitry Andric } 100581ad6265SDimitry Andric 100681ad6265SDimitry Andric char *EndPointer = nullptr; 100781ad6265SDimitry Andric ViewOpts.HighCovWatermark = 100881ad6265SDimitry Andric strtod(WaterMarkPair.first.begin(), &EndPointer); 100981ad6265SDimitry Andric if (EndPointer != WaterMarkPair.first.end()) { 101081ad6265SDimitry Andric error("invalid number '" + WaterMarkPair.first + 101181ad6265SDimitry Andric "', invalid value for 'high'", 101281ad6265SDimitry Andric "-coverage-watermark"); 101381ad6265SDimitry Andric return 1; 101481ad6265SDimitry Andric } 101581ad6265SDimitry Andric 101681ad6265SDimitry Andric ViewOpts.LowCovWatermark = 101781ad6265SDimitry Andric strtod(WaterMarkPair.second.begin(), &EndPointer); 101881ad6265SDimitry Andric if (EndPointer != WaterMarkPair.second.end()) { 101981ad6265SDimitry Andric error("invalid number '" + WaterMarkPair.second + 102081ad6265SDimitry Andric "', invalid value for 'low'", 102181ad6265SDimitry Andric "-coverage-watermark"); 102281ad6265SDimitry Andric return 1; 102381ad6265SDimitry Andric } 102481ad6265SDimitry Andric 102581ad6265SDimitry Andric if (ViewOpts.HighCovWatermark > 100 || ViewOpts.LowCovWatermark < 0 || 102681ad6265SDimitry Andric ViewOpts.HighCovWatermark <= ViewOpts.LowCovWatermark) { 102781ad6265SDimitry Andric error( 102881ad6265SDimitry Andric "invalid number range '" + CovWatermark + 102981ad6265SDimitry Andric "', must be both high and low should be between 0-100, and high " 103081ad6265SDimitry Andric "> low", 103181ad6265SDimitry Andric "-coverage-watermark"); 103281ad6265SDimitry Andric return 1; 103381ad6265SDimitry Andric } 103481ad6265SDimitry Andric } 103581ad6265SDimitry Andric 10360b57cec5SDimitry Andric ViewOpts.ShowLineNumbers = true; 10370b57cec5SDimitry Andric ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 10380b57cec5SDimitry Andric !ShowRegions || ShowBestLineRegionsCounts; 10390b57cec5SDimitry Andric ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 10400b57cec5SDimitry Andric ViewOpts.ShowExpandedRegions = ShowExpansions; 1041e8d8bef9SDimitry Andric ViewOpts.ShowBranchCounts = 1042e8d8bef9SDimitry Andric ShowBranches == CoverageViewOptions::BranchOutputType::Count; 1043e8d8bef9SDimitry Andric ViewOpts.ShowBranchPercents = 1044e8d8bef9SDimitry Andric ShowBranches == CoverageViewOptions::BranchOutputType::Percent; 10450b57cec5SDimitry Andric ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 10460b57cec5SDimitry Andric ViewOpts.ShowOutputDirectory = ShowOutputDirectory; 10470b57cec5SDimitry Andric ViewOpts.TabSize = TabSize; 10480b57cec5SDimitry Andric ViewOpts.ProjectTitle = ProjectTitle; 10490b57cec5SDimitry Andric 10500b57cec5SDimitry Andric if (ViewOpts.hasOutputDirectory()) { 10510b57cec5SDimitry Andric if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { 10520b57cec5SDimitry Andric error("Could not create output directory!", E.message()); 10530b57cec5SDimitry Andric return 1; 10540b57cec5SDimitry Andric } 10550b57cec5SDimitry Andric } 10560b57cec5SDimitry Andric 10570b57cec5SDimitry Andric sys::fs::file_status Status; 10585ffd83dbSDimitry Andric if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { 1059fcaf7f86SDimitry Andric error("Could not read profile data!" + EC.message(), PGOFilename); 10600b57cec5SDimitry Andric return 1; 10610b57cec5SDimitry Andric } 10620b57cec5SDimitry Andric 10630b57cec5SDimitry Andric auto ModifiedTime = Status.getLastModificationTime(); 10640b57cec5SDimitry Andric std::string ModifiedTimeStr = to_string(ModifiedTime); 10650b57cec5SDimitry Andric size_t found = ModifiedTimeStr.rfind(':'); 10660b57cec5SDimitry Andric ViewOpts.CreatedTimeStr = (found != std::string::npos) 10670b57cec5SDimitry Andric ? "Created: " + ModifiedTimeStr.substr(0, found) 10680b57cec5SDimitry Andric : "Created: " + ModifiedTimeStr; 10690b57cec5SDimitry Andric 10700b57cec5SDimitry Andric auto Coverage = load(); 10710b57cec5SDimitry Andric if (!Coverage) 10720b57cec5SDimitry Andric return 1; 10730b57cec5SDimitry Andric 10740b57cec5SDimitry Andric auto Printer = CoveragePrinter::create(ViewOpts); 10750b57cec5SDimitry Andric 1076e8d8bef9SDimitry Andric if (SourceFiles.empty() && !HadSourceFiles) 10770b57cec5SDimitry Andric // Get the source files from the function coverage mapping. 10780b57cec5SDimitry Andric for (StringRef Filename : Coverage->getUniqueSourceFiles()) { 10790b57cec5SDimitry Andric if (!IgnoreFilenameFilters.matchesFilename(Filename)) 10805ffd83dbSDimitry Andric SourceFiles.push_back(std::string(Filename)); 10810b57cec5SDimitry Andric } 10820b57cec5SDimitry Andric 10830b57cec5SDimitry Andric // Create an index out of the source files. 10840b57cec5SDimitry Andric if (ViewOpts.hasOutputDirectory()) { 10850b57cec5SDimitry Andric if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) { 10860b57cec5SDimitry Andric error("Could not create index file!", toString(std::move(E))); 10870b57cec5SDimitry Andric return 1; 10880b57cec5SDimitry Andric } 10890b57cec5SDimitry Andric } 10900b57cec5SDimitry Andric 10910b57cec5SDimitry Andric if (!Filters.empty()) { 10920b57cec5SDimitry Andric // Build the map of filenames to functions. 10930b57cec5SDimitry Andric std::map<llvm::StringRef, std::vector<const FunctionRecord *>> 10940b57cec5SDimitry Andric FilenameFunctionMap; 10950b57cec5SDimitry Andric for (const auto &SourceFile : SourceFiles) 10960b57cec5SDimitry Andric for (const auto &Function : Coverage->getCoveredFunctions(SourceFile)) 1097bdd1243dSDimitry Andric if (Filters.matches(*Coverage, Function)) 10980b57cec5SDimitry Andric FilenameFunctionMap[SourceFile].push_back(&Function); 10990b57cec5SDimitry Andric 11000b57cec5SDimitry Andric // Only print filter matching functions for each file. 11010b57cec5SDimitry Andric for (const auto &FileFunc : FilenameFunctionMap) { 11020b57cec5SDimitry Andric StringRef File = FileFunc.first; 11030b57cec5SDimitry Andric const auto &Functions = FileFunc.second; 11040b57cec5SDimitry Andric 11050b57cec5SDimitry Andric auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false); 11060b57cec5SDimitry Andric if (Error E = OSOrErr.takeError()) { 11070b57cec5SDimitry Andric error("Could not create view file!", toString(std::move(E))); 11080b57cec5SDimitry Andric return 1; 11090b57cec5SDimitry Andric } 11100b57cec5SDimitry Andric auto OS = std::move(OSOrErr.get()); 11110b57cec5SDimitry Andric 11120b57cec5SDimitry Andric bool ShowTitle = ViewOpts.hasOutputDirectory(); 11130b57cec5SDimitry Andric for (const auto *Function : Functions) { 11140b57cec5SDimitry Andric auto FunctionView = createFunctionView(*Function, *Coverage); 11150b57cec5SDimitry Andric if (!FunctionView) { 11160b57cec5SDimitry Andric warning("Could not read coverage for '" + Function->Name + "'."); 11170b57cec5SDimitry Andric continue; 11180b57cec5SDimitry Andric } 11190b57cec5SDimitry Andric FunctionView->print(*OS.get(), /*WholeFile=*/false, 11200b57cec5SDimitry Andric /*ShowSourceName=*/true, ShowTitle); 11210b57cec5SDimitry Andric ShowTitle = false; 11220b57cec5SDimitry Andric } 11230b57cec5SDimitry Andric 11240b57cec5SDimitry Andric Printer->closeViewFile(std::move(OS)); 11250b57cec5SDimitry Andric } 11260b57cec5SDimitry Andric return 0; 11270b57cec5SDimitry Andric } 11280b57cec5SDimitry Andric 11290b57cec5SDimitry Andric // Show files 11300b57cec5SDimitry Andric bool ShowFilenames = 11310b57cec5SDimitry Andric (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || 11320b57cec5SDimitry Andric (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); 11330b57cec5SDimitry Andric 11345ffd83dbSDimitry Andric ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads); 11355ffd83dbSDimitry Andric if (ViewOpts.NumThreads == 0) { 11365ffd83dbSDimitry Andric // If NumThreads is not specified, create one thread for each input, up to 11375ffd83dbSDimitry Andric // the number of hardware cores. 11385ffd83dbSDimitry Andric S = heavyweight_hardware_concurrency(SourceFiles.size()); 11395ffd83dbSDimitry Andric S.Limit = true; 11405ffd83dbSDimitry Andric } 11410b57cec5SDimitry Andric 11425ffd83dbSDimitry Andric if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) { 11430b57cec5SDimitry Andric for (const std::string &SourceFile : SourceFiles) 11440b57cec5SDimitry Andric writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), 11450b57cec5SDimitry Andric ShowFilenames); 11460b57cec5SDimitry Andric } else { 11470b57cec5SDimitry Andric // In -output-dir mode, it's safe to use multiple threads to print files. 11485ffd83dbSDimitry Andric ThreadPool Pool(S); 11490b57cec5SDimitry Andric for (const std::string &SourceFile : SourceFiles) 11500b57cec5SDimitry Andric Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, 11510b57cec5SDimitry Andric Coverage.get(), Printer.get(), ShowFilenames); 11520b57cec5SDimitry Andric Pool.wait(); 11530b57cec5SDimitry Andric } 11540b57cec5SDimitry Andric 11550b57cec5SDimitry Andric return 0; 11560b57cec5SDimitry Andric } 11570b57cec5SDimitry Andric 11580b57cec5SDimitry Andric int CodeCoverageTool::doReport(int argc, const char **argv, 11590b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 11600b57cec5SDimitry Andric cl::opt<bool> ShowFunctionSummaries( 11610b57cec5SDimitry Andric "show-functions", cl::Optional, cl::init(false), 11620b57cec5SDimitry Andric cl::desc("Show coverage summaries for each function")); 11630b57cec5SDimitry Andric 11640b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 11650b57cec5SDimitry Andric if (Err) 11660b57cec5SDimitry Andric return Err; 11670b57cec5SDimitry Andric 11680b57cec5SDimitry Andric if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { 11690b57cec5SDimitry Andric error("HTML output for summary reports is not yet supported."); 11700b57cec5SDimitry Andric return 1; 11710b57cec5SDimitry Andric } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 11720b57cec5SDimitry Andric error("Lcov format should be used with 'llvm-cov export'."); 11730b57cec5SDimitry Andric return 1; 11740b57cec5SDimitry Andric } 11750b57cec5SDimitry Andric 1176fcaf7f86SDimitry Andric sys::fs::file_status Status; 1177fcaf7f86SDimitry Andric if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { 1178fcaf7f86SDimitry Andric error("Could not read profile data!" + EC.message(), PGOFilename); 1179fcaf7f86SDimitry Andric return 1; 1180fcaf7f86SDimitry Andric } 1181fcaf7f86SDimitry Andric 11820b57cec5SDimitry Andric auto Coverage = load(); 11830b57cec5SDimitry Andric if (!Coverage) 11840b57cec5SDimitry Andric return 1; 11850b57cec5SDimitry Andric 1186bdd1243dSDimitry Andric CoverageReport Report(ViewOpts, *Coverage); 11870b57cec5SDimitry Andric if (!ShowFunctionSummaries) { 11880b57cec5SDimitry Andric if (SourceFiles.empty()) 11890b57cec5SDimitry Andric Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters); 11900b57cec5SDimitry Andric else 11910b57cec5SDimitry Andric Report.renderFileReports(llvm::outs(), SourceFiles); 11920b57cec5SDimitry Andric } else { 11930b57cec5SDimitry Andric if (SourceFiles.empty()) { 11940b57cec5SDimitry Andric error("Source files must be specified when -show-functions=true is " 11950b57cec5SDimitry Andric "specified"); 11960b57cec5SDimitry Andric return 1; 11970b57cec5SDimitry Andric } 11980b57cec5SDimitry Andric 11990b57cec5SDimitry Andric Report.renderFunctionReports(SourceFiles, DC, llvm::outs()); 12000b57cec5SDimitry Andric } 12010b57cec5SDimitry Andric return 0; 12020b57cec5SDimitry Andric } 12030b57cec5SDimitry Andric 12040b57cec5SDimitry Andric int CodeCoverageTool::doExport(int argc, const char **argv, 12050b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 12060b57cec5SDimitry Andric 12070b57cec5SDimitry Andric cl::OptionCategory ExportCategory("Exporting options"); 12080b57cec5SDimitry Andric 12090b57cec5SDimitry Andric cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional, 12100b57cec5SDimitry Andric cl::desc("Don't export expanded source regions"), 12110b57cec5SDimitry Andric cl::cat(ExportCategory)); 12120b57cec5SDimitry Andric 12130b57cec5SDimitry Andric cl::opt<bool> SkipFunctions("skip-functions", cl::Optional, 12140b57cec5SDimitry Andric cl::desc("Don't export per-function data"), 12150b57cec5SDimitry Andric cl::cat(ExportCategory)); 12160b57cec5SDimitry Andric 1217bdd1243dSDimitry Andric cl::opt<bool> SkipBranches("skip-branches", cl::Optional, 1218bdd1243dSDimitry Andric cl::desc("Don't export branch data (LCOV)"), 1219bdd1243dSDimitry Andric cl::cat(ExportCategory)); 1220bdd1243dSDimitry Andric 12210b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 12220b57cec5SDimitry Andric if (Err) 12230b57cec5SDimitry Andric return Err; 12240b57cec5SDimitry Andric 12250b57cec5SDimitry Andric ViewOpts.SkipExpansions = SkipExpansions; 12260b57cec5SDimitry Andric ViewOpts.SkipFunctions = SkipFunctions; 1227bdd1243dSDimitry Andric ViewOpts.SkipBranches = SkipBranches; 12280b57cec5SDimitry Andric 12290b57cec5SDimitry Andric if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && 12300b57cec5SDimitry Andric ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { 12310b57cec5SDimitry Andric error("Coverage data can only be exported as textual JSON or an " 12320b57cec5SDimitry Andric "lcov tracefile."); 12330b57cec5SDimitry Andric return 1; 12340b57cec5SDimitry Andric } 12350b57cec5SDimitry Andric 1236fcaf7f86SDimitry Andric sys::fs::file_status Status; 1237fcaf7f86SDimitry Andric if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { 1238fcaf7f86SDimitry Andric error("Could not read profile data!" + EC.message(), PGOFilename); 1239fcaf7f86SDimitry Andric return 1; 1240fcaf7f86SDimitry Andric } 1241fcaf7f86SDimitry Andric 12420b57cec5SDimitry Andric auto Coverage = load(); 12430b57cec5SDimitry Andric if (!Coverage) { 12440b57cec5SDimitry Andric error("Could not load coverage information"); 12450b57cec5SDimitry Andric return 1; 12460b57cec5SDimitry Andric } 12470b57cec5SDimitry Andric 12480b57cec5SDimitry Andric std::unique_ptr<CoverageExporter> Exporter; 12490b57cec5SDimitry Andric 12500b57cec5SDimitry Andric switch (ViewOpts.Format) { 12510b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 1252bdd1243dSDimitry Andric Exporter = 1253bdd1243dSDimitry Andric std::make_unique<CoverageExporterJson>(*Coverage, ViewOpts, outs()); 12540b57cec5SDimitry Andric break; 12550b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 12560b57cec5SDimitry Andric // Unreachable because we should have gracefully terminated with an error 12570b57cec5SDimitry Andric // above. 12580b57cec5SDimitry Andric llvm_unreachable("Export in HTML is not supported!"); 12590b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 1260bdd1243dSDimitry Andric Exporter = 1261bdd1243dSDimitry Andric std::make_unique<CoverageExporterLcov>(*Coverage, ViewOpts, outs()); 12620b57cec5SDimitry Andric break; 12630b57cec5SDimitry Andric } 12640b57cec5SDimitry Andric 12650b57cec5SDimitry Andric if (SourceFiles.empty()) 12660b57cec5SDimitry Andric Exporter->renderRoot(IgnoreFilenameFilters); 12670b57cec5SDimitry Andric else 12680b57cec5SDimitry Andric Exporter->renderRoot(SourceFiles); 12690b57cec5SDimitry Andric 12700b57cec5SDimitry Andric return 0; 12710b57cec5SDimitry Andric } 12720b57cec5SDimitry Andric 12730b57cec5SDimitry Andric int showMain(int argc, const char *argv[]) { 12740b57cec5SDimitry Andric CodeCoverageTool Tool; 12750b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Show, argc, argv); 12760b57cec5SDimitry Andric } 12770b57cec5SDimitry Andric 12780b57cec5SDimitry Andric int reportMain(int argc, const char *argv[]) { 12790b57cec5SDimitry Andric CodeCoverageTool Tool; 12800b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Report, argc, argv); 12810b57cec5SDimitry Andric } 12820b57cec5SDimitry Andric 12830b57cec5SDimitry Andric int exportMain(int argc, const char *argv[]) { 12840b57cec5SDimitry Andric CodeCoverageTool Tool; 12850b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Export, argc, argv); 12860b57cec5SDimitry Andric } 1287