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 // The user may have specified source files which aren't in the coverage 440 // mapping. Filter these files away. 441 llvm::erase_if(SourceFiles, [&](const std::string &SF) { 442 return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), SF); 443 }); 444 } 445 446 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { 447 if (!ViewOpts.hasDemangler()) 448 return; 449 450 // Pass function names to the demangler in a temporary file. 451 int InputFD; 452 SmallString<256> InputPath; 453 std::error_code EC = 454 sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath); 455 if (EC) { 456 error(InputPath, EC.message()); 457 return; 458 } 459 ToolOutputFile InputTOF{InputPath, InputFD}; 460 461 unsigned NumSymbols = 0; 462 for (const auto &Function : Coverage.getCoveredFunctions()) { 463 InputTOF.os() << Function.Name << '\n'; 464 ++NumSymbols; 465 } 466 InputTOF.os().close(); 467 468 // Use another temporary file to store the demangler's output. 469 int OutputFD; 470 SmallString<256> OutputPath; 471 EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD, 472 OutputPath); 473 if (EC) { 474 error(OutputPath, EC.message()); 475 return; 476 } 477 ToolOutputFile OutputTOF{OutputPath, OutputFD}; 478 OutputTOF.os().close(); 479 480 // Invoke the demangler. 481 std::vector<StringRef> ArgsV; 482 for (StringRef Arg : ViewOpts.DemanglerOpts) 483 ArgsV.push_back(Arg); 484 Optional<StringRef> Redirects[] = {InputPath.str(), OutputPath.str(), {""}}; 485 std::string ErrMsg; 486 int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV, 487 /*env=*/None, Redirects, /*secondsToWait=*/0, 488 /*memoryLimit=*/0, &ErrMsg); 489 if (RC) { 490 error(ErrMsg, ViewOpts.DemanglerOpts[0]); 491 return; 492 } 493 494 // Parse the demangler's output. 495 auto BufOrError = MemoryBuffer::getFile(OutputPath); 496 if (!BufOrError) { 497 error(OutputPath, BufOrError.getError().message()); 498 return; 499 } 500 501 std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError); 502 503 SmallVector<StringRef, 8> Symbols; 504 StringRef DemanglerData = DemanglerBuf->getBuffer(); 505 DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols, 506 /*KeepEmpty=*/false); 507 if (Symbols.size() != NumSymbols) { 508 error("Demangler did not provide expected number of symbols"); 509 return; 510 } 511 512 // Cache the demangled names. 513 unsigned I = 0; 514 for (const auto &Function : Coverage.getCoveredFunctions()) 515 // On Windows, lines in the demangler's output file end with "\r\n". 516 // Splitting by '\n' keeps '\r's, so cut them now. 517 DC.DemangledNames[Function.Name] = std::string(Symbols[I++].rtrim()); 518 } 519 520 void CodeCoverageTool::writeSourceFileView(StringRef SourceFile, 521 CoverageMapping *Coverage, 522 CoveragePrinter *Printer, 523 bool ShowFilenames) { 524 auto View = createSourceFileView(SourceFile, *Coverage); 525 if (!View) { 526 warning("The file '" + SourceFile + "' isn't covered."); 527 return; 528 } 529 530 auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); 531 if (Error E = OSOrErr.takeError()) { 532 error("Could not create view file!", toString(std::move(E))); 533 return; 534 } 535 auto OS = std::move(OSOrErr.get()); 536 537 View->print(*OS.get(), /*Wholefile=*/true, 538 /*ShowSourceName=*/ShowFilenames, 539 /*ShowTitle=*/ViewOpts.hasOutputDirectory()); 540 Printer->closeViewFile(std::move(OS)); 541 } 542 543 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 544 cl::opt<std::string> CovFilename( 545 cl::Positional, cl::desc("Covered executable or object file.")); 546 547 cl::list<std::string> CovFilenames( 548 "object", cl::desc("Coverage executable or object file"), cl::ZeroOrMore); 549 550 cl::opt<bool> DebugDumpCollectedObjects( 551 "dump-collected-objects", cl::Optional, cl::Hidden, 552 cl::desc("Show the collected coverage object files")); 553 554 cl::list<std::string> InputSourceFiles( 555 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); 556 557 cl::opt<bool> DebugDumpCollectedPaths( 558 "dump-collected-paths", cl::Optional, cl::Hidden, 559 cl::desc("Show the collected paths to source files")); 560 561 cl::opt<std::string, true> PGOFilename( 562 "instr-profile", cl::Required, cl::location(this->PGOFilename), 563 cl::desc( 564 "File with the profile data obtained after an instrumented run")); 565 566 cl::list<std::string> Arches( 567 "arch", cl::desc("architectures of the coverage mapping binaries")); 568 569 cl::opt<bool> DebugDump("dump", cl::Optional, 570 cl::desc("Show internal debug dump")); 571 572 cl::opt<CoverageViewOptions::OutputFormat> Format( 573 "format", cl::desc("Output format for line-based coverage reports"), 574 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", 575 "Text output"), 576 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", 577 "HTML output"), 578 clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov", 579 "lcov tracefile output")), 580 cl::init(CoverageViewOptions::OutputFormat::Text)); 581 582 cl::opt<std::string> PathRemap( 583 "path-equivalence", cl::Optional, 584 cl::desc("<from>,<to> Map coverage data paths to local source file " 585 "paths")); 586 587 cl::OptionCategory FilteringCategory("Function filtering options"); 588 589 cl::list<std::string> NameFilters( 590 "name", cl::Optional, 591 cl::desc("Show code coverage only for functions with the given name"), 592 cl::ZeroOrMore, cl::cat(FilteringCategory)); 593 594 cl::list<std::string> NameFilterFiles( 595 "name-whitelist", cl::Optional, 596 cl::desc("Show code coverage only for functions listed in the given " 597 "file"), 598 cl::ZeroOrMore, cl::cat(FilteringCategory)); 599 600 cl::list<std::string> NameRegexFilters( 601 "name-regex", cl::Optional, 602 cl::desc("Show code coverage only for functions that match the given " 603 "regular expression"), 604 cl::ZeroOrMore, cl::cat(FilteringCategory)); 605 606 cl::list<std::string> IgnoreFilenameRegexFilters( 607 "ignore-filename-regex", cl::Optional, 608 cl::desc("Skip source code files with file paths that match the given " 609 "regular expression"), 610 cl::ZeroOrMore, cl::cat(FilteringCategory)); 611 612 cl::opt<double> RegionCoverageLtFilter( 613 "region-coverage-lt", cl::Optional, 614 cl::desc("Show code coverage only for functions with region coverage " 615 "less than the given threshold"), 616 cl::cat(FilteringCategory)); 617 618 cl::opt<double> RegionCoverageGtFilter( 619 "region-coverage-gt", cl::Optional, 620 cl::desc("Show code coverage only for functions with region coverage " 621 "greater than the given threshold"), 622 cl::cat(FilteringCategory)); 623 624 cl::opt<double> LineCoverageLtFilter( 625 "line-coverage-lt", cl::Optional, 626 cl::desc("Show code coverage only for functions with line coverage less " 627 "than the given threshold"), 628 cl::cat(FilteringCategory)); 629 630 cl::opt<double> LineCoverageGtFilter( 631 "line-coverage-gt", cl::Optional, 632 cl::desc("Show code coverage only for functions with line coverage " 633 "greater than the given threshold"), 634 cl::cat(FilteringCategory)); 635 636 cl::opt<cl::boolOrDefault> UseColor( 637 "use-color", cl::desc("Emit colored output (default=autodetect)"), 638 cl::init(cl::BOU_UNSET)); 639 640 cl::list<std::string> DemanglerOpts( 641 "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); 642 643 cl::opt<bool> RegionSummary( 644 "show-region-summary", cl::Optional, 645 cl::desc("Show region statistics in summary table"), 646 cl::init(true)); 647 648 cl::opt<bool> InstantiationSummary( 649 "show-instantiation-summary", cl::Optional, 650 cl::desc("Show instantiation statistics in summary table")); 651 652 cl::opt<bool> SummaryOnly( 653 "summary-only", cl::Optional, 654 cl::desc("Export only summary information for each source file")); 655 656 cl::opt<unsigned> NumThreads( 657 "num-threads", cl::init(0), 658 cl::desc("Number of merge threads to use (default: autodetect)")); 659 cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"), 660 cl::aliasopt(NumThreads)); 661 662 auto commandLineParser = [&, this](int argc, const char **argv) -> int { 663 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 664 ViewOpts.Debug = DebugDump; 665 666 if (!CovFilename.empty()) 667 ObjectFilenames.emplace_back(CovFilename); 668 for (const std::string &Filename : CovFilenames) 669 ObjectFilenames.emplace_back(Filename); 670 if (ObjectFilenames.empty()) { 671 errs() << "No filenames specified!\n"; 672 ::exit(1); 673 } 674 675 if (DebugDumpCollectedObjects) { 676 for (StringRef OF : ObjectFilenames) 677 outs() << OF << '\n'; 678 ::exit(0); 679 } 680 681 ViewOpts.Format = Format; 682 switch (ViewOpts.Format) { 683 case CoverageViewOptions::OutputFormat::Text: 684 ViewOpts.Colors = UseColor == cl::BOU_UNSET 685 ? sys::Process::StandardOutHasColors() 686 : UseColor == cl::BOU_TRUE; 687 break; 688 case CoverageViewOptions::OutputFormat::HTML: 689 if (UseColor == cl::BOU_FALSE) 690 errs() << "Color output cannot be disabled when generating html.\n"; 691 ViewOpts.Colors = true; 692 break; 693 case CoverageViewOptions::OutputFormat::Lcov: 694 if (UseColor == cl::BOU_TRUE) 695 errs() << "Color output cannot be enabled when generating lcov.\n"; 696 ViewOpts.Colors = false; 697 break; 698 } 699 700 // If path-equivalence was given and is a comma seperated pair then set 701 // PathRemapping. 702 auto EquivPair = StringRef(PathRemap).split(','); 703 if (!(EquivPair.first.empty() && EquivPair.second.empty())) 704 PathRemapping = {std::string(EquivPair.first), 705 std::string(EquivPair.second)}; 706 707 // If a demangler is supplied, check if it exists and register it. 708 if (!DemanglerOpts.empty()) { 709 auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); 710 if (!DemanglerPathOrErr) { 711 error("Could not find the demangler!", 712 DemanglerPathOrErr.getError().message()); 713 return 1; 714 } 715 DemanglerOpts[0] = *DemanglerPathOrErr; 716 ViewOpts.DemanglerOpts.swap(DemanglerOpts); 717 } 718 719 // Read in -name-whitelist files. 720 if (!NameFilterFiles.empty()) { 721 std::string SpecialCaseListErr; 722 NameWhitelist = SpecialCaseList::create( 723 NameFilterFiles, *vfs::getRealFileSystem(), SpecialCaseListErr); 724 if (!NameWhitelist) 725 error(SpecialCaseListErr); 726 } 727 728 // Create the function filters 729 if (!NameFilters.empty() || NameWhitelist || !NameRegexFilters.empty()) { 730 auto NameFilterer = std::make_unique<CoverageFilters>(); 731 for (const auto &Name : NameFilters) 732 NameFilterer->push_back(std::make_unique<NameCoverageFilter>(Name)); 733 if (NameWhitelist) 734 NameFilterer->push_back( 735 std::make_unique<NameWhitelistCoverageFilter>(*NameWhitelist)); 736 for (const auto &Regex : NameRegexFilters) 737 NameFilterer->push_back( 738 std::make_unique<NameRegexCoverageFilter>(Regex)); 739 Filters.push_back(std::move(NameFilterer)); 740 } 741 742 if (RegionCoverageLtFilter.getNumOccurrences() || 743 RegionCoverageGtFilter.getNumOccurrences() || 744 LineCoverageLtFilter.getNumOccurrences() || 745 LineCoverageGtFilter.getNumOccurrences()) { 746 auto StatFilterer = std::make_unique<CoverageFilters>(); 747 if (RegionCoverageLtFilter.getNumOccurrences()) 748 StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( 749 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 750 if (RegionCoverageGtFilter.getNumOccurrences()) 751 StatFilterer->push_back(std::make_unique<RegionCoverageFilter>( 752 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 753 if (LineCoverageLtFilter.getNumOccurrences()) 754 StatFilterer->push_back(std::make_unique<LineCoverageFilter>( 755 LineCoverageFilter::LessThan, LineCoverageLtFilter)); 756 if (LineCoverageGtFilter.getNumOccurrences()) 757 StatFilterer->push_back(std::make_unique<LineCoverageFilter>( 758 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 759 Filters.push_back(std::move(StatFilterer)); 760 } 761 762 // Create the ignore filename filters. 763 for (const auto &RE : IgnoreFilenameRegexFilters) 764 IgnoreFilenameFilters.push_back( 765 std::make_unique<NameRegexCoverageFilter>(RE)); 766 767 if (!Arches.empty()) { 768 for (const std::string &Arch : Arches) { 769 if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { 770 error("Unknown architecture: " + Arch); 771 return 1; 772 } 773 CoverageArches.emplace_back(Arch); 774 } 775 if (CoverageArches.size() != ObjectFilenames.size()) { 776 error("Number of architectures doesn't match the number of objects"); 777 return 1; 778 } 779 } 780 781 // IgnoreFilenameFilters are applied even when InputSourceFiles specified. 782 for (const std::string &File : InputSourceFiles) 783 collectPaths(File); 784 785 if (DebugDumpCollectedPaths) { 786 for (const std::string &SF : SourceFiles) 787 outs() << SF << '\n'; 788 ::exit(0); 789 } 790 791 ViewOpts.ShowRegionSummary = RegionSummary; 792 ViewOpts.ShowInstantiationSummary = InstantiationSummary; 793 ViewOpts.ExportSummaryOnly = SummaryOnly; 794 ViewOpts.NumThreads = NumThreads; 795 796 return 0; 797 }; 798 799 switch (Cmd) { 800 case Show: 801 return doShow(argc, argv, commandLineParser); 802 case Report: 803 return doReport(argc, argv, commandLineParser); 804 case Export: 805 return doExport(argc, argv, commandLineParser); 806 } 807 return 0; 808 } 809 810 int CodeCoverageTool::doShow(int argc, const char **argv, 811 CommandLineParserType commandLineParser) { 812 813 cl::OptionCategory ViewCategory("Viewing options"); 814 815 cl::opt<bool> ShowLineExecutionCounts( 816 "show-line-counts", cl::Optional, 817 cl::desc("Show the execution counts for each line"), cl::init(true), 818 cl::cat(ViewCategory)); 819 820 cl::opt<bool> ShowRegions( 821 "show-regions", cl::Optional, 822 cl::desc("Show the execution counts for each region"), 823 cl::cat(ViewCategory)); 824 825 cl::opt<bool> ShowBestLineRegionsCounts( 826 "show-line-counts-or-regions", cl::Optional, 827 cl::desc("Show the execution counts for each line, or the execution " 828 "counts for each region on lines that have multiple regions"), 829 cl::cat(ViewCategory)); 830 831 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 832 cl::desc("Show expanded source regions"), 833 cl::cat(ViewCategory)); 834 835 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 836 cl::desc("Show function instantiations"), 837 cl::init(true), cl::cat(ViewCategory)); 838 839 cl::opt<std::string> ShowOutputDirectory( 840 "output-dir", cl::init(""), 841 cl::desc("Directory in which coverage information is written out")); 842 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), 843 cl::aliasopt(ShowOutputDirectory)); 844 845 cl::opt<uint32_t> TabSize( 846 "tab-size", cl::init(2), 847 cl::desc( 848 "Set tab expansion size for html coverage reports (default = 2)")); 849 850 cl::opt<std::string> ProjectTitle( 851 "project-title", cl::Optional, 852 cl::desc("Set project title for the coverage report")); 853 854 auto Err = commandLineParser(argc, argv); 855 if (Err) 856 return Err; 857 858 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 859 error("Lcov format should be used with 'llvm-cov export'."); 860 return 1; 861 } 862 863 ViewOpts.ShowLineNumbers = true; 864 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 865 !ShowRegions || ShowBestLineRegionsCounts; 866 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 867 ViewOpts.ShowExpandedRegions = ShowExpansions; 868 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 869 ViewOpts.ShowOutputDirectory = ShowOutputDirectory; 870 ViewOpts.TabSize = TabSize; 871 ViewOpts.ProjectTitle = ProjectTitle; 872 873 if (ViewOpts.hasOutputDirectory()) { 874 if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { 875 error("Could not create output directory!", E.message()); 876 return 1; 877 } 878 } 879 880 sys::fs::file_status Status; 881 if (std::error_code EC = sys::fs::status(PGOFilename, Status)) { 882 error("Could not read profile data!", EC.message()); 883 return 1; 884 } 885 886 auto ModifiedTime = Status.getLastModificationTime(); 887 std::string ModifiedTimeStr = to_string(ModifiedTime); 888 size_t found = ModifiedTimeStr.rfind(':'); 889 ViewOpts.CreatedTimeStr = (found != std::string::npos) 890 ? "Created: " + ModifiedTimeStr.substr(0, found) 891 : "Created: " + ModifiedTimeStr; 892 893 auto Coverage = load(); 894 if (!Coverage) 895 return 1; 896 897 auto Printer = CoveragePrinter::create(ViewOpts); 898 899 if (SourceFiles.empty() && !HadSourceFiles) 900 // Get the source files from the function coverage mapping. 901 for (StringRef Filename : Coverage->getUniqueSourceFiles()) { 902 if (!IgnoreFilenameFilters.matchesFilename(Filename)) 903 SourceFiles.push_back(std::string(Filename)); 904 } 905 906 // Create an index out of the source files. 907 if (ViewOpts.hasOutputDirectory()) { 908 if (Error E = Printer->createIndexFile(SourceFiles, *Coverage, Filters)) { 909 error("Could not create index file!", toString(std::move(E))); 910 return 1; 911 } 912 } 913 914 if (!Filters.empty()) { 915 // Build the map of filenames to functions. 916 std::map<llvm::StringRef, std::vector<const FunctionRecord *>> 917 FilenameFunctionMap; 918 for (const auto &SourceFile : SourceFiles) 919 for (const auto &Function : Coverage->getCoveredFunctions(SourceFile)) 920 if (Filters.matches(*Coverage.get(), Function)) 921 FilenameFunctionMap[SourceFile].push_back(&Function); 922 923 // Only print filter matching functions for each file. 924 for (const auto &FileFunc : FilenameFunctionMap) { 925 StringRef File = FileFunc.first; 926 const auto &Functions = FileFunc.second; 927 928 auto OSOrErr = Printer->createViewFile(File, /*InToplevel=*/false); 929 if (Error E = OSOrErr.takeError()) { 930 error("Could not create view file!", toString(std::move(E))); 931 return 1; 932 } 933 auto OS = std::move(OSOrErr.get()); 934 935 bool ShowTitle = ViewOpts.hasOutputDirectory(); 936 for (const auto *Function : Functions) { 937 auto FunctionView = createFunctionView(*Function, *Coverage); 938 if (!FunctionView) { 939 warning("Could not read coverage for '" + Function->Name + "'."); 940 continue; 941 } 942 FunctionView->print(*OS.get(), /*WholeFile=*/false, 943 /*ShowSourceName=*/true, ShowTitle); 944 ShowTitle = false; 945 } 946 947 Printer->closeViewFile(std::move(OS)); 948 } 949 return 0; 950 } 951 952 // Show files 953 bool ShowFilenames = 954 (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || 955 (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); 956 957 ThreadPoolStrategy S = hardware_concurrency(ViewOpts.NumThreads); 958 if (ViewOpts.NumThreads == 0) { 959 // If NumThreads is not specified, create one thread for each input, up to 960 // the number of hardware cores. 961 S = heavyweight_hardware_concurrency(SourceFiles.size()); 962 S.Limit = true; 963 } 964 965 if (!ViewOpts.hasOutputDirectory() || S.ThreadsRequested == 1) { 966 for (const std::string &SourceFile : SourceFiles) 967 writeSourceFileView(SourceFile, Coverage.get(), Printer.get(), 968 ShowFilenames); 969 } else { 970 // In -output-dir mode, it's safe to use multiple threads to print files. 971 ThreadPool Pool(S); 972 for (const std::string &SourceFile : SourceFiles) 973 Pool.async(&CodeCoverageTool::writeSourceFileView, this, SourceFile, 974 Coverage.get(), Printer.get(), ShowFilenames); 975 Pool.wait(); 976 } 977 978 return 0; 979 } 980 981 int CodeCoverageTool::doReport(int argc, const char **argv, 982 CommandLineParserType commandLineParser) { 983 cl::opt<bool> ShowFunctionSummaries( 984 "show-functions", cl::Optional, cl::init(false), 985 cl::desc("Show coverage summaries for each function")); 986 987 auto Err = commandLineParser(argc, argv); 988 if (Err) 989 return Err; 990 991 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { 992 error("HTML output for summary reports is not yet supported."); 993 return 1; 994 } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { 995 error("Lcov format should be used with 'llvm-cov export'."); 996 return 1; 997 } 998 999 auto Coverage = load(); 1000 if (!Coverage) 1001 return 1; 1002 1003 CoverageReport Report(ViewOpts, *Coverage.get()); 1004 if (!ShowFunctionSummaries) { 1005 if (SourceFiles.empty()) 1006 Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters); 1007 else 1008 Report.renderFileReports(llvm::outs(), SourceFiles); 1009 } else { 1010 if (SourceFiles.empty()) { 1011 error("Source files must be specified when -show-functions=true is " 1012 "specified"); 1013 return 1; 1014 } 1015 1016 Report.renderFunctionReports(SourceFiles, DC, llvm::outs()); 1017 } 1018 return 0; 1019 } 1020 1021 int CodeCoverageTool::doExport(int argc, const char **argv, 1022 CommandLineParserType commandLineParser) { 1023 1024 cl::OptionCategory ExportCategory("Exporting options"); 1025 1026 cl::opt<bool> SkipExpansions("skip-expansions", cl::Optional, 1027 cl::desc("Don't export expanded source regions"), 1028 cl::cat(ExportCategory)); 1029 1030 cl::opt<bool> SkipFunctions("skip-functions", cl::Optional, 1031 cl::desc("Don't export per-function data"), 1032 cl::cat(ExportCategory)); 1033 1034 auto Err = commandLineParser(argc, argv); 1035 if (Err) 1036 return Err; 1037 1038 ViewOpts.SkipExpansions = SkipExpansions; 1039 ViewOpts.SkipFunctions = SkipFunctions; 1040 1041 if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && 1042 ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { 1043 error("Coverage data can only be exported as textual JSON or an " 1044 "lcov tracefile."); 1045 return 1; 1046 } 1047 1048 auto Coverage = load(); 1049 if (!Coverage) { 1050 error("Could not load coverage information"); 1051 return 1; 1052 } 1053 1054 std::unique_ptr<CoverageExporter> Exporter; 1055 1056 switch (ViewOpts.Format) { 1057 case CoverageViewOptions::OutputFormat::Text: 1058 Exporter = std::make_unique<CoverageExporterJson>(*Coverage.get(), 1059 ViewOpts, outs()); 1060 break; 1061 case CoverageViewOptions::OutputFormat::HTML: 1062 // Unreachable because we should have gracefully terminated with an error 1063 // above. 1064 llvm_unreachable("Export in HTML is not supported!"); 1065 case CoverageViewOptions::OutputFormat::Lcov: 1066 Exporter = std::make_unique<CoverageExporterLcov>(*Coverage.get(), 1067 ViewOpts, outs()); 1068 break; 1069 } 1070 1071 if (SourceFiles.empty()) 1072 Exporter->renderRoot(IgnoreFilenameFilters); 1073 else 1074 Exporter->renderRoot(SourceFiles); 1075 1076 return 0; 1077 } 1078 1079 int showMain(int argc, const char *argv[]) { 1080 CodeCoverageTool Tool; 1081 return Tool.run(CodeCoverageTool::Show, argc, argv); 1082 } 1083 1084 int reportMain(int argc, const char *argv[]) { 1085 CodeCoverageTool Tool; 1086 return Tool.run(CodeCoverageTool::Report, argc, argv); 1087 } 1088 1089 int exportMain(int argc, const char *argv[]) { 1090 CodeCoverageTool Tool; 1091 return Tool.run(CodeCoverageTool::Export, argc, argv); 1092 } 1093