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