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 /// \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 &CR : Function.CountedRegions) { 201 if (CR.FileID != FileID) 202 continue; 203 LineStart = std::min(CR.LineStart, LineStart); 204 LineEnd = std::max(CR.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 &CR : Function.CountedRegions) { 240 if (CR.Kind == CounterMappingRegion::ExpansionRegion && 241 FilenameEquivalence[CR.FileID]) 242 IsExpandedFile[CR.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 &CR : Function.CountedRegions) { 258 if (CR.Kind == CounterMappingRegion::ExpansionRegion) 259 IsExpandedFile[CR.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 CountedRegion &ExpandedRegion, 272 const FunctionCoverageMapping &Function, SourceCoverageView &Parent) { 273 auto ExpandedLines = findExpandedFileInterestingLineRange( 274 ExpandedRegion.ExpandedFileID, Function); 275 if (ViewOpts.Debug) 276 llvm::errs() << "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>(SourceBuffer.get(), 286 Parent.getOptions()); 287 SourceCoverageDataManager RegionManager; 288 for (const auto &CR : Function.CountedRegions) { 289 if (CR.FileID == ExpandedRegion.ExpandedFileID) 290 RegionManager.insert(CR); 291 } 292 SubView->load(RegionManager); 293 createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function); 294 Parent.addExpansion(ExpandedRegion, std::move(SubView)); 295 } 296 297 void CodeCoverageTool::createExpansionSubViews( 298 SourceCoverageView &View, unsigned ViewFileID, 299 const FunctionCoverageMapping &Function) { 300 if (!ViewOpts.ShowExpandedRegions) 301 return; 302 for (const auto &CR : Function.CountedRegions) { 303 if (CR.Kind != CounterMappingRegion::ExpansionRegion) 304 continue; 305 if (CR.FileID != ViewFileID) 306 continue; 307 createExpansionSubView(CR, Function, View); 308 } 309 } 310 311 void CodeCoverageTool::createInstantiationSubView( 312 StringRef SourceFile, const FunctionCoverageMapping &Function, 313 SourceCoverageView &View) { 314 SourceCoverageDataManager RegionManager; 315 SmallSet<unsigned, 8> InterestingFileIDs; 316 if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs)) 317 return; 318 // Get the interesting regions 319 for (const auto &CR : Function.CountedRegions) { 320 if (InterestingFileIDs.count(CR.FileID)) 321 RegionManager.insert(CR); 322 } 323 View.load(RegionManager); 324 unsigned MainFileID; 325 if (findMainViewFileID(SourceFile, Function, MainFileID)) 326 return; 327 createExpansionSubViews(View, MainFileID, Function); 328 } 329 330 bool CodeCoverageTool::createSourceFileView( 331 StringRef SourceFile, SourceCoverageView &View, 332 ArrayRef<FunctionCoverageMapping> FunctionMappingRecords, 333 bool UseOnlyRegionsInMainFile) { 334 SourceCoverageDataManager RegionManager; 335 FunctionInstantiationSetCollector InstantiationSetCollector; 336 337 for (const auto &Function : FunctionMappingRecords) { 338 unsigned MainFileID; 339 if (findMainViewFileID(SourceFile, Function, MainFileID)) 340 continue; 341 SmallSet<unsigned, 8> InterestingFileIDs; 342 if (UseOnlyRegionsInMainFile) { 343 InterestingFileIDs.insert(MainFileID); 344 } else if (!gatherInterestingFileIDs(SourceFile, Function, 345 InterestingFileIDs)) 346 continue; 347 // Get the interesting regions 348 for (const auto &CR : Function.CountedRegions) { 349 if (InterestingFileIDs.count(CR.FileID)) 350 RegionManager.insert(CR); 351 } 352 InstantiationSetCollector.insert(Function, MainFileID); 353 createExpansionSubViews(View, MainFileID, Function); 354 } 355 if (RegionManager.getSourceRegions().empty()) 356 return true; 357 View.load(RegionManager); 358 // Show instantiations 359 if (!ViewOpts.ShowFunctionInstantiations) 360 return false; 361 for (const auto &InstantiationSet : InstantiationSetCollector) { 362 if (InstantiationSet.second.size() < 2) 363 continue; 364 for (auto Function : InstantiationSet.second) { 365 unsigned FileID = Function->CountedRegions.front().FileID; 366 unsigned Line = 0; 367 for (const auto &CR : Function->CountedRegions) 368 if (CR.FileID == FileID) 369 Line = std::max(CR.LineEnd, Line); 370 auto SourceBuffer = getSourceFile(Function->Filenames[FileID]); 371 if (!SourceBuffer) 372 continue; 373 auto SubView = llvm::make_unique<SourceCoverageView>(SourceBuffer.get(), 374 View.getOptions()); 375 createInstantiationSubView(SourceFile, *Function, *SubView); 376 View.addInstantiation(Function->Name, Line, std::move(SubView)); 377 } 378 } 379 return false; 380 } 381 382 bool CodeCoverageTool::load() { 383 auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename); 384 if (auto EC = CounterMappingBuff.getError()) { 385 error(EC.message(), ObjectFilename); 386 return true; 387 } 388 ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get()); 389 if (auto EC = MappingReader.readHeader()) { 390 error(EC.message(), ObjectFilename); 391 return true; 392 } 393 394 std::vector<uint64_t> Counts; 395 for (const auto &I : MappingReader) { 396 FunctionCoverageMapping Function(I.FunctionName, I.Filenames); 397 398 // Create the mapping regions with evaluated execution counts 399 Counts.clear(); 400 PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts); 401 402 // Get the biggest referenced counters 403 bool RegionError = false; 404 CounterMappingContext Ctx(I.Expressions, Counts); 405 for (const auto &R : I.MappingRegions) { 406 // Compute the values of mapped regions 407 if (ViewOpts.Debug) { 408 errs() << "File " << R.FileID << "| " << R.LineStart << ":" 409 << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd 410 << " = "; 411 Ctx.dump(R.Count); 412 if (R.Kind == CounterMappingRegion::ExpansionRegion) { 413 errs() << " (Expanded file id = " << R.ExpandedFileID << ") "; 414 } 415 errs() << "\n"; 416 } 417 ErrorOr<int64_t> ExecutionCount = Ctx.evaluate(R.Count); 418 if (ExecutionCount) { 419 Function.CountedRegions.push_back(CountedRegion(R, *ExecutionCount)); 420 } else if (!RegionError) { 421 colored_ostream(errs(), raw_ostream::RED) 422 << "error: Regions and counters don't match in a function '" 423 << Function.Name << "' (re-run the instrumented binary)."; 424 errs() << "\n"; 425 RegionError = true; 426 } 427 } 428 429 if (RegionError || !Filters.matches(Function)) 430 continue; 431 432 FunctionMappingRecords.push_back(Function); 433 } 434 return false; 435 } 436 437 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 438 // Print a stack trace if we signal out. 439 sys::PrintStackTraceOnErrorSignal(); 440 PrettyStackTraceProgram X(argc, argv); 441 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 442 443 cl::list<std::string> InputSourceFiles( 444 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); 445 446 cl::opt<std::string> PGOFilename( 447 "instr-profile", cl::Required, 448 cl::desc( 449 "File with the profile data obtained after an instrumented run")); 450 451 cl::opt<bool> DebugDump("dump", cl::Optional, 452 cl::desc("Show internal debug dump")); 453 454 cl::opt<bool> FilenameEquivalence( 455 "filename-equivalence", cl::Optional, 456 cl::desc("Compare the filenames instead of full filepaths")); 457 458 cl::OptionCategory FilteringCategory("Function filtering options"); 459 460 cl::list<std::string> NameFilters( 461 "name", cl::Optional, 462 cl::desc("Show code coverage only for functions with the given name"), 463 cl::ZeroOrMore, cl::cat(FilteringCategory)); 464 465 cl::list<std::string> NameRegexFilters( 466 "name-regex", cl::Optional, 467 cl::desc("Show code coverage only for functions that match the given " 468 "regular expression"), 469 cl::ZeroOrMore, cl::cat(FilteringCategory)); 470 471 cl::opt<double> RegionCoverageLtFilter( 472 "region-coverage-lt", cl::Optional, 473 cl::desc("Show code coverage only for functions with region coverage " 474 "less than the given threshold"), 475 cl::cat(FilteringCategory)); 476 477 cl::opt<double> RegionCoverageGtFilter( 478 "region-coverage-gt", cl::Optional, 479 cl::desc("Show code coverage only for functions with region coverage " 480 "greater than the given threshold"), 481 cl::cat(FilteringCategory)); 482 483 cl::opt<double> LineCoverageLtFilter( 484 "line-coverage-lt", cl::Optional, 485 cl::desc("Show code coverage only for functions with line coverage less " 486 "than the given threshold"), 487 cl::cat(FilteringCategory)); 488 489 cl::opt<double> LineCoverageGtFilter( 490 "line-coverage-gt", cl::Optional, 491 cl::desc("Show code coverage only for functions with line coverage " 492 "greater than the given threshold"), 493 cl::cat(FilteringCategory)); 494 495 auto commandLineParser = [&, this](int argc, const char **argv) -> int { 496 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 497 ViewOpts.Debug = DebugDump; 498 CompareFilenamesOnly = FilenameEquivalence; 499 500 if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) { 501 error(EC.message(), PGOFilename); 502 return 1; 503 } 504 505 // Create the function filters 506 if (!NameFilters.empty() || !NameRegexFilters.empty()) { 507 auto NameFilterer = new CoverageFilters; 508 for (const auto &Name : NameFilters) 509 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); 510 for (const auto &Regex : NameRegexFilters) 511 NameFilterer->push_back( 512 llvm::make_unique<NameRegexCoverageFilter>(Regex)); 513 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); 514 } 515 if (RegionCoverageLtFilter.getNumOccurrences() || 516 RegionCoverageGtFilter.getNumOccurrences() || 517 LineCoverageLtFilter.getNumOccurrences() || 518 LineCoverageGtFilter.getNumOccurrences()) { 519 auto StatFilterer = new CoverageFilters; 520 if (RegionCoverageLtFilter.getNumOccurrences()) 521 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 522 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 523 if (RegionCoverageGtFilter.getNumOccurrences()) 524 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 525 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 526 if (LineCoverageLtFilter.getNumOccurrences()) 527 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 528 LineCoverageFilter::LessThan, LineCoverageLtFilter)); 529 if (LineCoverageGtFilter.getNumOccurrences()) 530 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 531 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 532 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); 533 } 534 535 SourceFiles = InputSourceFiles; 536 return 0; 537 }; 538 539 // Parse the object filename 540 if (argc > 1) { 541 StringRef Arg(argv[1]); 542 if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) { 543 cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n"); 544 return 0; 545 } 546 ObjectFilename = Arg; 547 548 argv[1] = argv[0]; 549 --argc; 550 ++argv; 551 } else { 552 errs() << sys::path::filename(argv[0]) << ": No executable file given!\n"; 553 return 1; 554 } 555 556 switch (Cmd) { 557 case Show: 558 return show(argc, argv, commandLineParser); 559 case Report: 560 return report(argc, argv, commandLineParser); 561 } 562 return 0; 563 } 564 565 int CodeCoverageTool::show(int argc, const char **argv, 566 CommandLineParserType commandLineParser) { 567 568 cl::OptionCategory ViewCategory("Viewing options"); 569 570 cl::opt<bool> ShowLineExecutionCounts( 571 "show-line-counts", cl::Optional, 572 cl::desc("Show the execution counts for each line"), cl::init(true), 573 cl::cat(ViewCategory)); 574 575 cl::opt<bool> ShowRegions( 576 "show-regions", cl::Optional, 577 cl::desc("Show the execution counts for each region"), 578 cl::cat(ViewCategory)); 579 580 cl::opt<bool> ShowBestLineRegionsCounts( 581 "show-line-counts-or-regions", cl::Optional, 582 cl::desc("Show the execution counts for each line, or the execution " 583 "counts for each region on lines that have multiple regions"), 584 cl::cat(ViewCategory)); 585 586 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 587 cl::desc("Show expanded source regions"), 588 cl::cat(ViewCategory)); 589 590 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 591 cl::desc("Show function instantiations"), 592 cl::cat(ViewCategory)); 593 594 cl::opt<bool> NoColors("no-colors", cl::Optional, 595 cl::desc("Don't show text colors"), cl::init(false), 596 cl::cat(ViewCategory)); 597 598 auto Err = commandLineParser(argc, argv); 599 if (Err) 600 return Err; 601 602 ViewOpts.Colors = !NoColors; 603 ViewOpts.ShowLineNumbers = true; 604 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 605 !ShowRegions || ShowBestLineRegionsCounts; 606 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 607 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; 608 ViewOpts.ShowExpandedRegions = ShowExpansions; 609 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 610 611 if (load()) 612 return 1; 613 614 if (!Filters.empty()) { 615 // Show functions 616 for (const auto &Function : FunctionMappingRecords) { 617 unsigned MainFileID; 618 if (findMainViewFileID(Function, MainFileID)) 619 continue; 620 StringRef SourceFile = Function.Filenames[MainFileID]; 621 auto SourceBuffer = getSourceFile(SourceFile); 622 if (!SourceBuffer) 623 return 1; 624 SourceCoverageView mainView(SourceBuffer.get(), ViewOpts); 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