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