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