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