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