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 "CoverageFilters.h" 17 #include "CoverageReport.h" 18 #include "CoverageViewOptions.h" 19 #include "RenderingSupport.h" 20 #include "SourceCoverageView.h" 21 #include "llvm/ADT/SmallString.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/ADT/Triple.h" 24 #include "llvm/ProfileData/Coverage/CoverageMapping.h" 25 #include "llvm/ProfileData/InstrProfReader.h" 26 #include "llvm/Support/CommandLine.h" 27 #include "llvm/Support/FileSystem.h" 28 #include "llvm/Support/Format.h" 29 #include "llvm/Support/Path.h" 30 #include "llvm/Support/Process.h" 31 #include <functional> 32 #include <system_error> 33 34 using namespace llvm; 35 using namespace coverage; 36 37 namespace { 38 /// \brief The implementation of the coverage tool. 39 class CodeCoverageTool { 40 public: 41 enum Command { 42 /// \brief The show command. 43 Show, 44 /// \brief The report command. 45 Report 46 }; 47 48 /// \brief Print the error message to the error output stream. 49 void error(const Twine &Message, StringRef Whence = ""); 50 51 /// \brief Append a reference to a private copy of \p Path into SourceFiles. 52 void addCollectedPath(const std::string &Path); 53 54 /// \brief Return a memory buffer for the given source file. 55 ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile); 56 57 /// \brief Create source views for the expansions of the view. 58 void attachExpansionSubViews(SourceCoverageView &View, 59 ArrayRef<ExpansionRecord> Expansions, 60 CoverageMapping &Coverage); 61 62 /// \brief Create the source view of a particular function. 63 std::unique_ptr<SourceCoverageView> 64 createFunctionView(const FunctionRecord &Function, CoverageMapping &Coverage); 65 66 /// \brief Create the main source view of a particular source file. 67 std::unique_ptr<SourceCoverageView> 68 createSourceFileView(StringRef SourceFile, CoverageMapping &Coverage); 69 70 /// \brief Load the coverage mapping data. Return true if an error occured. 71 std::unique_ptr<CoverageMapping> load(); 72 73 int run(Command Cmd, int argc, const char **argv); 74 75 typedef llvm::function_ref<int(int, const char **)> CommandLineParserType; 76 77 int show(int argc, const char **argv, 78 CommandLineParserType commandLineParser); 79 80 int report(int argc, const char **argv, 81 CommandLineParserType commandLineParser); 82 83 std::string ObjectFilename; 84 CoverageViewOptions ViewOpts; 85 std::string PGOFilename; 86 CoverageFiltersMatchAll Filters; 87 std::vector<StringRef> SourceFiles; 88 std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>> 89 LoadedSourceFiles; 90 bool CompareFilenamesOnly; 91 StringMap<std::string> RemappedFilenames; 92 std::string CoverageArch; 93 94 private: 95 std::vector<std::string> CollectedPaths; 96 }; 97 } 98 99 void CodeCoverageTool::error(const Twine &Message, StringRef Whence) { 100 errs() << "error: "; 101 if (!Whence.empty()) 102 errs() << Whence << ": "; 103 errs() << Message << "\n"; 104 } 105 106 void CodeCoverageTool::addCollectedPath(const std::string &Path) { 107 CollectedPaths.push_back(Path); 108 SourceFiles.emplace_back(CollectedPaths.back()); 109 } 110 111 ErrorOr<const MemoryBuffer &> 112 CodeCoverageTool::getSourceFile(StringRef SourceFile) { 113 // If we've remapped filenames, look up the real location for this file. 114 if (!RemappedFilenames.empty()) { 115 auto Loc = RemappedFilenames.find(SourceFile); 116 if (Loc != RemappedFilenames.end()) 117 SourceFile = Loc->second; 118 } 119 for (const auto &Files : LoadedSourceFiles) 120 if (sys::fs::equivalent(SourceFile, Files.first)) 121 return *Files.second; 122 auto Buffer = MemoryBuffer::getFile(SourceFile); 123 if (auto EC = Buffer.getError()) { 124 error(EC.message(), SourceFile); 125 return EC; 126 } 127 LoadedSourceFiles.emplace_back(SourceFile, std::move(Buffer.get())); 128 return *LoadedSourceFiles.back().second; 129 } 130 131 void 132 CodeCoverageTool::attachExpansionSubViews(SourceCoverageView &View, 133 ArrayRef<ExpansionRecord> Expansions, 134 CoverageMapping &Coverage) { 135 if (!ViewOpts.ShowExpandedRegions) 136 return; 137 for (const auto &Expansion : Expansions) { 138 auto ExpansionCoverage = Coverage.getCoverageForExpansion(Expansion); 139 if (ExpansionCoverage.empty()) 140 continue; 141 auto SourceBuffer = getSourceFile(ExpansionCoverage.getFilename()); 142 if (!SourceBuffer) 143 continue; 144 145 auto SubViewExpansions = ExpansionCoverage.getExpansions(); 146 auto SubView = 147 SourceCoverageView::create(Expansion.Function.Name, SourceBuffer.get(), 148 ViewOpts, std::move(ExpansionCoverage)); 149 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 150 View.addExpansion(Expansion.Region, std::move(SubView)); 151 } 152 } 153 154 std::unique_ptr<SourceCoverageView> 155 CodeCoverageTool::createFunctionView(const FunctionRecord &Function, 156 CoverageMapping &Coverage) { 157 auto FunctionCoverage = Coverage.getCoverageForFunction(Function); 158 if (FunctionCoverage.empty()) 159 return nullptr; 160 auto SourceBuffer = getSourceFile(FunctionCoverage.getFilename()); 161 if (!SourceBuffer) 162 return nullptr; 163 164 auto Expansions = FunctionCoverage.getExpansions(); 165 auto View = SourceCoverageView::create(Function.Name, SourceBuffer.get(), 166 ViewOpts, std::move(FunctionCoverage)); 167 attachExpansionSubViews(*View, Expansions, Coverage); 168 169 return View; 170 } 171 172 std::unique_ptr<SourceCoverageView> 173 CodeCoverageTool::createSourceFileView(StringRef SourceFile, 174 CoverageMapping &Coverage) { 175 auto SourceBuffer = getSourceFile(SourceFile); 176 if (!SourceBuffer) 177 return nullptr; 178 auto FileCoverage = Coverage.getCoverageForFile(SourceFile); 179 if (FileCoverage.empty()) 180 return nullptr; 181 182 auto Expansions = FileCoverage.getExpansions(); 183 auto View = SourceCoverageView::create(SourceFile, SourceBuffer.get(), 184 ViewOpts, std::move(FileCoverage)); 185 attachExpansionSubViews(*View, Expansions, Coverage); 186 187 for (auto Function : Coverage.getInstantiations(SourceFile)) { 188 auto SubViewCoverage = Coverage.getCoverageForFunction(*Function); 189 auto SubViewExpansions = SubViewCoverage.getExpansions(); 190 auto SubView = 191 SourceCoverageView::create(Function->Name, SourceBuffer.get(), ViewOpts, 192 std::move(SubViewCoverage)); 193 attachExpansionSubViews(*SubView, SubViewExpansions, Coverage); 194 195 if (SubView) { 196 unsigned FileID = Function->CountedRegions.front().FileID; 197 unsigned Line = 0; 198 for (const auto &CR : Function->CountedRegions) 199 if (CR.FileID == FileID) 200 Line = std::max(CR.LineEnd, Line); 201 View->addInstantiation(Function->Name, Line, std::move(SubView)); 202 } 203 } 204 return View; 205 } 206 207 static bool modifiedTimeGT(StringRef LHS, StringRef RHS) { 208 sys::fs::file_status Status; 209 if (sys::fs::status(LHS, Status)) 210 return false; 211 auto LHSTime = Status.getLastModificationTime(); 212 if (sys::fs::status(RHS, Status)) 213 return false; 214 auto RHSTime = Status.getLastModificationTime(); 215 return LHSTime > RHSTime; 216 } 217 218 std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { 219 if (modifiedTimeGT(ObjectFilename, PGOFilename)) 220 errs() << "warning: profile data may be out of date - object is newer\n"; 221 auto CoverageOrErr = CoverageMapping::load(ObjectFilename, PGOFilename, 222 CoverageArch); 223 if (Error E = CoverageOrErr.takeError()) { 224 colored_ostream(errs(), raw_ostream::RED) 225 << "error: Failed to load coverage: " << toString(std::move(E)) << "\n"; 226 return nullptr; 227 } 228 auto Coverage = std::move(CoverageOrErr.get()); 229 unsigned Mismatched = Coverage->getMismatchedCount(); 230 if (Mismatched) { 231 colored_ostream(errs(), raw_ostream::RED) 232 << "warning: " << Mismatched << " functions have mismatched data. "; 233 errs() << "\n"; 234 } 235 236 if (CompareFilenamesOnly) { 237 auto CoveredFiles = Coverage.get()->getUniqueSourceFiles(); 238 for (auto &SF : SourceFiles) { 239 StringRef SFBase = sys::path::filename(SF); 240 for (const auto &CF : CoveredFiles) 241 if (SFBase == sys::path::filename(CF)) { 242 RemappedFilenames[CF] = SF; 243 SF = CF; 244 break; 245 } 246 } 247 } 248 249 return Coverage; 250 } 251 252 int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { 253 cl::opt<std::string, true> ObjectFilename( 254 cl::Positional, cl::Required, cl::location(this->ObjectFilename), 255 cl::desc("Covered executable or object file.")); 256 257 cl::list<std::string> InputSourceFiles( 258 cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore); 259 260 cl::opt<std::string, true> PGOFilename( 261 "instr-profile", cl::Required, cl::location(this->PGOFilename), 262 cl::desc( 263 "File with the profile data obtained after an instrumented run")); 264 265 cl::opt<std::string> Arch( 266 "arch", cl::desc("architecture of the coverage mapping binary")); 267 268 cl::opt<bool> DebugDump("dump", cl::Optional, 269 cl::desc("Show internal debug dump")); 270 271 cl::opt<CoverageViewOptions::OutputFormat> Format( 272 "format", cl::desc("Output format for line-based coverage reports"), 273 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", 274 "Text output"), 275 clEnumValEnd), 276 cl::init(CoverageViewOptions::OutputFormat::Text)); 277 278 cl::opt<bool> FilenameEquivalence( 279 "filename-equivalence", cl::Optional, 280 cl::desc("Treat source files as equivalent to paths in the coverage data " 281 "when the file names match, even if the full paths do not")); 282 283 cl::OptionCategory FilteringCategory("Function filtering options"); 284 285 cl::list<std::string> NameFilters( 286 "name", cl::Optional, 287 cl::desc("Show code coverage only for functions with the given name"), 288 cl::ZeroOrMore, cl::cat(FilteringCategory)); 289 290 cl::list<std::string> NameRegexFilters( 291 "name-regex", cl::Optional, 292 cl::desc("Show code coverage only for functions that match the given " 293 "regular expression"), 294 cl::ZeroOrMore, cl::cat(FilteringCategory)); 295 296 cl::opt<double> RegionCoverageLtFilter( 297 "region-coverage-lt", cl::Optional, 298 cl::desc("Show code coverage only for functions with region coverage " 299 "less than the given threshold"), 300 cl::cat(FilteringCategory)); 301 302 cl::opt<double> RegionCoverageGtFilter( 303 "region-coverage-gt", cl::Optional, 304 cl::desc("Show code coverage only for functions with region coverage " 305 "greater than the given threshold"), 306 cl::cat(FilteringCategory)); 307 308 cl::opt<double> LineCoverageLtFilter( 309 "line-coverage-lt", cl::Optional, 310 cl::desc("Show code coverage only for functions with line coverage less " 311 "than the given threshold"), 312 cl::cat(FilteringCategory)); 313 314 cl::opt<double> LineCoverageGtFilter( 315 "line-coverage-gt", cl::Optional, 316 cl::desc("Show code coverage only for functions with line coverage " 317 "greater than the given threshold"), 318 cl::cat(FilteringCategory)); 319 320 cl::opt<cl::boolOrDefault> UseColor( 321 "use-color", cl::desc("Emit colored output (default=autodetect)"), 322 cl::init(cl::BOU_UNSET)); 323 324 auto commandLineParser = [&, this](int argc, const char **argv) -> int { 325 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 326 ViewOpts.Debug = DebugDump; 327 CompareFilenamesOnly = FilenameEquivalence; 328 329 ViewOpts.Format = Format; 330 switch (ViewOpts.Format) { 331 case CoverageViewOptions::OutputFormat::Text: 332 ViewOpts.Colors = UseColor == cl::BOU_UNSET 333 ? sys::Process::StandardOutHasColors() 334 : UseColor == cl::BOU_TRUE; 335 break; 336 } 337 338 // Create the function filters 339 if (!NameFilters.empty() || !NameRegexFilters.empty()) { 340 auto NameFilterer = new CoverageFilters; 341 for (const auto &Name : NameFilters) 342 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); 343 for (const auto &Regex : NameRegexFilters) 344 NameFilterer->push_back( 345 llvm::make_unique<NameRegexCoverageFilter>(Regex)); 346 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); 347 } 348 if (RegionCoverageLtFilter.getNumOccurrences() || 349 RegionCoverageGtFilter.getNumOccurrences() || 350 LineCoverageLtFilter.getNumOccurrences() || 351 LineCoverageGtFilter.getNumOccurrences()) { 352 auto StatFilterer = new CoverageFilters; 353 if (RegionCoverageLtFilter.getNumOccurrences()) 354 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 355 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 356 if (RegionCoverageGtFilter.getNumOccurrences()) 357 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 358 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 359 if (LineCoverageLtFilter.getNumOccurrences()) 360 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 361 LineCoverageFilter::LessThan, LineCoverageLtFilter)); 362 if (LineCoverageGtFilter.getNumOccurrences()) 363 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 364 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 365 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); 366 } 367 368 if (!Arch.empty() && 369 Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { 370 errs() << "error: Unknown architecture: " << Arch << "\n"; 371 return 1; 372 } 373 CoverageArch = Arch; 374 375 for (const auto &File : InputSourceFiles) { 376 SmallString<128> Path(File); 377 if (!CompareFilenamesOnly) 378 if (std::error_code EC = sys::fs::make_absolute(Path)) { 379 errs() << "error: " << File << ": " << EC.message(); 380 return 1; 381 } 382 addCollectedPath(Path.str()); 383 } 384 return 0; 385 }; 386 387 switch (Cmd) { 388 case Show: 389 return show(argc, argv, commandLineParser); 390 case Report: 391 return report(argc, argv, commandLineParser); 392 } 393 return 0; 394 } 395 396 int CodeCoverageTool::show(int argc, const char **argv, 397 CommandLineParserType commandLineParser) { 398 399 cl::OptionCategory ViewCategory("Viewing options"); 400 401 cl::opt<bool> ShowLineExecutionCounts( 402 "show-line-counts", cl::Optional, 403 cl::desc("Show the execution counts for each line"), cl::init(true), 404 cl::cat(ViewCategory)); 405 406 cl::opt<bool> ShowRegions( 407 "show-regions", cl::Optional, 408 cl::desc("Show the execution counts for each region"), 409 cl::cat(ViewCategory)); 410 411 cl::opt<bool> ShowBestLineRegionsCounts( 412 "show-line-counts-or-regions", cl::Optional, 413 cl::desc("Show the execution counts for each line, or the execution " 414 "counts for each region on lines that have multiple regions"), 415 cl::cat(ViewCategory)); 416 417 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 418 cl::desc("Show expanded source regions"), 419 cl::cat(ViewCategory)); 420 421 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 422 cl::desc("Show function instantiations"), 423 cl::cat(ViewCategory)); 424 425 cl::opt<std::string> ShowOutputDirectory( 426 "output-dir", cl::init(""), 427 cl::desc("Directory in which coverage information is written out")); 428 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), 429 cl::aliasopt(ShowOutputDirectory)); 430 431 auto Err = commandLineParser(argc, argv); 432 if (Err) 433 return Err; 434 435 ViewOpts.ShowLineNumbers = true; 436 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 437 !ShowRegions || ShowBestLineRegionsCounts; 438 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 439 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; 440 ViewOpts.ShowExpandedRegions = ShowExpansions; 441 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 442 ViewOpts.ShowOutputDirectory = ShowOutputDirectory; 443 444 if (ViewOpts.hasOutputDirectory()) { 445 if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { 446 error("Could not create output directory!", E.message()); 447 return 1; 448 } 449 } 450 451 auto Coverage = load(); 452 if (!Coverage) 453 return 1; 454 455 auto Printer = CoveragePrinter::create(ViewOpts); 456 457 if (!Filters.empty()) { 458 auto OSOrErr = Printer->createViewFile("functions", /*InToplevel=*/true); 459 if (Error E = OSOrErr.takeError()) { 460 error(toString(std::move(E))); 461 return 1; 462 } 463 auto OS = std::move(OSOrErr.get()); 464 465 // Show functions. 466 for (const auto &Function : Coverage->getCoveredFunctions()) { 467 if (!Filters.matches(Function)) 468 continue; 469 470 auto mainView = createFunctionView(Function, *Coverage); 471 if (!mainView) { 472 ViewOpts.colored_ostream(errs(), raw_ostream::RED) 473 << "warning: Could not read coverage for '" << Function.Name << "'." 474 << "\n"; 475 continue; 476 } 477 478 mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true); 479 } 480 481 Printer->closeViewFile(std::move(OS)); 482 return 0; 483 } 484 485 // Show files 486 bool ShowFilenames = SourceFiles.size() != 1; 487 488 if (SourceFiles.empty()) 489 // Get the source files from the function coverage mapping. 490 for (StringRef Filename : Coverage->getUniqueSourceFiles()) 491 SourceFiles.push_back(Filename); 492 493 // Create an index out of the source files. 494 if (ViewOpts.hasOutputDirectory()) { 495 if (Error E = Printer->createIndexFile(SourceFiles)) { 496 error(toString(std::move(E))); 497 return 1; 498 } 499 } 500 501 for (const auto &SourceFile : SourceFiles) { 502 auto mainView = createSourceFileView(SourceFile, *Coverage); 503 if (!mainView) { 504 ViewOpts.colored_ostream(errs(), raw_ostream::RED) 505 << "warning: The file '" << SourceFile << "' isn't covered."; 506 errs() << "\n"; 507 continue; 508 } 509 510 auto OSOrErr = Printer->createViewFile(SourceFile, /*InToplevel=*/false); 511 if (Error E = OSOrErr.takeError()) { 512 error(toString(std::move(E))); 513 return 1; 514 } 515 auto OS = std::move(OSOrErr.get()); 516 mainView->print(*OS.get(), /*Wholefile=*/true, 517 /*ShowSourceName=*/ShowFilenames); 518 Printer->closeViewFile(std::move(OS)); 519 } 520 521 return 0; 522 } 523 524 int CodeCoverageTool::report(int argc, const char **argv, 525 CommandLineParserType commandLineParser) { 526 auto Err = commandLineParser(argc, argv); 527 if (Err) 528 return Err; 529 530 auto Coverage = load(); 531 if (!Coverage) 532 return 1; 533 534 CoverageReport Report(ViewOpts, std::move(Coverage)); 535 if (SourceFiles.empty()) 536 Report.renderFileReports(llvm::outs()); 537 else 538 Report.renderFunctionReports(SourceFiles, llvm::outs()); 539 return 0; 540 } 541 542 int showMain(int argc, const char *argv[]) { 543 CodeCoverageTool Tool; 544 return Tool.run(CodeCoverageTool::Show, argc, argv); 545 } 546 547 int reportMain(int argc, const char *argv[]) { 548 CodeCoverageTool Tool; 549 return Tool.run(CodeCoverageTool::Report, argc, argv); 550 } 551