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