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 ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count); 385 if (ExecutionCount) { 386 Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount)); 387 } else if (!RegionError) { 388 colored_ostream(errs(), raw_ostream::RED) 389 << "error: Regions and counters don't match in a function '" 390 << Function.Name << "' (re-run the instrumented binary)."; 391 errs() << "\n"; 392 RegionError = true; 393 } 394 } 395 396 if (RegionError || !Filters.matches(Function)) 397 continue; 398 399 FunctionMappingRecords.push_back(Function); 400 } 401 402 if (CompareFilenamesOnly) { 403 auto CoveredFiles = getUniqueFilenames(FunctionMappingRecords); 404 for (auto &SF : SourceFiles) { 405 StringRef SFBase = sys::path::filename(SF); 406 for (const auto &CF : CoveredFiles) 407 if (SFBase == sys::path::filename(CF)) { 408 RemappedFilenames[CF] = SF; 409 SF = CF; 410 break; 411 } 412 } 413 } 414 415 return false; 416 } 417 418 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 419 // Print a stack trace if we signal out. 420 sys::PrintStackTraceOnErrorSignal(); 421 PrettyStackTraceProgram X(argc, argv); 422 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 423 424 cl::list<std::string> InputSourceFiles( 425 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); 426 427 cl::opt<std::string> PGOFilename( 428 "instr-profile", cl::Required, 429 cl::desc( 430 "File with the profile data obtained after an instrumented run")); 431 432 cl::opt<bool> DebugDump("dump", cl::Optional, 433 cl::desc("Show internal debug dump")); 434 435 cl::opt<bool> FilenameEquivalence( 436 "filename-equivalence", cl::Optional, 437 cl::desc("Treat source files as equivalent to paths in the coverage data " 438 "when the file names match, even if the full paths do not")); 439 440 cl::OptionCategory FilteringCategory("Function filtering options"); 441 442 cl::list<std::string> NameFilters( 443 "name", cl::Optional, 444 cl::desc("Show code coverage only for functions with the given name"), 445 cl::ZeroOrMore, cl::cat(FilteringCategory)); 446 447 cl::list<std::string> NameRegexFilters( 448 "name-regex", cl::Optional, 449 cl::desc("Show code coverage only for functions that match the given " 450 "regular expression"), 451 cl::ZeroOrMore, cl::cat(FilteringCategory)); 452 453 cl::opt<double> RegionCoverageLtFilter( 454 "region-coverage-lt", cl::Optional, 455 cl::desc("Show code coverage only for functions with region coverage " 456 "less than the given threshold"), 457 cl::cat(FilteringCategory)); 458 459 cl::opt<double> RegionCoverageGtFilter( 460 "region-coverage-gt", cl::Optional, 461 cl::desc("Show code coverage only for functions with region coverage " 462 "greater than the given threshold"), 463 cl::cat(FilteringCategory)); 464 465 cl::opt<double> LineCoverageLtFilter( 466 "line-coverage-lt", cl::Optional, 467 cl::desc("Show code coverage only for functions with line coverage less " 468 "than the given threshold"), 469 cl::cat(FilteringCategory)); 470 471 cl::opt<double> LineCoverageGtFilter( 472 "line-coverage-gt", cl::Optional, 473 cl::desc("Show code coverage only for functions with line coverage " 474 "greater than the given threshold"), 475 cl::cat(FilteringCategory)); 476 477 auto commandLineParser = [&, this](int argc, const char **argv) -> int { 478 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 479 ViewOpts.Debug = DebugDump; 480 CompareFilenamesOnly = FilenameEquivalence; 481 482 if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) { 483 error(EC.message(), PGOFilename); 484 return 1; 485 } 486 487 // Create the function filters 488 if (!NameFilters.empty() || !NameRegexFilters.empty()) { 489 auto NameFilterer = new CoverageFilters; 490 for (const auto &Name : NameFilters) 491 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); 492 for (const auto &Regex : NameRegexFilters) 493 NameFilterer->push_back( 494 llvm::make_unique<NameRegexCoverageFilter>(Regex)); 495 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); 496 } 497 if (RegionCoverageLtFilter.getNumOccurrences() || 498 RegionCoverageGtFilter.getNumOccurrences() || 499 LineCoverageLtFilter.getNumOccurrences() || 500 LineCoverageGtFilter.getNumOccurrences()) { 501 auto StatFilterer = new CoverageFilters; 502 if (RegionCoverageLtFilter.getNumOccurrences()) 503 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 504 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 505 if (RegionCoverageGtFilter.getNumOccurrences()) 506 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 507 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 508 if (LineCoverageLtFilter.getNumOccurrences()) 509 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 510 LineCoverageFilter::LessThan, LineCoverageLtFilter)); 511 if (LineCoverageGtFilter.getNumOccurrences()) 512 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 513 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 514 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); 515 } 516 517 for (const auto &File : InputSourceFiles) { 518 SmallString<128> Path(File); 519 if (std::error_code EC = sys::fs::make_absolute(Path)) { 520 errs() << "error: " << File << ": " << EC.message(); 521 return 1; 522 } 523 SourceFiles.push_back(Path.str()); 524 } 525 return 0; 526 }; 527 528 // Parse the object filename 529 if (argc > 1) { 530 StringRef Arg(argv[1]); 531 if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) { 532 cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n"); 533 return 0; 534 } 535 ObjectFilename = Arg; 536 537 argv[1] = argv[0]; 538 --argc; 539 ++argv; 540 } else { 541 errs() << sys::path::filename(argv[0]) << ": No executable file given!\n"; 542 return 1; 543 } 544 545 switch (Cmd) { 546 case Show: 547 return show(argc, argv, commandLineParser); 548 case Report: 549 return report(argc, argv, commandLineParser); 550 } 551 return 0; 552 } 553 554 int CodeCoverageTool::show(int argc, const char **argv, 555 CommandLineParserType commandLineParser) { 556 557 cl::OptionCategory ViewCategory("Viewing options"); 558 559 cl::opt<bool> ShowLineExecutionCounts( 560 "show-line-counts", cl::Optional, 561 cl::desc("Show the execution counts for each line"), cl::init(true), 562 cl::cat(ViewCategory)); 563 564 cl::opt<bool> ShowRegions( 565 "show-regions", cl::Optional, 566 cl::desc("Show the execution counts for each region"), 567 cl::cat(ViewCategory)); 568 569 cl::opt<bool> ShowBestLineRegionsCounts( 570 "show-line-counts-or-regions", cl::Optional, 571 cl::desc("Show the execution counts for each line, or the execution " 572 "counts for each region on lines that have multiple regions"), 573 cl::cat(ViewCategory)); 574 575 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 576 cl::desc("Show expanded source regions"), 577 cl::cat(ViewCategory)); 578 579 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 580 cl::desc("Show function instantiations"), 581 cl::cat(ViewCategory)); 582 583 cl::opt<bool> NoColors("no-colors", cl::Optional, 584 cl::desc("Don't show text colors"), cl::init(false), 585 cl::cat(ViewCategory)); 586 587 auto Err = commandLineParser(argc, argv); 588 if (Err) 589 return Err; 590 591 ViewOpts.Colors = !NoColors; 592 ViewOpts.ShowLineNumbers = true; 593 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 594 !ShowRegions || ShowBestLineRegionsCounts; 595 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 596 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; 597 ViewOpts.ShowExpandedRegions = ShowExpansions; 598 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 599 600 if (load()) 601 return 1; 602 603 if (!Filters.empty()) { 604 // Show functions 605 for (const auto &Function : FunctionMappingRecords) { 606 unsigned MainFileID; 607 if (findMainViewFileID(Function, MainFileID)) 608 continue; 609 StringRef SourceFile = Function.Filenames[MainFileID]; 610 auto mainView = createSourceFileView(SourceFile, Function, true); 611 if (!mainView) { 612 ViewOpts.colored_ostream(outs(), raw_ostream::RED) 613 << "warning: Could not read coverage for '" << Function.Name 614 << " from " << SourceFile; 615 outs() << "\n"; 616 continue; 617 } 618 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) 619 << Function.Name << " from " << SourceFile << ":"; 620 outs() << "\n"; 621 mainView->render(outs(), /*WholeFile=*/false); 622 if (FunctionMappingRecords.size() > 1) 623 outs() << "\n"; 624 } 625 return 0; 626 } 627 628 // Show files 629 bool ShowFilenames = SourceFiles.size() != 1; 630 631 if (SourceFiles.empty()) 632 // Get the source files from the function coverage mapping 633 for (StringRef Filename : getUniqueFilenames(FunctionMappingRecords)) 634 SourceFiles.push_back(Filename); 635 636 for (const auto &SourceFile : SourceFiles) { 637 auto mainView = createSourceFileView(SourceFile, FunctionMappingRecords); 638 if (!mainView) { 639 ViewOpts.colored_ostream(outs(), raw_ostream::RED) 640 << "warning: The file '" << SourceFile << "' isn't covered."; 641 outs() << "\n"; 642 continue; 643 } 644 645 if (ShowFilenames) { 646 ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":"; 647 outs() << "\n"; 648 } 649 mainView->render(outs(), /*Wholefile=*/true); 650 if (SourceFiles.size() > 1) 651 outs() << "\n"; 652 } 653 654 return 0; 655 } 656 657 int CodeCoverageTool::report(int argc, const char **argv, 658 CommandLineParserType commandLineParser) { 659 cl::opt<bool> NoColors("no-colors", cl::Optional, 660 cl::desc("Don't show text colors"), cl::init(false)); 661 662 auto Err = commandLineParser(argc, argv); 663 if (Err) 664 return Err; 665 666 ViewOpts.Colors = !NoColors; 667 668 if (load()) 669 return 1; 670 671 CoverageSummary Summarizer; 672 Summarizer.createSummaries(FunctionMappingRecords); 673 CoverageReport Report(ViewOpts, Summarizer); 674 if (SourceFiles.empty() && Filters.empty()) { 675 Report.renderFileReports(llvm::outs()); 676 return 0; 677 } 678 679 Report.renderFunctionReports(llvm::outs()); 680 return 0; 681 } 682 683 int show_main(int argc, const char **argv) { 684 CodeCoverageTool Tool; 685 return Tool.run(CodeCoverageTool::Show, argc, argv); 686 } 687 688 int report_main(int argc, const char **argv) { 689 CodeCoverageTool Tool; 690 return Tool.run(CodeCoverageTool::Report, argc, argv); 691 } 692