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" 251ac55f4cSDimitry Andric #include "llvm/Debuginfod/BuildIDFetcher.h" 261ac55f4cSDimitry Andric #include "llvm/Debuginfod/Debuginfod.h" 271ac55f4cSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h" 281ac55f4cSDimitry Andric #include "llvm/Object/BuildID.h" 290b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMapping.h" 300b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h" 310b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 320b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 330b57cec5SDimitry Andric #include "llvm/Support/Format.h" 340b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 350b57cec5SDimitry Andric #include "llvm/Support/Path.h" 360b57cec5SDimitry Andric #include "llvm/Support/Process.h" 370b57cec5SDimitry Andric #include "llvm/Support/Program.h" 380b57cec5SDimitry Andric #include "llvm/Support/ScopedPrinter.h" 395ffd83dbSDimitry Andric #include "llvm/Support/SpecialCaseList.h" 400b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h" 410b57cec5SDimitry Andric #include "llvm/Support/Threading.h" 420b57cec5SDimitry Andric #include "llvm/Support/ToolOutputFile.h" 43480093f4SDimitry Andric #include "llvm/Support/VirtualFileSystem.h" 4406c3fb27SDimitry Andric #include "llvm/TargetParser/Triple.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. 103*0fca6ea1SDimitry Andric void attachBranchSubViews(SourceCoverageView &View, 104*0fca6ea1SDimitry Andric ArrayRef<CountedRegion> Branches); 105e8d8bef9SDimitry Andric 1065f757f3fSDimitry Andric /// Create source views for the MCDC records. 107*0fca6ea1SDimitry Andric void attachMCDCSubViews(SourceCoverageView &View, 108*0fca6ea1SDimitry Andric ArrayRef<MCDCRecord> MCDCRecords); 1095f757f3fSDimitry Andric 1100b57cec5SDimitry Andric /// Create the source view of a particular function. 1110b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 1120b57cec5SDimitry Andric createFunctionView(const FunctionRecord &Function, 1130b57cec5SDimitry Andric const CoverageMapping &Coverage); 1140b57cec5SDimitry Andric 1150b57cec5SDimitry Andric /// Create the main source view of a particular source file. 1160b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 1170b57cec5SDimitry Andric createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); 1180b57cec5SDimitry Andric 1190b57cec5SDimitry Andric /// Load the coverage mapping data. Return nullptr if an error occurred. 1200b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> load(); 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric /// Create a mapping from files in the Coverage data to local copies 1230b57cec5SDimitry Andric /// (path-equivalence). 1240b57cec5SDimitry Andric void remapPathNames(const CoverageMapping &Coverage); 1250b57cec5SDimitry Andric 1260b57cec5SDimitry Andric /// Remove input source files which aren't mapped by \p Coverage. 1270b57cec5SDimitry Andric void removeUnmappedInputs(const CoverageMapping &Coverage); 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric /// If a demangler is available, demangle all symbol names. 1300b57cec5SDimitry Andric void demangleSymbols(const CoverageMapping &Coverage); 1310b57cec5SDimitry Andric 1320b57cec5SDimitry Andric /// Write out a source file view to the filesystem. 1330b57cec5SDimitry Andric void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage, 1340b57cec5SDimitry Andric CoveragePrinter *Printer, bool ShowFilenames); 1350b57cec5SDimitry Andric 1360b57cec5SDimitry Andric typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; 1370b57cec5SDimitry Andric 1380b57cec5SDimitry Andric int doShow(int argc, const char **argv, 1390b57cec5SDimitry Andric CommandLineParserType commandLineParser); 1400b57cec5SDimitry Andric 1410b57cec5SDimitry Andric int doReport(int argc, const char **argv, 1420b57cec5SDimitry Andric CommandLineParserType commandLineParser); 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric int doExport(int argc, const char **argv, 1450b57cec5SDimitry Andric CommandLineParserType commandLineParser); 1460b57cec5SDimitry Andric 1470b57cec5SDimitry Andric std::vector<StringRef> ObjectFilenames; 1480b57cec5SDimitry Andric CoverageViewOptions ViewOpts; 1490b57cec5SDimitry Andric CoverageFiltersMatchAll Filters; 1500b57cec5SDimitry Andric CoverageFilters IgnoreFilenameFilters; 1510b57cec5SDimitry Andric 152e8d8bef9SDimitry Andric /// True if InputSourceFiles are provided. 153e8d8bef9SDimitry Andric bool HadSourceFiles = false; 154e8d8bef9SDimitry Andric 1550b57cec5SDimitry Andric /// The path to the indexed profile. 1560b57cec5SDimitry Andric std::string PGOFilename; 1570b57cec5SDimitry Andric 1580b57cec5SDimitry Andric /// A list of input source files. 1590b57cec5SDimitry Andric std::vector<std::string> SourceFiles; 1600b57cec5SDimitry Andric 1610b57cec5SDimitry Andric /// In -path-equivalence mode, this maps the absolute paths from the coverage 1620b57cec5SDimitry Andric /// mapping data to the input source files. 1630b57cec5SDimitry Andric StringMap<std::string> RemappedFilenames; 1640b57cec5SDimitry Andric 1650b57cec5SDimitry Andric /// The coverage data path to be remapped from, and the source path to be 1660b57cec5SDimitry Andric /// remapped to, when using -path-equivalence. 1675f757f3fSDimitry Andric std::optional<std::vector<std::pair<std::string, std::string>>> 1685f757f3fSDimitry Andric PathRemappings; 1690b57cec5SDimitry Andric 170fe6060f1SDimitry Andric /// File status cache used when finding the same file. 171bdd1243dSDimitry Andric StringMap<std::optional<sys::fs::file_status>> FileStatusCache; 172fe6060f1SDimitry Andric 1730b57cec5SDimitry Andric /// The architecture the coverage mapping data targets. 1740b57cec5SDimitry Andric std::vector<StringRef> CoverageArches; 1750b57cec5SDimitry Andric 1760b57cec5SDimitry Andric /// A cache for demangled symbols. 1770b57cec5SDimitry Andric DemangleCache DC; 1780b57cec5SDimitry Andric 1790b57cec5SDimitry Andric /// A lock which guards printing to stderr. 1800b57cec5SDimitry Andric std::mutex ErrsLock; 1810b57cec5SDimitry Andric 1820b57cec5SDimitry Andric /// A container for input source file buffers. 1830b57cec5SDimitry Andric std::mutex LoadedSourceFilesLock; 1840b57cec5SDimitry Andric std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> 1850b57cec5SDimitry Andric LoadedSourceFiles; 1860b57cec5SDimitry Andric 1874824e7fdSDimitry Andric /// Allowlist from -name-allowlist to be used for filtering. 1884824e7fdSDimitry Andric std::unique_ptr<SpecialCaseList> NameAllowlist; 1891ac55f4cSDimitry Andric 1901ac55f4cSDimitry Andric std::unique_ptr<object::BuildIDFetcher> BIDFetcher; 19106c3fb27SDimitry Andric 19206c3fb27SDimitry Andric bool CheckBinaryIDs; 1930b57cec5SDimitry Andric }; 1940b57cec5SDimitry Andric } 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric static std::string getErrorString(const Twine &Message, StringRef Whence, 1970b57cec5SDimitry Andric bool Warning) { 1980b57cec5SDimitry Andric std::string Str = (Warning ? "warning" : "error"); 1990b57cec5SDimitry Andric Str += ": "; 2000b57cec5SDimitry Andric if (!Whence.empty()) 2010b57cec5SDimitry Andric Str += Whence.str() + ": "; 2020b57cec5SDimitry Andric Str += Message.str() + "\n"; 2030b57cec5SDimitry Andric return Str; 2040b57cec5SDimitry Andric } 2050b57cec5SDimitry Andric 2060b57cec5SDimitry Andric void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { 2070b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{ErrsLock}; 2080b57cec5SDimitry Andric ViewOpts.colored_ostream(errs(), raw_ostream::RED) 2090b57cec5SDimitry Andric << getErrorString(Message, Whence, false); 2100b57cec5SDimitry Andric } 2110b57cec5SDimitry Andric 2120b57cec5SDimitry Andric void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { 2130b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{ErrsLock}; 2140b57cec5SDimitry Andric ViewOpts.colored_ostream(errs(), raw_ostream::RED) 2150b57cec5SDimitry Andric << getErrorString(Message, Whence, true); 2160b57cec5SDimitry Andric } 2170b57cec5SDimitry Andric 2180b57cec5SDimitry Andric void CodeCoverageTool::addCollectedPath(const std::string &Path) { 2190b57cec5SDimitry Andric SmallString<128> EffectivePath(Path); 2200b57cec5SDimitry Andric if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) { 2210b57cec5SDimitry Andric error(EC.message(), Path); 2220b57cec5SDimitry Andric return; 2230b57cec5SDimitry Andric } 22404eeddc0SDimitry Andric sys::path::remove_dots(EffectivePath, /*remove_dot_dot=*/true); 2250b57cec5SDimitry Andric if (!IgnoreFilenameFilters.matchesFilename(EffectivePath)) 2260b57cec5SDimitry Andric SourceFiles.emplace_back(EffectivePath.str()); 227e8d8bef9SDimitry Andric HadSourceFiles = !SourceFiles.empty(); 2280b57cec5SDimitry Andric } 2290b57cec5SDimitry Andric 2300b57cec5SDimitry Andric void CodeCoverageTool::collectPaths(const std::string &Path) { 2310b57cec5SDimitry Andric llvm::sys::fs::file_status Status; 2320b57cec5SDimitry Andric llvm::sys::fs::status(Path, Status); 2330b57cec5SDimitry Andric if (!llvm::sys::fs::exists(Status)) { 2345f757f3fSDimitry Andric if (PathRemappings) 2350b57cec5SDimitry Andric addCollectedPath(Path); 2360b57cec5SDimitry Andric else 2370b57cec5SDimitry Andric warning("Source file doesn't exist, proceeded by ignoring it.", Path); 2380b57cec5SDimitry Andric return; 2390b57cec5SDimitry Andric } 2400b57cec5SDimitry Andric 2410b57cec5SDimitry Andric if (llvm::sys::fs::is_regular_file(Status)) { 2420b57cec5SDimitry Andric addCollectedPath(Path); 2430b57cec5SDimitry Andric return; 2440b57cec5SDimitry Andric } 2450b57cec5SDimitry Andric 2460b57cec5SDimitry Andric if (llvm::sys::fs::is_directory(Status)) { 2470b57cec5SDimitry Andric std::error_code EC; 2480b57cec5SDimitry Andric for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; 2490b57cec5SDimitry Andric F != E; F.increment(EC)) { 2500b57cec5SDimitry Andric 2510b57cec5SDimitry Andric auto Status = F->status(); 2520b57cec5SDimitry Andric if (!Status) { 2530b57cec5SDimitry Andric warning(Status.getError().message(), F->path()); 2540b57cec5SDimitry Andric continue; 2550b57cec5SDimitry Andric } 2560b57cec5SDimitry Andric 2570b57cec5SDimitry Andric if (Status->type() == llvm::sys::fs::file_type::regular_file) 2580b57cec5SDimitry Andric addCollectedPath(F->path()); 2590b57cec5SDimitry Andric } 2600b57cec5SDimitry Andric } 2610b57cec5SDimitry Andric } 2620b57cec5SDimitry Andric 263bdd1243dSDimitry Andric std::optional<sys::fs::file_status> 264fe6060f1SDimitry Andric CodeCoverageTool::getFileStatus(StringRef FilePath) { 265fe6060f1SDimitry Andric auto It = FileStatusCache.try_emplace(FilePath); 266fe6060f1SDimitry Andric auto &CachedStatus = It.first->getValue(); 267fe6060f1SDimitry Andric if (!It.second) 268fe6060f1SDimitry Andric return CachedStatus; 269fe6060f1SDimitry Andric 270fe6060f1SDimitry Andric sys::fs::file_status Status; 271fe6060f1SDimitry Andric if (!sys::fs::status(FilePath, Status)) 272fe6060f1SDimitry Andric CachedStatus = Status; 273fe6060f1SDimitry Andric return CachedStatus; 274fe6060f1SDimitry Andric } 275fe6060f1SDimitry Andric 276fe6060f1SDimitry Andric bool CodeCoverageTool::isEquivalentFile(StringRef FilePath1, 277fe6060f1SDimitry Andric StringRef FilePath2) { 278fe6060f1SDimitry Andric auto Status1 = getFileStatus(FilePath1); 279fe6060f1SDimitry Andric auto Status2 = getFileStatus(FilePath2); 28081ad6265SDimitry Andric return Status1 && Status2 && sys::fs::equivalent(*Status1, *Status2); 281fe6060f1SDimitry Andric } 282fe6060f1SDimitry Andric 2830b57cec5SDimitry Andric ErrorOr<const MemoryBuffer &> 2840b57cec5SDimitry Andric CodeCoverageTool::getSourceFile(StringRef SourceFile) { 2850b57cec5SDimitry Andric // If we've remapped filenames, look up the real location for this file. 2860b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock}; 2870b57cec5SDimitry Andric if (!RemappedFilenames.empty()) { 2880b57cec5SDimitry Andric auto Loc = RemappedFilenames.find(SourceFile); 2890b57cec5SDimitry Andric if (Loc != RemappedFilenames.end()) 2900b57cec5SDimitry Andric SourceFile = Loc->second; 2910b57cec5SDimitry Andric } 2920b57cec5SDimitry Andric for (const auto &Files : LoadedSourceFiles) 293fe6060f1SDimitry Andric if (isEquivalentFile(SourceFile, Files.first)) 2940b57cec5SDimitry Andric return *Files.second; 2950b57cec5SDimitry Andric auto Buffer = MemoryBuffer::getFile(SourceFile); 2960b57cec5SDimitry Andric if (auto EC = Buffer.getError()) { 2970b57cec5SDimitry Andric error(EC.message(), SourceFile); 2980b57cec5SDimitry Andric return EC; 2990b57cec5SDimitry Andric } 3005ffd83dbSDimitry Andric LoadedSourceFiles.emplace_back(std::string(SourceFile), 3015ffd83dbSDimitry Andric std::move(Buffer.get())); 3020b57cec5SDimitry Andric return *LoadedSourceFiles.back().second; 3030b57cec5SDimitry Andric } 3040b57cec5SDimitry Andric 3050b57cec5SDimitry Andric void CodeCoverageTool::attachExpansionSubViews( 3060b57cec5SDimitry Andric SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions, 3070b57cec5SDimitry Andric const CoverageMapping &Coverage) { 3080b57cec5SDimitry Andric if (!ViewOpts.ShowExpandedRegions) 3090b57cec5SDimitry Andric return; 3100b57cec5SDimitry Andric for (const auto &Expansion : Expansions) { 3110b57cec5SDimitry Andric auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); 3120b57cec5SDimitry Andric if (ExpansionCoverage.empty()) 3130b57cec5SDimitry Andric continue; 3140b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename()); 3150b57cec5SDimitry Andric if (!SourceBuffer) 3160b57cec5SDimitry Andric continue; 3170b57cec5SDimitry Andric 318e8d8bef9SDimitry Andric auto SubViewBranches = ExpansionCoverage.getBranches(); 3190b57cec5SDimitry Andric auto SubViewExpansions = ExpansionCoverage.getExpansions(); 3200b57cec5SDimitry Andric auto SubView = 3210b57cec5SDimitry Andric SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), 3220b57cec5SDimitry Andric ViewOpts, std::move(ExpansionCoverage)); 3230b57cec5SDimitry Andric attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 324*0fca6ea1SDimitry Andric attachBranchSubViews(*SubView, SubViewBranches); 3250b57cec5SDimitry Andric View.addExpansion(Expansion.Region, std::move(SubView)); 3260b57cec5SDimitry Andric } 3270b57cec5SDimitry Andric } 3280b57cec5SDimitry Andric 329e8d8bef9SDimitry Andric void CodeCoverageTool::attachBranchSubViews(SourceCoverageView &View, 330*0fca6ea1SDimitry Andric ArrayRef<CountedRegion> Branches) { 331e8d8bef9SDimitry Andric if (!ViewOpts.ShowBranchCounts && !ViewOpts.ShowBranchPercents) 332e8d8bef9SDimitry Andric return; 333e8d8bef9SDimitry Andric 334e8d8bef9SDimitry Andric const auto *NextBranch = Branches.begin(); 335e8d8bef9SDimitry Andric const auto *EndBranch = Branches.end(); 336e8d8bef9SDimitry Andric 337e8d8bef9SDimitry Andric // Group branches that have the same line number into the same subview. 338e8d8bef9SDimitry Andric while (NextBranch != EndBranch) { 339*0fca6ea1SDimitry Andric SmallVector<CountedRegion, 0> ViewBranches; 340e8d8bef9SDimitry Andric unsigned CurrentLine = NextBranch->LineStart; 341e8d8bef9SDimitry Andric while (NextBranch != EndBranch && CurrentLine == NextBranch->LineStart) 342e8d8bef9SDimitry Andric ViewBranches.push_back(*NextBranch++); 343e8d8bef9SDimitry Andric 344*0fca6ea1SDimitry Andric View.addBranch(CurrentLine, std::move(ViewBranches)); 345e8d8bef9SDimitry Andric } 346e8d8bef9SDimitry Andric } 347e8d8bef9SDimitry Andric 3485f757f3fSDimitry Andric void CodeCoverageTool::attachMCDCSubViews(SourceCoverageView &View, 349*0fca6ea1SDimitry Andric ArrayRef<MCDCRecord> MCDCRecords) { 3505f757f3fSDimitry Andric if (!ViewOpts.ShowMCDC) 3515f757f3fSDimitry Andric return; 3525f757f3fSDimitry Andric 3535f757f3fSDimitry Andric const auto *NextRecord = MCDCRecords.begin(); 3545f757f3fSDimitry Andric const auto *EndRecord = MCDCRecords.end(); 3555f757f3fSDimitry Andric 3565f757f3fSDimitry Andric // Group and process MCDC records that have the same line number into the 3575f757f3fSDimitry Andric // same subview. 3585f757f3fSDimitry Andric while (NextRecord != EndRecord) { 359*0fca6ea1SDimitry Andric SmallVector<MCDCRecord, 0> ViewMCDCRecords; 3605f757f3fSDimitry Andric unsigned CurrentLine = NextRecord->getDecisionRegion().LineEnd; 3615f757f3fSDimitry Andric while (NextRecord != EndRecord && 362*0fca6ea1SDimitry Andric CurrentLine == NextRecord->getDecisionRegion().LineEnd) 3635f757f3fSDimitry Andric ViewMCDCRecords.push_back(*NextRecord++); 3645f757f3fSDimitry Andric 365*0fca6ea1SDimitry Andric View.addMCDCRecord(CurrentLine, std::move(ViewMCDCRecords)); 3665f757f3fSDimitry Andric } 3675f757f3fSDimitry Andric } 3685f757f3fSDimitry Andric 3690b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 3700b57cec5SDimitry Andric CodeCoverageTool::createFunctionView(const FunctionRecord &Function, 3710b57cec5SDimitry Andric const CoverageMapping &Coverage) { 3720b57cec5SDimitry Andric auto FunctionCoverage = Coverage.getCoverageForFunction(Function); 3730b57cec5SDimitry Andric if (FunctionCoverage.empty()) 3740b57cec5SDimitry Andric return nullptr; 3750b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); 3760b57cec5SDimitry Andric if (!SourceBuffer) 3770b57cec5SDimitry Andric return nullptr; 3780b57cec5SDimitry Andric 379e8d8bef9SDimitry Andric auto Branches = FunctionCoverage.getBranches(); 3800b57cec5SDimitry Andric auto Expansions = FunctionCoverage.getExpansions(); 3815f757f3fSDimitry Andric auto MCDCRecords = FunctionCoverage.getMCDCRecords(); 3820b57cec5SDimitry Andric auto View = SourceCoverageView::create(DC.demangle(Function.Name), 3830b57cec5SDimitry Andric SourceBuffer.get(), ViewOpts, 3840b57cec5SDimitry Andric std::move(FunctionCoverage)); 3850b57cec5SDimitry Andric attachExpansionSubViews(*View, Expansions, Coverage); 386*0fca6ea1SDimitry Andric attachBranchSubViews(*View, Branches); 387*0fca6ea1SDimitry Andric attachMCDCSubViews(*View, MCDCRecords); 3880b57cec5SDimitry Andric 3890b57cec5SDimitry Andric return View; 3900b57cec5SDimitry Andric } 3910b57cec5SDimitry Andric 3920b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 3930b57cec5SDimitry Andric CodeCoverageTool::createSourceFileView(StringRef SourceFile, 3940b57cec5SDimitry Andric const CoverageMapping &Coverage) { 3950b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(SourceFile); 3960b57cec5SDimitry Andric if (!SourceBuffer) 3970b57cec5SDimitry Andric return nullptr; 3980b57cec5SDimitry Andric auto FileCoverage = Coverage.getCoverageForFile(SourceFile); 3990b57cec5SDimitry Andric if (FileCoverage.empty()) 4000b57cec5SDimitry Andric return nullptr; 4010b57cec5SDimitry Andric 402e8d8bef9SDimitry Andric auto Branches = FileCoverage.getBranches(); 4030b57cec5SDimitry Andric auto Expansions = FileCoverage.getExpansions(); 4045f757f3fSDimitry Andric auto MCDCRecords = FileCoverage.getMCDCRecords(); 4050b57cec5SDimitry Andric auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), 4060b57cec5SDimitry Andric ViewOpts, std::move(FileCoverage)); 4070b57cec5SDimitry Andric attachExpansionSubViews(*View, Expansions, Coverage); 408*0fca6ea1SDimitry Andric attachBranchSubViews(*View, Branches); 409*0fca6ea1SDimitry Andric attachMCDCSubViews(*View, MCDCRecords); 4100b57cec5SDimitry Andric if (!ViewOpts.ShowFunctionInstantiations) 4110b57cec5SDimitry Andric return View; 4120b57cec5SDimitry Andric 4130b57cec5SDimitry Andric for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) { 4140b57cec5SDimitry Andric // Skip functions which have a single instantiation. 4150b57cec5SDimitry Andric if (Group.size() < 2) 4160b57cec5SDimitry Andric continue; 4170b57cec5SDimitry Andric 4180b57cec5SDimitry Andric for (const FunctionRecord *Function : Group.getInstantiations()) { 4190b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> SubView{nullptr}; 4200b57cec5SDimitry Andric 4210b57cec5SDimitry Andric StringRef Funcname = DC.demangle(Function->Name); 4220b57cec5SDimitry Andric 4230b57cec5SDimitry Andric if (Function->ExecutionCount > 0) { 4240b57cec5SDimitry Andric auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); 4250b57cec5SDimitry Andric auto SubViewExpansions = SubViewCoverage.getExpansions(); 426e8d8bef9SDimitry Andric auto SubViewBranches = SubViewCoverage.getBranches(); 4275f757f3fSDimitry Andric auto SubViewMCDCRecords = SubViewCoverage.getMCDCRecords(); 4280b57cec5SDimitry Andric SubView = SourceCoverageView::create( 4290b57cec5SDimitry Andric Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); 4300b57cec5SDimitry Andric attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 431*0fca6ea1SDimitry Andric attachBranchSubViews(*SubView, SubViewBranches); 432*0fca6ea1SDimitry Andric attachMCDCSubViews(*SubView, SubViewMCDCRecords); 4330b57cec5SDimitry Andric } 4340b57cec5SDimitry Andric 4350b57cec5SDimitry Andric unsigned FileID = Function->CountedRegions.front().FileID; 4360b57cec5SDimitry Andric unsigned Line = 0; 4370b57cec5SDimitry Andric for (const auto &CR : Function->CountedRegions) 4380b57cec5SDimitry Andric if (CR.FileID == FileID) 4390b57cec5SDimitry Andric Line = std::max(CR.LineEnd, Line); 4400b57cec5SDimitry Andric View->addInstantiation(Funcname, Line, std::move(SubView)); 4410b57cec5SDimitry Andric } 4420b57cec5SDimitry Andric } 4430b57cec5SDimitry Andric return View; 4440b57cec5SDimitry Andric } 4450b57cec5SDimitry Andric 4460b57cec5SDimitry Andric static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { 4470b57cec5SDimitry Andric sys::fs::file_status Status; 4480b57cec5SDimitry Andric if (sys::fs::status(LHS, Status)) 4490b57cec5SDimitry Andric return false; 4500b57cec5SDimitry Andric auto LHSTime = Status.getLastModificationTime(); 4510b57cec5SDimitry Andric if (sys::fs::status(RHS, Status)) 4520b57cec5SDimitry Andric return false; 4530b57cec5SDimitry Andric auto RHSTime = Status.getLastModificationTime(); 4540b57cec5SDimitry Andric return LHSTime > RHSTime; 4550b57cec5SDimitry Andric } 4560b57cec5SDimitry Andric 4570b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { 4580b57cec5SDimitry Andric for (StringRef ObjectFilename : ObjectFilenames) 4590b57cec5SDimitry Andric if (modifiedTimeGT(ObjectFilename, PGOFilename)) 4600b57cec5SDimitry Andric warning("profile data may be out of date - object is newer", 4610b57cec5SDimitry Andric ObjectFilename); 46206c3fb27SDimitry Andric auto FS = vfs::getRealFileSystem(); 46306c3fb27SDimitry Andric auto CoverageOrErr = CoverageMapping::load( 46406c3fb27SDimitry Andric ObjectFilenames, PGOFilename, *FS, CoverageArches, 46506c3fb27SDimitry Andric ViewOpts.CompilationDirectory, BIDFetcher.get(), CheckBinaryIDs); 4660b57cec5SDimitry Andric if (Error E = CoverageOrErr.takeError()) { 4675f757f3fSDimitry Andric error("failed to load coverage: " + toString(std::move(E))); 4680b57cec5SDimitry Andric return nullptr; 4690b57cec5SDimitry Andric } 4700b57cec5SDimitry Andric auto Coverage = std::move(CoverageOrErr.get()); 4710b57cec5SDimitry Andric unsigned Mismatched = Coverage->getMismatchedCount(); 4720b57cec5SDimitry Andric if (Mismatched) { 4730b57cec5SDimitry Andric warning(Twine(Mismatched) + " functions have mismatched data"); 4740b57cec5SDimitry Andric 4750b57cec5SDimitry Andric if (ViewOpts.Debug) { 4760b57cec5SDimitry Andric for (const auto &HashMismatch : Coverage->getHashMismatches()) 4770b57cec5SDimitry Andric errs() << "hash-mismatch: " 4780b57cec5SDimitry Andric << "No profile record found for '" << HashMismatch.first << "'" 4790b57cec5SDimitry Andric << " with hash = 0x" << Twine::utohexstr(HashMismatch.second) 4800b57cec5SDimitry Andric << '\n'; 4810b57cec5SDimitry Andric } 4820b57cec5SDimitry Andric } 4830b57cec5SDimitry Andric 4840b57cec5SDimitry Andric remapPathNames(*Coverage); 4850b57cec5SDimitry Andric 4860b57cec5SDimitry Andric if (!SourceFiles.empty()) 4870b57cec5SDimitry Andric removeUnmappedInputs(*Coverage); 4880b57cec5SDimitry Andric 4890b57cec5SDimitry Andric demangleSymbols(*Coverage); 4900b57cec5SDimitry Andric 4910b57cec5SDimitry Andric return Coverage; 4920b57cec5SDimitry Andric } 4930b57cec5SDimitry Andric 4940b57cec5SDimitry Andric void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { 4955f757f3fSDimitry Andric if (!PathRemappings) 4960b57cec5SDimitry Andric return; 4970b57cec5SDimitry Andric 498*0fca6ea1SDimitry Andric // Convert remapping paths to native paths with trailing separators. 4990b57cec5SDimitry Andric auto nativeWithTrailing = [](StringRef Path) -> std::string { 5000b57cec5SDimitry Andric if (Path.empty()) 5010b57cec5SDimitry Andric return ""; 5020b57cec5SDimitry Andric SmallString<128> NativePath; 5030b57cec5SDimitry Andric sys::path::native(Path, NativePath); 504e8d8bef9SDimitry Andric sys::path::remove_dots(NativePath, true); 505fe6060f1SDimitry Andric if (!NativePath.empty() && !sys::path::is_separator(NativePath.back())) 5060b57cec5SDimitry Andric NativePath += sys::path::get_separator(); 5070b57cec5SDimitry Andric return NativePath.c_str(); 5080b57cec5SDimitry Andric }; 5095f757f3fSDimitry Andric 5105f757f3fSDimitry Andric for (std::pair<std::string, std::string> &PathRemapping : *PathRemappings) { 5115f757f3fSDimitry Andric std::string RemapFrom = nativeWithTrailing(PathRemapping.first); 5125f757f3fSDimitry Andric std::string RemapTo = nativeWithTrailing(PathRemapping.second); 5130b57cec5SDimitry Andric 5140b57cec5SDimitry Andric // Create a mapping from coverage data file paths to local paths. 5150b57cec5SDimitry Andric for (StringRef Filename : Coverage.getUniqueSourceFiles()) { 5165f757f3fSDimitry Andric if (RemappedFilenames.count(Filename) == 1) 5175f757f3fSDimitry Andric continue; 5185f757f3fSDimitry Andric 5190b57cec5SDimitry Andric SmallString<128> NativeFilename; 5200b57cec5SDimitry Andric sys::path::native(Filename, NativeFilename); 521e8d8bef9SDimitry Andric sys::path::remove_dots(NativeFilename, true); 5225f757f3fSDimitry Andric if (NativeFilename.starts_with(RemapFrom)) { 5230b57cec5SDimitry Andric RemappedFilenames[Filename] = 5240b57cec5SDimitry Andric RemapTo + NativeFilename.substr(RemapFrom.size()).str(); 5250b57cec5SDimitry Andric } 5260b57cec5SDimitry Andric } 5275f757f3fSDimitry Andric } 5280b57cec5SDimitry Andric 5290b57cec5SDimitry Andric // Convert input files from local paths to coverage data file paths. 5300b57cec5SDimitry Andric StringMap<std::string> InvRemappedFilenames; 5310b57cec5SDimitry Andric for (const auto &RemappedFilename : RemappedFilenames) 5325ffd83dbSDimitry Andric InvRemappedFilenames[RemappedFilename.getValue()] = 5335ffd83dbSDimitry Andric std::string(RemappedFilename.getKey()); 5340b57cec5SDimitry Andric 5350b57cec5SDimitry Andric for (std::string &Filename : SourceFiles) { 5360b57cec5SDimitry Andric SmallString<128> NativeFilename; 5370b57cec5SDimitry Andric sys::path::native(Filename, NativeFilename); 5380b57cec5SDimitry Andric auto CovFileName = InvRemappedFilenames.find(NativeFilename); 5390b57cec5SDimitry Andric if (CovFileName != InvRemappedFilenames.end()) 5400b57cec5SDimitry Andric Filename = CovFileName->second; 5410b57cec5SDimitry Andric } 5420b57cec5SDimitry Andric } 5430b57cec5SDimitry Andric 5440b57cec5SDimitry Andric void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { 5450b57cec5SDimitry Andric std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); 5460b57cec5SDimitry Andric 5470b57cec5SDimitry Andric // The user may have specified source files which aren't in the coverage 5480b57cec5SDimitry Andric // mapping. Filter these files away. 549e8d8bef9SDimitry Andric llvm::erase_if(SourceFiles, [&](const std::string &SF) { 550e8d8bef9SDimitry Andric return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF); 5510b57cec5SDimitry Andric }); 5520b57cec5SDimitry Andric } 5530b57cec5SDimitry Andric 5540b57cec5SDimitry Andric void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { 5550b57cec5SDimitry Andric if (!ViewOpts.hasDemangler()) 5560b57cec5SDimitry Andric return; 5570b57cec5SDimitry Andric 5580b57cec5SDimitry Andric // Pass function names to the demangler in a temporary file. 5590b57cec5SDimitry Andric int InputFD; 5600b57cec5SDimitry Andric SmallString<256> InputPath; 5610b57cec5SDimitry Andric std::error_code EC = 5620b57cec5SDimitry Andric sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath); 5630b57cec5SDimitry Andric if (EC) { 5640b57cec5SDimitry Andric error(InputPath, EC.message()); 5650b57cec5SDimitry Andric return; 5660b57cec5SDimitry Andric } 5670b57cec5SDimitry Andric ToolOutputFile InputTOF{InputPath, InputFD}; 5680b57cec5SDimitry Andric 5690b57cec5SDimitry Andric unsigned NumSymbols = 0; 5700b57cec5SDimitry Andric for (const auto &Function : Coverage.getCoveredFunctions()) { 5710b57cec5SDimitry Andric InputTOF.os() << Function.Name << '\n'; 5720b57cec5SDimitry Andric ++NumSymbols; 5730b57cec5SDimitry Andric } 5740b57cec5SDimitry Andric InputTOF.os().close(); 5750b57cec5SDimitry Andric 5760b57cec5SDimitry Andric // Use another temporary file to store the demangler's output. 5770b57cec5SDimitry Andric int OutputFD; 5780b57cec5SDimitry Andric SmallString<256> OutputPath; 5790b57cec5SDimitry Andric EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD, 5800b57cec5SDimitry Andric OutputPath); 5810b57cec5SDimitry Andric if (EC) { 5820b57cec5SDimitry Andric error(OutputPath, EC.message()); 5830b57cec5SDimitry Andric return; 5840b57cec5SDimitry Andric } 5850b57cec5SDimitry Andric ToolOutputFile OutputTOF{OutputPath, OutputFD}; 5860b57cec5SDimitry Andric OutputTOF.os().close(); 5870b57cec5SDimitry Andric 5880b57cec5SDimitry Andric // Invoke the demangler. 5890b57cec5SDimitry Andric std::vector<StringRef> ArgsV; 590bdd1243dSDimitry Andric ArgsV.reserve(ViewOpts.DemanglerOpts.size()); 5910b57cec5SDimitry Andric for (StringRef Arg : ViewOpts.DemanglerOpts) 5920b57cec5SDimitry Andric ArgsV.push_back(Arg); 593bdd1243dSDimitry Andric std::optional<StringRef> Redirects[] = { 594bdd1243dSDimitry Andric InputPath.str(), OutputPath.str(), {""}}; 5950b57cec5SDimitry Andric std::string ErrMsg; 596bdd1243dSDimitry Andric int RC = 597bdd1243dSDimitry Andric sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV, 598bdd1243dSDimitry Andric /*env=*/std::nullopt, Redirects, /*secondsToWait=*/0, 5990b57cec5SDimitry Andric /*memoryLimit=*/0, &ErrMsg); 6000b57cec5SDimitry Andric if (RC) { 6010b57cec5SDimitry Andric error(ErrMsg, ViewOpts.DemanglerOpts[0]); 6020b57cec5SDimitry Andric return; 6030b57cec5SDimitry Andric } 6040b57cec5SDimitry Andric 6050b57cec5SDimitry Andric // Parse the demangler's output. 6060b57cec5SDimitry Andric auto BufOrError = MemoryBuffer::getFile(OutputPath); 6070b57cec5SDimitry Andric if (!BufOrError) { 6080b57cec5SDimitry Andric error(OutputPath, BufOrError.getError().message()); 6090b57cec5SDimitry Andric return; 6100b57cec5SDimitry Andric } 6110b57cec5SDimitry Andric 6120b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError); 6130b57cec5SDimitry Andric 6140b57cec5SDimitry Andric SmallVector<StringRef, 8> Symbols; 6150b57cec5SDimitry Andric StringRef DemanglerData = DemanglerBuf->getBuffer(); 6160b57cec5SDimitry Andric DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols, 6170b57cec5SDimitry Andric /*KeepEmpty=*/false); 6180b57cec5SDimitry Andric if (Symbols.size() != NumSymbols) { 6195f757f3fSDimitry Andric error("demangler did not provide expected number of symbols"); 6200b57cec5SDimitry Andric return; 6210b57cec5SDimitry Andric } 6220b57cec5SDimitry Andric 6230b57cec5SDimitry Andric // Cache the demangled names. 6240b57cec5SDimitry Andric unsigned I = 0; 6250b57cec5SDimitry Andric for (const auto &Function : Coverage.getCoveredFunctions()) 6260b57cec5SDimitry Andric // On Windows, lines in the demangler's output file end with "\r\n". 6270b57cec5SDimitry Andric // Splitting by '\n' keeps '\r's, so cut them now. 6285ffd83dbSDimitry Andric DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim()); 6290b57cec5SDimitry Andric } 6300b57cec5SDimitry Andric 6310b57cec5SDimitry Andric void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, 6320b57cec5SDimitry Andric CoverageMapping *Coverage, 6330b57cec5SDimitry Andric CoveragePrinter *Printer, 6340b57cec5SDimitry Andric bool ShowFilenames) { 6350b57cec5SDimitry Andric auto View = createSourceFileView(SourceFile, *Coverage); 6360b57cec5SDimitry Andric if (!View) { 6370b57cec5SDimitry Andric warning("The file '" + SourceFile + "' isn't covered."); 6380b57cec5SDimitry Andric return; 6390b57cec5SDimitry Andric } 6400b57cec5SDimitry Andric 6410b57cec5SDimitry Andric auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); 6420b57cec5SDimitry Andric if (Error E = OSOrErr.takeError()) { 6435f757f3fSDimitry Andric error("could not create view file!", toString(std::move(E))); 6440b57cec5SDimitry Andric return; 6450b57cec5SDimitry Andric } 6460b57cec5SDimitry Andric auto OS = std::move(OSOrErr.get()); 6470b57cec5SDimitry Andric 6480b57cec5SDimitry Andric View->print(*OS.get(), /*Wholefile=*/true, 6490b57cec5SDimitry Andric /*ShowSourceName=*/ShowFilenames, 6500b57cec5SDimitry Andric /*ShowTitle=*/ViewOpts.hasOutputDirectory()); 6510b57cec5SDimitry Andric Printer->closeViewFile(std::move(OS)); 6520b57cec5SDimitry Andric } 6530b57cec5SDimitry Andric 6540b57cec5SDimitry Andric int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 6550b57cec5SDimitry Andric cl::opt<std::string> CovFilename( 6560b57cec5SDimitry Andric cl::Positional, cl::desc("Covered executable or object file.")); 6570b57cec5SDimitry Andric 6580b57cec5SDimitry Andric cl::list<std::string> CovFilenames( 65981ad6265SDimitry Andric "object", cl::desc("Coverage executable or object file")); 660e8d8bef9SDimitry Andric 661e8d8bef9SDimitry Andric cl::opt<bool> DebugDumpCollectedObjects( 662e8d8bef9SDimitry Andric "dump-collected-objects", cl::Optional, cl::Hidden, 663e8d8bef9SDimitry Andric cl::desc("Show the collected coverage object files")); 6640b57cec5SDimitry Andric 6651ac55f4cSDimitry Andric cl::list<std::string> InputSourceFiles("sources", cl::Positional, 66681ad6265SDimitry Andric cl::desc("<Source files>")); 6670b57cec5SDimitry Andric 6680b57cec5SDimitry Andric cl::opt<bool> DebugDumpCollectedPaths( 6690b57cec5SDimitry Andric "dump-collected-paths", cl::Optional, cl::Hidden, 6700b57cec5SDimitry Andric cl::desc("Show the collected paths to source files")); 6710b57cec5SDimitry Andric 6720b57cec5SDimitry Andric cl::opt<std::string, true> PGOFilename( 6730b57cec5SDimitry Andric "instr-profile", cl::Required, cl::location(this->PGOFilename), 6740b57cec5SDimitry Andric cl::desc( 6750b57cec5SDimitry Andric "File with the profile data obtained after an instrumented run")); 6760b57cec5SDimitry Andric 6770b57cec5SDimitry Andric cl::list<std::string> Arches( 6780b57cec5SDimitry Andric "arch", cl::desc("architectures of the coverage mapping binaries")); 6790b57cec5SDimitry Andric 6800b57cec5SDimitry Andric cl::opt<bool> DebugDump("dump", cl::Optional, 6810b57cec5SDimitry Andric cl::desc("Show internal debug dump")); 6820b57cec5SDimitry Andric 6831ac55f4cSDimitry Andric cl::list<std::string> DebugFileDirectory( 6841ac55f4cSDimitry Andric "debug-file-directory", 6851ac55f4cSDimitry Andric cl::desc("Directories to search for object files by build ID")); 6861ac55f4cSDimitry Andric cl::opt<bool> Debuginfod( 6871ac55f4cSDimitry Andric "debuginfod", cl::ZeroOrMore, 6881ac55f4cSDimitry Andric cl::desc("Use debuginfod to look up object files from profile"), 6891ac55f4cSDimitry Andric cl::init(canUseDebuginfod())); 6901ac55f4cSDimitry Andric 6910b57cec5SDimitry Andric cl::opt<CoverageViewOptions::OutputFormat> Format( 6920b57cec5SDimitry Andric "format", cl::desc("Output format for line-based coverage reports"), 6930b57cec5SDimitry Andric cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", 6940b57cec5SDimitry Andric "Text output"), 6950b57cec5SDimitry Andric clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", 6960b57cec5SDimitry Andric "HTML output"), 6970b57cec5SDimitry Andric clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov", 6980b57cec5SDimitry Andric "lcov tracefile output")), 6990b57cec5SDimitry Andric cl::init(CoverageViewOptions::OutputFormat::Text)); 7000b57cec5SDimitry Andric 7015f757f3fSDimitry Andric cl::list<std::string> PathRemaps( 7020b57cec5SDimitry Andric "path-equivalence", cl::Optional, 7030b57cec5SDimitry Andric cl::desc("<from>,<to> Map coverage data paths to local source file " 7040b57cec5SDimitry Andric "paths")); 7050b57cec5SDimitry Andric 7060b57cec5SDimitry Andric cl::OptionCategory FilteringCategory("Function filtering options"); 7070b57cec5SDimitry Andric 7080b57cec5SDimitry Andric cl::list<std::string> NameFilters( 7090b57cec5SDimitry Andric "name", cl::Optional, 7100b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with the given name"), 71181ad6265SDimitry Andric cl::cat(FilteringCategory)); 7120b57cec5SDimitry Andric 7130b57cec5SDimitry Andric cl::list<std::string> NameFilterFiles( 7144824e7fdSDimitry Andric "name-allowlist", cl::Optional, 7150b57cec5SDimitry Andric cl::desc("Show code coverage only for functions listed in the given " 7160b57cec5SDimitry Andric "file"), 71781ad6265SDimitry Andric cl::cat(FilteringCategory)); 7180b57cec5SDimitry Andric 7190b57cec5SDimitry Andric cl::list<std::string> NameRegexFilters( 7200b57cec5SDimitry Andric "name-regex", cl::Optional, 7210b57cec5SDimitry Andric cl::desc("Show code coverage only for functions that match the given " 7220b57cec5SDimitry Andric "regular expression"), 72381ad6265SDimitry Andric cl::cat(FilteringCategory)); 7240b57cec5SDimitry Andric 7250b57cec5SDimitry Andric cl::list<std::string> IgnoreFilenameRegexFilters( 7260b57cec5SDimitry Andric "ignore-filename-regex", cl::Optional, 7270b57cec5SDimitry Andric cl::desc("Skip source code files with file paths that match the given " 7280b57cec5SDimitry Andric "regular expression"), 72981ad6265SDimitry Andric cl::cat(FilteringCategory)); 7300b57cec5SDimitry Andric 7310b57cec5SDimitry Andric cl::opt<double> RegionCoverageLtFilter( 7320b57cec5SDimitry Andric "region-coverage-lt", cl::Optional, 7330b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with region coverage " 7340b57cec5SDimitry Andric "less than the given threshold"), 7350b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7360b57cec5SDimitry Andric 7370b57cec5SDimitry Andric cl::opt<double> RegionCoverageGtFilter( 7380b57cec5SDimitry Andric "region-coverage-gt", cl::Optional, 7390b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with region coverage " 7400b57cec5SDimitry Andric "greater than the given threshold"), 7410b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7420b57cec5SDimitry Andric 7430b57cec5SDimitry Andric cl::opt<double> LineCoverageLtFilter( 7440b57cec5SDimitry Andric "line-coverage-lt", cl::Optional, 7450b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with line coverage less " 7460b57cec5SDimitry Andric "than the given threshold"), 7470b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7480b57cec5SDimitry Andric 7490b57cec5SDimitry Andric cl::opt<double> LineCoverageGtFilter( 7500b57cec5SDimitry Andric "line-coverage-gt", cl::Optional, 7510b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with line coverage " 7520b57cec5SDimitry Andric "greater than the given threshold"), 7530b57cec5SDimitry Andric cl::cat(FilteringCategory)); 7540b57cec5SDimitry Andric 7550b57cec5SDimitry Andric cl::opt<cl::boolOrDefault> UseColor( 7560b57cec5SDimitry Andric "use-color", cl::desc("Emit colored output (default=autodetect)"), 7570b57cec5SDimitry Andric cl::init(cl::BOU_UNSET)); 7580b57cec5SDimitry Andric 7590b57cec5SDimitry Andric cl::list<std::string> DemanglerOpts( 7600b57cec5SDimitry Andric "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); 7610b57cec5SDimitry Andric 7620b57cec5SDimitry Andric cl::opt<bool> RegionSummary( 7630b57cec5SDimitry Andric "show-region-summary", cl::Optional, 7640b57cec5SDimitry Andric cl::desc("Show region statistics in summary table"), 7650b57cec5SDimitry Andric cl::init(true)); 7660b57cec5SDimitry Andric 767e8d8bef9SDimitry Andric cl::opt<bool> BranchSummary( 768e8d8bef9SDimitry Andric "show-branch-summary", cl::Optional, 769e8d8bef9SDimitry Andric cl::desc("Show branch condition statistics in summary table"), 770e8d8bef9SDimitry Andric cl::init(true)); 771e8d8bef9SDimitry Andric 7725f757f3fSDimitry Andric cl::opt<bool> MCDCSummary("show-mcdc-summary", cl::Optional, 7735f757f3fSDimitry Andric cl::desc("Show MCDC statistics in summary table"), 7745f757f3fSDimitry Andric cl::init(false)); 7755f757f3fSDimitry Andric 7760b57cec5SDimitry Andric cl::opt<bool> InstantiationSummary( 7770b57cec5SDimitry Andric "show-instantiation-summary", cl::Optional, 7780b57cec5SDimitry Andric cl::desc("Show instantiation statistics in summary table")); 7790b57cec5SDimitry Andric 7800b57cec5SDimitry Andric cl::opt<bool> SummaryOnly( 7810b57cec5SDimitry Andric "summary-only", cl::Optional, 7820b57cec5SDimitry Andric cl::desc("Export only summary information for each source file")); 7830b57cec5SDimitry Andric 7840b57cec5SDimitry Andric cl::opt<unsigned> NumThreads( 7850b57cec5SDimitry Andric "num-threads", cl::init(0), 7860b57cec5SDimitry Andric cl::desc("Number of merge threads to use (default: autodetect)")); 7870b57cec5SDimitry Andric cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), 7880b57cec5SDimitry Andric cl::aliasopt(NumThreads)); 7890b57cec5SDimitry Andric 790fe6060f1SDimitry Andric cl::opt<std::string> CompilationDirectory( 791fe6060f1SDimitry Andric "compilation-dir", cl::init(""), 792fe6060f1SDimitry Andric cl::desc("Directory used as a base for relative coverage mapping paths")); 793fe6060f1SDimitry Andric 79406c3fb27SDimitry Andric cl::opt<bool> CheckBinaryIDs( 79506c3fb27SDimitry Andric "check-binary-ids", cl::desc("Fail if an object couldn't be found for a " 79606c3fb27SDimitry Andric "binary ID in the profile")); 79706c3fb27SDimitry Andric 7980b57cec5SDimitry Andric auto commandLineParser = [&, this](int argc, const char **argv) -> int { 7990b57cec5SDimitry Andric cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 8000b57cec5SDimitry Andric ViewOpts.Debug = DebugDump; 8011ac55f4cSDimitry Andric if (Debuginfod) { 8021ac55f4cSDimitry Andric HTTPClient::initialize(); 8031ac55f4cSDimitry Andric BIDFetcher = std::make_unique<DebuginfodFetcher>(DebugFileDirectory); 8041ac55f4cSDimitry Andric } else { 8051ac55f4cSDimitry Andric BIDFetcher = std::make_unique<object::BuildIDFetcher>(DebugFileDirectory); 8061ac55f4cSDimitry Andric } 80706c3fb27SDimitry Andric this->CheckBinaryIDs = CheckBinaryIDs; 8080b57cec5SDimitry Andric 8090b57cec5SDimitry Andric if (!CovFilename.empty()) 8100b57cec5SDimitry Andric ObjectFilenames.emplace_back(CovFilename); 8110b57cec5SDimitry Andric for (const std::string &Filename : CovFilenames) 8120b57cec5SDimitry Andric ObjectFilenames.emplace_back(Filename); 8131ac55f4cSDimitry Andric if (ObjectFilenames.empty() && !Debuginfod && DebugFileDirectory.empty()) { 8140b57cec5SDimitry Andric errs() << "No filenames specified!\n"; 8150b57cec5SDimitry Andric ::exit(1); 8160b57cec5SDimitry Andric } 8170b57cec5SDimitry Andric 818e8d8bef9SDimitry Andric if (DebugDumpCollectedObjects) { 819e8d8bef9SDimitry Andric for (StringRef OF : ObjectFilenames) 820e8d8bef9SDimitry Andric outs() << OF << '\n'; 821e8d8bef9SDimitry Andric ::exit(0); 822e8d8bef9SDimitry Andric } 823e8d8bef9SDimitry Andric 8240b57cec5SDimitry Andric ViewOpts.Format = Format; 8250b57cec5SDimitry Andric switch (ViewOpts.Format) { 8260b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 8270b57cec5SDimitry Andric ViewOpts.Colors = UseColor == cl::BOU_UNSET 8280b57cec5SDimitry Andric ? sys::Process::StandardOutHasColors() 8290b57cec5SDimitry Andric : UseColor == cl::BOU_TRUE; 8300b57cec5SDimitry Andric break; 8310b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 8320b57cec5SDimitry Andric if (UseColor == cl::BOU_FALSE) 8330b57cec5SDimitry Andric errs() << "Color output cannot be disabled when generating html.\n"; 8340b57cec5SDimitry Andric ViewOpts.Colors = true; 8350b57cec5SDimitry Andric break; 8360b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 8370b57cec5SDimitry Andric if (UseColor == cl::BOU_TRUE) 8380b57cec5SDimitry Andric errs() << "Color output cannot be enabled when generating lcov.\n"; 8390b57cec5SDimitry Andric ViewOpts.Colors = false; 8400b57cec5SDimitry Andric break; 8410b57cec5SDimitry Andric } 8420b57cec5SDimitry Andric 8435f757f3fSDimitry Andric if (!PathRemaps.empty()) { 8445f757f3fSDimitry Andric std::vector<std::pair<std::string, std::string>> Remappings; 8455f757f3fSDimitry Andric 8465f757f3fSDimitry Andric for (const std::string &PathRemap : PathRemaps) { 8470b57cec5SDimitry Andric auto EquivPair = StringRef(PathRemap).split(','); 848349cc55cSDimitry Andric if (EquivPair.first.empty() || EquivPair.second.empty()) { 849349cc55cSDimitry Andric error("invalid argument '" + PathRemap + 850349cc55cSDimitry Andric "', must be in format 'from,to'", 851349cc55cSDimitry Andric "-path-equivalence"); 852349cc55cSDimitry Andric return 1; 853349cc55cSDimitry Andric } 854349cc55cSDimitry Andric 8555f757f3fSDimitry Andric Remappings.push_back( 8565f757f3fSDimitry Andric {std::string(EquivPair.first), std::string(EquivPair.second)}); 8575f757f3fSDimitry Andric } 8585f757f3fSDimitry Andric 8595f757f3fSDimitry Andric PathRemappings = Remappings; 860349cc55cSDimitry Andric } 8610b57cec5SDimitry Andric 8620b57cec5SDimitry Andric // If a demangler is supplied, check if it exists and register it. 8630b57cec5SDimitry Andric if (!DemanglerOpts.empty()) { 8640b57cec5SDimitry Andric auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); 8650b57cec5SDimitry Andric if (!DemanglerPathOrErr) { 8665f757f3fSDimitry Andric error("could not find the demangler!", 8670b57cec5SDimitry Andric DemanglerPathOrErr.getError().message()); 8680b57cec5SDimitry Andric return 1; 8690b57cec5SDimitry Andric } 8700b57cec5SDimitry Andric DemanglerOpts[0] = *DemanglerPathOrErr; 8710b57cec5SDimitry Andric ViewOpts.DemanglerOpts.swap(DemanglerOpts); 8720b57cec5SDimitry Andric } 8730b57cec5SDimitry Andric 8744824e7fdSDimitry Andric // Read in -name-allowlist files. 875bdd1243dSDimitry Andric if (!NameFilterFiles.empty()) { 8760b57cec5SDimitry Andric std::string SpecialCaseListErr; 8774824e7fdSDimitry Andric NameAllowlist = SpecialCaseList::create( 878480093f4SDimitry Andric NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr); 8794824e7fdSDimitry Andric if (!NameAllowlist) 8800b57cec5SDimitry Andric error(SpecialCaseListErr); 8810b57cec5SDimitry Andric } 8820b57cec5SDimitry Andric 8830b57cec5SDimitry Andric // Create the function filters 8844824e7fdSDimitry Andric if (!NameFilters.empty() || NameAllowlist || !NameRegexFilters.empty()) { 8858bcb0991SDimitry Andric auto NameFilterer = std::make_unique<CoverageFilters>(); 8860b57cec5SDimitry Andric for (const auto &Name : NameFilters) 8878bcb0991SDimitry Andric NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name)); 888bdd1243dSDimitry Andric if (NameAllowlist && !NameFilterFiles.empty()) 8890b57cec5SDimitry Andric NameFilterer->push_back( 8904824e7fdSDimitry Andric std::make_unique<NameAllowlistCoverageFilter>(*NameAllowlist)); 8910b57cec5SDimitry Andric for (const auto &Regex : NameRegexFilters) 8920b57cec5SDimitry Andric NameFilterer->push_back( 8938bcb0991SDimitry Andric std::make_unique<NameRegexCoverageFilter>(Regex)); 8940b57cec5SDimitry Andric Filters.push_back(std::move(NameFilterer)); 8950b57cec5SDimitry Andric } 8960b57cec5SDimitry Andric 8970b57cec5SDimitry Andric if (RegionCoverageLtFilter.getNumOccurrences() || 8980b57cec5SDimitry Andric RegionCoverageGtFilter.getNumOccurrences() || 8990b57cec5SDimitry Andric LineCoverageLtFilter.getNumOccurrences() || 9000b57cec5SDimitry Andric LineCoverageGtFilter.getNumOccurrences()) { 9018bcb0991SDimitry Andric auto StatFilterer = std::make_unique<CoverageFilters>(); 9020b57cec5SDimitry Andric if (RegionCoverageLtFilter.getNumOccurrences()) 9038bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( 9040b57cec5SDimitry Andric RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 9050b57cec5SDimitry Andric if (RegionCoverageGtFilter.getNumOccurrences()) 9068bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( 9070b57cec5SDimitry Andric RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 9080b57cec5SDimitry Andric if (LineCoverageLtFilter.getNumOccurrences()) 9098bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<LineCoverageFilter>( 9100b57cec5SDimitry Andric LineCoverageFilter::LessThan, LineCoverageLtFilter)); 9110b57cec5SDimitry Andric if (LineCoverageGtFilter.getNumOccurrences()) 9128bcb0991SDimitry Andric StatFilterer->push_back(std::make_unique<LineCoverageFilter>( 9130b57cec5SDimitry Andric RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 9140b57cec5SDimitry Andric Filters.push_back(std::move(StatFilterer)); 9150b57cec5SDimitry Andric } 9160b57cec5SDimitry Andric 9170b57cec5SDimitry Andric // Create the ignore filename filters. 9180b57cec5SDimitry Andric for (const auto &RE : IgnoreFilenameRegexFilters) 9190b57cec5SDimitry Andric IgnoreFilenameFilters.push_back( 9208bcb0991SDimitry Andric std::make_unique<NameRegexCoverageFilter>(RE)); 9210b57cec5SDimitry Andric 9220b57cec5SDimitry Andric if (!Arches.empty()) { 9230b57cec5SDimitry Andric for (const std::string &Arch : Arches) { 9240b57cec5SDimitry Andric if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { 9255f757f3fSDimitry Andric error("unknown architecture: " + Arch); 9260b57cec5SDimitry Andric return 1; 9270b57cec5SDimitry Andric } 9280b57cec5SDimitry Andric CoverageArches.emplace_back(Arch); 9290b57cec5SDimitry Andric } 9301ac55f4cSDimitry Andric if (CoverageArches.size() != 1 && 9311ac55f4cSDimitry Andric CoverageArches.size() != ObjectFilenames.size()) { 9325f757f3fSDimitry Andric error("number of architectures doesn't match the number of objects"); 9330b57cec5SDimitry Andric return 1; 9340b57cec5SDimitry Andric } 9350b57cec5SDimitry Andric } 9360b57cec5SDimitry Andric 9370b57cec5SDimitry Andric // IgnoreFilenameFilters are applied even when InputSourceFiles specified. 9380b57cec5SDimitry Andric for (const std::string &File : InputSourceFiles) 9390b57cec5SDimitry Andric collectPaths(File); 9400b57cec5SDimitry Andric 9410b57cec5SDimitry Andric if (DebugDumpCollectedPaths) { 9420b57cec5SDimitry Andric for (const std::string &SF : SourceFiles) 9430b57cec5SDimitry Andric outs() << SF << '\n'; 9440b57cec5SDimitry Andric ::exit(0); 9450b57cec5SDimitry Andric } 9460b57cec5SDimitry Andric 9475f757f3fSDimitry Andric ViewOpts.ShowMCDCSummary = MCDCSummary; 948e8d8bef9SDimitry Andric ViewOpts.ShowBranchSummary = BranchSummary; 9490b57cec5SDimitry Andric ViewOpts.ShowRegionSummary = RegionSummary; 9500b57cec5SDimitry Andric ViewOpts.ShowInstantiationSummary = InstantiationSummary; 9510b57cec5SDimitry Andric ViewOpts.ExportSummaryOnly = SummaryOnly; 9520b57cec5SDimitry Andric ViewOpts.NumThreads = NumThreads; 953fe6060f1SDimitry Andric ViewOpts.CompilationDirectory = CompilationDirectory; 9540b57cec5SDimitry Andric 9550b57cec5SDimitry Andric return 0; 9560b57cec5SDimitry Andric }; 9570b57cec5SDimitry Andric 9580b57cec5SDimitry Andric switch (Cmd) { 9590b57cec5SDimitry Andric case Show: 9600b57cec5SDimitry Andric return doShow(argc, argv, commandLineParser); 9610b57cec5SDimitry Andric case Report: 9620b57cec5SDimitry Andric return doReport(argc, argv, commandLineParser); 9630b57cec5SDimitry Andric case Export: 9640b57cec5SDimitry Andric return doExport(argc, argv, commandLineParser); 9650b57cec5SDimitry Andric } 9660b57cec5SDimitry Andric return 0; 9670b57cec5SDimitry Andric } 9680b57cec5SDimitry Andric 9690b57cec5SDimitry Andric int CodeCoverageTool::doShow(int argc, const char **argv, 9700b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 9710b57cec5SDimitry Andric 9720b57cec5SDimitry Andric cl::OptionCategory ViewCategory("Viewing options"); 9730b57cec5SDimitry Andric 9740b57cec5SDimitry Andric cl::opt<bool> ShowLineExecutionCounts( 9750b57cec5SDimitry Andric "show-line-counts", cl::Optional, 9760b57cec5SDimitry Andric cl::desc("Show the execution counts for each line"), cl::init(true), 9770b57cec5SDimitry Andric cl::cat(ViewCategory)); 9780b57cec5SDimitry Andric 9790b57cec5SDimitry Andric cl::opt<bool> ShowRegions( 9800b57cec5SDimitry Andric "show-regions", cl::Optional, 9810b57cec5SDimitry Andric cl::desc("Show the execution counts for each region"), 9820b57cec5SDimitry Andric cl::cat(ViewCategory)); 9830b57cec5SDimitry Andric 984e8d8bef9SDimitry Andric cl::opt<CoverageViewOptions::BranchOutputType> ShowBranches( 985e8d8bef9SDimitry Andric "show-branches", cl::Optional, 986e8d8bef9SDimitry Andric cl::desc("Show coverage for branch conditions"), cl::cat(ViewCategory), 987e8d8bef9SDimitry Andric cl::values(clEnumValN(CoverageViewOptions::BranchOutputType::Count, 988e8d8bef9SDimitry Andric "count", "Show True/False counts"), 989e8d8bef9SDimitry Andric clEnumValN(CoverageViewOptions::BranchOutputType::Percent, 990e8d8bef9SDimitry Andric "percent", "Show True/False percent")), 991e8d8bef9SDimitry Andric cl::init(CoverageViewOptions::BranchOutputType::Off)); 992e8d8bef9SDimitry Andric 9935f757f3fSDimitry Andric cl::opt<bool> ShowMCDC( 9945f757f3fSDimitry Andric "show-mcdc", cl::Optional, 9955f757f3fSDimitry Andric cl::desc("Show the MCDC Coverage for each applicable boolean expression"), 9965f757f3fSDimitry Andric cl::cat(ViewCategory)); 9975f757f3fSDimitry Andric 9980b57cec5SDimitry Andric cl::opt<bool> ShowBestLineRegionsCounts( 9990b57cec5SDimitry Andric "show-line-counts-or-regions", cl::Optional, 10000b57cec5SDimitry Andric cl::desc("Show the execution counts for each line, or the execution " 10010b57cec5SDimitry Andric "counts for each region on lines that have multiple regions"), 10020b57cec5SDimitry Andric cl::cat(ViewCategory)); 10030b57cec5SDimitry Andric 10040b57cec5SDimitry Andric cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 10050b57cec5SDimitry Andric cl::desc("Show expanded source regions"), 10060b57cec5SDimitry Andric cl::cat(ViewCategory)); 10070b57cec5SDimitry Andric 10080b57cec5SDimitry Andric cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 10090b57cec5SDimitry Andric cl::desc("Show function instantiations"), 10100b57cec5SDimitry Andric cl::init(true), cl::cat(ViewCategory)); 10110b57cec5SDimitry Andric 10125f757f3fSDimitry Andric cl::opt<bool> ShowDirectoryCoverage("show-directory-coverage", cl::Optional, 10135f757f3fSDimitry Andric cl::desc("Show directory coverage"), 10145f757f3fSDimitry Andric cl::cat(ViewCategory)); 10155f757f3fSDimitry Andric 10160b57cec5SDimitry Andric cl::opt<std::string> ShowOutputDirectory( 10170b57cec5SDimitry Andric "output-dir", cl::init(""), 10180b57cec5SDimitry Andric cl::desc("Directory in which coverage information is written out")); 10190b57cec5SDimitry Andric cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), 10200b57cec5SDimitry Andric cl::aliasopt(ShowOutputDirectory)); 10210b57cec5SDimitry Andric 10220b57cec5SDimitry Andric cl::opt<uint32_t> TabSize( 10230b57cec5SDimitry Andric "tab-size", cl::init(2), 10240b57cec5SDimitry Andric cl::desc( 10250b57cec5SDimitry Andric "Set tab expansion size for html coverage reports (default = 2)")); 10260b57cec5SDimitry Andric 10270b57cec5SDimitry Andric cl::opt<std::string> ProjectTitle( 10280b57cec5SDimitry Andric "project-title", cl::Optional, 10290b57cec5SDimitry Andric cl::desc("Set project title for the coverage report")); 10300b57cec5SDimitry Andric 103181ad6265SDimitry Andric cl::opt<std::string> CovWatermark( 103281ad6265SDimitry Andric "coverage-watermark", cl::Optional, 103381ad6265SDimitry Andric cl::desc("<high>,<low> value indicate thresholds for high and low" 103481ad6265SDimitry Andric "coverage watermark")); 103581ad6265SDimitry Andric 10360b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 10370b57cec5SDimitry Andric if (Err) 10380b57cec5SDimitry Andric return Err; 10390b57cec5SDimitry Andric 10400b57cec5SDimitry Andric if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 10415f757f3fSDimitry Andric error("lcov format should be used with 'llvm-cov export'."); 10420b57cec5SDimitry Andric return 1; 10430b57cec5SDimitry Andric } 10440b57cec5SDimitry Andric 104581ad6265SDimitry Andric ViewOpts.HighCovWatermark = 100.0; 104681ad6265SDimitry Andric ViewOpts.LowCovWatermark = 80.0; 104781ad6265SDimitry Andric if (!CovWatermark.empty()) { 104881ad6265SDimitry Andric auto WaterMarkPair = StringRef(CovWatermark).split(','); 104981ad6265SDimitry Andric if (WaterMarkPair.first.empty() || WaterMarkPair.second.empty()) { 105081ad6265SDimitry Andric error("invalid argument '" + CovWatermark + 105181ad6265SDimitry Andric "', must be in format 'high,low'", 105281ad6265SDimitry Andric "-coverage-watermark"); 105381ad6265SDimitry Andric return 1; 105481ad6265SDimitry Andric } 105581ad6265SDimitry Andric 105681ad6265SDimitry Andric char *EndPointer = nullptr; 105781ad6265SDimitry Andric ViewOpts.HighCovWatermark = 105881ad6265SDimitry Andric strtod(WaterMarkPair.first.begin(), &EndPointer); 105981ad6265SDimitry Andric if (EndPointer != WaterMarkPair.first.end()) { 106081ad6265SDimitry Andric error("invalid number '" + WaterMarkPair.first + 106181ad6265SDimitry Andric "', invalid value for 'high'", 106281ad6265SDimitry Andric "-coverage-watermark"); 106381ad6265SDimitry Andric return 1; 106481ad6265SDimitry Andric } 106581ad6265SDimitry Andric 106681ad6265SDimitry Andric ViewOpts.LowCovWatermark = 106781ad6265SDimitry Andric strtod(WaterMarkPair.second.begin(), &EndPointer); 106881ad6265SDimitry Andric if (EndPointer != WaterMarkPair.second.end()) { 106981ad6265SDimitry Andric error("invalid number '" + WaterMarkPair.second + 107081ad6265SDimitry Andric "', invalid value for 'low'", 107181ad6265SDimitry Andric "-coverage-watermark"); 107281ad6265SDimitry Andric return 1; 107381ad6265SDimitry Andric } 107481ad6265SDimitry Andric 107581ad6265SDimitry Andric if (ViewOpts.HighCovWatermark > 100 || ViewOpts.LowCovWatermark < 0 || 107681ad6265SDimitry Andric ViewOpts.HighCovWatermark <= ViewOpts.LowCovWatermark) { 107781ad6265SDimitry Andric error( 107881ad6265SDimitry Andric "invalid number range '" + CovWatermark + 107981ad6265SDimitry Andric "', must be both high and low should be between 0-100, and high " 108081ad6265SDimitry Andric "> low", 108181ad6265SDimitry Andric "-coverage-watermark"); 108281ad6265SDimitry Andric return 1; 108381ad6265SDimitry Andric } 108481ad6265SDimitry Andric } 108581ad6265SDimitry Andric 10860b57cec5SDimitry Andric ViewOpts.ShowLineNumbers = true; 10870b57cec5SDimitry Andric ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 10880b57cec5SDimitry Andric !ShowRegions || ShowBestLineRegionsCounts; 10890b57cec5SDimitry Andric ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 10900b57cec5SDimitry Andric ViewOpts.ShowExpandedRegions = ShowExpansions; 1091e8d8bef9SDimitry Andric ViewOpts.ShowBranchCounts = 1092e8d8bef9SDimitry Andric ShowBranches == CoverageViewOptions::BranchOutputType::Count; 10935f757f3fSDimitry Andric ViewOpts.ShowMCDC = ShowMCDC; 1094e8d8bef9SDimitry Andric ViewOpts.ShowBranchPercents = 1095e8d8bef9SDimitry Andric ShowBranches == CoverageViewOptions::BranchOutputType::Percent; 10960b57cec5SDimitry Andric ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 10975f757f3fSDimitry Andric ViewOpts.ShowDirectoryCoverage = ShowDirectoryCoverage; 10980b57cec5SDimitry Andric ViewOpts.ShowOutputDirectory = ShowOutputDirectory; 10990b57cec5SDimitry Andric ViewOpts.TabSize = TabSize; 11000b57cec5SDimitry Andric ViewOpts.ProjectTitle = ProjectTitle; 11010b57cec5SDimitry Andric 11020b57cec5SDimitry Andric if (ViewOpts.hasOutputDirectory()) { 11030b57cec5SDimitry Andric if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { 11045f757f3fSDimitry Andric error("could not create output directory!", E.message()); 11050b57cec5SDimitry Andric return 1; 11060b57cec5SDimitry Andric } 11070b57cec5SDimitry Andric } 11080b57cec5SDimitry Andric 11090b57cec5SDimitry Andric sys::fs::file_status Status; 11105ffd83dbSDimitry Andric if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { 11115f757f3fSDimitry Andric error("could not read profile data!" + EC.message(), PGOFilename); 11120b57cec5SDimitry Andric return 1; 11130b57cec5SDimitry Andric } 11140b57cec5SDimitry Andric 11150b57cec5SDimitry Andric auto ModifiedTime = Status.getLastModificationTime(); 11160b57cec5SDimitry Andric std::string ModifiedTimeStr = to_string(ModifiedTime); 11170b57cec5SDimitry Andric size_t found = ModifiedTimeStr.rfind(':'); 11180b57cec5SDimitry Andric ViewOpts.CreatedTimeStr = (found != std::string::npos) 11190b57cec5SDimitry Andric ? "Created: " + ModifiedTimeStr.substr(0, found) 11200b57cec5SDimitry Andric : "Created: " + ModifiedTimeStr; 11210b57cec5SDimitry Andric 11220b57cec5SDimitry Andric auto Coverage = load(); 11230b57cec5SDimitry Andric if (!Coverage) 11240b57cec5SDimitry Andric return 1; 11250b57cec5SDimitry Andric 11260b57cec5SDimitry Andric auto Printer = CoveragePrinter::create(ViewOpts); 11270b57cec5SDimitry Andric 1128e8d8bef9SDimitry Andric if (SourceFiles.empty() && !HadSourceFiles) 11290b57cec5SDimitry Andric // Get the source files from the function coverage mapping. 11300b57cec5SDimitry Andric for (StringRef Filename : Coverage->getUniqueSourceFiles()) { 11310b57cec5SDimitry Andric if (!IgnoreFilenameFilters.matchesFilename(Filename)) 11325ffd83dbSDimitry Andric SourceFiles.push_back(std::string(Filename)); 11330b57cec5SDimitry Andric } 11340b57cec5SDimitry Andric 11350b57cec5SDimitry Andric // Create an index out of the source files. 11360b57cec5SDimitry Andric if (ViewOpts.hasOutputDirectory()) { 11370b57cec5SDimitry Andric if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) { 11385f757f3fSDimitry Andric error("could not create index file!", toString(std::move(E))); 11390b57cec5SDimitry Andric return 1; 11400b57cec5SDimitry Andric } 11410b57cec5SDimitry Andric } 11420b57cec5SDimitry Andric 11430b57cec5SDimitry Andric if (!Filters.empty()) { 11440b57cec5SDimitry Andric // Build the map of filenames to functions. 11450b57cec5SDimitry Andric std::map<llvm::StringRef, std::vector<const FunctionRecord *>> 11460b57cec5SDimitry Andric FilenameFunctionMap; 11470b57cec5SDimitry Andric for (const auto &SourceFile : SourceFiles) 11480b57cec5SDimitry Andric for (const auto &Function : Coverage->getCoveredFunctions(SourceFile)) 1149bdd1243dSDimitry Andric if (Filters.matches(*Coverage, Function)) 11500b57cec5SDimitry Andric FilenameFunctionMap[SourceFile].push_back(&Function); 11510b57cec5SDimitry Andric 11520b57cec5SDimitry Andric // Only print filter matching functions for each file. 11530b57cec5SDimitry Andric for (const auto &FileFunc : FilenameFunctionMap) { 11540b57cec5SDimitry Andric StringRef File = FileFunc.first; 11550b57cec5SDimitry Andric const auto &Functions = FileFunc.second; 11560b57cec5SDimitry Andric 11570b57cec5SDimitry Andric auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false); 11580b57cec5SDimitry Andric if (Error E = OSOrErr.takeError()) { 11595f757f3fSDimitry Andric error("could not create view file!", toString(std::move(E))); 11600b57cec5SDimitry Andric return 1; 11610b57cec5SDimitry Andric } 11620b57cec5SDimitry Andric auto OS = std::move(OSOrErr.get()); 11630b57cec5SDimitry Andric 11640b57cec5SDimitry Andric bool ShowTitle = ViewOpts.hasOutputDirectory(); 11650b57cec5SDimitry Andric for (const auto *Function : Functions) { 11660b57cec5SDimitry Andric auto FunctionView = createFunctionView(*Function, *Coverage); 11670b57cec5SDimitry Andric if (!FunctionView) { 11680b57cec5SDimitry Andric warning("Could not read coverage for '" + Function->Name + "'."); 11690b57cec5SDimitry Andric continue; 11700b57cec5SDimitry Andric } 11710b57cec5SDimitry Andric FunctionView->print(*OS.get(), /*WholeFile=*/false, 11720b57cec5SDimitry Andric /*ShowSourceName=*/true, ShowTitle); 11730b57cec5SDimitry Andric ShowTitle = false; 11740b57cec5SDimitry Andric } 11750b57cec5SDimitry Andric 11760b57cec5SDimitry Andric Printer->closeViewFile(std::move(OS)); 11770b57cec5SDimitry Andric } 11780b57cec5SDimitry Andric return 0; 11790b57cec5SDimitry Andric } 11800b57cec5SDimitry Andric 11810b57cec5SDimitry Andric // Show files 11820b57cec5SDimitry Andric bool ShowFilenames = 11830b57cec5SDimitry Andric (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || 11840b57cec5SDimitry Andric (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); 11850b57cec5SDimitry Andric 11865ffd83dbSDimitry Andric ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads); 11875ffd83dbSDimitry Andric if (ViewOpts.NumThreads == 0) { 11885ffd83dbSDimitry Andric // If NumThreads is not specified, create one thread for each input, up to 11895ffd83dbSDimitry Andric // the number of hardware cores. 11905ffd83dbSDimitry Andric S = heavyweight_hardware_concurrency(SourceFiles.size()); 11915ffd83dbSDimitry Andric S.Limit = true; 11925ffd83dbSDimitry Andric } 11930b57cec5SDimitry Andric 11945ffd83dbSDimitry Andric if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) { 11950b57cec5SDimitry Andric for (const std::string &SourceFile : SourceFiles) 11960b57cec5SDimitry Andric writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), 11970b57cec5SDimitry Andric ShowFilenames); 11980b57cec5SDimitry Andric } else { 11990b57cec5SDimitry Andric // In -output-dir mode, it's safe to use multiple threads to print files. 1200*0fca6ea1SDimitry Andric DefaultThreadPool Pool(S); 12010b57cec5SDimitry Andric for (const std::string &SourceFile : SourceFiles) 12020b57cec5SDimitry Andric Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, 12030b57cec5SDimitry Andric Coverage.get(), Printer.get(), ShowFilenames); 12040b57cec5SDimitry Andric Pool.wait(); 12050b57cec5SDimitry Andric } 12060b57cec5SDimitry Andric 12070b57cec5SDimitry Andric return 0; 12080b57cec5SDimitry Andric } 12090b57cec5SDimitry Andric 12100b57cec5SDimitry Andric int CodeCoverageTool::doReport(int argc, const char **argv, 12110b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 12120b57cec5SDimitry Andric cl::opt<bool> ShowFunctionSummaries( 12130b57cec5SDimitry Andric "show-functions", cl::Optional, cl::init(false), 12140b57cec5SDimitry Andric cl::desc("Show coverage summaries for each function")); 12150b57cec5SDimitry Andric 12160b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 12170b57cec5SDimitry Andric if (Err) 12180b57cec5SDimitry Andric return Err; 12190b57cec5SDimitry Andric 12200b57cec5SDimitry Andric if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { 12210b57cec5SDimitry Andric error("HTML output for summary reports is not yet supported."); 12220b57cec5SDimitry Andric return 1; 12230b57cec5SDimitry Andric } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 12245f757f3fSDimitry Andric error("lcov format should be used with 'llvm-cov export'."); 12250b57cec5SDimitry Andric return 1; 12260b57cec5SDimitry Andric } 12270b57cec5SDimitry Andric 1228fcaf7f86SDimitry Andric sys::fs::file_status Status; 1229fcaf7f86SDimitry Andric if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { 12305f757f3fSDimitry Andric error("could not read profile data!" + EC.message(), PGOFilename); 1231fcaf7f86SDimitry Andric return 1; 1232fcaf7f86SDimitry Andric } 1233fcaf7f86SDimitry Andric 12340b57cec5SDimitry Andric auto Coverage = load(); 12350b57cec5SDimitry Andric if (!Coverage) 12360b57cec5SDimitry Andric return 1; 12370b57cec5SDimitry Andric 1238bdd1243dSDimitry Andric CoverageReport Report(ViewOpts, *Coverage); 12390b57cec5SDimitry Andric if (!ShowFunctionSummaries) { 12400b57cec5SDimitry Andric if (SourceFiles.empty()) 12410b57cec5SDimitry Andric Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters); 12420b57cec5SDimitry Andric else 12430b57cec5SDimitry Andric Report.renderFileReports(llvm::outs(), SourceFiles); 12440b57cec5SDimitry Andric } else { 12450b57cec5SDimitry Andric if (SourceFiles.empty()) { 12465f757f3fSDimitry Andric error("source files must be specified when -show-functions=true is " 12470b57cec5SDimitry Andric "specified"); 12480b57cec5SDimitry Andric return 1; 12490b57cec5SDimitry Andric } 12500b57cec5SDimitry Andric 12510b57cec5SDimitry Andric Report.renderFunctionReports(SourceFiles, DC, llvm::outs()); 12520b57cec5SDimitry Andric } 12530b57cec5SDimitry Andric return 0; 12540b57cec5SDimitry Andric } 12550b57cec5SDimitry Andric 12560b57cec5SDimitry Andric int CodeCoverageTool::doExport(int argc, const char **argv, 12570b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 12580b57cec5SDimitry Andric 12590b57cec5SDimitry Andric cl::OptionCategory ExportCategory("Exporting options"); 12600b57cec5SDimitry Andric 12610b57cec5SDimitry Andric cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional, 12620b57cec5SDimitry Andric cl::desc("Don't export expanded source regions"), 12630b57cec5SDimitry Andric cl::cat(ExportCategory)); 12640b57cec5SDimitry Andric 12650b57cec5SDimitry Andric cl::opt<bool> SkipFunctions("skip-functions", cl::Optional, 12660b57cec5SDimitry Andric cl::desc("Don't export per-function data"), 12670b57cec5SDimitry Andric cl::cat(ExportCategory)); 12680b57cec5SDimitry Andric 1269bdd1243dSDimitry Andric cl::opt<bool> SkipBranches("skip-branches", cl::Optional, 1270bdd1243dSDimitry Andric cl::desc("Don't export branch data (LCOV)"), 1271bdd1243dSDimitry Andric cl::cat(ExportCategory)); 1272bdd1243dSDimitry Andric 12730b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 12740b57cec5SDimitry Andric if (Err) 12750b57cec5SDimitry Andric return Err; 12760b57cec5SDimitry Andric 12770b57cec5SDimitry Andric ViewOpts.SkipExpansions = SkipExpansions; 12780b57cec5SDimitry Andric ViewOpts.SkipFunctions = SkipFunctions; 1279bdd1243dSDimitry Andric ViewOpts.SkipBranches = SkipBranches; 12800b57cec5SDimitry Andric 12810b57cec5SDimitry Andric if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && 12820b57cec5SDimitry Andric ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { 12835f757f3fSDimitry Andric error("coverage data can only be exported as textual JSON or an " 12840b57cec5SDimitry Andric "lcov tracefile."); 12850b57cec5SDimitry Andric return 1; 12860b57cec5SDimitry Andric } 12870b57cec5SDimitry Andric 1288fcaf7f86SDimitry Andric sys::fs::file_status Status; 1289fcaf7f86SDimitry Andric if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { 12905f757f3fSDimitry Andric error("could not read profile data!" + EC.message(), PGOFilename); 1291fcaf7f86SDimitry Andric return 1; 1292fcaf7f86SDimitry Andric } 1293fcaf7f86SDimitry Andric 12940b57cec5SDimitry Andric auto Coverage = load(); 12950b57cec5SDimitry Andric if (!Coverage) { 12965f757f3fSDimitry Andric error("could not load coverage information"); 12970b57cec5SDimitry Andric return 1; 12980b57cec5SDimitry Andric } 12990b57cec5SDimitry Andric 13000b57cec5SDimitry Andric std::unique_ptr<CoverageExporter> Exporter; 13010b57cec5SDimitry Andric 13020b57cec5SDimitry Andric switch (ViewOpts.Format) { 13030b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 1304bdd1243dSDimitry Andric Exporter = 1305bdd1243dSDimitry Andric std::make_unique<CoverageExporterJson>(*Coverage, ViewOpts, outs()); 13060b57cec5SDimitry Andric break; 13070b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 13080b57cec5SDimitry Andric // Unreachable because we should have gracefully terminated with an error 13090b57cec5SDimitry Andric // above. 13100b57cec5SDimitry Andric llvm_unreachable("Export in HTML is not supported!"); 13110b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 1312bdd1243dSDimitry Andric Exporter = 1313bdd1243dSDimitry Andric std::make_unique<CoverageExporterLcov>(*Coverage, ViewOpts, outs()); 13140b57cec5SDimitry Andric break; 13150b57cec5SDimitry Andric } 13160b57cec5SDimitry Andric 13170b57cec5SDimitry Andric if (SourceFiles.empty()) 13180b57cec5SDimitry Andric Exporter->renderRoot(IgnoreFilenameFilters); 13190b57cec5SDimitry Andric else 13200b57cec5SDimitry Andric Exporter->renderRoot(SourceFiles); 13210b57cec5SDimitry Andric 13220b57cec5SDimitry Andric return 0; 13230b57cec5SDimitry Andric } 13240b57cec5SDimitry Andric 13250b57cec5SDimitry Andric int showMain(int argc, const char *argv[]) { 13260b57cec5SDimitry Andric CodeCoverageTool Tool; 13270b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Show, argc, argv); 13280b57cec5SDimitry Andric } 13290b57cec5SDimitry Andric 13300b57cec5SDimitry Andric int reportMain(int argc, const char *argv[]) { 13310b57cec5SDimitry Andric CodeCoverageTool Tool; 13320b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Report, argc, argv); 13330b57cec5SDimitry Andric } 13340b57cec5SDimitry Andric 13350b57cec5SDimitry Andric int exportMain(int argc, const char *argv[]) { 13360b57cec5SDimitry Andric CodeCoverageTool Tool; 13370b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Export, argc, argv); 13380b57cec5SDimitry Andric } 1339