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