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<bool> FilenameEquivalence( 272 "filename-equivalence", cl::Optional, 273 cl::desc("Treat source files as equivalent to paths in the coverage data " 274 "when the file names match, even if the full paths do not")); 275 276 cl::OptionCategory FilteringCategory("Function filtering options"); 277 278 cl::list<std::string> NameFilters( 279 "name", cl::Optional, 280 cl::desc("Show code coverage only for functions with the given name"), 281 cl::ZeroOrMore, cl::cat(FilteringCategory)); 282 283 cl::list<std::string> NameRegexFilters( 284 "name-regex", cl::Optional, 285 cl::desc("Show code coverage only for functions that match the given " 286 "regular expression"), 287 cl::ZeroOrMore, cl::cat(FilteringCategory)); 288 289 cl::opt<double> RegionCoverageLtFilter( 290 "region-coverage-lt", cl::Optional, 291 cl::desc("Show code coverage only for functions with region coverage " 292 "less than the given threshold"), 293 cl::cat(FilteringCategory)); 294 295 cl::opt<double> RegionCoverageGtFilter( 296 "region-coverage-gt", cl::Optional, 297 cl::desc("Show code coverage only for functions with region coverage " 298 "greater than the given threshold"), 299 cl::cat(FilteringCategory)); 300 301 cl::opt<double> LineCoverageLtFilter( 302 "line-coverage-lt", cl::Optional, 303 cl::desc("Show code coverage only for functions with line coverage less " 304 "than the given threshold"), 305 cl::cat(FilteringCategory)); 306 307 cl::opt<double> LineCoverageGtFilter( 308 "line-coverage-gt", cl::Optional, 309 cl::desc("Show code coverage only for functions with line coverage " 310 "greater than the given threshold"), 311 cl::cat(FilteringCategory)); 312 313 cl::opt<cl::boolOrDefault> UseColor( 314 "use-color", cl::desc("Emit colored output (default=autodetect)"), 315 cl::init(cl::BOU_UNSET)); 316 317 auto commandLineParser = [&, this](int argc, const char **argv) -> int { 318 cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n"); 319 ViewOpts.Debug = DebugDump; 320 CompareFilenamesOnly = FilenameEquivalence; 321 322 ViewOpts.Colors = UseColor == cl::BOU_UNSET 323 ? sys::Process::StandardOutHasColors() 324 : UseColor == cl::BOU_TRUE; 325 326 // Create the function filters 327 if (!NameFilters.empty() || !NameRegexFilters.empty()) { 328 auto NameFilterer = new CoverageFilters; 329 for (const auto &Name : NameFilters) 330 NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name)); 331 for (const auto &Regex : NameRegexFilters) 332 NameFilterer->push_back( 333 llvm::make_unique<NameRegexCoverageFilter>(Regex)); 334 Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer)); 335 } 336 if (RegionCoverageLtFilter.getNumOccurrences() || 337 RegionCoverageGtFilter.getNumOccurrences() || 338 LineCoverageLtFilter.getNumOccurrences() || 339 LineCoverageGtFilter.getNumOccurrences()) { 340 auto StatFilterer = new CoverageFilters; 341 if (RegionCoverageLtFilter.getNumOccurrences()) 342 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 343 RegionCoverageFilter::LessThan, RegionCoverageLtFilter)); 344 if (RegionCoverageGtFilter.getNumOccurrences()) 345 StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>( 346 RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter)); 347 if (LineCoverageLtFilter.getNumOccurrences()) 348 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 349 LineCoverageFilter::LessThan, LineCoverageLtFilter)); 350 if (LineCoverageGtFilter.getNumOccurrences()) 351 StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>( 352 RegionCoverageFilter::GreaterThan, LineCoverageGtFilter)); 353 Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer)); 354 } 355 356 if (!Arch.empty() && 357 Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) { 358 errs() << "error: Unknown architecture: " << Arch << "\n"; 359 return 1; 360 } 361 CoverageArch = Arch; 362 363 for (const auto &File : InputSourceFiles) { 364 SmallString<128> Path(File); 365 if (!CompareFilenamesOnly) 366 if (std::error_code EC = sys::fs::make_absolute(Path)) { 367 errs() << "error: " << File << ": " << EC.message(); 368 return 1; 369 } 370 addCollectedPath(Path.str()); 371 } 372 return 0; 373 }; 374 375 switch (Cmd) { 376 case Show: 377 return show(argc, argv, commandLineParser); 378 case Report: 379 return report(argc, argv, commandLineParser); 380 } 381 return 0; 382 } 383 384 int CodeCoverageTool::show(int argc, const char **argv, 385 CommandLineParserType commandLineParser) { 386 387 cl::OptionCategory ViewCategory("Viewing options"); 388 389 cl::opt<bool> ShowLineExecutionCounts( 390 "show-line-counts", cl::Optional, 391 cl::desc("Show the execution counts for each line"), cl::init(true), 392 cl::cat(ViewCategory)); 393 394 cl::opt<bool> ShowRegions( 395 "show-regions", cl::Optional, 396 cl::desc("Show the execution counts for each region"), 397 cl::cat(ViewCategory)); 398 399 cl::opt<bool> ShowBestLineRegionsCounts( 400 "show-line-counts-or-regions", cl::Optional, 401 cl::desc("Show the execution counts for each line, or the execution " 402 "counts for each region on lines that have multiple regions"), 403 cl::cat(ViewCategory)); 404 405 cl::opt<bool> ShowExpansions("show-expansions", cl::Optional, 406 cl::desc("Show expanded source regions"), 407 cl::cat(ViewCategory)); 408 409 cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional, 410 cl::desc("Show function instantiations"), 411 cl::cat(ViewCategory)); 412 413 cl::opt<CoverageViewOptions::OutputFormat> Format( 414 "format", cl::desc("Output format for line-based coverage reports"), 415 cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", 416 "Text output"), 417 clEnumValEnd), 418 cl::init(CoverageViewOptions::OutputFormat::Text)); 419 420 cl::opt<std::string> ShowOutputDirectory( 421 "output-dir", cl::init(""), 422 cl::desc("Directory in which coverage information is written out")); 423 cl::alias ShowOutputDirectoryA("o", cl::desc("Alias for --output-dir"), 424 cl::aliasopt(ShowOutputDirectory)); 425 426 auto Err = commandLineParser(argc, argv); 427 if (Err) 428 return Err; 429 430 ViewOpts.ShowLineNumbers = true; 431 ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || 432 !ShowRegions || ShowBestLineRegionsCounts; 433 ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts; 434 ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts; 435 ViewOpts.ShowExpandedRegions = ShowExpansions; 436 ViewOpts.ShowFunctionInstantiations = ShowInstantiations; 437 ViewOpts.Format = Format; 438 ViewOpts.ShowOutputDirectory = ShowOutputDirectory; 439 440 if (ViewOpts.hasOutputDirectory()) { 441 if (auto E = sys::fs::create_directories(ViewOpts.ShowOutputDirectory)) { 442 error("Could not create output directory!", E.message()); 443 return 1; 444 } 445 } 446 447 auto Coverage = load(); 448 if (!Coverage) 449 return 1; 450 451 if (!Filters.empty()) { 452 // Show functions 453 for (const auto &Function : Coverage->getCoveredFunctions()) { 454 if (!Filters.matches(Function)) 455 continue; 456 457 auto mainView = createFunctionView(Function, *Coverage); 458 if (!mainView) { 459 ViewOpts.colored_ostream(errs(), raw_ostream::RED) 460 << "warning: Could not read coverage for '" << Function.Name << "'." 461 << "\n"; 462 continue; 463 } 464 465 auto OSOrErr = 466 mainView->createOutputFile("functions", /*InToplevel=*/true); 467 if (Error E = OSOrErr.takeError()) { 468 error(toString(std::move(E))); 469 return 1; 470 } 471 auto OS = std::move(OSOrErr.get()); 472 mainView->print(*OS.get(), /*WholeFile=*/false, /*ShowSourceName=*/true); 473 mainView->closeOutputFile(std::move(OS)); 474 } 475 return 0; 476 } 477 478 // Show files 479 bool ShowFilenames = SourceFiles.size() != 1; 480 481 if (SourceFiles.empty()) 482 // Get the source files from the function coverage mapping. 483 for (StringRef Filename : Coverage->getUniqueSourceFiles()) 484 SourceFiles.push_back(Filename); 485 486 for (const auto &SourceFile : SourceFiles) { 487 auto mainView = createSourceFileView(SourceFile, *Coverage); 488 if (!mainView) { 489 ViewOpts.colored_ostream(errs(), raw_ostream::RED) 490 << "warning: The file '" << SourceFile << "' isn't covered."; 491 errs() << "\n"; 492 continue; 493 } 494 495 auto OSOrErr = mainView->createOutputFile(SourceFile, /*InToplevel=*/false); 496 if (Error E = OSOrErr.takeError()) { 497 error(toString(std::move(E))); 498 return 1; 499 } 500 auto OS = std::move(OSOrErr.get()); 501 mainView->print(*OS.get(), /*Wholefile=*/true, 502 /*ShowSourceName=*/ShowFilenames); 503 mainView->closeOutputFile(std::move(OS)); 504 } 505 506 return 0; 507 } 508 509 int CodeCoverageTool::report(int argc, const char **argv, 510 CommandLineParserType commandLineParser) { 511 auto Err = commandLineParser(argc, argv); 512 if (Err) 513 return Err; 514 515 auto Coverage = load(); 516 if (!Coverage) 517 return 1; 518 519 CoverageReport Report(ViewOpts, std::move(Coverage)); 520 if (SourceFiles.empty()) 521 Report.renderFileReports(llvm::outs()); 522 else 523 Report.renderFunctionReports(SourceFiles, llvm::outs()); 524 return 0; 525 } 526 527 int showMain(int argc, const char *argv[]) { 528 CodeCoverageTool Tool; 529 return Tool.run(CodeCoverageTool::Show, argc, argv); 530 } 531 532 int reportMain(int argc, const char *argv[]) { 533 CodeCoverageTool Tool; 534 return Tool.run(CodeCoverageTool::Report, argc, argv); 535 } 536