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