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