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 "RenderingSupport.h" 17 #include "CoverageViewOptions.h" 18 #include "CoverageFilters.h" 19 #include "SourceCoverageDataManager.h" 20 #include "SourceCoverageView.h" 21 #include "CoverageSummary.h" 22 #include "CoverageReport.h" 23 #include "llvm/ADT/DenseMap.h" 24 #include "llvm/ADT/StringRef.h" 25 #include "llvm/ADT/SmallString.h" 26 #include "llvm/ADT/SmallSet.h" 27 #include "llvm/ADT/DenseSet.h" 28 #include "llvm/ProfileData/InstrProfReader.h" 29 #include "llvm/ProfileData/CoverageMapping.h" 30 #include "llvm/ProfileData/CoverageMappingReader.h" 31 #include "llvm/Support/CommandLine.h" 32 #include "llvm/Support/FileSystem.h" 33 #include "llvm/Support/ManagedStatic.h" 34 #include "llvm/Support/MemoryObject.h" 35 #include "llvm/Support/Format.h" 36 #include "llvm/Support/Path.h" 37 #include "llvm/Support/Signals.h" 38 #include "llvm/Support/PrettyStackTrace.h" 39 #include <functional> 40 #include <system_error> 41 42 using namespace llvm; 43 using namespace coverage; 44 45 namespace { 46 /// \brief Distribute the functions into instantiation sets. 47 /// 48 /// An instantiation set is a collection of functions that have the same source 49 /// code, ie, template functions specializations. 50 class FunctionInstantiationSetCollector { 51 typedef DenseMap<std::pair<unsigned, unsigned>, 52 std::vector<const FunctionCoverageMapping *>> MapT; 53 MapT InstantiatedFunctions; 54 55 public: 56 void insert(const FunctionCoverageMapping &Function, unsigned FileID) { 57 auto I = Function.CountedRegions.begin(), E = Function.CountedRegions.end(); 58 while (I != E && I->FileID != FileID) 59 ++I; 60 assert(I != E && "function does not cover the given file"); 61 auto &Functions = InstantiatedFunctions[I->startLoc()]; 62 Functions.push_back(&Function); 63 } 64 65 MapT::iterator begin() { 66 return InstantiatedFunctions.begin(); 67 } 68 69 MapT::iterator end() { 70 return InstantiatedFunctions.end(); 71 } 72 }; 73 74 /// \brief The implementation of the coverage tool. 75 class CodeCoverageTool { 76 public: 77 enum Command { 78 /// \brief The show command. 79 Show, 80 /// \brief The report command. 81 Report 82 }; 83 84 /// \brief Print the error message to the error output stream. 85 void error(const Twine &Message, StringRef Whence = ""); 86 87 /// \brief Return a memory buffer for the given source file. 88 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); 89 90 /// \brief Collect a set of function's file ids which correspond to the 91 /// given source file. Return false if the set is empty. 92 bool gatherInterestingFileIDs(StringRef SourceFile, 93 const FunctionCoverageMapping &Function, 94 SmallSet<unsigned, 8> &InterestingFileIDs); 95 96 /// \brief Find the file id which is not an expanded file id. 97 bool findMainViewFileID(StringRef SourceFile, 98 const FunctionCoverageMapping &Function, 99 unsigned &MainViewFileID); 100 101 bool findMainViewFileID(const FunctionCoverageMapping &Function, 102 unsigned &MainViewFileID); 103 104 /// \brief Create a source view which shows coverage for an expansion 105 /// of a file. 106 std::unique_ptr<SourceCoverageView> 107 createExpansionSubView(const CountedRegion &ExpandedRegion, 108 const FunctionCoverageMapping &Function); 109 110 void attachExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID, 111 const FunctionCoverageMapping &Function); 112 113 /// \brief Create a source view which shows coverage for an instantiation 114 /// of a funciton. 115 std::unique_ptr<SourceCoverageView> 116 createInstantiationSubView(StringRef SourceFile, 117 const FunctionCoverageMapping &Function); 118 119 /// \brief Create the main source view of a particular source file. 120 std::unique_ptr<SourceCoverageView> 121 createSourceFileView(StringRef SourceFile, 122 ArrayRef<FunctionCoverageMapping> FunctionMappingRecords, 123 bool UseOnlyRegionsInMainFile = false); 124 125 /// \brief Load the coverage mapping data. Return true if an error occured. 126 bool load(); 127 128 int run(Command Cmd, int argc, const char **argv); 129 130 typedef std::function<int(int, const char **)> CommandLineParserType; 131 132 int show(int argc, const char **argv, 133 CommandLineParserType commandLineParser); 134 135 int report(int argc, const char **argv, 136 CommandLineParserType commandLineParser); 137 138 StringRef ObjectFilename; 139 CoverageViewOptions ViewOpts; 140 std::unique_ptr<IndexedInstrProfReader> PGOReader; 141 CoverageFiltersMatchAll Filters; 142 std::vector<std::string> SourceFiles; 143 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> 144 LoadedSourceFiles; 145 std::vector<FunctionCoverageMapping> FunctionMappingRecords; 146 bool CompareFilenamesOnly; 147 StringMap<std::string> RemappedFilenames; 148 }; 149 } 150 151 static std::vector<StringRef> 152 getUniqueFilenames(ArrayRef<FunctionCoverageMapping> FunctionMappingRecords) { 153 std::vector<StringRef> Filenames; 154 for (const auto &Function : FunctionMappingRecords) 155 for (const auto &Filename : Function.Filenames) 156 Filenames.push_back(Filename); 157 std::sort(Filenames.begin(), Filenames.end()); 158 auto Last = std::unique(Filenames.begin(), Filenames.end()); 159 Filenames.erase(Last, Filenames.end()); 160 return Filenames; 161 } 162 163 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { 164 errs() << "error: "; 165 if (!Whence.empty()) 166 errs() << Whence << ": "; 167 errs() << Message << "\n"; 168 } 169 170 ErrorOr<const MemoryBuffer &> 171 CodeCoverageTool::getSourceFile(StringRef SourceFile) { 172 // If we've remapped filenames, look up the real location for this file. 173 if (!RemappedFilenames.empty()) { 174 auto Loc = RemappedFilenames.find(SourceFile); 175 if (Loc != RemappedFilenames.end()) 176 SourceFile = Loc->second; 177 } 178 for (const auto &Files : LoadedSourceFiles) 179 if (sys::fs::equivalent(SourceFile, Files.first)) 180 return *Files.second; 181 auto Buffer = MemoryBuffer::getFile(SourceFile); 182 if (auto EC = Buffer.getError()) { 183 error(EC.message(), SourceFile); 184 return EC; 185 } 186 LoadedSourceFiles.push_back( 187 std::make_pair(SourceFile, std::move(Buffer.get()))); 188 return *LoadedSourceFiles.back().second; 189 } 190 191 bool CodeCoverageTool::gatherInterestingFileIDs( 192 StringRef SourceFile, const FunctionCoverageMapping &Function, 193 SmallSet<unsigned, 8> &InterestingFileIDs) { 194 bool Interesting = false; 195 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) { 196 if (SourceFile == Function.Filenames[I]) { 197 InterestingFileIDs.insert(I); 198 Interesting = true; 199 } 200 } 201 return Interesting; 202 } 203 204 bool 205 CodeCoverageTool::findMainViewFileID(StringRef SourceFile, 206 const FunctionCoverageMapping &Function, 207 unsigned &MainViewFileID) { 208 llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false); 209 llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(), 210 false); 211 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) { 212 if (SourceFile == Function.Filenames[I]) 213 FilenameEquivalence[I] = true; 214 } 215 for (const auto &CR : Function.CountedRegions) { 216 if (CR.Kind == CounterMappingRegion::ExpansionRegion && 217 FilenameEquivalence[CR.FileID]) 218 IsExpandedFile[CR.ExpandedFileID] = true; 219 } 220 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) { 221 if (!FilenameEquivalence[I] || IsExpandedFile[I]) 222 continue; 223 MainViewFileID = I; 224 return false; 225 } 226 return true; 227 } 228 229 bool 230 CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function, 231 unsigned &MainViewFileID) { 232 llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false); 233 for (const auto &CR : Function.CountedRegions) { 234 if (CR.Kind == CounterMappingRegion::ExpansionRegion) 235 IsExpandedFile[CR.ExpandedFileID] = true; 236 } 237 for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) { 238 if (IsExpandedFile[I]) 239 continue; 240 MainViewFileID = I; 241 return false; 242 } 243 return true; 244 } 245 246 std::unique_ptr<SourceCoverageView> CodeCoverageTool::createExpansionSubView( 247 const CountedRegion &ExpandedRegion, 248 const FunctionCoverageMapping &Function) { 249 auto SourceBuffer = 250 getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]); 251 if (!SourceBuffer) 252 return nullptr; 253 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>(); 254 for (const auto &CR : Function.CountedRegions) { 255 if (CR.FileID == ExpandedRegion.ExpandedFileID) 256 RegionManager->insert(CR); 257 } 258 auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(), 259 ViewOpts); 260 SubView->load(std::move(RegionManager)); 261 attachExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function); 262 return SubView; 263 } 264 265 void CodeCoverageTool::attachExpansionSubViews( 266 SourceCoverageView &View, unsigned ViewFileID, 267 const FunctionCoverageMapping &Function) { 268 if (!ViewOpts.ShowExpandedRegions) 269 return; 270 for (const auto &CR : Function.CountedRegions) { 271 if (CR.Kind != CounterMappingRegion::ExpansionRegion) 272 continue; 273 if (CR.FileID != ViewFileID) 274 continue; 275 auto SubView = createExpansionSubView(CR, Function); 276 if (SubView) 277 View.addExpansion(CR, std::move(SubView)); 278 } 279 } 280 281 std::unique_ptr<SourceCoverageView> 282 CodeCoverageTool::createInstantiationSubView( 283 StringRef SourceFile, const FunctionCoverageMapping &Function) { 284 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>(); 285 SmallSet<unsigned, 8> InterestingFileIDs; 286 if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs)) 287 return nullptr; 288 // Get the interesting regions 289 for (const auto &CR : Function.CountedRegions) { 290 if (InterestingFileIDs.count(CR.FileID)) 291 RegionManager->insert(CR); 292 } 293 294 auto SourceBuffer = getSourceFile(SourceFile); 295 if (!SourceBuffer) 296 return nullptr; 297 auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(), 298 ViewOpts); 299 SubView->load(std::move(RegionManager)); 300 unsigned MainFileID; 301 if (!findMainViewFileID(SourceFile, Function, MainFileID)) 302 attachExpansionSubViews(*SubView, MainFileID, Function); 303 return SubView; 304 } 305 306 std::unique_ptr<SourceCoverageView> CodeCoverageTool::createSourceFileView( 307 StringRef SourceFile, 308 ArrayRef<FunctionCoverageMapping> FunctionMappingRecords, 309 bool UseOnlyRegionsInMainFile) { 310 auto RegionManager = llvm::make_unique<SourceCoverageDataManager>(); 311 FunctionInstantiationSetCollector InstantiationSetCollector; 312 313 auto SourceBuffer = getSourceFile(SourceFile); 314 if (!SourceBuffer) 315 return nullptr; 316 auto View = 317 llvm::make_unique<SourceCoverageView>(SourceBuffer.get(), ViewOpts); 318 319 for (const auto &Function : FunctionMappingRecords) { 320 unsigned MainFileID; 321 if (findMainViewFileID(SourceFile, Function, MainFileID)) 322 continue; 323 SmallSet<unsigned, 8> InterestingFileIDs; 324 if (UseOnlyRegionsInMainFile) { 325 InterestingFileIDs.insert(MainFileID); 326 } else if (!gatherInterestingFileIDs(SourceFile, Function, 327 InterestingFileIDs)) 328 continue; 329 // Get the interesting regions 330 for (const auto &CR : Function.CountedRegions) { 331 if (InterestingFileIDs.count(CR.FileID)) 332 RegionManager->insert(CR); 333 } 334 InstantiationSetCollector.insert(Function, MainFileID); 335 attachExpansionSubViews(*View, MainFileID, Function); 336 } 337 if (RegionManager->getCoverageSegments().empty()) 338 return nullptr; 339 View->load(std::move(RegionManager)); 340 // Show instantiations 341 if (!ViewOpts.ShowFunctionInstantiations) 342 return View; 343 for (const auto &InstantiationSet : InstantiationSetCollector) { 344 if (InstantiationSet.second.size() < 2) 345 continue; 346 for (auto Function : InstantiationSet.second) { 347 unsigned FileID = Function->CountedRegions.front().FileID; 348 unsigned Line = 0; 349 for (const auto &CR : Function->CountedRegions) 350 if (CR.FileID == FileID) 351 Line = std::max(CR.LineEnd, Line); 352 auto SubView = createInstantiationSubView(SourceFile, *Function); 353 if (SubView) 354 View->addInstantiation(Function->Name, Line, std::move(SubView)); 355 } 356 } 357 return View; 358 } 359 360 bool CodeCoverageTool::load() { 361 auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename); 362 if (auto EC = CounterMappingBuff.getError()) { 363 error(EC.message(), ObjectFilename); 364 return true; 365 } 366 ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get()); 367 if (auto EC = MappingReader.readHeader()) { 368 error(EC.message(), ObjectFilename); 369 return true; 370 } 371 372 std::vector<uint64_t> Counts; 373 for (const auto &I : MappingReader) { 374 FunctionCoverageMapping Function(I.FunctionName, I.Filenames); 375 376 // Create the mapping regions with evaluated execution counts 377 Counts.clear(); 378 PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts); 379 380 // Get the biggest referenced counters 381 bool RegionError = false; 382 CounterMappingContext Ctx(I.Expressions, Counts); 383 for (const auto &R : I.MappingRegions) { 384 // Compute the values of mapped regions 385 if (ViewOpts.Debug) { 386 errs() << "File " << R.FileID << "| " << R.LineStart << ":" 387 << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd 388 << " = "; 389 Ctx.dump(R.Count); 390 if (R.Kind == CounterMappingRegion::ExpansionRegion) { 391 errs() << " (Expanded file id = " << R.ExpandedFileID << ") "; 392 } 393 errs() << "\n"; 394 } 395 ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count); 396 if (ExecutionCount) { 397 Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount)); 398 } else if (!RegionError) { 399 colored_ostream(errs(), raw_ostream::RED) 400 << "error: Regions and counters don't match in a function '" 401 << Function.Name << "' (re-run the instrumented binary)."; 402 errs() << "\n"; 403 RegionError = true; 404 } 405 } 406 407 if (RegionError || !Filters.matches(Function)) 408 continue; 409 410 FunctionMappingRecords.push_back(Function); 411 } 412 413 if (CompareFilenamesOnly) { 414 auto CoveredFiles = getUniqueFilenames(FunctionMappingRecords); 415 for (auto &SF : SourceFiles) { 416 StringRef SFBase = sys::path::filename(SF); 417 for (const auto &CF : CoveredFiles) 418 if (SFBase == sys::path::filename(CF)) { 419 RemappedFilenames[CF] = SF; 420 SF = CF; 421 break; 422 } 423 } 424 } 425 426 return false; 427 } 428 429 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 430 // Print a stack trace if we signal out. 431 sys::PrintStackTraceOnErrorSignal(); 432 PrettyStackTraceProgram X(argc, argv); 433 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 434 435 cl::list<std::string> InputSourceFiles( 436 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); 437 438 cl::opt<std::string> PGOFilename( 439 "instr-profile", cl::Required, 440 cl::desc( 441 "File with the profile data obtained after an instrumented run")); 442 443 cl::opt<bool> DebugDump("dump", cl::Optional, 444 cl::desc("Show internal debug dump")); 445 446 cl::opt<bool> FilenameEquivalence( 447 "filename-equivalence", cl::Optional, 448 cl::desc("Treat source files as equivalent to paths in the coverage data " 449 "when the file names match, even if the full paths do not")); 450 451 cl::OptionCategory FilteringCategory("Function filtering options"); 452 453 cl::list<std::string> NameFilters( 454 "name", cl::Optional, 455 cl::desc("Show code coverage only for functions with the given name"), 456 cl::ZeroOrMore, cl::cat(FilteringCategory)); 457 458 cl::list<std::string> NameRegexFilters( 459 "name-regex", cl::Optional, 460 cl::desc("Show code coverage only for functions that match the given " 461 "regular expression"), 462 cl::ZeroOrMore, cl::cat(FilteringCategory)); 463 464 cl::opt<double> RegionCoverageLtFilter( 465 "region-coverage-lt", cl::Optional, 466 cl::desc("Show code coverage only for functions with region coverage " 467 "less than the given threshold"), 468 cl::cat(FilteringCategory)); 469 470 cl::opt<double> RegionCoverageGtFilter( 471 "region-coverage-gt", cl::Optional, 472 cl::desc("Show code coverage only for functions with region coverage " 473 "greater than the given threshold"), 474 cl::cat(FilteringCategory)); 475 476 cl::opt<double> LineCoverageLtFilter( 477 "line-coverage-lt", cl::Optional, 478 cl::desc("Show code coverage only for functions with line coverage less " 479 "than the given threshold"), 480 cl::cat(FilteringCategory)); 481 482 cl::opt<double> LineCoverageGtFilter( 483 "line-coverage-gt", cl::Optional, 484 cl::desc("Show code coverage only for functions with line coverage " 485 "greater than the given threshold"), 486 cl::cat(FilteringCategory)); 487 488 auto commandLineParser = [&, this](int argc, const char **argv) -> int { 489 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 490 ViewOpts.Debug = DebugDump; 491 CompareFilenamesOnly = FilenameEquivalence; 492 493 if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) { 494 error(EC.message(), PGOFilename); 495 return 1; 496 } 497 498 // Create the function filters 499 if (!NameFilters.empty() || !NameRegexFilters.empty()) { 500 auto NameFilterer = new CoverageFilters; 501 for (const auto &Name : NameFilters) 502 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); 503 for (const auto &Regex : NameRegexFilters) 504 NameFilterer->push_back( 505 llvm::make_unique<NameRegexCoverageFilter>(Regex)); 506 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); 507 } 508 if (RegionCoverageLtFilter.getNumOccurrences() || 509 RegionCoverageGtFilter.getNumOccurrences() || 510 LineCoverageLtFilter.getNumOccurrences() || 511 LineCoverageGtFilter.getNumOccurrences()) { 512 auto StatFilterer = new CoverageFilters; 513 if (RegionCoverageLtFilter.getNumOccurrences()) 514 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 515 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 516 if (RegionCoverageGtFilter.getNumOccurrences()) 517 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 518 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 519 if (LineCoverageLtFilter.getNumOccurrences()) 520 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 521 LineCoverageFilter::LessThan, LineCoverageLtFilter)); 522 if (LineCoverageGtFilter.getNumOccurrences()) 523 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 524 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 525 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); 526 } 527 528 for (const auto &File : InputSourceFiles) { 529 SmallString<128> Path(File); 530 if (std::error_code EC = sys::fs::make_absolute(Path)) { 531 errs() << "error: " << File << ": " << EC.message(); 532 return 1; 533 } 534 SourceFiles.push_back(Path.str()); 535 } 536 return 0; 537 }; 538 539 // Parse the object filename 540 if (argc > 1) { 541 StringRef Arg(argv[1]); 542 if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) { 543 cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n"); 544 return 0; 545 } 546 ObjectFilename = Arg; 547 548 argv[1] = argv[0]; 549 --argc; 550 ++argv; 551 } else { 552 errs() << sys::path::filename(argv[0]) << ": No executable file given!\n"; 553 return 1; 554 } 555 556 switch (Cmd) { 557 case Show: 558 return show(argc, argv, commandLineParser); 559 case Report: 560 return report(argc, argv, commandLineParser); 561 } 562 return 0; 563 } 564 565 int CodeCoverageTool::show(int argc, const char **argv, 566 CommandLineParserType commandLineParser) { 567 568 cl::OptionCategory ViewCategory("Viewing options"); 569 570 cl::opt<bool> ShowLineExecutionCounts( 571 "show-line-counts", cl::Optional, 572 cl::desc("Show the execution counts for each line"), cl::init(true), 573 cl::cat(ViewCategory)); 574 575 cl::opt<bool> ShowRegions( 576 "show-regions", cl::Optional, 577 cl::desc("Show the execution counts for each region"), 578 cl::cat(ViewCategory)); 579 580 cl::opt<bool> ShowBestLineRegionsCounts( 581 "show-line-counts-or-regions", cl::Optional, 582 cl::desc("Show the execution counts for each line, or the execution " 583 "counts for each region on lines that have multiple regions"), 584 cl::cat(ViewCategory)); 585 586 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 587 cl::desc("Show expanded source regions"), 588 cl::cat(ViewCategory)); 589 590 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 591 cl::desc("Show function instantiations"), 592 cl::cat(ViewCategory)); 593 594 cl::opt<bool> NoColors("no-colors", cl::Optional, 595 cl::desc("Don't show text colors"), cl::init(false), 596 cl::cat(ViewCategory)); 597 598 auto Err = commandLineParser(argc, argv); 599 if (Err) 600 return Err; 601 602 ViewOpts.Colors = !NoColors; 603 ViewOpts.ShowLineNumbers = true; 604 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 605 !ShowRegions || ShowBestLineRegionsCounts; 606 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 607 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; 608 ViewOpts.ShowExpandedRegions = ShowExpansions; 609 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 610 611 if (load()) 612 return 1; 613 614 if (!Filters.empty()) { 615 // Show functions 616 for (const auto &Function : FunctionMappingRecords) { 617 unsigned MainFileID; 618 if (findMainViewFileID(Function, MainFileID)) 619 continue; 620 StringRef SourceFile = Function.Filenames[MainFileID]; 621 auto mainView = createSourceFileView(SourceFile, Function, true); 622 if (!mainView) { 623 ViewOpts.colored_ostream(outs(), raw_ostream::RED) 624 << "warning: Could not read coverage for '" << Function.Name 625 << " from " << SourceFile; 626 outs() << "\n"; 627 continue; 628 } 629 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) 630 << Function.Name << " from " << SourceFile << ":"; 631 outs() << "\n"; 632 mainView->render(outs(), /*WholeFile=*/false); 633 if (FunctionMappingRecords.size() > 1) 634 outs() << "\n"; 635 } 636 return 0; 637 } 638 639 // Show files 640 bool ShowFilenames = SourceFiles.size() != 1; 641 642 if (SourceFiles.empty()) 643 // Get the source files from the function coverage mapping 644 for (StringRef Filename : getUniqueFilenames(FunctionMappingRecords)) 645 SourceFiles.push_back(Filename); 646 647 for (const auto &SourceFile : SourceFiles) { 648 auto mainView = createSourceFileView(SourceFile, FunctionMappingRecords); 649 if (!mainView) { 650 ViewOpts.colored_ostream(outs(), raw_ostream::RED) 651 << "warning: The file '" << SourceFile << "' isn't covered."; 652 outs() << "\n"; 653 continue; 654 } 655 656 if (ShowFilenames) { 657 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":"; 658 outs() << "\n"; 659 } 660 mainView->render(outs(), /*Wholefile=*/true); 661 if (SourceFiles.size() > 1) 662 outs() << "\n"; 663 } 664 665 return 0; 666 } 667 668 int CodeCoverageTool::report(int argc, const char **argv, 669 CommandLineParserType commandLineParser) { 670 cl::opt<bool> NoColors("no-colors", cl::Optional, 671 cl::desc("Don't show text colors"), cl::init(false)); 672 673 auto Err = commandLineParser(argc, argv); 674 if (Err) 675 return Err; 676 677 ViewOpts.Colors = !NoColors; 678 679 if (load()) 680 return 1; 681 682 CoverageSummary Summarizer; 683 Summarizer.createSummaries(FunctionMappingRecords); 684 CoverageReport Report(ViewOpts, Summarizer); 685 if (SourceFiles.empty() && Filters.empty()) { 686 Report.renderFileReports(llvm::outs()); 687 return 0; 688 } 689 690 Report.renderFunctionReports(llvm::outs()); 691 return 0; 692 } 693 694 int show_main(int argc, const char **argv) { 695 CodeCoverageTool Tool; 696 return Tool.run(CodeCoverageTool::Show, argc, argv); 697 } 698 699 int report_main(int argc, const char **argv) { 700 CodeCoverageTool Tool; 701 return Tool.run(CodeCoverageTool::Report, argc, argv); 702 } 703