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