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