1*0b57cec5SDimitry Andric //===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // The 'CodeCoverageTool' class implements a command line tool to analyze and 10*0b57cec5SDimitry Andric // report coverage information using the profiling instrumentation and code 11*0b57cec5SDimitry Andric // coverage mapping. 12*0b57cec5SDimitry Andric // 13*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 14*0b57cec5SDimitry Andric 15*0b57cec5SDimitry Andric #include "CoverageExporterJson.h" 16*0b57cec5SDimitry Andric #include "CoverageExporterLcov.h" 17*0b57cec5SDimitry Andric #include "CoverageFilters.h" 18*0b57cec5SDimitry Andric #include "CoverageReport.h" 19*0b57cec5SDimitry Andric #include "CoverageSummaryInfo.h" 20*0b57cec5SDimitry Andric #include "CoverageViewOptions.h" 21*0b57cec5SDimitry Andric #include "RenderingSupport.h" 22*0b57cec5SDimitry Andric #include "SourceCoverageView.h" 23*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 24*0b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 25*0b57cec5SDimitry Andric #include "llvm/ADT/Triple.h" 26*0b57cec5SDimitry Andric #include "llvm/ProfileData/Coverage/CoverageMapping.h" 27*0b57cec5SDimitry Andric #include "llvm/ProfileData/InstrProfReader.h" 28*0b57cec5SDimitry Andric #include "llvm/Support/CommandLine.h" 29*0b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 30*0b57cec5SDimitry Andric #include "llvm/Support/Format.h" 31*0b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 32*0b57cec5SDimitry Andric #include "llvm/Support/Path.h" 33*0b57cec5SDimitry Andric #include "llvm/Support/Process.h" 34*0b57cec5SDimitry Andric #include "llvm/Support/Program.h" 35*0b57cec5SDimitry Andric #include "llvm/Support/ScopedPrinter.h" 36*0b57cec5SDimitry Andric #include "llvm/Support/ThreadPool.h" 37*0b57cec5SDimitry Andric #include "llvm/Support/Threading.h" 38*0b57cec5SDimitry Andric #include "llvm/Support/ToolOutputFile.h" 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric #include <functional> 41*0b57cec5SDimitry Andric #include <map> 42*0b57cec5SDimitry Andric #include <system_error> 43*0b57cec5SDimitry Andric 44*0b57cec5SDimitry Andric using namespace llvm; 45*0b57cec5SDimitry Andric using namespace coverage; 46*0b57cec5SDimitry Andric 47*0b57cec5SDimitry Andric void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping, 48*0b57cec5SDimitry Andric const CoverageViewOptions &Options, 49*0b57cec5SDimitry Andric raw_ostream &OS); 50*0b57cec5SDimitry Andric 51*0b57cec5SDimitry Andric namespace { 52*0b57cec5SDimitry Andric /// The implementation of the coverage tool. 53*0b57cec5SDimitry Andric class CodeCoverageTool { 54*0b57cec5SDimitry Andric public: 55*0b57cec5SDimitry Andric enum Command { 56*0b57cec5SDimitry Andric /// The show command. 57*0b57cec5SDimitry Andric Show, 58*0b57cec5SDimitry Andric /// The report command. 59*0b57cec5SDimitry Andric Report, 60*0b57cec5SDimitry Andric /// The export command. 61*0b57cec5SDimitry Andric Export 62*0b57cec5SDimitry Andric }; 63*0b57cec5SDimitry Andric 64*0b57cec5SDimitry Andric int run(Command Cmd, int argc, const char **argv); 65*0b57cec5SDimitry Andric 66*0b57cec5SDimitry Andric private: 67*0b57cec5SDimitry Andric /// Print the error message to the error output stream. 68*0b57cec5SDimitry Andric void error(const Twine &Message, StringRef Whence = ""); 69*0b57cec5SDimitry Andric 70*0b57cec5SDimitry Andric /// Print the warning message to the error output stream. 71*0b57cec5SDimitry Andric void warning(const Twine &Message, StringRef Whence = ""); 72*0b57cec5SDimitry Andric 73*0b57cec5SDimitry Andric /// Convert \p Path into an absolute path and append it to the list 74*0b57cec5SDimitry Andric /// of collected paths. 75*0b57cec5SDimitry Andric void addCollectedPath(const std::string &Path); 76*0b57cec5SDimitry Andric 77*0b57cec5SDimitry Andric /// If \p Path is a regular file, collect the path. If it's a 78*0b57cec5SDimitry Andric /// directory, recursively collect all of the paths within the directory. 79*0b57cec5SDimitry Andric void collectPaths(const std::string &Path); 80*0b57cec5SDimitry Andric 81*0b57cec5SDimitry Andric /// Return a memory buffer for the given source file. 82*0b57cec5SDimitry Andric ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); 83*0b57cec5SDimitry Andric 84*0b57cec5SDimitry Andric /// Create source views for the expansions of the view. 85*0b57cec5SDimitry Andric void attachExpansionSubViews(SourceCoverageView &View, 86*0b57cec5SDimitry Andric ArrayRef<ExpansionRecord> Expansions, 87*0b57cec5SDimitry Andric const CoverageMapping &Coverage); 88*0b57cec5SDimitry Andric 89*0b57cec5SDimitry Andric /// Create the source view of a particular function. 90*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 91*0b57cec5SDimitry Andric createFunctionView(const FunctionRecord &Function, 92*0b57cec5SDimitry Andric const CoverageMapping &Coverage); 93*0b57cec5SDimitry Andric 94*0b57cec5SDimitry Andric /// Create the main source view of a particular source file. 95*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 96*0b57cec5SDimitry Andric createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); 97*0b57cec5SDimitry Andric 98*0b57cec5SDimitry Andric /// Load the coverage mapping data. Return nullptr if an error occurred. 99*0b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> load(); 100*0b57cec5SDimitry Andric 101*0b57cec5SDimitry Andric /// Create a mapping from files in the Coverage data to local copies 102*0b57cec5SDimitry Andric /// (path-equivalence). 103*0b57cec5SDimitry Andric void remapPathNames(const CoverageMapping &Coverage); 104*0b57cec5SDimitry Andric 105*0b57cec5SDimitry Andric /// Remove input source files which aren't mapped by \p Coverage. 106*0b57cec5SDimitry Andric void removeUnmappedInputs(const CoverageMapping &Coverage); 107*0b57cec5SDimitry Andric 108*0b57cec5SDimitry Andric /// If a demangler is available, demangle all symbol names. 109*0b57cec5SDimitry Andric void demangleSymbols(const CoverageMapping &Coverage); 110*0b57cec5SDimitry Andric 111*0b57cec5SDimitry Andric /// Write out a source file view to the filesystem. 112*0b57cec5SDimitry Andric void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage, 113*0b57cec5SDimitry Andric CoveragePrinter *Printer, bool ShowFilenames); 114*0b57cec5SDimitry Andric 115*0b57cec5SDimitry Andric typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; 116*0b57cec5SDimitry Andric 117*0b57cec5SDimitry Andric int doShow(int argc, const char **argv, 118*0b57cec5SDimitry Andric CommandLineParserType commandLineParser); 119*0b57cec5SDimitry Andric 120*0b57cec5SDimitry Andric int doReport(int argc, const char **argv, 121*0b57cec5SDimitry Andric CommandLineParserType commandLineParser); 122*0b57cec5SDimitry Andric 123*0b57cec5SDimitry Andric int doExport(int argc, const char **argv, 124*0b57cec5SDimitry Andric CommandLineParserType commandLineParser); 125*0b57cec5SDimitry Andric 126*0b57cec5SDimitry Andric std::vector<StringRef> ObjectFilenames; 127*0b57cec5SDimitry Andric CoverageViewOptions ViewOpts; 128*0b57cec5SDimitry Andric CoverageFiltersMatchAll Filters; 129*0b57cec5SDimitry Andric CoverageFilters IgnoreFilenameFilters; 130*0b57cec5SDimitry Andric 131*0b57cec5SDimitry Andric /// The path to the indexed profile. 132*0b57cec5SDimitry Andric std::string PGOFilename; 133*0b57cec5SDimitry Andric 134*0b57cec5SDimitry Andric /// A list of input source files. 135*0b57cec5SDimitry Andric std::vector<std::string> SourceFiles; 136*0b57cec5SDimitry Andric 137*0b57cec5SDimitry Andric /// In -path-equivalence mode, this maps the absolute paths from the coverage 138*0b57cec5SDimitry Andric /// mapping data to the input source files. 139*0b57cec5SDimitry Andric StringMap<std::string> RemappedFilenames; 140*0b57cec5SDimitry Andric 141*0b57cec5SDimitry Andric /// The coverage data path to be remapped from, and the source path to be 142*0b57cec5SDimitry Andric /// remapped to, when using -path-equivalence. 143*0b57cec5SDimitry Andric Optional<std::pair<std::string, std::string>> PathRemapping; 144*0b57cec5SDimitry Andric 145*0b57cec5SDimitry Andric /// The architecture the coverage mapping data targets. 146*0b57cec5SDimitry Andric std::vector<StringRef> CoverageArches; 147*0b57cec5SDimitry Andric 148*0b57cec5SDimitry Andric /// A cache for demangled symbols. 149*0b57cec5SDimitry Andric DemangleCache DC; 150*0b57cec5SDimitry Andric 151*0b57cec5SDimitry Andric /// A lock which guards printing to stderr. 152*0b57cec5SDimitry Andric std::mutex ErrsLock; 153*0b57cec5SDimitry Andric 154*0b57cec5SDimitry Andric /// A container for input source file buffers. 155*0b57cec5SDimitry Andric std::mutex LoadedSourceFilesLock; 156*0b57cec5SDimitry Andric std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> 157*0b57cec5SDimitry Andric LoadedSourceFiles; 158*0b57cec5SDimitry Andric 159*0b57cec5SDimitry Andric /// Whitelist from -name-whitelist to be used for filtering. 160*0b57cec5SDimitry Andric std::unique_ptr<SpecialCaseList> NameWhitelist; 161*0b57cec5SDimitry Andric }; 162*0b57cec5SDimitry Andric } 163*0b57cec5SDimitry Andric 164*0b57cec5SDimitry Andric static std::string getErrorString(const Twine &Message, StringRef Whence, 165*0b57cec5SDimitry Andric bool Warning) { 166*0b57cec5SDimitry Andric std::string Str = (Warning ? "warning" : "error"); 167*0b57cec5SDimitry Andric Str += ": "; 168*0b57cec5SDimitry Andric if (!Whence.empty()) 169*0b57cec5SDimitry Andric Str += Whence.str() + ": "; 170*0b57cec5SDimitry Andric Str += Message.str() + "\n"; 171*0b57cec5SDimitry Andric return Str; 172*0b57cec5SDimitry Andric } 173*0b57cec5SDimitry Andric 174*0b57cec5SDimitry Andric void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { 175*0b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{ErrsLock}; 176*0b57cec5SDimitry Andric ViewOpts.colored_ostream(errs(), raw_ostream::RED) 177*0b57cec5SDimitry Andric << getErrorString(Message, Whence, false); 178*0b57cec5SDimitry Andric } 179*0b57cec5SDimitry Andric 180*0b57cec5SDimitry Andric void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { 181*0b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{ErrsLock}; 182*0b57cec5SDimitry Andric ViewOpts.colored_ostream(errs(), raw_ostream::RED) 183*0b57cec5SDimitry Andric << getErrorString(Message, Whence, true); 184*0b57cec5SDimitry Andric } 185*0b57cec5SDimitry Andric 186*0b57cec5SDimitry Andric void CodeCoverageTool::addCollectedPath(const std::string &Path) { 187*0b57cec5SDimitry Andric SmallString<128> EffectivePath(Path); 188*0b57cec5SDimitry Andric if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) { 189*0b57cec5SDimitry Andric error(EC.message(), Path); 190*0b57cec5SDimitry Andric return; 191*0b57cec5SDimitry Andric } 192*0b57cec5SDimitry Andric sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true); 193*0b57cec5SDimitry Andric if (!IgnoreFilenameFilters.matchesFilename(EffectivePath)) 194*0b57cec5SDimitry Andric SourceFiles.emplace_back(EffectivePath.str()); 195*0b57cec5SDimitry Andric } 196*0b57cec5SDimitry Andric 197*0b57cec5SDimitry Andric void CodeCoverageTool::collectPaths(const std::string &Path) { 198*0b57cec5SDimitry Andric llvm::sys::fs::file_status Status; 199*0b57cec5SDimitry Andric llvm::sys::fs::status(Path, Status); 200*0b57cec5SDimitry Andric if (!llvm::sys::fs::exists(Status)) { 201*0b57cec5SDimitry Andric if (PathRemapping) 202*0b57cec5SDimitry Andric addCollectedPath(Path); 203*0b57cec5SDimitry Andric else 204*0b57cec5SDimitry Andric warning("Source file doesn't exist, proceeded by ignoring it.", Path); 205*0b57cec5SDimitry Andric return; 206*0b57cec5SDimitry Andric } 207*0b57cec5SDimitry Andric 208*0b57cec5SDimitry Andric if (llvm::sys::fs::is_regular_file(Status)) { 209*0b57cec5SDimitry Andric addCollectedPath(Path); 210*0b57cec5SDimitry Andric return; 211*0b57cec5SDimitry Andric } 212*0b57cec5SDimitry Andric 213*0b57cec5SDimitry Andric if (llvm::sys::fs::is_directory(Status)) { 214*0b57cec5SDimitry Andric std::error_code EC; 215*0b57cec5SDimitry Andric for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; 216*0b57cec5SDimitry Andric F != E; F.increment(EC)) { 217*0b57cec5SDimitry Andric 218*0b57cec5SDimitry Andric auto Status = F->status(); 219*0b57cec5SDimitry Andric if (!Status) { 220*0b57cec5SDimitry Andric warning(Status.getError().message(), F->path()); 221*0b57cec5SDimitry Andric continue; 222*0b57cec5SDimitry Andric } 223*0b57cec5SDimitry Andric 224*0b57cec5SDimitry Andric if (Status->type() == llvm::sys::fs::file_type::regular_file) 225*0b57cec5SDimitry Andric addCollectedPath(F->path()); 226*0b57cec5SDimitry Andric } 227*0b57cec5SDimitry Andric } 228*0b57cec5SDimitry Andric } 229*0b57cec5SDimitry Andric 230*0b57cec5SDimitry Andric ErrorOr<const MemoryBuffer &> 231*0b57cec5SDimitry Andric CodeCoverageTool::getSourceFile(StringRef SourceFile) { 232*0b57cec5SDimitry Andric // If we've remapped filenames, look up the real location for this file. 233*0b57cec5SDimitry Andric std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock}; 234*0b57cec5SDimitry Andric if (!RemappedFilenames.empty()) { 235*0b57cec5SDimitry Andric auto Loc = RemappedFilenames.find(SourceFile); 236*0b57cec5SDimitry Andric if (Loc != RemappedFilenames.end()) 237*0b57cec5SDimitry Andric SourceFile = Loc->second; 238*0b57cec5SDimitry Andric } 239*0b57cec5SDimitry Andric for (const auto &Files : LoadedSourceFiles) 240*0b57cec5SDimitry Andric if (sys::fs::equivalent(SourceFile, Files.first)) 241*0b57cec5SDimitry Andric return *Files.second; 242*0b57cec5SDimitry Andric auto Buffer = MemoryBuffer::getFile(SourceFile); 243*0b57cec5SDimitry Andric if (auto EC = Buffer.getError()) { 244*0b57cec5SDimitry Andric error(EC.message(), SourceFile); 245*0b57cec5SDimitry Andric return EC; 246*0b57cec5SDimitry Andric } 247*0b57cec5SDimitry Andric LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get())); 248*0b57cec5SDimitry Andric return *LoadedSourceFiles.back().second; 249*0b57cec5SDimitry Andric } 250*0b57cec5SDimitry Andric 251*0b57cec5SDimitry Andric void CodeCoverageTool::attachExpansionSubViews( 252*0b57cec5SDimitry Andric SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions, 253*0b57cec5SDimitry Andric const CoverageMapping &Coverage) { 254*0b57cec5SDimitry Andric if (!ViewOpts.ShowExpandedRegions) 255*0b57cec5SDimitry Andric return; 256*0b57cec5SDimitry Andric for (const auto &Expansion : Expansions) { 257*0b57cec5SDimitry Andric auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); 258*0b57cec5SDimitry Andric if (ExpansionCoverage.empty()) 259*0b57cec5SDimitry Andric continue; 260*0b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename()); 261*0b57cec5SDimitry Andric if (!SourceBuffer) 262*0b57cec5SDimitry Andric continue; 263*0b57cec5SDimitry Andric 264*0b57cec5SDimitry Andric auto SubViewExpansions = ExpansionCoverage.getExpansions(); 265*0b57cec5SDimitry Andric auto SubView = 266*0b57cec5SDimitry Andric SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), 267*0b57cec5SDimitry Andric ViewOpts, std::move(ExpansionCoverage)); 268*0b57cec5SDimitry Andric attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 269*0b57cec5SDimitry Andric View.addExpansion(Expansion.Region, std::move(SubView)); 270*0b57cec5SDimitry Andric } 271*0b57cec5SDimitry Andric } 272*0b57cec5SDimitry Andric 273*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 274*0b57cec5SDimitry Andric CodeCoverageTool::createFunctionView(const FunctionRecord &Function, 275*0b57cec5SDimitry Andric const CoverageMapping &Coverage) { 276*0b57cec5SDimitry Andric auto FunctionCoverage = Coverage.getCoverageForFunction(Function); 277*0b57cec5SDimitry Andric if (FunctionCoverage.empty()) 278*0b57cec5SDimitry Andric return nullptr; 279*0b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); 280*0b57cec5SDimitry Andric if (!SourceBuffer) 281*0b57cec5SDimitry Andric return nullptr; 282*0b57cec5SDimitry Andric 283*0b57cec5SDimitry Andric auto Expansions = FunctionCoverage.getExpansions(); 284*0b57cec5SDimitry Andric auto View = SourceCoverageView::create(DC.demangle(Function.Name), 285*0b57cec5SDimitry Andric SourceBuffer.get(), ViewOpts, 286*0b57cec5SDimitry Andric std::move(FunctionCoverage)); 287*0b57cec5SDimitry Andric attachExpansionSubViews(*View, Expansions, Coverage); 288*0b57cec5SDimitry Andric 289*0b57cec5SDimitry Andric return View; 290*0b57cec5SDimitry Andric } 291*0b57cec5SDimitry Andric 292*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> 293*0b57cec5SDimitry Andric CodeCoverageTool::createSourceFileView(StringRef SourceFile, 294*0b57cec5SDimitry Andric const CoverageMapping &Coverage) { 295*0b57cec5SDimitry Andric auto SourceBuffer = getSourceFile(SourceFile); 296*0b57cec5SDimitry Andric if (!SourceBuffer) 297*0b57cec5SDimitry Andric return nullptr; 298*0b57cec5SDimitry Andric auto FileCoverage = Coverage.getCoverageForFile(SourceFile); 299*0b57cec5SDimitry Andric if (FileCoverage.empty()) 300*0b57cec5SDimitry Andric return nullptr; 301*0b57cec5SDimitry Andric 302*0b57cec5SDimitry Andric auto Expansions = FileCoverage.getExpansions(); 303*0b57cec5SDimitry Andric auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), 304*0b57cec5SDimitry Andric ViewOpts, std::move(FileCoverage)); 305*0b57cec5SDimitry Andric attachExpansionSubViews(*View, Expansions, Coverage); 306*0b57cec5SDimitry Andric if (!ViewOpts.ShowFunctionInstantiations) 307*0b57cec5SDimitry Andric return View; 308*0b57cec5SDimitry Andric 309*0b57cec5SDimitry Andric for (const auto &Group : Coverage.getInstantiationGroups(SourceFile)) { 310*0b57cec5SDimitry Andric // Skip functions which have a single instantiation. 311*0b57cec5SDimitry Andric if (Group.size() < 2) 312*0b57cec5SDimitry Andric continue; 313*0b57cec5SDimitry Andric 314*0b57cec5SDimitry Andric for (const FunctionRecord *Function : Group.getInstantiations()) { 315*0b57cec5SDimitry Andric std::unique_ptr<SourceCoverageView> SubView{nullptr}; 316*0b57cec5SDimitry Andric 317*0b57cec5SDimitry Andric StringRef Funcname = DC.demangle(Function->Name); 318*0b57cec5SDimitry Andric 319*0b57cec5SDimitry Andric if (Function->ExecutionCount > 0) { 320*0b57cec5SDimitry Andric auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); 321*0b57cec5SDimitry Andric auto SubViewExpansions = SubViewCoverage.getExpansions(); 322*0b57cec5SDimitry Andric SubView = SourceCoverageView::create( 323*0b57cec5SDimitry Andric Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); 324*0b57cec5SDimitry Andric attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 325*0b57cec5SDimitry Andric } 326*0b57cec5SDimitry Andric 327*0b57cec5SDimitry Andric unsigned FileID = Function->CountedRegions.front().FileID; 328*0b57cec5SDimitry Andric unsigned Line = 0; 329*0b57cec5SDimitry Andric for (const auto &CR : Function->CountedRegions) 330*0b57cec5SDimitry Andric if (CR.FileID == FileID) 331*0b57cec5SDimitry Andric Line = std::max(CR.LineEnd, Line); 332*0b57cec5SDimitry Andric View->addInstantiation(Funcname, Line, std::move(SubView)); 333*0b57cec5SDimitry Andric } 334*0b57cec5SDimitry Andric } 335*0b57cec5SDimitry Andric return View; 336*0b57cec5SDimitry Andric } 337*0b57cec5SDimitry Andric 338*0b57cec5SDimitry Andric static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { 339*0b57cec5SDimitry Andric sys::fs::file_status Status; 340*0b57cec5SDimitry Andric if (sys::fs::status(LHS, Status)) 341*0b57cec5SDimitry Andric return false; 342*0b57cec5SDimitry Andric auto LHSTime = Status.getLastModificationTime(); 343*0b57cec5SDimitry Andric if (sys::fs::status(RHS, Status)) 344*0b57cec5SDimitry Andric return false; 345*0b57cec5SDimitry Andric auto RHSTime = Status.getLastModificationTime(); 346*0b57cec5SDimitry Andric return LHSTime > RHSTime; 347*0b57cec5SDimitry Andric } 348*0b57cec5SDimitry Andric 349*0b57cec5SDimitry Andric std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { 350*0b57cec5SDimitry Andric for (StringRef ObjectFilename : ObjectFilenames) 351*0b57cec5SDimitry Andric if (modifiedTimeGT(ObjectFilename, PGOFilename)) 352*0b57cec5SDimitry Andric warning("profile data may be out of date - object is newer", 353*0b57cec5SDimitry Andric ObjectFilename); 354*0b57cec5SDimitry Andric auto CoverageOrErr = 355*0b57cec5SDimitry Andric CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches); 356*0b57cec5SDimitry Andric if (Error E = CoverageOrErr.takeError()) { 357*0b57cec5SDimitry Andric error("Failed to load coverage: " + toString(std::move(E)), 358*0b57cec5SDimitry Andric join(ObjectFilenames.begin(), ObjectFilenames.end(), ", ")); 359*0b57cec5SDimitry Andric return nullptr; 360*0b57cec5SDimitry Andric } 361*0b57cec5SDimitry Andric auto Coverage = std::move(CoverageOrErr.get()); 362*0b57cec5SDimitry Andric unsigned Mismatched = Coverage->getMismatchedCount(); 363*0b57cec5SDimitry Andric if (Mismatched) { 364*0b57cec5SDimitry Andric warning(Twine(Mismatched) + " functions have mismatched data"); 365*0b57cec5SDimitry Andric 366*0b57cec5SDimitry Andric if (ViewOpts.Debug) { 367*0b57cec5SDimitry Andric for (const auto &HashMismatch : Coverage->getHashMismatches()) 368*0b57cec5SDimitry Andric errs() << "hash-mismatch: " 369*0b57cec5SDimitry Andric << "No profile record found for '" << HashMismatch.first << "'" 370*0b57cec5SDimitry Andric << " with hash = 0x" << Twine::utohexstr(HashMismatch.second) 371*0b57cec5SDimitry Andric << '\n'; 372*0b57cec5SDimitry Andric } 373*0b57cec5SDimitry Andric } 374*0b57cec5SDimitry Andric 375*0b57cec5SDimitry Andric remapPathNames(*Coverage); 376*0b57cec5SDimitry Andric 377*0b57cec5SDimitry Andric if (!SourceFiles.empty()) 378*0b57cec5SDimitry Andric removeUnmappedInputs(*Coverage); 379*0b57cec5SDimitry Andric 380*0b57cec5SDimitry Andric demangleSymbols(*Coverage); 381*0b57cec5SDimitry Andric 382*0b57cec5SDimitry Andric return Coverage; 383*0b57cec5SDimitry Andric } 384*0b57cec5SDimitry Andric 385*0b57cec5SDimitry Andric void CodeCoverageTool::remapPathNames(const CoverageMapping &Coverage) { 386*0b57cec5SDimitry Andric if (!PathRemapping) 387*0b57cec5SDimitry Andric return; 388*0b57cec5SDimitry Andric 389*0b57cec5SDimitry Andric // Convert remapping paths to native paths with trailing seperators. 390*0b57cec5SDimitry Andric auto nativeWithTrailing = [](StringRef Path) -> std::string { 391*0b57cec5SDimitry Andric if (Path.empty()) 392*0b57cec5SDimitry Andric return ""; 393*0b57cec5SDimitry Andric SmallString<128> NativePath; 394*0b57cec5SDimitry Andric sys::path::native(Path, NativePath); 395*0b57cec5SDimitry Andric if (!sys::path::is_separator(NativePath.back())) 396*0b57cec5SDimitry Andric NativePath += sys::path::get_separator(); 397*0b57cec5SDimitry Andric return NativePath.c_str(); 398*0b57cec5SDimitry Andric }; 399*0b57cec5SDimitry Andric std::string RemapFrom = nativeWithTrailing(PathRemapping->first); 400*0b57cec5SDimitry Andric std::string RemapTo = nativeWithTrailing(PathRemapping->second); 401*0b57cec5SDimitry Andric 402*0b57cec5SDimitry Andric // Create a mapping from coverage data file paths to local paths. 403*0b57cec5SDimitry Andric for (StringRef Filename : Coverage.getUniqueSourceFiles()) { 404*0b57cec5SDimitry Andric SmallString<128> NativeFilename; 405*0b57cec5SDimitry Andric sys::path::native(Filename, NativeFilename); 406*0b57cec5SDimitry Andric if (NativeFilename.startswith(RemapFrom)) { 407*0b57cec5SDimitry Andric RemappedFilenames[Filename] = 408*0b57cec5SDimitry Andric RemapTo + NativeFilename.substr(RemapFrom.size()).str(); 409*0b57cec5SDimitry Andric } 410*0b57cec5SDimitry Andric } 411*0b57cec5SDimitry Andric 412*0b57cec5SDimitry Andric // Convert input files from local paths to coverage data file paths. 413*0b57cec5SDimitry Andric StringMap<std::string> InvRemappedFilenames; 414*0b57cec5SDimitry Andric for (const auto &RemappedFilename : RemappedFilenames) 415*0b57cec5SDimitry Andric InvRemappedFilenames[RemappedFilename.getValue()] = RemappedFilename.getKey(); 416*0b57cec5SDimitry Andric 417*0b57cec5SDimitry Andric for (std::string &Filename : SourceFiles) { 418*0b57cec5SDimitry Andric SmallString<128> NativeFilename; 419*0b57cec5SDimitry Andric sys::path::native(Filename, NativeFilename); 420*0b57cec5SDimitry Andric auto CovFileName = InvRemappedFilenames.find(NativeFilename); 421*0b57cec5SDimitry Andric if (CovFileName != InvRemappedFilenames.end()) 422*0b57cec5SDimitry Andric Filename = CovFileName->second; 423*0b57cec5SDimitry Andric } 424*0b57cec5SDimitry Andric } 425*0b57cec5SDimitry Andric 426*0b57cec5SDimitry Andric void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { 427*0b57cec5SDimitry Andric std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); 428*0b57cec5SDimitry Andric 429*0b57cec5SDimitry Andric auto UncoveredFilesIt = SourceFiles.end(); 430*0b57cec5SDimitry Andric // The user may have specified source files which aren't in the coverage 431*0b57cec5SDimitry Andric // mapping. Filter these files away. 432*0b57cec5SDimitry Andric UncoveredFilesIt = std::remove_if( 433*0b57cec5SDimitry Andric SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) { 434*0b57cec5SDimitry Andric return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), 435*0b57cec5SDimitry Andric SF); 436*0b57cec5SDimitry Andric }); 437*0b57cec5SDimitry Andric 438*0b57cec5SDimitry Andric SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); 439*0b57cec5SDimitry Andric } 440*0b57cec5SDimitry Andric 441*0b57cec5SDimitry Andric void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { 442*0b57cec5SDimitry Andric if (!ViewOpts.hasDemangler()) 443*0b57cec5SDimitry Andric return; 444*0b57cec5SDimitry Andric 445*0b57cec5SDimitry Andric // Pass function names to the demangler in a temporary file. 446*0b57cec5SDimitry Andric int InputFD; 447*0b57cec5SDimitry Andric SmallString<256> InputPath; 448*0b57cec5SDimitry Andric std::error_code EC = 449*0b57cec5SDimitry Andric sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath); 450*0b57cec5SDimitry Andric if (EC) { 451*0b57cec5SDimitry Andric error(InputPath, EC.message()); 452*0b57cec5SDimitry Andric return; 453*0b57cec5SDimitry Andric } 454*0b57cec5SDimitry Andric ToolOutputFile InputTOF{InputPath, InputFD}; 455*0b57cec5SDimitry Andric 456*0b57cec5SDimitry Andric unsigned NumSymbols = 0; 457*0b57cec5SDimitry Andric for (const auto &Function : Coverage.getCoveredFunctions()) { 458*0b57cec5SDimitry Andric InputTOF.os() << Function.Name << '\n'; 459*0b57cec5SDimitry Andric ++NumSymbols; 460*0b57cec5SDimitry Andric } 461*0b57cec5SDimitry Andric InputTOF.os().close(); 462*0b57cec5SDimitry Andric 463*0b57cec5SDimitry Andric // Use another temporary file to store the demangler's output. 464*0b57cec5SDimitry Andric int OutputFD; 465*0b57cec5SDimitry Andric SmallString<256> OutputPath; 466*0b57cec5SDimitry Andric EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD, 467*0b57cec5SDimitry Andric OutputPath); 468*0b57cec5SDimitry Andric if (EC) { 469*0b57cec5SDimitry Andric error(OutputPath, EC.message()); 470*0b57cec5SDimitry Andric return; 471*0b57cec5SDimitry Andric } 472*0b57cec5SDimitry Andric ToolOutputFile OutputTOF{OutputPath, OutputFD}; 473*0b57cec5SDimitry Andric OutputTOF.os().close(); 474*0b57cec5SDimitry Andric 475*0b57cec5SDimitry Andric // Invoke the demangler. 476*0b57cec5SDimitry Andric std::vector<StringRef> ArgsV; 477*0b57cec5SDimitry Andric for (StringRef Arg : ViewOpts.DemanglerOpts) 478*0b57cec5SDimitry Andric ArgsV.push_back(Arg); 479*0b57cec5SDimitry Andric Optional<StringRef> Redirects[] = {InputPath.str(), OutputPath.str(), {""}}; 480*0b57cec5SDimitry Andric std::string ErrMsg; 481*0b57cec5SDimitry Andric int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV, 482*0b57cec5SDimitry Andric /*env=*/None, Redirects, /*secondsToWait=*/0, 483*0b57cec5SDimitry Andric /*memoryLimit=*/0, &ErrMsg); 484*0b57cec5SDimitry Andric if (RC) { 485*0b57cec5SDimitry Andric error(ErrMsg, ViewOpts.DemanglerOpts[0]); 486*0b57cec5SDimitry Andric return; 487*0b57cec5SDimitry Andric } 488*0b57cec5SDimitry Andric 489*0b57cec5SDimitry Andric // Parse the demangler's output. 490*0b57cec5SDimitry Andric auto BufOrError = MemoryBuffer::getFile(OutputPath); 491*0b57cec5SDimitry Andric if (!BufOrError) { 492*0b57cec5SDimitry Andric error(OutputPath, BufOrError.getError().message()); 493*0b57cec5SDimitry Andric return; 494*0b57cec5SDimitry Andric } 495*0b57cec5SDimitry Andric 496*0b57cec5SDimitry Andric std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError); 497*0b57cec5SDimitry Andric 498*0b57cec5SDimitry Andric SmallVector<StringRef, 8> Symbols; 499*0b57cec5SDimitry Andric StringRef DemanglerData = DemanglerBuf->getBuffer(); 500*0b57cec5SDimitry Andric DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols, 501*0b57cec5SDimitry Andric /*KeepEmpty=*/false); 502*0b57cec5SDimitry Andric if (Symbols.size() != NumSymbols) { 503*0b57cec5SDimitry Andric error("Demangler did not provide expected number of symbols"); 504*0b57cec5SDimitry Andric return; 505*0b57cec5SDimitry Andric } 506*0b57cec5SDimitry Andric 507*0b57cec5SDimitry Andric // Cache the demangled names. 508*0b57cec5SDimitry Andric unsigned I = 0; 509*0b57cec5SDimitry Andric for (const auto &Function : Coverage.getCoveredFunctions()) 510*0b57cec5SDimitry Andric // On Windows, lines in the demangler's output file end with "\r\n". 511*0b57cec5SDimitry Andric // Splitting by '\n' keeps '\r's, so cut them now. 512*0b57cec5SDimitry Andric DC.DemangledNames[Function.Name] = Symbols[I++].rtrim(); 513*0b57cec5SDimitry Andric } 514*0b57cec5SDimitry Andric 515*0b57cec5SDimitry Andric void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, 516*0b57cec5SDimitry Andric CoverageMapping *Coverage, 517*0b57cec5SDimitry Andric CoveragePrinter *Printer, 518*0b57cec5SDimitry Andric bool ShowFilenames) { 519*0b57cec5SDimitry Andric auto View = createSourceFileView(SourceFile, *Coverage); 520*0b57cec5SDimitry Andric if (!View) { 521*0b57cec5SDimitry Andric warning("The file '" + SourceFile + "' isn't covered."); 522*0b57cec5SDimitry Andric return; 523*0b57cec5SDimitry Andric } 524*0b57cec5SDimitry Andric 525*0b57cec5SDimitry Andric auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); 526*0b57cec5SDimitry Andric if (Error E = OSOrErr.takeError()) { 527*0b57cec5SDimitry Andric error("Could not create view file!", toString(std::move(E))); 528*0b57cec5SDimitry Andric return; 529*0b57cec5SDimitry Andric } 530*0b57cec5SDimitry Andric auto OS = std::move(OSOrErr.get()); 531*0b57cec5SDimitry Andric 532*0b57cec5SDimitry Andric View->print(*OS.get(), /*Wholefile=*/true, 533*0b57cec5SDimitry Andric /*ShowSourceName=*/ShowFilenames, 534*0b57cec5SDimitry Andric /*ShowTitle=*/ViewOpts.hasOutputDirectory()); 535*0b57cec5SDimitry Andric Printer->closeViewFile(std::move(OS)); 536*0b57cec5SDimitry Andric } 537*0b57cec5SDimitry Andric 538*0b57cec5SDimitry Andric int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 539*0b57cec5SDimitry Andric cl::opt<std::string> CovFilename( 540*0b57cec5SDimitry Andric cl::Positional, cl::desc("Covered executable or object file.")); 541*0b57cec5SDimitry Andric 542*0b57cec5SDimitry Andric cl::list<std::string> CovFilenames( 543*0b57cec5SDimitry Andric "object", cl::desc("Coverage executable or object file"), cl::ZeroOrMore, 544*0b57cec5SDimitry Andric cl::CommaSeparated); 545*0b57cec5SDimitry Andric 546*0b57cec5SDimitry Andric cl::list<std::string> InputSourceFiles( 547*0b57cec5SDimitry Andric cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); 548*0b57cec5SDimitry Andric 549*0b57cec5SDimitry Andric cl::opt<bool> DebugDumpCollectedPaths( 550*0b57cec5SDimitry Andric "dump-collected-paths", cl::Optional, cl::Hidden, 551*0b57cec5SDimitry Andric cl::desc("Show the collected paths to source files")); 552*0b57cec5SDimitry Andric 553*0b57cec5SDimitry Andric cl::opt<std::string, true> PGOFilename( 554*0b57cec5SDimitry Andric "instr-profile", cl::Required, cl::location(this->PGOFilename), 555*0b57cec5SDimitry Andric cl::desc( 556*0b57cec5SDimitry Andric "File with the profile data obtained after an instrumented run")); 557*0b57cec5SDimitry Andric 558*0b57cec5SDimitry Andric cl::list<std::string> Arches( 559*0b57cec5SDimitry Andric "arch", cl::desc("architectures of the coverage mapping binaries")); 560*0b57cec5SDimitry Andric 561*0b57cec5SDimitry Andric cl::opt<bool> DebugDump("dump", cl::Optional, 562*0b57cec5SDimitry Andric cl::desc("Show internal debug dump")); 563*0b57cec5SDimitry Andric 564*0b57cec5SDimitry Andric cl::opt<CoverageViewOptions::OutputFormat> Format( 565*0b57cec5SDimitry Andric "format", cl::desc("Output format for line-based coverage reports"), 566*0b57cec5SDimitry Andric cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", 567*0b57cec5SDimitry Andric "Text output"), 568*0b57cec5SDimitry Andric clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", 569*0b57cec5SDimitry Andric "HTML output"), 570*0b57cec5SDimitry Andric clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov", 571*0b57cec5SDimitry Andric "lcov tracefile output")), 572*0b57cec5SDimitry Andric cl::init(CoverageViewOptions::OutputFormat::Text)); 573*0b57cec5SDimitry Andric 574*0b57cec5SDimitry Andric cl::opt<std::string> PathRemap( 575*0b57cec5SDimitry Andric "path-equivalence", cl::Optional, 576*0b57cec5SDimitry Andric cl::desc("<from>,<to> Map coverage data paths to local source file " 577*0b57cec5SDimitry Andric "paths")); 578*0b57cec5SDimitry Andric 579*0b57cec5SDimitry Andric cl::OptionCategory FilteringCategory("Function filtering options"); 580*0b57cec5SDimitry Andric 581*0b57cec5SDimitry Andric cl::list<std::string> NameFilters( 582*0b57cec5SDimitry Andric "name", cl::Optional, 583*0b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with the given name"), 584*0b57cec5SDimitry Andric cl::ZeroOrMore, cl::cat(FilteringCategory)); 585*0b57cec5SDimitry Andric 586*0b57cec5SDimitry Andric cl::list<std::string> NameFilterFiles( 587*0b57cec5SDimitry Andric "name-whitelist", cl::Optional, 588*0b57cec5SDimitry Andric cl::desc("Show code coverage only for functions listed in the given " 589*0b57cec5SDimitry Andric "file"), 590*0b57cec5SDimitry Andric cl::ZeroOrMore, cl::cat(FilteringCategory)); 591*0b57cec5SDimitry Andric 592*0b57cec5SDimitry Andric cl::list<std::string> NameRegexFilters( 593*0b57cec5SDimitry Andric "name-regex", cl::Optional, 594*0b57cec5SDimitry Andric cl::desc("Show code coverage only for functions that match the given " 595*0b57cec5SDimitry Andric "regular expression"), 596*0b57cec5SDimitry Andric cl::ZeroOrMore, cl::cat(FilteringCategory)); 597*0b57cec5SDimitry Andric 598*0b57cec5SDimitry Andric cl::list<std::string> IgnoreFilenameRegexFilters( 599*0b57cec5SDimitry Andric "ignore-filename-regex", cl::Optional, 600*0b57cec5SDimitry Andric cl::desc("Skip source code files with file paths that match the given " 601*0b57cec5SDimitry Andric "regular expression"), 602*0b57cec5SDimitry Andric cl::ZeroOrMore, cl::cat(FilteringCategory)); 603*0b57cec5SDimitry Andric 604*0b57cec5SDimitry Andric cl::opt<double> RegionCoverageLtFilter( 605*0b57cec5SDimitry Andric "region-coverage-lt", cl::Optional, 606*0b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with region coverage " 607*0b57cec5SDimitry Andric "less than the given threshold"), 608*0b57cec5SDimitry Andric cl::cat(FilteringCategory)); 609*0b57cec5SDimitry Andric 610*0b57cec5SDimitry Andric cl::opt<double> RegionCoverageGtFilter( 611*0b57cec5SDimitry Andric "region-coverage-gt", cl::Optional, 612*0b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with region coverage " 613*0b57cec5SDimitry Andric "greater than the given threshold"), 614*0b57cec5SDimitry Andric cl::cat(FilteringCategory)); 615*0b57cec5SDimitry Andric 616*0b57cec5SDimitry Andric cl::opt<double> LineCoverageLtFilter( 617*0b57cec5SDimitry Andric "line-coverage-lt", cl::Optional, 618*0b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with line coverage less " 619*0b57cec5SDimitry Andric "than the given threshold"), 620*0b57cec5SDimitry Andric cl::cat(FilteringCategory)); 621*0b57cec5SDimitry Andric 622*0b57cec5SDimitry Andric cl::opt<double> LineCoverageGtFilter( 623*0b57cec5SDimitry Andric "line-coverage-gt", cl::Optional, 624*0b57cec5SDimitry Andric cl::desc("Show code coverage only for functions with line coverage " 625*0b57cec5SDimitry Andric "greater than the given threshold"), 626*0b57cec5SDimitry Andric cl::cat(FilteringCategory)); 627*0b57cec5SDimitry Andric 628*0b57cec5SDimitry Andric cl::opt<cl::boolOrDefault> UseColor( 629*0b57cec5SDimitry Andric "use-color", cl::desc("Emit colored output (default=autodetect)"), 630*0b57cec5SDimitry Andric cl::init(cl::BOU_UNSET)); 631*0b57cec5SDimitry Andric 632*0b57cec5SDimitry Andric cl::list<std::string> DemanglerOpts( 633*0b57cec5SDimitry Andric "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); 634*0b57cec5SDimitry Andric 635*0b57cec5SDimitry Andric cl::opt<bool> RegionSummary( 636*0b57cec5SDimitry Andric "show-region-summary", cl::Optional, 637*0b57cec5SDimitry Andric cl::desc("Show region statistics in summary table"), 638*0b57cec5SDimitry Andric cl::init(true)); 639*0b57cec5SDimitry Andric 640*0b57cec5SDimitry Andric cl::opt<bool> InstantiationSummary( 641*0b57cec5SDimitry Andric "show-instantiation-summary", cl::Optional, 642*0b57cec5SDimitry Andric cl::desc("Show instantiation statistics in summary table")); 643*0b57cec5SDimitry Andric 644*0b57cec5SDimitry Andric cl::opt<bool> SummaryOnly( 645*0b57cec5SDimitry Andric "summary-only", cl::Optional, 646*0b57cec5SDimitry Andric cl::desc("Export only summary information for each source file")); 647*0b57cec5SDimitry Andric 648*0b57cec5SDimitry Andric cl::opt<unsigned> NumThreads( 649*0b57cec5SDimitry Andric "num-threads", cl::init(0), 650*0b57cec5SDimitry Andric cl::desc("Number of merge threads to use (default: autodetect)")); 651*0b57cec5SDimitry Andric cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), 652*0b57cec5SDimitry Andric cl::aliasopt(NumThreads)); 653*0b57cec5SDimitry Andric 654*0b57cec5SDimitry Andric auto commandLineParser = [&, this](int argc, const char **argv) -> int { 655*0b57cec5SDimitry Andric cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 656*0b57cec5SDimitry Andric ViewOpts.Debug = DebugDump; 657*0b57cec5SDimitry Andric 658*0b57cec5SDimitry Andric if (!CovFilename.empty()) 659*0b57cec5SDimitry Andric ObjectFilenames.emplace_back(CovFilename); 660*0b57cec5SDimitry Andric for (const std::string &Filename : CovFilenames) 661*0b57cec5SDimitry Andric ObjectFilenames.emplace_back(Filename); 662*0b57cec5SDimitry Andric if (ObjectFilenames.empty()) { 663*0b57cec5SDimitry Andric errs() << "No filenames specified!\n"; 664*0b57cec5SDimitry Andric ::exit(1); 665*0b57cec5SDimitry Andric } 666*0b57cec5SDimitry Andric 667*0b57cec5SDimitry Andric ViewOpts.Format = Format; 668*0b57cec5SDimitry Andric switch (ViewOpts.Format) { 669*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 670*0b57cec5SDimitry Andric ViewOpts.Colors = UseColor == cl::BOU_UNSET 671*0b57cec5SDimitry Andric ? sys::Process::StandardOutHasColors() 672*0b57cec5SDimitry Andric : UseColor == cl::BOU_TRUE; 673*0b57cec5SDimitry Andric break; 674*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 675*0b57cec5SDimitry Andric if (UseColor == cl::BOU_FALSE) 676*0b57cec5SDimitry Andric errs() << "Color output cannot be disabled when generating html.\n"; 677*0b57cec5SDimitry Andric ViewOpts.Colors = true; 678*0b57cec5SDimitry Andric break; 679*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 680*0b57cec5SDimitry Andric if (UseColor == cl::BOU_TRUE) 681*0b57cec5SDimitry Andric errs() << "Color output cannot be enabled when generating lcov.\n"; 682*0b57cec5SDimitry Andric ViewOpts.Colors = false; 683*0b57cec5SDimitry Andric break; 684*0b57cec5SDimitry Andric } 685*0b57cec5SDimitry Andric 686*0b57cec5SDimitry Andric // If path-equivalence was given and is a comma seperated pair then set 687*0b57cec5SDimitry Andric // PathRemapping. 688*0b57cec5SDimitry Andric auto EquivPair = StringRef(PathRemap).split(','); 689*0b57cec5SDimitry Andric if (!(EquivPair.first.empty() && EquivPair.second.empty())) 690*0b57cec5SDimitry Andric PathRemapping = EquivPair; 691*0b57cec5SDimitry Andric 692*0b57cec5SDimitry Andric // If a demangler is supplied, check if it exists and register it. 693*0b57cec5SDimitry Andric if (!DemanglerOpts.empty()) { 694*0b57cec5SDimitry Andric auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); 695*0b57cec5SDimitry Andric if (!DemanglerPathOrErr) { 696*0b57cec5SDimitry Andric error("Could not find the demangler!", 697*0b57cec5SDimitry Andric DemanglerPathOrErr.getError().message()); 698*0b57cec5SDimitry Andric return 1; 699*0b57cec5SDimitry Andric } 700*0b57cec5SDimitry Andric DemanglerOpts[0] = *DemanglerPathOrErr; 701*0b57cec5SDimitry Andric ViewOpts.DemanglerOpts.swap(DemanglerOpts); 702*0b57cec5SDimitry Andric } 703*0b57cec5SDimitry Andric 704*0b57cec5SDimitry Andric // Read in -name-whitelist files. 705*0b57cec5SDimitry Andric if (!NameFilterFiles.empty()) { 706*0b57cec5SDimitry Andric std::string SpecialCaseListErr; 707*0b57cec5SDimitry Andric NameWhitelist = 708*0b57cec5SDimitry Andric SpecialCaseList::create(NameFilterFiles, SpecialCaseListErr); 709*0b57cec5SDimitry Andric if (!NameWhitelist) 710*0b57cec5SDimitry Andric error(SpecialCaseListErr); 711*0b57cec5SDimitry Andric } 712*0b57cec5SDimitry Andric 713*0b57cec5SDimitry Andric // Create the function filters 714*0b57cec5SDimitry Andric if (!NameFilters.empty() || NameWhitelist || !NameRegexFilters.empty()) { 715*0b57cec5SDimitry Andric auto NameFilterer = llvm::make_unique<CoverageFilters>(); 716*0b57cec5SDimitry Andric for (const auto &Name : NameFilters) 717*0b57cec5SDimitry Andric NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); 718*0b57cec5SDimitry Andric if (NameWhitelist) 719*0b57cec5SDimitry Andric NameFilterer->push_back( 720*0b57cec5SDimitry Andric llvm::make_unique<NameWhitelistCoverageFilter>(*NameWhitelist)); 721*0b57cec5SDimitry Andric for (const auto &Regex : NameRegexFilters) 722*0b57cec5SDimitry Andric NameFilterer->push_back( 723*0b57cec5SDimitry Andric llvm::make_unique<NameRegexCoverageFilter>(Regex)); 724*0b57cec5SDimitry Andric Filters.push_back(std::move(NameFilterer)); 725*0b57cec5SDimitry Andric } 726*0b57cec5SDimitry Andric 727*0b57cec5SDimitry Andric if (RegionCoverageLtFilter.getNumOccurrences() || 728*0b57cec5SDimitry Andric RegionCoverageGtFilter.getNumOccurrences() || 729*0b57cec5SDimitry Andric LineCoverageLtFilter.getNumOccurrences() || 730*0b57cec5SDimitry Andric LineCoverageGtFilter.getNumOccurrences()) { 731*0b57cec5SDimitry Andric auto StatFilterer = llvm::make_unique<CoverageFilters>(); 732*0b57cec5SDimitry Andric if (RegionCoverageLtFilter.getNumOccurrences()) 733*0b57cec5SDimitry Andric StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 734*0b57cec5SDimitry Andric RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 735*0b57cec5SDimitry Andric if (RegionCoverageGtFilter.getNumOccurrences()) 736*0b57cec5SDimitry Andric StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 737*0b57cec5SDimitry Andric RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 738*0b57cec5SDimitry Andric if (LineCoverageLtFilter.getNumOccurrences()) 739*0b57cec5SDimitry Andric StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 740*0b57cec5SDimitry Andric LineCoverageFilter::LessThan, LineCoverageLtFilter)); 741*0b57cec5SDimitry Andric if (LineCoverageGtFilter.getNumOccurrences()) 742*0b57cec5SDimitry Andric StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 743*0b57cec5SDimitry Andric RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 744*0b57cec5SDimitry Andric Filters.push_back(std::move(StatFilterer)); 745*0b57cec5SDimitry Andric } 746*0b57cec5SDimitry Andric 747*0b57cec5SDimitry Andric // Create the ignore filename filters. 748*0b57cec5SDimitry Andric for (const auto &RE : IgnoreFilenameRegexFilters) 749*0b57cec5SDimitry Andric IgnoreFilenameFilters.push_back( 750*0b57cec5SDimitry Andric llvm::make_unique<NameRegexCoverageFilter>(RE)); 751*0b57cec5SDimitry Andric 752*0b57cec5SDimitry Andric if (!Arches.empty()) { 753*0b57cec5SDimitry Andric for (const std::string &Arch : Arches) { 754*0b57cec5SDimitry Andric if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { 755*0b57cec5SDimitry Andric error("Unknown architecture: " + Arch); 756*0b57cec5SDimitry Andric return 1; 757*0b57cec5SDimitry Andric } 758*0b57cec5SDimitry Andric CoverageArches.emplace_back(Arch); 759*0b57cec5SDimitry Andric } 760*0b57cec5SDimitry Andric if (CoverageArches.size() != ObjectFilenames.size()) { 761*0b57cec5SDimitry Andric error("Number of architectures doesn't match the number of objects"); 762*0b57cec5SDimitry Andric return 1; 763*0b57cec5SDimitry Andric } 764*0b57cec5SDimitry Andric } 765*0b57cec5SDimitry Andric 766*0b57cec5SDimitry Andric // IgnoreFilenameFilters are applied even when InputSourceFiles specified. 767*0b57cec5SDimitry Andric for (const std::string &File : InputSourceFiles) 768*0b57cec5SDimitry Andric collectPaths(File); 769*0b57cec5SDimitry Andric 770*0b57cec5SDimitry Andric if (DebugDumpCollectedPaths) { 771*0b57cec5SDimitry Andric for (const std::string &SF : SourceFiles) 772*0b57cec5SDimitry Andric outs() << SF << '\n'; 773*0b57cec5SDimitry Andric ::exit(0); 774*0b57cec5SDimitry Andric } 775*0b57cec5SDimitry Andric 776*0b57cec5SDimitry Andric ViewOpts.ShowRegionSummary = RegionSummary; 777*0b57cec5SDimitry Andric ViewOpts.ShowInstantiationSummary = InstantiationSummary; 778*0b57cec5SDimitry Andric ViewOpts.ExportSummaryOnly = SummaryOnly; 779*0b57cec5SDimitry Andric ViewOpts.NumThreads = NumThreads; 780*0b57cec5SDimitry Andric 781*0b57cec5SDimitry Andric return 0; 782*0b57cec5SDimitry Andric }; 783*0b57cec5SDimitry Andric 784*0b57cec5SDimitry Andric switch (Cmd) { 785*0b57cec5SDimitry Andric case Show: 786*0b57cec5SDimitry Andric return doShow(argc, argv, commandLineParser); 787*0b57cec5SDimitry Andric case Report: 788*0b57cec5SDimitry Andric return doReport(argc, argv, commandLineParser); 789*0b57cec5SDimitry Andric case Export: 790*0b57cec5SDimitry Andric return doExport(argc, argv, commandLineParser); 791*0b57cec5SDimitry Andric } 792*0b57cec5SDimitry Andric return 0; 793*0b57cec5SDimitry Andric } 794*0b57cec5SDimitry Andric 795*0b57cec5SDimitry Andric int CodeCoverageTool::doShow(int argc, const char **argv, 796*0b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 797*0b57cec5SDimitry Andric 798*0b57cec5SDimitry Andric cl::OptionCategory ViewCategory("Viewing options"); 799*0b57cec5SDimitry Andric 800*0b57cec5SDimitry Andric cl::opt<bool> ShowLineExecutionCounts( 801*0b57cec5SDimitry Andric "show-line-counts", cl::Optional, 802*0b57cec5SDimitry Andric cl::desc("Show the execution counts for each line"), cl::init(true), 803*0b57cec5SDimitry Andric cl::cat(ViewCategory)); 804*0b57cec5SDimitry Andric 805*0b57cec5SDimitry Andric cl::opt<bool> ShowRegions( 806*0b57cec5SDimitry Andric "show-regions", cl::Optional, 807*0b57cec5SDimitry Andric cl::desc("Show the execution counts for each region"), 808*0b57cec5SDimitry Andric cl::cat(ViewCategory)); 809*0b57cec5SDimitry Andric 810*0b57cec5SDimitry Andric cl::opt<bool> ShowBestLineRegionsCounts( 811*0b57cec5SDimitry Andric "show-line-counts-or-regions", cl::Optional, 812*0b57cec5SDimitry Andric cl::desc("Show the execution counts for each line, or the execution " 813*0b57cec5SDimitry Andric "counts for each region on lines that have multiple regions"), 814*0b57cec5SDimitry Andric cl::cat(ViewCategory)); 815*0b57cec5SDimitry Andric 816*0b57cec5SDimitry Andric cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 817*0b57cec5SDimitry Andric cl::desc("Show expanded source regions"), 818*0b57cec5SDimitry Andric cl::cat(ViewCategory)); 819*0b57cec5SDimitry Andric 820*0b57cec5SDimitry Andric cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 821*0b57cec5SDimitry Andric cl::desc("Show function instantiations"), 822*0b57cec5SDimitry Andric cl::init(true), cl::cat(ViewCategory)); 823*0b57cec5SDimitry Andric 824*0b57cec5SDimitry Andric cl::opt<std::string> ShowOutputDirectory( 825*0b57cec5SDimitry Andric "output-dir", cl::init(""), 826*0b57cec5SDimitry Andric cl::desc("Directory in which coverage information is written out")); 827*0b57cec5SDimitry Andric cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), 828*0b57cec5SDimitry Andric cl::aliasopt(ShowOutputDirectory)); 829*0b57cec5SDimitry Andric 830*0b57cec5SDimitry Andric cl::opt<uint32_t> TabSize( 831*0b57cec5SDimitry Andric "tab-size", cl::init(2), 832*0b57cec5SDimitry Andric cl::desc( 833*0b57cec5SDimitry Andric "Set tab expansion size for html coverage reports (default = 2)")); 834*0b57cec5SDimitry Andric 835*0b57cec5SDimitry Andric cl::opt<std::string> ProjectTitle( 836*0b57cec5SDimitry Andric "project-title", cl::Optional, 837*0b57cec5SDimitry Andric cl::desc("Set project title for the coverage report")); 838*0b57cec5SDimitry Andric 839*0b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 840*0b57cec5SDimitry Andric if (Err) 841*0b57cec5SDimitry Andric return Err; 842*0b57cec5SDimitry Andric 843*0b57cec5SDimitry Andric if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 844*0b57cec5SDimitry Andric error("Lcov format should be used with 'llvm-cov export'."); 845*0b57cec5SDimitry Andric return 1; 846*0b57cec5SDimitry Andric } 847*0b57cec5SDimitry Andric 848*0b57cec5SDimitry Andric ViewOpts.ShowLineNumbers = true; 849*0b57cec5SDimitry Andric ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 850*0b57cec5SDimitry Andric !ShowRegions || ShowBestLineRegionsCounts; 851*0b57cec5SDimitry Andric ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 852*0b57cec5SDimitry Andric ViewOpts.ShowExpandedRegions = ShowExpansions; 853*0b57cec5SDimitry Andric ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 854*0b57cec5SDimitry Andric ViewOpts.ShowOutputDirectory = ShowOutputDirectory; 855*0b57cec5SDimitry Andric ViewOpts.TabSize = TabSize; 856*0b57cec5SDimitry Andric ViewOpts.ProjectTitle = ProjectTitle; 857*0b57cec5SDimitry Andric 858*0b57cec5SDimitry Andric if (ViewOpts.hasOutputDirectory()) { 859*0b57cec5SDimitry Andric if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { 860*0b57cec5SDimitry Andric error("Could not create output directory!", E.message()); 861*0b57cec5SDimitry Andric return 1; 862*0b57cec5SDimitry Andric } 863*0b57cec5SDimitry Andric } 864*0b57cec5SDimitry Andric 865*0b57cec5SDimitry Andric sys::fs::file_status Status; 866*0b57cec5SDimitry Andric if (sys::fs::status(PGOFilename, Status)) { 867*0b57cec5SDimitry Andric error("profdata file error: can not get the file status. \n"); 868*0b57cec5SDimitry Andric return 1; 869*0b57cec5SDimitry Andric } 870*0b57cec5SDimitry Andric 871*0b57cec5SDimitry Andric auto ModifiedTime = Status.getLastModificationTime(); 872*0b57cec5SDimitry Andric std::string ModifiedTimeStr = to_string(ModifiedTime); 873*0b57cec5SDimitry Andric size_t found = ModifiedTimeStr.rfind(':'); 874*0b57cec5SDimitry Andric ViewOpts.CreatedTimeStr = (found != std::string::npos) 875*0b57cec5SDimitry Andric ? "Created: " + ModifiedTimeStr.substr(0, found) 876*0b57cec5SDimitry Andric : "Created: " + ModifiedTimeStr; 877*0b57cec5SDimitry Andric 878*0b57cec5SDimitry Andric auto Coverage = load(); 879*0b57cec5SDimitry Andric if (!Coverage) 880*0b57cec5SDimitry Andric return 1; 881*0b57cec5SDimitry Andric 882*0b57cec5SDimitry Andric auto Printer = CoveragePrinter::create(ViewOpts); 883*0b57cec5SDimitry Andric 884*0b57cec5SDimitry Andric if (SourceFiles.empty()) 885*0b57cec5SDimitry Andric // Get the source files from the function coverage mapping. 886*0b57cec5SDimitry Andric for (StringRef Filename : Coverage->getUniqueSourceFiles()) { 887*0b57cec5SDimitry Andric if (!IgnoreFilenameFilters.matchesFilename(Filename)) 888*0b57cec5SDimitry Andric SourceFiles.push_back(Filename); 889*0b57cec5SDimitry Andric } 890*0b57cec5SDimitry Andric 891*0b57cec5SDimitry Andric // Create an index out of the source files. 892*0b57cec5SDimitry Andric if (ViewOpts.hasOutputDirectory()) { 893*0b57cec5SDimitry Andric if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) { 894*0b57cec5SDimitry Andric error("Could not create index file!", toString(std::move(E))); 895*0b57cec5SDimitry Andric return 1; 896*0b57cec5SDimitry Andric } 897*0b57cec5SDimitry Andric } 898*0b57cec5SDimitry Andric 899*0b57cec5SDimitry Andric if (!Filters.empty()) { 900*0b57cec5SDimitry Andric // Build the map of filenames to functions. 901*0b57cec5SDimitry Andric std::map<llvm::StringRef, std::vector<const FunctionRecord *>> 902*0b57cec5SDimitry Andric FilenameFunctionMap; 903*0b57cec5SDimitry Andric for (const auto &SourceFile : SourceFiles) 904*0b57cec5SDimitry Andric for (const auto &Function : Coverage->getCoveredFunctions(SourceFile)) 905*0b57cec5SDimitry Andric if (Filters.matches(*Coverage.get(), Function)) 906*0b57cec5SDimitry Andric FilenameFunctionMap[SourceFile].push_back(&Function); 907*0b57cec5SDimitry Andric 908*0b57cec5SDimitry Andric // Only print filter matching functions for each file. 909*0b57cec5SDimitry Andric for (const auto &FileFunc : FilenameFunctionMap) { 910*0b57cec5SDimitry Andric StringRef File = FileFunc.first; 911*0b57cec5SDimitry Andric const auto &Functions = FileFunc.second; 912*0b57cec5SDimitry Andric 913*0b57cec5SDimitry Andric auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false); 914*0b57cec5SDimitry Andric if (Error E = OSOrErr.takeError()) { 915*0b57cec5SDimitry Andric error("Could not create view file!", toString(std::move(E))); 916*0b57cec5SDimitry Andric return 1; 917*0b57cec5SDimitry Andric } 918*0b57cec5SDimitry Andric auto OS = std::move(OSOrErr.get()); 919*0b57cec5SDimitry Andric 920*0b57cec5SDimitry Andric bool ShowTitle = ViewOpts.hasOutputDirectory(); 921*0b57cec5SDimitry Andric for (const auto *Function : Functions) { 922*0b57cec5SDimitry Andric auto FunctionView = createFunctionView(*Function, *Coverage); 923*0b57cec5SDimitry Andric if (!FunctionView) { 924*0b57cec5SDimitry Andric warning("Could not read coverage for '" + Function->Name + "'."); 925*0b57cec5SDimitry Andric continue; 926*0b57cec5SDimitry Andric } 927*0b57cec5SDimitry Andric FunctionView->print(*OS.get(), /*WholeFile=*/false, 928*0b57cec5SDimitry Andric /*ShowSourceName=*/true, ShowTitle); 929*0b57cec5SDimitry Andric ShowTitle = false; 930*0b57cec5SDimitry Andric } 931*0b57cec5SDimitry Andric 932*0b57cec5SDimitry Andric Printer->closeViewFile(std::move(OS)); 933*0b57cec5SDimitry Andric } 934*0b57cec5SDimitry Andric return 0; 935*0b57cec5SDimitry Andric } 936*0b57cec5SDimitry Andric 937*0b57cec5SDimitry Andric // Show files 938*0b57cec5SDimitry Andric bool ShowFilenames = 939*0b57cec5SDimitry Andric (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || 940*0b57cec5SDimitry Andric (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); 941*0b57cec5SDimitry Andric 942*0b57cec5SDimitry Andric auto NumThreads = ViewOpts.NumThreads; 943*0b57cec5SDimitry Andric 944*0b57cec5SDimitry Andric // If NumThreads is not specified, auto-detect a good default. 945*0b57cec5SDimitry Andric if (NumThreads == 0) 946*0b57cec5SDimitry Andric NumThreads = 947*0b57cec5SDimitry Andric std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(), 948*0b57cec5SDimitry Andric unsigned(SourceFiles.size()))); 949*0b57cec5SDimitry Andric 950*0b57cec5SDimitry Andric if (!ViewOpts.hasOutputDirectory() || NumThreads == 1) { 951*0b57cec5SDimitry Andric for (const std::string &SourceFile : SourceFiles) 952*0b57cec5SDimitry Andric writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), 953*0b57cec5SDimitry Andric ShowFilenames); 954*0b57cec5SDimitry Andric } else { 955*0b57cec5SDimitry Andric // In -output-dir mode, it's safe to use multiple threads to print files. 956*0b57cec5SDimitry Andric ThreadPool Pool(NumThreads); 957*0b57cec5SDimitry Andric for (const std::string &SourceFile : SourceFiles) 958*0b57cec5SDimitry Andric Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, 959*0b57cec5SDimitry Andric Coverage.get(), Printer.get(), ShowFilenames); 960*0b57cec5SDimitry Andric Pool.wait(); 961*0b57cec5SDimitry Andric } 962*0b57cec5SDimitry Andric 963*0b57cec5SDimitry Andric return 0; 964*0b57cec5SDimitry Andric } 965*0b57cec5SDimitry Andric 966*0b57cec5SDimitry Andric int CodeCoverageTool::doReport(int argc, const char **argv, 967*0b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 968*0b57cec5SDimitry Andric cl::opt<bool> ShowFunctionSummaries( 969*0b57cec5SDimitry Andric "show-functions", cl::Optional, cl::init(false), 970*0b57cec5SDimitry Andric cl::desc("Show coverage summaries for each function")); 971*0b57cec5SDimitry Andric 972*0b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 973*0b57cec5SDimitry Andric if (Err) 974*0b57cec5SDimitry Andric return Err; 975*0b57cec5SDimitry Andric 976*0b57cec5SDimitry Andric if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { 977*0b57cec5SDimitry Andric error("HTML output for summary reports is not yet supported."); 978*0b57cec5SDimitry Andric return 1; 979*0b57cec5SDimitry Andric } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 980*0b57cec5SDimitry Andric error("Lcov format should be used with 'llvm-cov export'."); 981*0b57cec5SDimitry Andric return 1; 982*0b57cec5SDimitry Andric } 983*0b57cec5SDimitry Andric 984*0b57cec5SDimitry Andric auto Coverage = load(); 985*0b57cec5SDimitry Andric if (!Coverage) 986*0b57cec5SDimitry Andric return 1; 987*0b57cec5SDimitry Andric 988*0b57cec5SDimitry Andric CoverageReport Report(ViewOpts, *Coverage.get()); 989*0b57cec5SDimitry Andric if (!ShowFunctionSummaries) { 990*0b57cec5SDimitry Andric if (SourceFiles.empty()) 991*0b57cec5SDimitry Andric Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters); 992*0b57cec5SDimitry Andric else 993*0b57cec5SDimitry Andric Report.renderFileReports(llvm::outs(), SourceFiles); 994*0b57cec5SDimitry Andric } else { 995*0b57cec5SDimitry Andric if (SourceFiles.empty()) { 996*0b57cec5SDimitry Andric error("Source files must be specified when -show-functions=true is " 997*0b57cec5SDimitry Andric "specified"); 998*0b57cec5SDimitry Andric return 1; 999*0b57cec5SDimitry Andric } 1000*0b57cec5SDimitry Andric 1001*0b57cec5SDimitry Andric Report.renderFunctionReports(SourceFiles, DC, llvm::outs()); 1002*0b57cec5SDimitry Andric } 1003*0b57cec5SDimitry Andric return 0; 1004*0b57cec5SDimitry Andric } 1005*0b57cec5SDimitry Andric 1006*0b57cec5SDimitry Andric int CodeCoverageTool::doExport(int argc, const char **argv, 1007*0b57cec5SDimitry Andric CommandLineParserType commandLineParser) { 1008*0b57cec5SDimitry Andric 1009*0b57cec5SDimitry Andric cl::OptionCategory ExportCategory("Exporting options"); 1010*0b57cec5SDimitry Andric 1011*0b57cec5SDimitry Andric cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional, 1012*0b57cec5SDimitry Andric cl::desc("Don't export expanded source regions"), 1013*0b57cec5SDimitry Andric cl::cat(ExportCategory)); 1014*0b57cec5SDimitry Andric 1015*0b57cec5SDimitry Andric cl::opt<bool> SkipFunctions("skip-functions", cl::Optional, 1016*0b57cec5SDimitry Andric cl::desc("Don't export per-function data"), 1017*0b57cec5SDimitry Andric cl::cat(ExportCategory)); 1018*0b57cec5SDimitry Andric 1019*0b57cec5SDimitry Andric auto Err = commandLineParser(argc, argv); 1020*0b57cec5SDimitry Andric if (Err) 1021*0b57cec5SDimitry Andric return Err; 1022*0b57cec5SDimitry Andric 1023*0b57cec5SDimitry Andric ViewOpts.SkipExpansions = SkipExpansions; 1024*0b57cec5SDimitry Andric ViewOpts.SkipFunctions = SkipFunctions; 1025*0b57cec5SDimitry Andric 1026*0b57cec5SDimitry Andric if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && 1027*0b57cec5SDimitry Andric ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { 1028*0b57cec5SDimitry Andric error("Coverage data can only be exported as textual JSON or an " 1029*0b57cec5SDimitry Andric "lcov tracefile."); 1030*0b57cec5SDimitry Andric return 1; 1031*0b57cec5SDimitry Andric } 1032*0b57cec5SDimitry Andric 1033*0b57cec5SDimitry Andric auto Coverage = load(); 1034*0b57cec5SDimitry Andric if (!Coverage) { 1035*0b57cec5SDimitry Andric error("Could not load coverage information"); 1036*0b57cec5SDimitry Andric return 1; 1037*0b57cec5SDimitry Andric } 1038*0b57cec5SDimitry Andric 1039*0b57cec5SDimitry Andric std::unique_ptr<CoverageExporter> Exporter; 1040*0b57cec5SDimitry Andric 1041*0b57cec5SDimitry Andric switch (ViewOpts.Format) { 1042*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Text: 1043*0b57cec5SDimitry Andric Exporter = llvm::make_unique<CoverageExporterJson>(*Coverage.get(), 1044*0b57cec5SDimitry Andric ViewOpts, outs()); 1045*0b57cec5SDimitry Andric break; 1046*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::HTML: 1047*0b57cec5SDimitry Andric // Unreachable because we should have gracefully terminated with an error 1048*0b57cec5SDimitry Andric // above. 1049*0b57cec5SDimitry Andric llvm_unreachable("Export in HTML is not supported!"); 1050*0b57cec5SDimitry Andric case CoverageViewOptions::OutputFormat::Lcov: 1051*0b57cec5SDimitry Andric Exporter = llvm::make_unique<CoverageExporterLcov>(*Coverage.get(), 1052*0b57cec5SDimitry Andric ViewOpts, outs()); 1053*0b57cec5SDimitry Andric break; 1054*0b57cec5SDimitry Andric } 1055*0b57cec5SDimitry Andric 1056*0b57cec5SDimitry Andric if (SourceFiles.empty()) 1057*0b57cec5SDimitry Andric Exporter->renderRoot(IgnoreFilenameFilters); 1058*0b57cec5SDimitry Andric else 1059*0b57cec5SDimitry Andric Exporter->renderRoot(SourceFiles); 1060*0b57cec5SDimitry Andric 1061*0b57cec5SDimitry Andric return 0; 1062*0b57cec5SDimitry Andric } 1063*0b57cec5SDimitry Andric 1064*0b57cec5SDimitry Andric int showMain(int argc, const char *argv[]) { 1065*0b57cec5SDimitry Andric CodeCoverageTool Tool; 1066*0b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Show, argc, argv); 1067*0b57cec5SDimitry Andric } 1068*0b57cec5SDimitry Andric 1069*0b57cec5SDimitry Andric int reportMain(int argc, const char *argv[]) { 1070*0b57cec5SDimitry Andric CodeCoverageTool Tool; 1071*0b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Report, argc, argv); 1072*0b57cec5SDimitry Andric } 1073*0b57cec5SDimitry Andric 1074*0b57cec5SDimitry Andric int exportMain(int argc, const char *argv[]) { 1075*0b57cec5SDimitry Andric CodeCoverageTool Tool; 1076*0b57cec5SDimitry Andric return Tool.run(CodeCoverageTool::Export, argc, argv); 1077*0b57cec5SDimitry Andric } 1078