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