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