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