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 "CoverageFilters.h" 17 #include "CoverageReport.h" 18 #include "CoverageViewOptions.h" 19 #include "RenderingSupport.h" 20 #include "SourceCoverageView.h" 21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/ADT/Triple.h" 24 #include "llvm/ProfileData/Coverage/CoverageMapping.h" 25 #include "llvm/ProfileData/InstrProfReader.h" 26 #include "llvm/Support/CommandLine.h" 27 #include "llvm/Support/FileSystem.h" 28 #include "llvm/Support/Format.h" 29 #include "llvm/Support/MemoryBuffer.h" 30 #include "llvm/Support/Path.h" 31 #include "llvm/Support/Process.h" 32 #include "llvm/Support/Program.h" 33 #include "llvm/Support/ThreadPool.h" 34 #include "llvm/Support/ToolOutputFile.h" 35 #include <functional> 36 #include <system_error> 37 38 using namespace llvm; 39 using namespace coverage; 40 41 void exportCoverageDataToJson(StringRef ObjectFilename, 42 const coverage::CoverageMapping &CoverageMapping, 43 raw_ostream &OS); 44 45 namespace { 46 /// \brief The implementation of the coverage tool. 47 class CodeCoverageTool { 48 public: 49 enum Command { 50 /// \brief The show command. 51 Show, 52 /// \brief The report command. 53 Report, 54 /// \brief The export command. 55 Export 56 }; 57 58 int run(Command Cmd, int argc, const char **argv); 59 60 private: 61 /// \brief Print the error message to the error output stream. 62 void error(const Twine &Message, StringRef Whence = ""); 63 64 /// \brief Print the warning message to the error output stream. 65 void warning(const Twine &Message, StringRef Whence = ""); 66 67 /// \brief Convert \p Path into an absolute path and append it to the list 68 /// of collected paths. 69 void addCollectedPath(const std::string &Path); 70 71 /// \brief If \p Path is a regular file, collect the path. If it's a 72 /// directory, recursively collect all of the paths within the directory. 73 void collectPaths(const std::string &Path); 74 75 /// \brief Return a memory buffer for the given source file. 76 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); 77 78 /// \brief Create source views for the expansions of the view. 79 void attachExpansionSubViews(SourceCoverageView &View, 80 ArrayRef<ExpansionRecord> Expansions, 81 const CoverageMapping &Coverage); 82 83 /// \brief Create the source view of a particular function. 84 std::unique_ptr<SourceCoverageView> 85 createFunctionView(const FunctionRecord &Function, 86 const CoverageMapping &Coverage); 87 88 /// \brief Create the main source view of a particular source file. 89 std::unique_ptr<SourceCoverageView> 90 createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage); 91 92 /// \brief Load the coverage mapping data. Return nullptr if an error occured. 93 std::unique_ptr<CoverageMapping> load(); 94 95 /// \brief Remove input source files which aren't mapped by \p Coverage. 96 void removeUnmappedInputs(const CoverageMapping &Coverage); 97 98 /// \brief If a demangler is available, demangle all symbol names. 99 void demangleSymbols(const CoverageMapping &Coverage); 100 101 /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym. 102 StringRef getSymbolForHumans(StringRef Sym) const; 103 104 typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; 105 106 int show(int argc, const char **argv, 107 CommandLineParserType commandLineParser); 108 109 int report(int argc, const char **argv, 110 CommandLineParserType commandLineParser); 111 112 int export_(int argc, const char **argv, 113 CommandLineParserType commandLineParser); 114 115 std::string ObjectFilename; 116 CoverageViewOptions ViewOpts; 117 CoverageFiltersMatchAll Filters; 118 119 /// The path to the indexed profile. 120 std::string PGOFilename; 121 122 /// A list of input source files. 123 std::vector<std::string> SourceFiles; 124 125 /// Whether or not we're in -filename-equivalence mode. 126 bool CompareFilenamesOnly; 127 128 /// In -filename-equivalence mode, this maps absolute paths from the 129 /// coverage mapping data to input source files. 130 StringMap<std::string> RemappedFilenames; 131 132 /// The architecture the coverage mapping data targets. 133 std::string CoverageArch; 134 135 /// A cache for demangled symbol names. 136 StringMap<std::string> DemangledNames; 137 138 /// Errors and warnings which have not been printed. 139 std::mutex ErrsLock; 140 141 /// A container for input source file buffers. 142 std::mutex LoadedSourceFilesLock; 143 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> 144 LoadedSourceFiles; 145 }; 146 } 147 148 static std::string getErrorString(const Twine &Message, StringRef Whence, 149 bool Warning) { 150 std::string Str = (Warning ? "warning" : "error"); 151 Str += ": "; 152 if (!Whence.empty()) 153 Str += Whence.str() + ": "; 154 Str += Message.str() + "\n"; 155 return Str; 156 } 157 158 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { 159 std::unique_lock<std::mutex> Guard{ErrsLock}; 160 ViewOpts.colored_ostream(errs(), raw_ostream::RED) 161 << getErrorString(Message, Whence, false); 162 } 163 164 void CodeCoverageTool::warning(const Twine &Message, StringRef Whence) { 165 std::unique_lock<std::mutex> Guard{ErrsLock}; 166 ViewOpts.colored_ostream(errs(), raw_ostream::RED) 167 << getErrorString(Message, Whence, true); 168 } 169 170 void CodeCoverageTool::addCollectedPath(const std::string &Path) { 171 if (CompareFilenamesOnly) { 172 SourceFiles.emplace_back(Path); 173 } else { 174 SmallString<128> EffectivePath(Path); 175 if (std::error_code EC = sys::fs::make_absolute(EffectivePath)) { 176 error(EC.message(), Path); 177 return; 178 } 179 sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true); 180 SourceFiles.emplace_back(EffectivePath.str()); 181 } 182 } 183 184 void CodeCoverageTool::collectPaths(const std::string &Path) { 185 llvm::sys::fs::file_status Status; 186 llvm::sys::fs::status(Path, Status); 187 if (!llvm::sys::fs::exists(Status)) { 188 if (CompareFilenamesOnly) 189 addCollectedPath(Path); 190 else 191 error("Missing source file", Path); 192 return; 193 } 194 195 if (llvm::sys::fs::is_regular_file(Status)) { 196 addCollectedPath(Path); 197 return; 198 } 199 200 if (llvm::sys::fs::is_directory(Status)) { 201 std::error_code EC; 202 for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; 203 F != E && !EC; F.increment(EC)) { 204 if (llvm::sys::fs::is_regular_file(F->path())) 205 addCollectedPath(F->path()); 206 } 207 if (EC) 208 warning(EC.message(), Path); 209 } 210 } 211 212 ErrorOr<const MemoryBuffer &> 213 CodeCoverageTool::getSourceFile(StringRef SourceFile) { 214 // If we've remapped filenames, look up the real location for this file. 215 std::unique_lock<std::mutex> Guard{LoadedSourceFilesLock}; 216 if (!RemappedFilenames.empty()) { 217 auto Loc = RemappedFilenames.find(SourceFile); 218 if (Loc != RemappedFilenames.end()) 219 SourceFile = Loc->second; 220 } 221 for (const auto &Files : LoadedSourceFiles) 222 if (sys::fs::equivalent(SourceFile, Files.first)) 223 return *Files.second; 224 auto Buffer = MemoryBuffer::getFile(SourceFile); 225 if (auto EC = Buffer.getError()) { 226 error(EC.message(), SourceFile); 227 return EC; 228 } 229 LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get())); 230 return *LoadedSourceFiles.back().second; 231 } 232 233 void CodeCoverageTool::attachExpansionSubViews( 234 SourceCoverageView &View, ArrayRef<ExpansionRecord> Expansions, 235 const CoverageMapping &Coverage) { 236 if (!ViewOpts.ShowExpandedRegions) 237 return; 238 for (const auto &Expansion : Expansions) { 239 auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); 240 if (ExpansionCoverage.empty()) 241 continue; 242 auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename()); 243 if (!SourceBuffer) 244 continue; 245 246 auto SubViewExpansions = ExpansionCoverage.getExpansions(); 247 auto SubView = 248 SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), 249 ViewOpts, std::move(ExpansionCoverage)); 250 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 251 View.addExpansion(Expansion.Region, std::move(SubView)); 252 } 253 } 254 255 std::unique_ptr<SourceCoverageView> 256 CodeCoverageTool::createFunctionView(const FunctionRecord &Function, 257 const CoverageMapping &Coverage) { 258 auto FunctionCoverage = Coverage.getCoverageForFunction(Function); 259 if (FunctionCoverage.empty()) 260 return nullptr; 261 auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); 262 if (!SourceBuffer) 263 return nullptr; 264 265 auto Expansions = FunctionCoverage.getExpansions(); 266 auto View = SourceCoverageView::create(getSymbolForHumans(Function.Name), 267 SourceBuffer.get(), ViewOpts, 268 std::move(FunctionCoverage)); 269 attachExpansionSubViews(*View, Expansions, Coverage); 270 271 return View; 272 } 273 274 std::unique_ptr<SourceCoverageView> 275 CodeCoverageTool::createSourceFileView(StringRef SourceFile, 276 const CoverageMapping &Coverage) { 277 auto SourceBuffer = getSourceFile(SourceFile); 278 if (!SourceBuffer) 279 return nullptr; 280 auto FileCoverage = Coverage.getCoverageForFile(SourceFile); 281 if (FileCoverage.empty()) 282 return nullptr; 283 284 auto Expansions = FileCoverage.getExpansions(); 285 auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), 286 ViewOpts, std::move(FileCoverage)); 287 attachExpansionSubViews(*View, Expansions, Coverage); 288 289 for (const auto *Function : Coverage.getInstantiations(SourceFile)) { 290 std::unique_ptr<SourceCoverageView> SubView{nullptr}; 291 292 StringRef Funcname = getSymbolForHumans(Function->Name); 293 294 if (Function->ExecutionCount > 0) { 295 auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); 296 auto SubViewExpansions = SubViewCoverage.getExpansions(); 297 SubView = SourceCoverageView::create( 298 Funcname, SourceBuffer.get(), ViewOpts, std::move(SubViewCoverage)); 299 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 300 } 301 302 unsigned FileID = Function->CountedRegions.front().FileID; 303 unsigned Line = 0; 304 for (const auto &CR : Function->CountedRegions) 305 if (CR.FileID == FileID) 306 Line = std::max(CR.LineEnd, Line); 307 View->addInstantiation(Funcname, Line, std::move(SubView)); 308 } 309 return View; 310 } 311 312 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { 313 sys::fs::file_status Status; 314 if (sys::fs::status(LHS, Status)) 315 return false; 316 auto LHSTime = Status.getLastModificationTime(); 317 if (sys::fs::status(RHS, Status)) 318 return false; 319 auto RHSTime = Status.getLastModificationTime(); 320 return LHSTime > RHSTime; 321 } 322 323 std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { 324 if (modifiedTimeGT(ObjectFilename, PGOFilename)) 325 warning("profile data may be out of date - object is newer", 326 ObjectFilename); 327 auto CoverageOrErr = 328 CoverageMapping::load(ObjectFilename, PGOFilename, CoverageArch); 329 if (Error E = CoverageOrErr.takeError()) { 330 error("Failed to load coverage: " + toString(std::move(E)), ObjectFilename); 331 return nullptr; 332 } 333 auto Coverage = std::move(CoverageOrErr.get()); 334 unsigned Mismatched = Coverage->getMismatchedCount(); 335 if (Mismatched) 336 warning(utostr(Mismatched) + " functions have mismatched data"); 337 338 if (!SourceFiles.empty()) 339 removeUnmappedInputs(*Coverage); 340 341 demangleSymbols(*Coverage); 342 343 return Coverage; 344 } 345 346 void CodeCoverageTool::removeUnmappedInputs(const CoverageMapping &Coverage) { 347 std::vector<StringRef> CoveredFiles = Coverage.getUniqueSourceFiles(); 348 349 auto UncoveredFilesIt = SourceFiles.end(); 350 if (!CompareFilenamesOnly) { 351 // The user may have specified source files which aren't in the coverage 352 // mapping. Filter these files away. 353 UncoveredFilesIt = std::remove_if( 354 SourceFiles.begin(), SourceFiles.end(), [&](const std::string &SF) { 355 return !std::binary_search(CoveredFiles.begin(), CoveredFiles.end(), 356 SF); 357 }); 358 } else { 359 for (auto &SF : SourceFiles) { 360 StringRef SFBase = sys::path::filename(SF); 361 for (const auto &CF : CoveredFiles) { 362 if (SFBase == sys::path::filename(CF)) { 363 RemappedFilenames[CF] = SF; 364 SF = CF; 365 break; 366 } 367 } 368 } 369 UncoveredFilesIt = std::remove_if( 370 SourceFiles.begin(), SourceFiles.end(), 371 [&](const std::string &SF) { return !RemappedFilenames.count(SF); }); 372 } 373 374 SourceFiles.erase(UncoveredFilesIt, SourceFiles.end()); 375 } 376 377 void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) { 378 if (!ViewOpts.hasDemangler()) 379 return; 380 381 // Pass function names to the demangler in a temporary file. 382 int InputFD; 383 SmallString<256> InputPath; 384 std::error_code EC = 385 sys::fs::createTemporaryFile("demangle-in", "list", InputFD, InputPath); 386 if (EC) { 387 error(InputPath, EC.message()); 388 return; 389 } 390 tool_output_file InputTOF{InputPath, InputFD}; 391 392 unsigned NumSymbols = 0; 393 for (const auto &Function : Coverage.getCoveredFunctions()) { 394 InputTOF.os() << Function.Name << '\n'; 395 ++NumSymbols; 396 } 397 InputTOF.os().close(); 398 399 // Use another temporary file to store the demangler's output. 400 int OutputFD; 401 SmallString<256> OutputPath; 402 EC = sys::fs::createTemporaryFile("demangle-out", "list", OutputFD, 403 OutputPath); 404 if (EC) { 405 error(OutputPath, EC.message()); 406 return; 407 } 408 tool_output_file OutputTOF{OutputPath, OutputFD}; 409 OutputTOF.os().close(); 410 411 // Invoke the demangler. 412 std::vector<const char *> ArgsV; 413 for (const std::string &Arg : ViewOpts.DemanglerOpts) 414 ArgsV.push_back(Arg.c_str()); 415 ArgsV.push_back(nullptr); 416 StringRef InputPathRef = InputPath.str(); 417 StringRef OutputPathRef = OutputPath.str(); 418 StringRef StderrRef; 419 const StringRef *Redirects[] = {&InputPathRef, &OutputPathRef, &StderrRef}; 420 std::string ErrMsg; 421 int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV.data(), 422 /*env=*/nullptr, Redirects, /*secondsToWait=*/0, 423 /*memoryLimit=*/0, &ErrMsg); 424 if (RC) { 425 error(ErrMsg, ViewOpts.DemanglerOpts[0]); 426 return; 427 } 428 429 // Parse the demangler's output. 430 auto BufOrError = MemoryBuffer::getFile(OutputPath); 431 if (!BufOrError) { 432 error(OutputPath, BufOrError.getError().message()); 433 return; 434 } 435 436 std::unique_ptr<MemoryBuffer> DemanglerBuf = std::move(*BufOrError); 437 438 SmallVector<StringRef, 8> Symbols; 439 StringRef DemanglerData = DemanglerBuf->getBuffer(); 440 DemanglerData.split(Symbols, '\n', /*MaxSplit=*/NumSymbols, 441 /*KeepEmpty=*/false); 442 if (Symbols.size() != NumSymbols) { 443 error("Demangler did not provide expected number of symbols"); 444 return; 445 } 446 447 // Cache the demangled names. 448 unsigned I = 0; 449 for (const auto &Function : Coverage.getCoveredFunctions()) 450 DemangledNames[Function.Name] = Symbols[I++]; 451 } 452 453 StringRef CodeCoverageTool::getSymbolForHumans(StringRef Sym) const { 454 const auto DemangledName = DemangledNames.find(Sym); 455 if (DemangledName == DemangledNames.end()) 456 return Sym; 457 return DemangledName->getValue(); 458 } 459 460 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 461 cl::opt<std::string, true> ObjectFilename( 462 cl::Positional, cl::Required, cl::location(this->ObjectFilename), 463 cl::desc("Covered executable or object file.")); 464 465 cl::list<std::string> InputSourceFiles( 466 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); 467 468 cl::opt<bool> DebugDumpCollectedPaths( 469 "dump-collected-paths", cl::Optional, cl::Hidden, 470 cl::desc("Show the collected paths to source files")); 471 472 cl::opt<std::string, true> PGOFilename( 473 "instr-profile", cl::Required, cl::location(this->PGOFilename), 474 cl::desc( 475 "File with the profile data obtained after an instrumented run")); 476 477 cl::opt<std::string> Arch( 478 "arch", cl::desc("architecture of the coverage mapping binary")); 479 480 cl::opt<bool> DebugDump("dump", cl::Optional, 481 cl::desc("Show internal debug dump")); 482 483 cl::opt<CoverageViewOptions::OutputFormat> Format( 484 "format", cl::desc("Output format for line-based coverage reports"), 485 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", 486 "Text output"), 487 clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", 488 "HTML output")), 489 cl::init(CoverageViewOptions::OutputFormat::Text)); 490 491 cl::opt<bool> FilenameEquivalence( 492 "filename-equivalence", cl::Optional, 493 cl::desc("Treat source files as equivalent to paths in the coverage data " 494 "when the file names match, even if the full paths do not")); 495 496 cl::OptionCategory FilteringCategory("Function filtering options"); 497 498 cl::list<std::string> NameFilters( 499 "name", cl::Optional, 500 cl::desc("Show code coverage only for functions with the given name"), 501 cl::ZeroOrMore, cl::cat(FilteringCategory)); 502 503 cl::list<std::string> NameRegexFilters( 504 "name-regex", cl::Optional, 505 cl::desc("Show code coverage only for functions that match the given " 506 "regular expression"), 507 cl::ZeroOrMore, cl::cat(FilteringCategory)); 508 509 cl::opt<double> RegionCoverageLtFilter( 510 "region-coverage-lt", cl::Optional, 511 cl::desc("Show code coverage only for functions with region coverage " 512 "less than the given threshold"), 513 cl::cat(FilteringCategory)); 514 515 cl::opt<double> RegionCoverageGtFilter( 516 "region-coverage-gt", cl::Optional, 517 cl::desc("Show code coverage only for functions with region coverage " 518 "greater than the given threshold"), 519 cl::cat(FilteringCategory)); 520 521 cl::opt<double> LineCoverageLtFilter( 522 "line-coverage-lt", cl::Optional, 523 cl::desc("Show code coverage only for functions with line coverage less " 524 "than the given threshold"), 525 cl::cat(FilteringCategory)); 526 527 cl::opt<double> LineCoverageGtFilter( 528 "line-coverage-gt", cl::Optional, 529 cl::desc("Show code coverage only for functions with line coverage " 530 "greater than the given threshold"), 531 cl::cat(FilteringCategory)); 532 533 cl::opt<cl::boolOrDefault> UseColor( 534 "use-color", cl::desc("Emit colored output (default=autodetect)"), 535 cl::init(cl::BOU_UNSET)); 536 537 cl::list<std::string> DemanglerOpts( 538 "Xdemangler", cl::desc("<demangler-path>|<demangler-option>")); 539 540 auto commandLineParser = [&, this](int argc, const char **argv) -> int { 541 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 542 ViewOpts.Debug = DebugDump; 543 CompareFilenamesOnly = FilenameEquivalence; 544 545 ViewOpts.Format = Format; 546 SmallString<128> ObjectFilePath(this->ObjectFilename); 547 if (std::error_code EC = sys::fs::make_absolute(ObjectFilePath)) { 548 error(EC.message(), this->ObjectFilename); 549 return 1; 550 } 551 sys::path::native(ObjectFilePath); 552 ViewOpts.ObjectFilename = ObjectFilePath.c_str(); 553 switch (ViewOpts.Format) { 554 case CoverageViewOptions::OutputFormat::Text: 555 ViewOpts.Colors = UseColor == cl::BOU_UNSET 556 ? sys::Process::StandardOutHasColors() 557 : UseColor == cl::BOU_TRUE; 558 break; 559 case CoverageViewOptions::OutputFormat::HTML: 560 if (UseColor == cl::BOU_FALSE) 561 error("Color output cannot be disabled when generating html."); 562 ViewOpts.Colors = true; 563 break; 564 } 565 566 // If a demangler is supplied, check if it exists and register it. 567 if (DemanglerOpts.size()) { 568 auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); 569 if (!DemanglerPathOrErr) { 570 error("Could not find the demangler!", 571 DemanglerPathOrErr.getError().message()); 572 return 1; 573 } 574 DemanglerOpts[0] = *DemanglerPathOrErr; 575 ViewOpts.DemanglerOpts.swap(DemanglerOpts); 576 } 577 578 // Create the function filters 579 if (!NameFilters.empty() || !NameRegexFilters.empty()) { 580 auto NameFilterer = new CoverageFilters; 581 for (const auto &Name : NameFilters) 582 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); 583 for (const auto &Regex : NameRegexFilters) 584 NameFilterer->push_back( 585 llvm::make_unique<NameRegexCoverageFilter>(Regex)); 586 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); 587 } 588 if (RegionCoverageLtFilter.getNumOccurrences() || 589 RegionCoverageGtFilter.getNumOccurrences() || 590 LineCoverageLtFilter.getNumOccurrences() || 591 LineCoverageGtFilter.getNumOccurrences()) { 592 auto StatFilterer = new CoverageFilters; 593 if (RegionCoverageLtFilter.getNumOccurrences()) 594 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 595 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 596 if (RegionCoverageGtFilter.getNumOccurrences()) 597 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 598 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 599 if (LineCoverageLtFilter.getNumOccurrences()) 600 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 601 LineCoverageFilter::LessThan, LineCoverageLtFilter)); 602 if (LineCoverageGtFilter.getNumOccurrences()) 603 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 604 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 605 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); 606 } 607 608 if (!Arch.empty() && 609 Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { 610 error("Unknown architecture: " + Arch); 611 return 1; 612 } 613 CoverageArch = Arch; 614 615 for (const std::string &File : InputSourceFiles) 616 collectPaths(File); 617 618 if (DebugDumpCollectedPaths) { 619 for (const std::string &SF : SourceFiles) 620 outs() << SF << '\n'; 621 ::exit(0); 622 } 623 624 return 0; 625 }; 626 627 switch (Cmd) { 628 case Show: 629 return show(argc, argv, commandLineParser); 630 case Report: 631 return report(argc, argv, commandLineParser); 632 case Export: 633 return export_(argc, argv, commandLineParser); 634 } 635 return 0; 636 } 637 638 int CodeCoverageTool::show(int argc, const char **argv, 639 CommandLineParserType commandLineParser) { 640 641 cl::OptionCategory ViewCategory("Viewing options"); 642 643 cl::opt<bool> ShowLineExecutionCounts( 644 "show-line-counts", cl::Optional, 645 cl::desc("Show the execution counts for each line"), cl::init(true), 646 cl::cat(ViewCategory)); 647 648 cl::opt<bool> ShowRegions( 649 "show-regions", cl::Optional, 650 cl::desc("Show the execution counts for each region"), 651 cl::cat(ViewCategory)); 652 653 cl::opt<bool> ShowBestLineRegionsCounts( 654 "show-line-counts-or-regions", cl::Optional, 655 cl::desc("Show the execution counts for each line, or the execution " 656 "counts for each region on lines that have multiple regions"), 657 cl::cat(ViewCategory)); 658 659 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 660 cl::desc("Show expanded source regions"), 661 cl::cat(ViewCategory)); 662 663 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 664 cl::desc("Show function instantiations"), 665 cl::cat(ViewCategory)); 666 667 cl::opt<std::string> ShowOutputDirectory( 668 "output-dir", cl::init(""), 669 cl::desc("Directory in which coverage information is written out")); 670 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), 671 cl::aliasopt(ShowOutputDirectory)); 672 673 cl::opt<uint32_t> TabSize( 674 "tab-size", cl::init(2), 675 cl::desc( 676 "Set tab expansion size for html coverage reports (default = 2)")); 677 678 cl::opt<std::string> ProjectTitle( 679 "project-title", cl::Optional, 680 cl::desc("Set project title for the coverage report")); 681 682 auto Err = commandLineParser(argc, argv); 683 if (Err) 684 return Err; 685 686 ViewOpts.ShowLineNumbers = true; 687 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 688 !ShowRegions || ShowBestLineRegionsCounts; 689 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 690 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; 691 ViewOpts.ShowExpandedRegions = ShowExpansions; 692 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 693 ViewOpts.ShowOutputDirectory = ShowOutputDirectory; 694 ViewOpts.TabSize = TabSize; 695 ViewOpts.ProjectTitle = ProjectTitle; 696 697 if (ViewOpts.hasOutputDirectory()) { 698 if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { 699 error("Could not create output directory!", E.message()); 700 return 1; 701 } 702 } 703 704 sys::fs::file_status Status; 705 if (sys::fs::status(PGOFilename, Status)) { 706 error("profdata file error: can not get the file status. \n"); 707 return 1; 708 } 709 710 auto ModifiedTime = Status.getLastModificationTime(); 711 std::string ModifiedTimeStr = ModifiedTime.str(); 712 size_t found = ModifiedTimeStr.rfind(":"); 713 ViewOpts.CreatedTimeStr = (found != std::string::npos) 714 ? "Created: " + ModifiedTimeStr.substr(0, found) 715 : "Created: " + ModifiedTimeStr; 716 717 auto Coverage = load(); 718 if (!Coverage) 719 return 1; 720 721 auto Printer = CoveragePrinter::create(ViewOpts); 722 723 if (!Filters.empty()) { 724 auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true); 725 if (Error E = OSOrErr.takeError()) { 726 error("Could not create view file!", toString(std::move(E))); 727 return 1; 728 } 729 auto OS = std::move(OSOrErr.get()); 730 731 // Show functions. 732 for (const auto &Function : Coverage->getCoveredFunctions()) { 733 if (!Filters.matches(Function)) 734 continue; 735 736 auto mainView = createFunctionView(Function, *Coverage); 737 if (!mainView) { 738 warning("Could not read coverage for '" + Function.Name + "'."); 739 continue; 740 } 741 742 mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true); 743 } 744 745 Printer->closeViewFile(std::move(OS)); 746 return 0; 747 } 748 749 // Show files 750 bool ShowFilenames = 751 (SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() || 752 (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML); 753 754 if (SourceFiles.empty()) 755 // Get the source files from the function coverage mapping. 756 for (StringRef Filename : Coverage->getUniqueSourceFiles()) 757 SourceFiles.push_back(Filename); 758 759 // Create an index out of the source files. 760 if (ViewOpts.hasOutputDirectory()) { 761 if (Error E = Printer->createIndexFile(SourceFiles, *Coverage)) { 762 error("Could not create index file!", toString(std::move(E))); 763 return 1; 764 } 765 } 766 767 // In -output-dir mode, it's safe to use multiple threads to print files. 768 unsigned ThreadCount = 1; 769 if (ViewOpts.hasOutputDirectory()) 770 ThreadCount = std::thread::hardware_concurrency(); 771 ThreadPool Pool(ThreadCount); 772 773 for (const std::string &SourceFile : SourceFiles) { 774 Pool.async([this, &SourceFile, &Coverage, &Printer, ShowFilenames] { 775 auto View = createSourceFileView(SourceFile, *Coverage); 776 if (!View) { 777 warning("The file '" + SourceFile + "' isn't covered."); 778 return; 779 } 780 781 auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); 782 if (Error E = OSOrErr.takeError()) { 783 error("Could not create view file!", toString(std::move(E))); 784 return; 785 } 786 auto OS = std::move(OSOrErr.get()); 787 788 View->print(*OS.get(), /*Wholefile=*/true, 789 /*ShowSourceName=*/ShowFilenames); 790 Printer->closeViewFile(std::move(OS)); 791 }); 792 } 793 794 Pool.wait(); 795 796 return 0; 797 } 798 799 int CodeCoverageTool::report(int argc, const char **argv, 800 CommandLineParserType commandLineParser) { 801 auto Err = commandLineParser(argc, argv); 802 if (Err) 803 return Err; 804 805 if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) 806 error("HTML output for summary reports is not yet supported."); 807 808 auto Coverage = load(); 809 if (!Coverage) 810 return 1; 811 812 CoverageReport Report(ViewOpts, *Coverage.get()); 813 if (SourceFiles.empty()) 814 Report.renderFileReports(llvm::outs()); 815 else 816 Report.renderFunctionReports(SourceFiles, llvm::outs()); 817 return 0; 818 } 819 820 int CodeCoverageTool::export_(int argc, const char **argv, 821 CommandLineParserType commandLineParser) { 822 823 auto Err = commandLineParser(argc, argv); 824 if (Err) 825 return Err; 826 827 auto Coverage = load(); 828 if (!Coverage) { 829 error("Could not load coverage information"); 830 return 1; 831 } 832 833 exportCoverageDataToJson(ObjectFilename, *Coverage.get(), outs()); 834 835 return 0; 836 } 837 838 int showMain(int argc, const char *argv[]) { 839 CodeCoverageTool Tool; 840 return Tool.run(CodeCoverageTool::Show, argc, argv); 841 } 842 843 int reportMain(int argc, const char *argv[]) { 844 CodeCoverageTool Tool; 845 return Tool.run(CodeCoverageTool::Report, argc, argv); 846 } 847 848 int exportMain(int argc, const char *argv[]) { 849 CodeCoverageTool Tool; 850 return Tool.run(CodeCoverageTool::Export, argc, argv); 851 } 852