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