<div dir="ltr"><div>Hey Alex,</div><div><br></div><div>Overall this looks pretty good. I have quite a few nitpicky style</div><div>comments and one or two bigger things below.</div><div><br></div><div>-- Justin</div><div><br>
</div><div>Alex Lorenz <<a href="mailto:arphaman@gmail.com">arphaman@gmail.com</a>> writes:</div><div>> Index: tools/llvm-cov/CMakeLists.txt</div><div>> ===================================================================</div>
<div>> --- tools/llvm-cov/CMakeLists.txt</div><div>> +++ tools/llvm-cov/CMakeLists.txt</div><div>> @@ -1,6 +1,14 @@</div><div>> -set(LLVM_LINK_COMPONENTS core support )</div><div>> +set(LLVM_LINK_COMPONENTS core support object)</div>
<div>></div><div>>  add_llvm_tool(llvm-cov</div><div>>    llvm-cov.cpp</div><div>>    gcov.cpp</div><div>> +  CodeCoverage.cpp</div><div>> +  CoverageFilters.cpp</div><div>> +  CoverageReport.cpp</div>
<div>> +  CoverageSummary.cpp</div><div>> +  CoverageSummaryInfo.cpp</div><div>> +  SourceCoverageDataManager.cpp</div><div>> +  SourceCoverageView.cpp</div><div>> +  SourceLanguages.cpp</div><div>>    )</div>
<div>> Index: tools/llvm-cov/CodeCoverage.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CodeCoverage.cpp</div><div>
> @@ -0,0 +1,707 @@</div><div>> +//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>
> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements a command line tool to analyze and report coverage</div><div>> +// information using the profiling instrumentation and code coverage mapping.</div><div><br></div><div>
What is "This class"? This is a file. Maybe mention CodeCoverageTool</div><div>here.</div><div><br></div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "CoverageViewOptions.h"</div><div>> +#include "CoverageFilters.h"</div><div>> +#include "SourceLanguages.h"</div>
<div>> +#include "SourceCoverageDataManager.h"</div><div>> +#include "SourceCoverageView.h"</div><div>> +#include "CoverageSummary.h"</div><div>> +#include "CoverageReport.h"</div>
<div>> +#include "llvm/ADT/StringRef.h"</div><div>> +#include "llvm/ADT/SmallString.h"</div><div>> +#include "llvm/ADT/SmallSet.h"</div><div>> +#include "llvm/ADT/DenseSet.h"</div>
<div>> +#include "llvm/ProfileData/InstrProfReader.h"</div><div>> +#include "llvm/ProfileData/CoverageMapping.h"</div><div>> +#include "llvm/ProfileData/CoverageMappingReader.h"</div>
<div>> +#include "llvm/Support/CommandLine.h"</div><div>> +#include "llvm/Support/FileSystem.h"</div><div>> +#include "llvm/Support/ManagedStatic.h"</div><div>> +#include "llvm/Support/MemoryObject.h"</div>
<div>> +#include "llvm/Support/Format.h"</div><div>> +#include "llvm/Support/Path.h"</div><div>> +#include "llvm/Support/Signals.h"</div><div>> +#include "llvm/Support/PrettyStackTrace.h"</div>
<div>> +#include <system_error></div><div>> +#include <functional></div><div>> +</div><div>> +using namespace llvm;</div><div>> +using namespace coverage;</div><div>> +</div><div>> +namespace {</div>
<div>> +/// \brief Distribute the functions into instantiation sets.</div><div>> +/// An instantiation set is a collection of functions</div><div>> +/// that have the same source code, e.g.</div><div>> +/// template functions specializations.</div>
<div>> +class FunctionInstantiationSetCollector {</div><div>> +  ArrayRef<FunctionCoverageMapping> FunctionMappings;</div><div>> +  typedef uint64_t KeyType;</div><div>> +  typedef std::vector<const FunctionCoverageMapping *> SetType;</div>
<div>> +  std::unordered_map<uint64_t, SetType> InstantiatedFunctions;</div><div>> +</div><div>> +  static KeyType getKey(const MappingRegion &R) {</div><div>> +    return uint64_t(R.LineStart) | uint64_t(R.ColumnStart) << 32;</div>
<div>> +  }</div><div>> +</div><div>> +public:</div><div>> +  void insert(const FunctionCoverageMapping &Function, unsigned FileID) {</div><div>> +    KeyType Key = 0;</div><div>> +    for (const auto &R : Function.MappingRegions) {</div>
<div>> +      if (R.FileID == FileID) {</div><div>> +        Key = getKey(R);</div><div>> +        break;</div><div>> +      }</div><div>> +    }</div><div>> +    auto I = InstantiatedFunctions.find(Key);</div>
<div>> +    if (I == InstantiatedFunctions.end()) {</div><div>> +      SetType Set;</div><div>> +      Set.push_back(&Function);</div><div>> +      InstantiatedFunctions.insert(std::make_pair(Key, Set));</div>
<div>> +    } else</div><div>> +      I->second.push_back(&Function);</div><div>> +  }</div><div>> +</div><div>> +  std::unordered_map<KeyType, SetType>::iterator begin() {</div><div>> +    return InstantiatedFunctions.begin();</div>
<div>> +  }</div><div>> +</div><div>> +  std::unordered_map<KeyType, SetType>::iterator end() {</div><div>> +    return InstantiatedFunctions.end();</div><div>> +  }</div><div>> +};</div><div>> +</div>
<div>> +/// \brief The implementation of the coverage tool.</div><div>> +class CodeCoverageTool {</div><div>> +public:</div><div>> +  enum Command {</div><div>> +    /// \brief The show command.</div><div>> +    Show,</div>
<div>> +    /// \brief The report command.</div><div>> +    Report</div><div>> +  };</div><div>> +</div><div>> +  /// \brief Print the error message to the error output stream.</div><div>> +  void error(const Twine &Message, StringRef Whence = "");</div>
<div>> +</div><div>> +  /// \brief Return a memory buffer for the given source file.</div><div>> +  ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);</div><div>> +</div><div>> +  /// \brief Collect a set of function's file ids which correspond to the</div>
<div>> +  /// given source file. Return false if the set is empty.</div><div>> +  bool gatherInterestingFileIDs(StringRef SourceFile,</div><div>> +                                const FunctionCoverageMapping &Function,</div>
<div>> +                                SmallSet<unsigned, 8> &InterestingFileIDs);</div><div>> +</div><div>> +  /// \brief Find the file id which is not an expanded file id.</div><div>> +  bool findMainViewFileID(StringRef SourceFile,</div>
<div>> +                          const FunctionCoverageMapping &Function,</div><div>> +                          unsigned &MainViewFileID);</div><div>> +</div><div>> +  bool findMainViewFileID(const FunctionCoverageMapping &Function,</div>
<div>> +                          unsigned &MainViewFileID);</div><div>> +</div><div>> +  /// \brief Create a source view which shows coverage for an expansion</div><div>> +  /// of a file.</div><div>> +  void createExpansionSubView(const MappingRegion &ExpandedRegion,</div>
<div>> +                              const FunctionCoverageMapping &Function,</div><div>> +                              SourceCoverageView &Parent);</div><div>> +</div><div>> +  void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,</div>
<div>> +                               const FunctionCoverageMapping &Function);</div><div>> +</div><div>> +  /// \brief Create a source view which shows coverage for an instantiation</div><div>> +  /// of a funciton.</div>
<div>> +  void createInstantiationSubView(StringRef SourceFile,</div><div>> +                                  const FunctionCoverageMapping &Function,</div><div>> +                                  SourceCoverageView &View);</div>
<div>> +</div><div>> +  /// \brief Create the main source view of a particular source file.</div><div>> +  /// Return true if this particular source file is not covered.</div><div>> +  bool</div><div>> +  createSourceFileView(StringRef SourceFile, SourceCoverageView &View,</div>
<div>> +                       ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,</div><div>> +                       bool UseOnlyRegionsInMainFile = false);</div><div>> +</div><div>> +  /// \brief Return true if an error occured during loading</div>
<div>> +  /// of the coverage mapping data.</div><div>> +  bool load();</div><div><br></div><div>This wording doesn't make it clear that this, you know, loads the</div><div>coverage mapping data. :)</div><div><br>
</div><div>> +</div><div>> +  int run(Command Cmd, int argc, const char **argv);</div><div>> +</div><div>> +  typedef std::function<int(int, const char **)> CommandLineParserType;</div><div>> +</div><div>
> +  int show(int argc, const char **argv,</div><div>> +           CommandLineParserType commandLineParser);</div><div>> +</div><div>> +  int report(int argc, const char **argv,</div><div>> +             CommandLineParserType commandLineParser);</div>
<div>> +</div><div>> +  StringRef ObjectFilename;</div><div>> +  CoverageViewOptions ViewOpts;</div><div>> +  std::unique_ptr<IndexedInstrProfReader> PGOReader;</div><div>> +  CoverageFiltersMatchAll Filters;</div>
<div>> +  SourceLanguages Languages;</div><div>> +  std::vector<std::string> SourceFiles;</div><div>> +  std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>></div><div>> +      LoadedSourceFiles;</div>
<div>> +  std::vector<FunctionCoverageMapping> FunctionMappingRecords;</div><div>> +};</div><div>> +}</div><div>> +</div><div>> +void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {</div>
<div>> +  errs() << "error: ";</div><div>> +  if (!Whence.empty())</div><div>> +    errs() << Whence << ": ";</div><div>> +  errs() << Message << "\n";</div>
<div>> +}</div><div>> +</div><div>> +ErrorOr<const MemoryBuffer &></div><div>> +CodeCoverageTool::getSourceFile(StringRef SourceFile) {</div><div>> +  SmallString<256> Path(SourceFile);</div>
<div>> +  sys::fs::make_absolute(Path);</div><div>> +  for (const auto &Files : LoadedSourceFiles) {</div><div>> +    if (sys::fs::equivalent(Path.str(), Files.first)) {</div><div>> +      return *Files.second;</div>
<div>> +    }</div><div>> +  }</div><div>> +  auto Buffer = MemoryBuffer::getFile(SourceFile);</div><div>> +  if (auto EC = Buffer.getError()) {</div><div>> +    error(EC.message(), SourceFile);</div><div>> +    return EC;</div>
<div>> +  }</div><div>> +  LoadedSourceFiles.push_back(std::make_pair(</div><div>> +      std::string(Path.begin(), Path.end()), std::move(Buffer.get())));</div><div>> +  return *LoadedSourceFiles.back().second;</div>
<div>> +}</div><div>> +</div><div>> +/// \brief Return a line start - line end range which contains</div><div>> +/// all the mapping regions of a given function with a particular file id.</div><div>> +std::pair<unsigned, unsigned></div>
<div>> +findExpandedFileInterestingLineRange(unsigned FileID,</div><div>> +                                     const FunctionCoverageMapping &Function) {</div><div>> +  unsigned LineStart = std::numeric_limits<unsigned>::max();</div>
<div>> +  unsigned LineEnd = 0;</div><div>> +  for (const auto &I : Function.MappingRegions) {</div><div><br></div><div>I isn't a great name. Better to use something descriptive like Region.</div><div><br></div>
<div>> +    if (I.FileID != FileID)</div><div>> +      continue;</div><div>> +    LineStart = std::min(I.LineStart, LineStart);</div><div>> +    LineEnd = std::max(I.LineEnd, LineEnd);</div><div>> +  }</div>
<div>> +  return std::make_pair(LineStart, LineEnd);</div><div>> +}</div><div>> +</div><div>> +bool CodeCoverageTool::gatherInterestingFileIDs(</div><div>> +    StringRef SourceFile, const FunctionCoverageMapping &Function,</div>
<div>> +    SmallSet<unsigned, 8> &InterestingFileIDs) {</div><div>> +  bool Interesting = false;</div><div>> +  for (unsigned I = 0, S = Function.Filenames.size(); I < S; ++I) {</div><div><br></div>
<div>We usually use E (for end) in these loops. You use S (for size?) a few</div><div>times in this patch. Please change them to E to match the usual LLVM</div><div>convention.</div><div><br></div><div>> +    if (llvm::sys::fs::equivalent(SourceFile, Function.Filenames[I])) {</div>
<div>> +      InterestingFileIDs.insert(I);</div><div>> +      Interesting = true;</div><div>> +    }</div><div>> +  }</div><div>> +  return Interesting;</div><div>> +}</div><div>> +</div><div>> +bool</div>
<div>> +CodeCoverageTool::findMainViewFileID(StringRef SourceFile,</div><div>> +                                     const FunctionCoverageMapping &Function,</div><div>> +                                     unsigned &MainViewFileID) {</div>
<div>> +  llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);</div><div>> +  llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(),</div><div>> +                                                 false);</div>
<div>> +  for (unsigned I = 0, S = Function.Filenames.size(); I < S; ++I) {</div><div>> +    if (llvm::sys::fs::equivalent(SourceFile, Function.Filenames[I]))</div><div>> +      FilenameEquivalence[I] = true;</div>
<div>> +  }</div><div>> +  for (const auto &R : Function.MappingRegions) {</div><div>> +    if (R.Kind == MappingRegion::ExpansionRegion &&</div><div>> +        FilenameEquivalence[R.FileID])</div>
<div>> +      IsExpandedFile[R.ExpandedFileID] = true;</div><div>> +  }</div><div>> +  for (unsigned I = 0; I < Function.Filenames.size(); ++I) {</div><div><br></div><div>Please change this to "I = 0, E = size(); I < E; ..." like the above.</div>
<div><br></div><div>> +    if (!FilenameEquivalence[I] || IsExpandedFile[I])</div><div>> +      continue;</div><div>> +    MainViewFileID = I;</div><div>> +    return false;</div><div>> +  }</div><div>> +  return true;</div>
<div>> +}</div><div>> +</div><div>> +bool</div><div>> +CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,</div><div>> +                                     unsigned &MainViewFileID) {</div>
<div>> +  llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);</div><div>> +  for (const auto &R : Function.MappingRegions) {</div><div>> +    if (R.Kind == MappingRegion::ExpansionRegion)</div>
<div>> +      IsExpandedFile[R.ExpandedFileID] = true;</div><div>> +  }</div><div>> +  for (unsigned I = 0; I < Function.Filenames.size(); ++I) {</div><div><br></div><div>Ditto.</div><div><br></div><div>> +    if (IsExpandedFile[I])</div>
<div>> +      continue;</div><div>> +    MainViewFileID = I;</div><div>> +    return false;</div><div>> +  }</div><div>> +  return true;</div><div>> +}</div><div>> +</div><div>> +void CodeCoverageTool::createExpansionSubView(</div>
<div>> +    const MappingRegion &ExpandedRegion,</div><div>> +    const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {</div><div>> +  auto ExpandedLines = findExpandedFileInterestingLineRange(</div>
<div>> +      ExpandedRegion.ExpandedFileID, Function);</div><div>> +  if (ViewOpts.Debug)</div><div>> +    llvm::outs() << "Expansion of " << ExpandedRegion.ExpandedFileID << ":"</div>
<div>> +                 << ExpandedLines.first << " -> " << ExpandedLines.second</div><div>> +                 << " @ " << ExpandedRegion.FileID << ", "</div>
<div>> +                 << ExpandedRegion.LineStart << ":"</div><div>> +                 << ExpandedRegion.ColumnStart << "\n";</div><div>> +  auto SourceBuffer =</div>
<div>> +      getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);</div><div>> +  if (!SourceBuffer)</div><div>> +    return;</div><div>> +  auto SubView = new SourceCoverageView(SourceBuffer.get(), Parent.getOptions(),</div>
<div>> +                                        ExpandedLines.first,</div><div>> +                                        ExpandedLines.second, ExpandedRegion);</div><div><br></div><div>I see later Parent.addChild wraps this in a unique pointer - better to</div>
<div>pass a unique pointer in so that it's obvious who owns this.</div><div><br></div><div>> +  Parent.addChild(SubView);</div><div>> +  SourceCoverageDataManager RegionManager;</div><div>> +  for (const auto &I : Function.MappingRegions) {</div>
<div>> +    if (I.FileID == ExpandedRegion.ExpandedFileID)</div><div>> +      RegionManager.insert(I);</div><div>> +  }</div><div>> +  SubView->load(RegionManager);</div><div>> +  createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);</div>
<div>> +}</div><div>> +</div><div>> +void CodeCoverageTool::createExpansionSubViews(</div><div>> +    SourceCoverageView &View, unsigned ViewFileID,</div><div>> +    const FunctionCoverageMapping &Function) {</div>
<div>> +  if (!ViewOpts.ShowExpandedRegions)</div><div>> +    return;</div><div>> +  for (const auto &Region : Function.MappingRegions) {</div><div>> +    if (Region.Kind != CounterMappingRegion::ExpansionRegion)</div>
<div>> +      continue;</div><div>> +    if (Region.FileID != ViewFileID)</div><div>> +      continue;</div><div>> +    createExpansionSubView(Region, Function, View);</div><div>> +  }</div><div>> +}</div>
<div>> +</div><div>> +void CodeCoverageTool::createInstantiationSubView(</div><div>> +    StringRef SourceFile, const FunctionCoverageMapping &Function,</div><div>> +    SourceCoverageView &View) {</div>
<div>> +  SourceCoverageDataManager RegionManager;</div><div>> +  SmallSet<unsigned, 8> InterestingFileIDs;</div><div>> +  if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))</div><div>
> +    return;</div><div>> +  // Get the interesting regions</div><div>> +  for (const auto &I : Function.MappingRegions) {</div><div>> +    if (InterestingFileIDs.count(I.FileID))</div><div>> +      RegionManager.insert(I);</div>
<div>> +  }</div><div>> +  View.load(RegionManager);</div><div>> +  unsigned MainFileID;</div><div>> +  if (findMainViewFileID(SourceFile, Function, MainFileID))</div><div>> +    return;</div><div>> +  createExpansionSubViews(View, MainFileID, Function);</div>
<div>> +}</div><div>> +</div><div>> +bool CodeCoverageTool::createSourceFileView(</div><div>> +    StringRef SourceFile, SourceCoverageView &View,</div><div>> +    ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,</div>
<div>> +    bool UseOnlyRegionsInMainFile) {</div><div>> +  SourceCoverageDataManager RegionManager;</div><div>> +  FunctionInstantiationSetCollector InstantiationSetCollector;</div><div>> +</div><div>> +  for (const auto &Function : FunctionMappingRecords) {</div>
<div>> +    unsigned MainFileID;</div><div>> +    if (findMainViewFileID(SourceFile, Function, MainFileID))</div><div>> +      continue;</div><div>> +    SmallSet<unsigned, 8> InterestingFileIDs;</div><div>
> +    if (UseOnlyRegionsInMainFile) {</div><div>> +      InterestingFileIDs.insert(MainFileID);</div><div>> +    } else {</div><div>> +      if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))</div>
<div>> +        continue;</div><div>> +    }</div><div><br></div><div>Isn't this just "else if"?</div><div><br></div><div>> +    // Get the interesting regions</div><div>> +    for (const auto &Region : Function.MappingRegions) {</div>
<div>> +      if (InterestingFileIDs.count(Region.FileID))</div><div>> +        RegionManager.insert(Region);</div><div>> +    }</div><div>> +    InstantiationSetCollector.insert(Function, MainFileID);</div><div>
> +    createExpansionSubViews(View, MainFileID, Function);</div><div>> +  }</div><div>> +  if (RegionManager.getSourceRegions().empty())</div><div>> +    return true;</div><div>> +  View.load(RegionManager);</div>
<div>> +  // Show instantiations</div><div>> +  if (!ViewOpts.ShowFunctionInstantiations)</div><div>> +    return false;</div><div>> +  for (const auto &InstantiationSet : InstantiationSetCollector) {</div>
<div>> +    if (InstantiationSet.second.size() < 2)</div><div>> +      continue;</div><div>> +    auto InterestingRange = findExpandedFileInterestingLineRange(</div><div>> +        InstantiationSet.second.front()->MappingRegions.front().FileID,</div>
<div>> +        *InstantiationSet.second.front());</div><div>> +    for (auto Function : InstantiationSet.second) {</div><div>> +      auto SubView =</div><div>> +          new SourceCoverageView(View, InterestingRange.first,</div>
<div>> +                                 InterestingRange.second, Function->PrettyName);</div><div>> +      View.addChild(SubView);</div><div><br></div><div>> +      createInstantiationSubView(SourceFile, *Function, *SubView);</div>
<div>> +    }</div><div>> +  }</div><div>> +  return false;</div><div>> +}</div><div>> +</div><div>> +bool CodeCoverageTool::load() {</div><div>> +  auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);</div>
<div>> +  if (auto EC = CounterMappingBuff.getError()) {</div><div>> +    error(EC.message(), ObjectFilename);</div><div>> +    return true;</div><div>> +  }</div><div>> +  ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());</div>
<div>> +  if (auto EC = MappingReader.readHeader()) {</div><div>> +    error(EC.message(), ObjectFilename);</div><div>> +    return true;</div><div>> +  }</div><div>> +</div><div>> +  std::vector<uint64_t> Counts;</div>
<div>> +  for (const auto &I : MappingReader) {</div><div>> +    FunctionCoverageMapping Function(I.FunctionName, I.Filenames);</div><div>> +</div><div>> +    // Demangle</div><div>> +    Languages.get(Function)->demangle(Function.PrettyName);</div>
<div>> +</div><div>> +    // Create the mapping regions with evaluated execution counts</div><div>> +    Counts.clear();</div><div>> +    uint64_t FuncHash;</div><div>> +    PGOReader->getFunctionCounts(Function.Name, FuncHash, Counts);</div>
<div>> +</div><div>> +    // Get the biggest referenced counters</div><div>> +    bool RegionError = false;</div><div>> +    CounterMappingContext Ctx(I.Expressions, Counts);</div><div>> +    for (const auto &R : I.MappingRegions) {</div>
<div>> +      // Compute the values of mapped regions</div><div>> +      if (ViewOpts.Debug) {</div><div>> +        outs() << "File " << R.FileID << "| " << R.LineStart << ":"</div>
<div>> +               << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd</div><div>> +               << " = ";</div><div>> +        Ctx.dump(R.Count);</div>
<div>> +        if (R.Kind == CounterMappingRegion::ExpansionRegion) {</div><div>> +          outs() << " (Expanded file id = " << R.ExpandedFileID << ") ";</div><div>> +        }</div>
<div>> +        outs() << "\n";</div><div>> +      }</div><div>> +      std::error_code Error;</div><div>> +      Function.MappingRegions.push_back(</div><div>> +          MappingRegion(R, Ctx.evaluate(R.Count, Error)));</div>
<div>> +      if (Error && !RegionError) {</div><div>> +        errs().changeColor(raw_ostream::RED);</div><div>> +        errs() << "error: Regions and counters don't match in a function '"</div>
<div>> +               << Function.PrettyName << "' (re-run the instrumented binary).";</div><div>> +        errs().resetColor();</div><div>> +        errs() << "\n";</div>
<div>> +        RegionError = true;</div><div>> +      }</div><div>> +    }</div><div>> +</div><div>> +    if (RegionError || !Filters.matches(Function))</div><div>> +      continue;</div><div>> +</div>
<div>> +    FunctionMappingRecords.push_back(Function);</div><div>> +  }</div><div>> +  return false;</div><div>> +}</div><div>> +</div><div>> +int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {</div>
<div>> +  // Print a stack trace if we signal out.</div><div>> +  sys::PrintStackTraceOnErrorSignal();</div><div>> +  PrettyStackTraceProgram X(argc, argv);</div><div>> +  llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.</div>
<div>> +</div><div>> +  cl::list<std::string> InputSourceFiles(</div><div>> +      cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);</div><div>> +</div><div>> +  cl::opt<std::string> PGOFilename(</div>
<div>> +      "instr-profile", cl::Required,</div><div>> +      cl::desc(</div><div>> +          "File with the profile data obtained after an instrumented run"));</div><div>> +</div><div>> +  cl::opt<bool> DebugDump("dump", cl::Optional,</div>
<div>> +                          cl::desc("Show internal debug dump"));</div><div>> +</div><div>> +  cl::OptionCategory FilteringCategory("Function filtering options");</div><div>> +</div>
<div>> +  cl::list<std::string> NameFilters(</div><div>> +      "name", cl::Optional,</div><div>> +      cl::desc("Show code coverage only for functions with the given name"),</div><div>
> +      cl::ZeroOrMore, cl::cat(FilteringCategory));</div><div>> +</div><div>> +  cl::list<std::string> NameRegexFilters(</div><div>> +      "name-regex", cl::Optional,</div><div>> +      cl::desc("Show code coverage only for functions that match the given "</div>
<div>> +               "regular expression"),</div><div>> +      cl::ZeroOrMore, cl::cat(FilteringCategory));</div><div>> +</div><div>> +  cl::opt<double> RegionCoverageLtFilter(</div><div>> +      "region-coverage-lt", cl::Optional,</div>
<div>> +      cl::desc("Show code coverage only for functions with region coverage "</div><div>> +               "less than the given threshold"),</div><div>> +      cl::cat(FilteringCategory));</div>
<div>> +</div><div>> +  cl::opt<double> RegionCoverageGtFilter(</div><div>> +      "region-coverage-gt", cl::Optional,</div><div>> +      cl::desc("Show code coverage only for functions with region coverage "</div>
<div>> +               "greater than the given threshold"),</div><div>> +      cl::cat(FilteringCategory));</div><div>> +</div><div>> +  cl::opt<double> LineCoverageLtFilter(</div><div>> +      "line-coverage-lt", cl::Optional,</div>
<div>> +      cl::desc("Show code coverage only for functions with line coverage less "</div><div>> +               "than the given threshold"),</div><div>> +      cl::cat(FilteringCategory));</div>
<div>> +</div><div>> +  cl::opt<double> LineCoverageGtFilter(</div><div>> +      "line-coverage-gt", cl::Optional,</div><div>> +      cl::desc("Show code coverage only for functions with line coverage "</div>
<div>> +               "greater than the given threshold"),</div><div>> +      cl::cat(FilteringCategory));</div><div>> +</div><div>> +  auto commandLineParser = [&, this](int argc, const char **argv) -> int {</div>
<div>> +    cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");</div><div>> +    ViewOpts.Debug = DebugDump;</div><div>> +</div><div>> +    if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {</div>
<div>> +      error(EC.message(), PGOFilename);</div><div>> +      return 1;</div><div>> +    }</div><div>> +</div><div>> +    // Create the function filters</div><div>> +    if (!NameFilters.empty() || !NameRegexFilters.empty()) {</div>
<div>> +      auto NameFilterer = new CoverageFilters;</div><div>> +      for (const auto &Name : NameFilters)</div><div>> +        NameFilterer->push_back(new NameCoverageFilter(Name));</div><div>> +      for (const auto &Regex : NameRegexFilters)</div>
<div>> +        NameFilterer->push_back(new NameRegexCoverageFilter(Regex));</div><div>> +      Filters.push_back(NameFilterer);</div><div>> +    }</div><div>> +    if (RegionCoverageLtFilter.getNumOccurrences() ||</div>
<div>> +        RegionCoverageGtFilter.getNumOccurrences() ||</div><div>> +        LineCoverageLtFilter.getNumOccurrences() ||</div><div>> +        LineCoverageGtFilter.getNumOccurrences()) {</div><div>> +      auto StatFilterer = new CoverageFilters;</div>
<div>> +      if (RegionCoverageLtFilter.getNumOccurrences())</div><div>> +        StatFilterer->push_back(new RegionCoverageFilter(</div><div>> +            RegionCoverageFilter::LessThan, RegionCoverageLtFilter));</div>
<div>> +      if (RegionCoverageGtFilter.getNumOccurrences())</div><div>> +        StatFilterer->push_back(new RegionCoverageFilter(</div><div>> +            RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));</div>
<div>> +      if (LineCoverageLtFilter.getNumOccurrences())</div><div>> +        StatFilterer->push_back(new LineCoverageFilter(</div><div>> +            LineCoverageFilter::LessThan, LineCoverageLtFilter));</div>
<div>> +      if (LineCoverageGtFilter.getNumOccurrences())</div><div>> +        StatFilterer->push_back(new LineCoverageFilter(</div><div>> +            RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));</div>
<div>> +      Filters.push_back(StatFilterer);</div><div>> +    }</div><div>> +</div><div>> +    SourceFiles = InputSourceFiles;</div><div>> +    return 0;</div><div>> +  };</div><div>> +</div><div>> +  // Parse the object filename</div>
<div>> +  if (argc > 1) {</div><div>> +    StringRef Arg(argv[1]);</div><div>> +    if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {</div><div>> +      cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");</div>
<div>> +      return 0;</div><div>> +    }</div><div>> +    ObjectFilename = Arg;</div><div>> +</div><div>> +    argv[1] = argv[0];</div><div>> +    --argc;</div><div>> +    ++argv;</div><div>> +  } else {</div>
<div>> +    errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";</div><div>> +    return 1;</div><div>> +  }</div><div>> +</div><div>> +  switch (Cmd) {</div><div>
> +  case Show:</div><div>> +    return show(argc, argv, commandLineParser);</div><div>> +  case Report:</div><div>> +    return report(argc, argv, commandLineParser);</div><div>> +  }</div><div>> +  return 0;</div>
<div>> +}</div><div>> +</div><div>> +int CodeCoverageTool::show(int argc, const char **argv,</div><div>> +                           CommandLineParserType commandLineParser) {</div><div>> +</div><div>> +  cl::OptionCategory ViewCategory("Viewing options");</div>
<div>> +</div><div>> +  cl::opt<bool> ShowLineExecutionCounts(</div><div>> +      "show-line-counts", cl::Optional,</div><div>> +      cl::desc("Show the execution counts for each line"), cl::init(true),</div>
<div>> +      cl::cat(ViewCategory));</div><div>> +</div><div>> +  cl::opt<bool> ShowRegions(</div><div>> +      "show-regions", cl::Optional,</div><div>> +      cl::desc("Show the execution counts for each region"),</div>
<div>> +      cl::cat(ViewCategory));</div><div>> +</div><div>> +  cl::opt<bool> ShowBestLineRegionsCounts(</div><div>> +      "show-line-counts-or-regions", cl::Optional,</div><div>> +      cl::desc("Show the execution counts for each line, or the execution "</div>
<div>> +               "counts for each region on lines that have multiple regions"),</div><div>> +      cl::cat(ViewCategory));</div><div>> +</div><div>> +  cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,</div>
<div>> +                               cl::desc("Show expanded source regions"),</div><div>> +                               cl::cat(ViewCategory));</div><div>> +</div><div>> +  cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,</div>
<div>> +                                   cl::desc("Show function instantiations"),</div><div>> +                                   cl::cat(ViewCategory));</div><div>> +</div><div>> +  cl::opt<bool> NoColors("no-colors", cl::Optional,</div>
<div>> +                         cl::desc("Don't show text colors"), cl::init(false),</div><div>> +                         cl::cat(ViewCategory));</div><div>> +</div><div>> +  auto Err = commandLineParser(argc, argv);</div>
<div>> +  if (Err)</div><div>> +    return Err;</div><div>> +</div><div>> +  ViewOpts.Colors = !NoColors;</div><div>> +  ViewOpts.ShowLineNumbers = true;</div><div>> +  ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||</div>
<div>> +                           !ShowRegions || ShowBestLineRegionsCounts;</div><div>> +  ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;</div><div>> +  ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;</div>
<div>> +  ViewOpts.ShowExpandedRegions = ShowExpansions;</div><div>> +  ViewOpts.ShowFunctionInstantiations = ShowInstantiations;</div><div>> +</div><div>> +  if (load())</div><div>> +    return 1;</div><div>
> +</div><div>> +  if (!Filters.empty()) {</div><div>> +    // Show functions</div><div>> +    for (const auto &Function : FunctionMappingRecords) {</div><div>> +      unsigned MainFileID;</div><div>> +      if (findMainViewFileID(Function, MainFileID))</div>
<div>> +        continue;</div><div>> +      StringRef SourceFile = Function.Filenames[MainFileID];</div><div>> +      std::unique_ptr<SourceCoverageView> mainView;</div><div>> +      auto SourceBuffer = getSourceFile(SourceFile);</div>
<div>> +      if (!SourceBuffer)</div><div>> +        return 1;</div><div>> +      auto Range = findExpandedFileInterestingLineRange(MainFileID, Function);</div><div>> +      mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts,</div>
<div>> +                                            Range.first, Range.second));</div><div>> +      createSourceFileView(SourceFile, *mainView, Function, true);</div><div>> +      if (ViewOpts.Colors)</div><div>> +        outs().changeColor(raw_ostream::CYAN);</div>
<div>> +      outs() << Function.PrettyName << " from " << SourceFile << ":";</div><div>> +      outs().resetColor();</div><div><br></div><div>Does resetColor do the right thing if we didn't change the color?</div>
<div><br></div><div>> +      outs() << "\n";</div><div>> +      mainView->render(outs());</div><div>> +      if (FunctionMappingRecords.size() > 1)</div><div>> +        outs() << "\n";</div>
<div>> +    }</div><div>> +    return 0;</div><div>> +  }</div><div>> +</div><div>> +  // Show files</div><div>> +  bool ShowFilenames = SourceFiles.size() != 1;</div><div>> +</div><div>> +  if (SourceFiles.empty()) {</div>
<div>> +    // Get the source files from the function coverage mapping</div><div>> +    std::set<StringRef> UniqueFilenames;</div><div>> +    for (const auto &Function : FunctionMappingRecords) {</div><div>
> +      for (const auto &Filename : Function.Filenames)</div><div>> +        UniqueFilenames.insert(Filename);</div><div>> +    }</div><div>> +    for (const auto &Filename : UniqueFilenames)</div><div>
> +      SourceFiles.push_back(Filename);</div><div>> +  }</div><div>> +</div><div>> +  for (const auto &SourceFile : SourceFiles) {</div><div>> +    std::unique_ptr<SourceCoverageView> mainView;</div>
<div>> +    auto SourceBuffer = getSourceFile(SourceFile);</div><div>> +    if (!SourceBuffer)</div><div>> +      return 1;</div><div>> +    mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts));</div>
<div>> +    if (createSourceFileView(SourceFile, *mainView, FunctionMappingRecords)) {</div><div>> +      if (ViewOpts.Colors)</div><div>> +        outs().changeColor(raw_ostream::RED);</div><div>> +      outs() << "warning: The file '" << SourceFile << "' isn't covered.";</div>
<div>> +      outs().resetColor();</div><div>> +      outs() << "\n";</div><div>> +      continue;</div><div>> +    }</div><div>> +</div><div>> +    if (ShowFilenames) {</div><div>> +      if (ViewOpts.Colors)</div>
<div>> +        outs().changeColor(raw_ostream::CYAN);</div><div>> +      outs() << SourceFile << ":";</div><div>> +      outs().resetColor();</div><div>> +      outs() << "\n";</div>
<div>> +    }</div><div>> +    mainView->render(outs());</div><div>> +    if (SourceFiles.size() > 1)</div><div>> +      outs() << "\n";</div><div>> +  }</div><div>> +</div><div>> +  return 0;</div>
<div>> +}</div><div>> +</div><div>> +int CodeCoverageTool::report(int argc, const char **argv,</div><div>> +                             CommandLineParserType commandLineParser) {</div><div>> +  cl::opt<bool> NoColors("no-colors", cl::Optional,</div>
<div>> +                         cl::desc("Don't show text colors"), cl::init(false));</div><div>> +</div><div>> +  auto Err = commandLineParser(argc, argv);</div><div>> +  if (Err)</div><div>> +    return Err;</div>
<div>> +</div><div>> +  ViewOpts.Colors = !NoColors;</div><div>> +</div><div>> +  if (load())</div><div>> +    return 1;</div><div>> +</div><div>> +  CoverageSummary Summarizer;</div><div>> +  Summarizer.createSummaries(FunctionMappingRecords);</div>
<div>> +  CoverageReport Report(ViewOpts, Summarizer);</div><div>> +  if (SourceFiles.empty() && Filters.empty()) {</div><div>> +    Report.renderFileReports(llvm::outs());</div><div>> +    return 0;</div>
<div>> +  }</div><div>> +</div><div>> +  Report.renderFunctionReports(llvm::outs());</div><div>> +  return 0;</div><div>> +}</div><div>> +</div><div>> +int show_main(int argc, const char **argv) {</div>
<div>> +  CodeCoverageTool Tool;</div><div>> +  return Tool.run(CodeCoverageTool::Show, argc, argv);</div><div>> +}</div><div>> +</div><div>> +int report_main(int argc, const char **argv) {</div><div>> +  CodeCoverageTool Tool;</div>
<div>> +  return Tool.run(CodeCoverageTool::Report, argc, argv);</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageFilters.cpp</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageFilters.cpp</div><div>> @@ -0,0 +1,57 @@</div><div>> +//===- CoverageFilters.cpp - Function coverage mapping filters ------------===//</div><div>> +//</div>
<div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// These classes provide filtering for function coverage mapping records.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "CoverageFilters.h"</div><div>> +#include "CoverageSummaryInfo.h"</div>
<div>> +#include "llvm/Support/Regex.h"</div><div>> +</div><div>> +using namespace llvm;</div><div>> +</div><div>> +bool NameCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div>
<div>> +  StringRef FuncName = Function.PrettyName;</div><div>> +  return FuncName.find(Name) != StringRef::npos;</div><div>> +}</div><div>> +</div><div>> +bool NameRegexCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div>
<div>> +  return llvm::Regex(Regex).match(Function.PrettyName);</div><div>> +}</div><div>> +</div><div>> +bool RegionCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div><div>> +  return PassesThreshold(FunctionCoverageSummary::get(Function)</div>
<div>> +                             .RegionCoverage.getPercentCovered());</div><div>> +}</div><div>> +</div><div>> +bool LineCoverageFilter::matches(const FunctionCoverageMapping &Function) {</div><div>> +  return PassesThreshold(</div>
<div>> +      FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered());</div><div>> +}</div><div>> +</div><div>> +void CoverageFilters::push_back(CoverageFilter *filter) {</div><div>> +  Filters.push_back(std::unique_ptr<CoverageFilter>(filter));</div>
<div>> +}</div><div><br></div><div>Making the argument a unique_ptr is clearer about ownership.</div><div><br></div><div>> +</div><div>> +bool CoverageFilters::matches(const FunctionCoverageMapping &Function) {</div>
<div>> +  for (const auto &Filter : Filters) {</div><div>> +    if (Filter->matches(Function))</div><div>> +      return true;</div><div>> +  }</div><div>> +  return false;</div><div>> +}</div><div>
> +</div><div>> +bool CoverageFiltersMatchAll::matches(const FunctionCoverageMapping &Function) {</div><div>> +  for (const auto &Filter : Filters) {</div><div>> +    if (!Filter->matches(Function))</div>
<div>> +      return false;</div><div>> +  }</div><div>> +  return true;</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageFilters.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageFilters.h</div><div>> @@ -0,0 +1,123 @@</div><div>> +//===- CoverageFilters.h - Function coverage mapping filters --------------===//</div><div>> +//</div>
<div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// These classes provide filtering for function coverage mapping records.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGEFILTERS_H</div><div>> +#define COVERAGEFILTERS_H</div><div>
<br></div><div>Header guards should be more specific as to avoid collisions in case</div><div>things move around. How about LLVM_COV_COVERAGEFILTERS_H? This applies</div><div>to all of the other headers as well.</div><div>
<br></div><div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include <vector></div><div>> +#include <memory></div><div>> +</div><div>> +namespace llvm {</div><div>
> +</div><div>> +/// \brief Matches specific functions that pass the requirement of this filter.</div><div>> +class CoverageFilter {</div><div>> +public:</div><div>> +  /// \brief Return true if the function passes the requirements of this filter.</div>
<div>> +  virtual bool matches(const FunctionCoverageMapping &Function) { return true; }</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions that contain a specific string in their name.</div>
<div>> +class NameCoverageFilter : public CoverageFilter {</div><div>> +  StringRef Name;</div><div>> +</div><div>> +public:</div><div>> +  NameCoverageFilter(StringRef Name) : Name(Name) {}</div><div>> +</div>
<div>> +  bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions whose name matches a certain regular expression.</div><div>
> +class NameRegexCoverageFilter : public CoverageFilter {</div><div>> +  StringRef Regex;</div><div>> +</div><div>> +public:</div><div>> +  NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}</div><div>
> +</div><div>> +  bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches numbers that pass a certain threshold.</div><div>> +template <typename T> class StatisticThresholdFilter {</div>
<div>> +public:</div><div>> +  enum Operation { LessThan, GreaterThan };</div><div>> +</div><div>> +protected:</div><div>> +  Operation Op;</div><div>> +  T Threshold;</div><div>> +</div><div>> +  StatisticThresholdFilter(Operation Op, T Threshold)</div>
<div>> +      : Op(Op), Threshold(Threshold) {}</div><div>> +</div><div>> +  /// \brief Return true if the given number is less than</div><div>> +  /// or greater than the certain threshold.</div><div>> +  bool PassesThreshold(T Value) const {</div>
<div>> +    switch (Op) {</div><div>> +    case LessThan:</div><div>> +      return Value < Threshold;</div><div>> +    case GreaterThan:</div><div>> +      return Value > Threshold;</div><div>> +    }</div>
<div>> +    return false;</div><div>> +  }</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions whose region coverage percentage</div><div>> +/// is above/below a certain percentage.</div>
<div>> +class RegionCoverageFilter : public CoverageFilter,</div><div>> +                             public StatisticThresholdFilter<double> {</div><div>> +public:</div><div>> +  RegionCoverageFilter(Operation Op, double Threshold)</div>
<div>> +      : StatisticThresholdFilter(Op, Threshold) {}</div><div>> +</div><div>> +  bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +/// \brief Matches functions whose line coverage percentage</div>
<div>> +/// is above/below a certain percentage.</div><div>> +class LineCoverageFilter : public CoverageFilter,</div><div>> +                           public StatisticThresholdFilter<double> {</div><div>> +public:</div>
<div>> +  LineCoverageFilter(Operation Op, double Threshold)</div><div>> +      : StatisticThresholdFilter(Op, Threshold) {}</div><div>> +</div><div>> +  bool matches(const FunctionCoverageMapping &Function) override;</div>
<div>> +};</div><div>> +</div><div>> +/// \brief A collection of filters.</div><div>> +/// Matches functions that match any filters contained</div><div>> +/// in an instance of this class.</div><div>> +class CoverageFilters : public CoverageFilter {</div>
<div>> +protected:</div><div>> +  std::vector<std::unique_ptr<CoverageFilter>> Filters;</div><div>> +</div><div>> +public:</div><div>> +  /// \brief Append a filter to this collection.</div><div>
> +  void push_back(CoverageFilter *filter);</div><div>> +</div><div>> +  bool empty() const { return Filters.empty(); }</div><div>> +</div><div>> +  bool matches(const FunctionCoverageMapping &Function) override;</div>
<div>> +};</div><div>> +</div><div>> +/// \brief A collection of filters.</div><div>> +/// Matches functions that match all of the filters contained</div><div>> +/// in an instance of this class.</div><div>
> +class CoverageFiltersMatchAll : public CoverageFilters {</div><div>> +public:</div><div>> +  bool matches(const FunctionCoverageMapping &Function) override;</div><div>> +};</div><div>> +</div><div>> +} // namespace llvm</div>
<div>> +</div><div>> +#endif // COVERAGEFILTERS_H</div><div>> Index: tools/llvm-cov/CoverageReport.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div>
<div>> +++ tools/llvm-cov/CoverageReport.cpp</div><div>> @@ -0,0 +1,204 @@</div><div>> +//===- CoverageReport.cpp - Code coverage report -------------------------===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements rendering of a code coverage report.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div>
<div>> +#include "CoverageReport.h"</div><div>> +#include "CoverageSummary.h"</div><div>> +#include "llvm/Support/Format.h"</div><div>> +#include "llvm/Support/FileSystem.h"</div>
<div>> +</div><div>> +using namespace llvm;</div><div>> +namespace {</div><div>> +/// \brief Helper struct which prints trimmed and aligned columns.</div><div>> +struct Column {</div><div>> +  enum TrimKind { NoTrim, LeftTrim, RightTrim };</div>
<div>> +</div><div>> +  enum AlignmentKind { LeftAlignment, RightAlignment };</div><div>> +</div><div>> +  StringRef Str;</div><div>> +  unsigned Width;</div><div>> +  TrimKind Trim;</div><div>> +  AlignmentKind Alignment;</div>
<div>> +</div><div>> +  Column(StringRef Str, unsigned Width)</div><div>> +      : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}</div><div>> +</div><div>> +  Column &set(TrimKind Value) {</div>
<div>> +    Trim = Value;</div><div>> +    return *this;</div><div>> +  }</div><div>> +</div><div>> +  Column &set(AlignmentKind Value) {</div><div>> +    Alignment = Value;</div><div>> +    return *this;</div>
<div>> +  }</div><div>> +</div><div>> +  void render(raw_ostream &OS) const;</div><div>> +};</div><div>> +raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {</div><div>> +  Value.render(OS);</div>
<div>> +  return OS;</div><div>> +}</div><div>> +}</div><div>> +</div><div>> +void Column::render(raw_ostream &OS) const {</div><div>> +  if (Str.size() <= Width) {</div><div>> +    if (Alignment == RightAlignment) {</div>
<div>> +      OS.indent(Width - Str.size());</div><div>> +      OS << Str;</div><div>> +      return;</div><div>> +    }</div><div>> +    OS << Str;</div><div>> +    OS.indent(Width - Str.size());</div>
<div>> +    return;</div><div>> +  }</div><div>> +</div><div>> +  switch (Trim) {</div><div>> +  case NoTrim:</div><div>> +    OS << Str.substr(0, Width);</div><div>> +    break;</div><div>> +  case LeftTrim:</div>
<div>> +    OS << "..." << Str.substr(Str.size() - Width + 3);</div><div>> +    break;</div><div>> +  case RightTrim:</div><div>> +    OS << Str.substr(0, Width - 3) << "...";</div>
<div>> +    break;</div><div>> +  }</div><div>> +}</div><div>> +</div><div>> +static Column column(StringRef Str, unsigned Width) {</div><div>> +  return Column(Str, Width);</div><div>> +}</div><div>> +</div>
<div>> +template <typename T></div><div>> +static Column column(StringRef Str, unsigned Width, const T &Value) {</div><div>> +  return Column(Str, Width).set(Value);</div><div>> +}</div><div>> +</div>
<div>> +static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 8};</div><div>> +static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};</div><div>> +</div><div>> +/// \brief Prints a horizontal divider which spans across the given columns.</div>
<div>> +template <typename T, size_t N></div><div>> +static void renderDivider(T (&Columns)[N], raw_ostream &OS) {</div><div>> +  unsigned Length = 0;</div><div>> +  for (unsigned I = 0; I < N; ++I)</div>
<div>> +    Length += Columns[I];</div><div>> +  for (unsigned I = 0; I < Length; ++I)</div><div>> +    OS << '-';</div><div>> +}</div><div>> +</div><div>> +/// \brief Return the color which correponds to the coverage</div>
<div>> +/// percentage of a certain metric.</div><div>> +template <typename T></div><div>> +static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {</div><div>> +  if (Info.isFullyCovered())</div>
<div>> +    return raw_ostream::GREEN;</div><div>> +  return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW</div><div>> +                                          : raw_ostream::RED;</div><div>> +}</div>
<div>> +</div><div>> +void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {</div><div>> +  OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)</div><div>> +     << format("%*zd", FileReportColumns[1], File.RegionCoverage.NumRegions);</div>
<div>> +  if (Options.Colors)</div><div>> +    OS.changeColor(File.RegionCoverage.isFullyCovered() ? raw_ostream::GREEN</div><div>> +                                                        : raw_ostream::RED);</div>
<div>> +  OS << format("%*zd", FileReportColumns[2], File.RegionCoverage.NotCovered);</div><div>> +  if (Options.Colors)</div><div>> +    OS.changeColor(determineCoveragePercentageColor(File.RegionCoverage));</div>
<div>> +  OS << format("%*.2f", FileReportColumns[3] - 1,</div><div>> +               File.RegionCoverage.getPercentCovered()) << '%';</div><div>> +  OS.resetColor();</div><div>> +  OS << format("%*zd", FileReportColumns[4],</div>
<div>> +               File.FunctionCoverage.NumFunctions);</div><div>> +  if (Options.Colors)</div><div>> +    OS.changeColor(determineCoveragePercentageColor(File.FunctionCoverage));</div><div>> +  OS << format("%*.2f", FileReportColumns[5] - 1,</div>
<div>> +               File.FunctionCoverage.getPercentCovered()) << '%';</div><div>> +  OS.resetColor();</div><div>> +  OS << "\n";</div><div>> +}</div><div><br></div><div>Might be nicer to have an "unsigned Col" and increment it instead of</div>
<div>writing out the column index by hand. I don't feel strongly though.</div><div><br></div><div>> +</div><div>> +void CoverageReport::render(const FunctionCoverageSummary &Function,</div><div>> +                            raw_ostream &OS) {</div>
<div>> +  OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim)</div><div>> +     << format("%*zd", FunctionReportColumns[1],</div><div>> +               Function.RegionCoverage.NumRegions);</div>
<div>> +  if (Options.Colors)</div><div>> +    OS.changeColor(Function.RegionCoverage.isFullyCovered() ? raw_ostream::GREEN</div><div>> +                                                            : raw_ostream::RED);</div>
<div>> +  OS << format("%*zd", FunctionReportColumns[2],</div><div>> +               Function.RegionCoverage.NotCovered);</div><div>> +  if (Options.Colors)</div><div>> +    OS.changeColor(determineCoveragePercentageColor(Function.RegionCoverage));</div>
<div>> +  OS << format("%*.2f", FunctionReportColumns[3] - 1,</div><div>> +               Function.RegionCoverage.getPercentCovered()) << '%';</div><div>> +  OS.resetColor();</div><div>
> +  OS << format("%*zd", FunctionReportColumns[4],</div><div>> +               Function.LineCoverage.NumLines);</div><div>> +  if (Options.Colors)</div><div>> +    OS.changeColor(Function.LineCoverage.isFullyCovered() ? raw_ostream::GREEN</div>
<div>> +                                                          : raw_ostream::RED);</div><div>> +  OS << format("%*zd", FunctionReportColumns[5],</div><div>> +               Function.LineCoverage.NotCovered);</div>
<div>> +  if (Options.Colors)</div><div>> +    OS.changeColor(determineCoveragePercentageColor(Function.LineCoverage));</div><div>> +  OS << format("%*.2f", FunctionReportColumns[6] - 1,</div><div>
> +               Function.LineCoverage.getPercentCovered()) << '%';</div><div>> +  OS.resetColor();</div><div>> +  OS << "\n";</div><div>> +}</div><div>> +</div><div>> +void CoverageReport::renderFunctionReports(raw_ostream &OS) {</div>
<div>> +  bool isFirst = true;</div><div>> +  for (const auto &File : Summary.getFileSummaries()) {</div><div>> +    if (isFirst)</div><div>> +      isFirst = false;</div><div>> +    else</div><div>> +      OS << "\n";</div>
<div>> +    OS << "File '" << File.Name << "':\n";</div><div>> +    OS << column("Name", FunctionReportColumns[0])</div><div>> +       << column("Regions", FunctionReportColumns[1], Column::RightAlignment)</div>
<div>> +       << column("Miss", FunctionReportColumns[2], Column::RightAlignment)</div><div>> +       << column("Cover", FunctionReportColumns[3], Column::RightAlignment)</div><div>> +       << column("Lines", FunctionReportColumns[4], Column::RightAlignment)</div>
<div>> +       << column("Miss", FunctionReportColumns[5], Column::RightAlignment)</div><div>> +       << column("Cover", FunctionReportColumns[6], Column::RightAlignment);</div><div>
> +    OS << "\n";</div><div>> +    renderDivider(FunctionReportColumns, OS);</div><div>> +    OS << "\n";</div><div>> +    for (const auto &Function : File.FunctionSummaries)</div>
<div>> +      render(Function, OS);</div><div>> +    renderDivider(FunctionReportColumns, OS);</div><div>> +    OS << "\n";</div><div>> +    render(FunctionCoverageSummary("TOTAL", File.RegionCoverage,</div>
<div>> +                                   File.LineCoverage),</div><div>> +           OS);</div><div>> +  }</div><div>> +}</div><div>> +</div><div>> +void CoverageReport::renderFileReports(raw_ostream &OS) {</div>
<div>> +  OS << column("Filename", FileReportColumns[0])</div><div>> +     << column("Regions", FileReportColumns[1], Column::RightAlignment)</div><div>> +     << column("Miss", FileReportColumns[2], Column::RightAlignment)</div>
<div>> +     << column("Cover", FileReportColumns[3], Column::RightAlignment)</div><div>> +     << column("Functions", FileReportColumns[4], Column::RightAlignment)</div><div>> +     << column("Cover", FileReportColumns[5], Column::RightAlignment) << "\n";</div>
<div>> +  renderDivider(FileReportColumns, OS);</div><div>> +  OS << "\n";</div><div>> +  for (const auto &File : Summary.getFileSummaries())</div><div>> +    render(File, OS);</div><div>> +  renderDivider(FileReportColumns, OS);</div>
<div>> +  OS << "\n";</div><div>> +  render(Summary.getCombinedFileSummaries(), OS);</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageReport.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageReport.h</div><div>> @@ -0,0 +1,40 @@</div><div>> +//===- CoverageReport.h - Code coverage report ---------------------------===//</div><div>> +//</div>
<div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class implements rendering of a code coverage report.</div><div>> +//</div>
<div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGEREPORT_H</div><div>> +#define COVERAGEREPORT_H</div><div>> +</div><div>> +#include "CoverageViewOptions.h"</div>
<div>> +#include "CoverageSummary.h"</div><div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Displays the code coverage report</div><div>> +class CoverageReport {</div>
<div>> +  const CoverageViewOptions &Options;</div><div>> +  CoverageSummary &Summary;</div><div>> +</div><div>> +  void render(const FileCoverageSummary &File, raw_ostream &OS);</div><div>> +  void render(const FunctionCoverageSummary &Function, raw_ostream &OS);</div>
<div>> +</div><div>> +public:</div><div>> +  CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary)</div><div>> +      : Options(Options), Summary(Summary) {}</div><div>> +</div>
<div>> +  void renderFunctionReports(raw_ostream &OS);</div><div>> +</div><div>> +  void renderFileReports(raw_ostream &OS);</div><div>> +};</div><div>> +}</div><div>> +</div><div>> +#endif // COVERAGEREPORT_H</div>
<div>> Index: tools/llvm-cov/CoverageSummary.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageSummary.cpp</div>
<div>> @@ -0,0 +1,93 @@</div><div>> +//===- CoverageSummary.cpp - Code coverage summary ------------------------===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>
> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements data management and rendering for the code coverage</div><div>> +// summaries of all files and functions.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +</div><div>> +#include "CoverageSummary.h"</div><div>> +#include "llvm/Support/FileSystem.h"</div><div>> +#include "llvm/Support/Format.h"</div><div>> +</div><div>> +using namespace llvm;</div>
<div>> +</div><div>> +unsigned CoverageSummary::getFileID(StringRef Filename) {</div><div>> +  for (unsigned I = 0, S = Filenames.size(); I < S; ++I) {</div><div>> +    if (sys::fs::equivalent(Filenames[I], Filename))</div>
<div>> +      return I;</div><div>> +  }</div><div>> +  Filenames.push_back(Filename);</div><div>> +  return Filenames.size() - 1;</div><div>> +}</div><div>> +</div><div>> +void</div><div>> +CoverageSummary::createSummaries(ArrayRef<FunctionCoverageMapping> Functions) {</div>
<div>> +  std::vector<std::pair<unsigned, size_t>> FunctionFileIDs;</div><div>> +</div><div>> +  FunctionFileIDs.resize(Functions.size());</div><div>> +  for (size_t I = 0, S = Functions.size(); I < S; ++I) {</div>
<div>> +    StringRef Filename = Functions[I].Filenames[0];</div><div>> +    FunctionFileIDs[I] = std::make_pair(getFileID(Filename), I);</div><div>> +  }</div><div>> +</div><div>> +  // Sort the function records by file ids</div>
<div>> +  std::sort(FunctionFileIDs.begin(), FunctionFileIDs.end(),</div><div>> +            [](const std::pair<unsigned, size_t> &lhs,</div><div>> +               const std::pair<unsigned, size_t> &rhs) {</div>
<div>> +    return lhs.first < rhs.first;</div><div>> +  });</div><div>> +</div><div>> +  // Create function summaries in a sorted order (by file ids)</div><div>> +  FunctionSummaries.reserve(Functions.size());</div>
<div>> +  for (size_t I = 0, S = Functions.size(); I < S; ++I)</div><div>> +    FunctionSummaries.push_back(</div><div>> +        FunctionCoverageSummary::get(Functions[FunctionFileIDs[I].second]));</div><div>
> +</div><div>> +  // Create file summaries</div><div>> +  size_t CurrentSummary = 0;</div><div>> +  for (unsigned FileID = 0; FileID < Filenames.size(); ++FileID) {</div><div>> +    // Gather the relevant functions summaries</div>
<div>> +    auto PrevSummary = CurrentSummary;</div><div>> +    for (; CurrentSummary < FunctionSummaries.size() &&</div><div>> +               FunctionFileIDs[CurrentSummary].first == FileID;</div><div>
> +         ++CurrentSummary)</div><div>> +      ;</div><div><br></div><div>It's clearer (and shorter!) to use a while loop here:</div><div><br></div><div>    while (CurrentSummary < FunctionSummaries.size() &&</div>
<div>           FunctionFileIDs[CurrentSummary].first == FileID)</div><div>        ++CurrentSummary;</div><div><br></div><div>> +    ArrayRef<FunctionCoverageSummary> LocalSummaries(</div><div>> +        FunctionSummaries.data() + PrevSummary,</div>
<div>> +        FunctionSummaries.data() + CurrentSummary);</div><div>> +    if (LocalSummaries.empty())</div><div>> +      continue;</div><div>> +</div><div>> +    FileSummaries.push_back(</div><div>> +        FileCoverageSummary::get(Filenames[FileID], LocalSummaries));</div>
<div>> +  }</div><div>> +}</div><div>> +</div><div>> +FileCoverageSummary CoverageSummary::getCombinedFileSummaries() {</div><div>> +  size_t NumRegions = 0, CoveredRegions = 0;</div><div>> +  size_t NumLines = 0, NumNonCodeLines = 0, CoveredLines = 0;</div>
<div>> +  size_t NumFunctionsCovered = 0, NumFunctions = 0;</div><div>> +  for (const auto &File : FileSummaries) {</div><div>> +    NumRegions += File.RegionCoverage.NumRegions;</div><div>> +    CoveredRegions += File.RegionCoverage.Covered;</div>
<div>> +</div><div>> +    NumLines += File.LineCoverage.NumLines;</div><div>> +    NumNonCodeLines += File.LineCoverage.NumNonCodeLines;</div><div>> +    CoveredLines += File.LineCoverage.Covered;</div><div>> +</div>
<div>> +    NumFunctionsCovered += File.FunctionCoverage.FullyCovered;</div><div>> +    NumFunctions += File.FunctionCoverage.NumFunctions;</div><div>> +  }</div><div>> +  return FileCoverageSummary(</div><div>
> +      "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions),</div><div>> +      LineCoverageInfo(CoveredLines, NumNonCodeLines, NumLines),</div><div>> +      FunctionCoverageInfo(NumFunctionsCovered, NumFunctions),</div>
<div>> +      ArrayRef<FunctionCoverageSummary>());</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageSummary.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageSummary.h</div><div>> @@ -0,0 +1,45 @@</div><div>> +//===- CoverageSummary.h - Code coverage summary --------------------------===//</div><div>> +//</div>
<div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class implements data management and rendering for the code coverage</div>
<div>> +// summaries of all files and functions.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGESUMMARY_H</div>
<div>> +#define COVERAGESUMMARY_H</div><div>> +</div><div>> +#include "CoverageSummaryInfo.h"</div><div>> +#include <vector></div><div>> +</div><div>> +namespace llvm {</div><div>> +</div>
<div>> +/// \brief Manager for the function and file code coverage summaries.</div><div>> +class CoverageSummary {</div><div>> +  std::vector<StringRef> Filenames;</div><div>> +  std::vector<FunctionCoverageSummary> FunctionSummaries;</div>
<div>> +  std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs;</div><div>> +  std::vector<FileCoverageSummary> FileSummaries;</div><div>> +</div><div>> +  unsigned getFileID(StringRef Filename);</div>
<div>> +</div><div>> +public:</div><div>> +  void createSummaries(ArrayRef<FunctionCoverageMapping> Functions);</div><div>> +</div><div>> +  ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; }</div>
<div>> +</div><div>> +  FileCoverageSummary getCombinedFileSummaries();</div><div>> +</div><div>> +  void render(const FunctionCoverageSummary &Summary, raw_ostream &OS);</div><div>> +</div><div>> +  void render(raw_ostream &OS);</div>
<div>> +};</div><div>> +}</div><div>> +</div><div>> +#endif // COVERAGESUMMARY_H</div><div>> Index: tools/llvm-cov/CoverageSummaryInfo.cpp</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageSummaryInfo.cpp</div><div>> @@ -0,0 +1,95 @@</div><div>> +//===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===//</div><div>
> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// These structures are used to represent code coverage metrics</div><div>> +// for functions/files.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "CoverageSummaryInfo.h"</div><div>> +</div><div>> +using namespace llvm;</div>
<div>> +using namespace coverage;</div><div>> +</div><div>> +FunctionCoverageSummary</div><div>> +FunctionCoverageSummary::get(const FunctionCoverageMapping &Function) {</div><div>> +  // Compute the region coverage</div>
<div>> +  size_t NumCodeRegions = 0, CoveredRegions = 0;</div><div>> +  for (auto &Region : Function.MappingRegions) {</div><div>> +    if (Region.Kind != CounterMappingRegion::CodeRegion)</div><div>> +      continue;</div>
<div>> +    ++NumCodeRegions;</div><div>> +    if (Region.ExecutionCount != 0)</div><div>> +      ++CoveredRegions;</div><div>> +  }</div><div>> +</div><div>> +  // Compute the line coverage</div><div>> +  size_t NumLines = 0, CoveredLines = 0;</div>
<div>> +  for (unsigned FileID = 0, S = Function.Filenames.size(); FileID < S;</div><div>> +       ++FileID) {</div><div>> +    // Find the line start and end of the function's source code</div><div>> +    // in that particular file</div>
<div>> +    unsigned LineStart = std::numeric_limits<unsigned>::max();</div><div>> +    unsigned LineEnd = 0;</div><div>> +    for (auto &Region : Function.MappingRegions) {</div><div>> +      if (Region.FileID != FileID)</div>
<div>> +        continue;</div><div>> +      LineStart = std::min(LineStart, Region.LineStart);</div><div>> +      LineEnd = std::max(LineEnd, Region.LineEnd);</div><div>> +    }</div><div>> +    unsigned LineCount = LineEnd - LineStart + 1;</div>
<div>> +</div><div>> +    // Get counters</div><div>> +    llvm::SmallVector<uint64_t, 16> ExecutionCounts;</div><div>> +    ExecutionCounts.resize(LineCount, 0);</div><div>> +    for (auto &Region : Function.MappingRegions) {</div>
<div>> +      if (Region.FileID != FileID)</div><div>> +        continue;</div><div>> +      // Ignore the lines that were skipped by the preprocessor.</div><div>> +      auto ExecutionCount = Region.ExecutionCount;</div>
<div>> +      if (Region.Kind == MappingRegion::SkippedRegion) {</div><div>> +        LineCount -= Region.LineEnd - Region.LineStart + 1;</div><div>> +        ExecutionCount = 1;</div><div>> +      }</div><div>
> +      for (unsigned I = Region.LineStart; I <= Region.LineEnd; ++I)</div><div>> +        ExecutionCounts[I - LineStart] = ExecutionCount;</div><div>> +    }</div><div>> +    CoveredLines += LineCount - std::count(ExecutionCounts.begin(),</div>
<div>> +                                           ExecutionCounts.end(), 0);</div><div>> +    NumLines += LineCount;</div><div>> +  }</div><div>> +  return FunctionCoverageSummary(</div><div>> +      Function.PrettyName, RegionCoverageInfo(CoveredRegions, NumCodeRegions),</div>
<div>> +      LineCoverageInfo(CoveredLines, 0, NumLines));</div><div>> +}</div><div>> +</div><div>> +FileCoverageSummary</div><div>> +FileCoverageSummary::get(StringRef Name,</div><div>> +                         ArrayRef<FunctionCoverageSummary> FunctionSummaries) {</div>
<div>> +  size_t NumRegions = 0, CoveredRegions = 0;</div><div>> +  size_t NumLines = 0, NumNonCodeLines = 0, CoveredLines = 0;</div><div>> +  size_t NumFunctionsCovered = 0;</div><div>> +  for (const auto &Func : FunctionSummaries) {</div>
<div>> +    CoveredRegions += Func.RegionCoverage.Covered;</div><div>> +    NumRegions += Func.RegionCoverage.NumRegions;</div><div>> +</div><div>> +    CoveredLines += Func.LineCoverage.Covered;</div><div>> +    NumNonCodeLines += Func.LineCoverage.NumNonCodeLines;</div>
<div>> +    NumLines += Func.LineCoverage.NumLines;</div><div>> +</div><div>> +    if (Func.RegionCoverage.isFullyCovered())</div><div>> +      ++NumFunctionsCovered;</div><div>> +  }</div><div>> +</div>
<div>> +  return FileCoverageSummary(</div><div>> +      Name, RegionCoverageInfo(CoveredRegions, NumRegions),</div><div>> +      LineCoverageInfo(CoveredLines, NumNonCodeLines, NumLines),</div><div>> +      FunctionCoverageInfo(NumFunctionsCovered, FunctionSummaries.size()),</div>
<div>> +      FunctionSummaries);</div><div>> +}</div><div>> Index: tools/llvm-cov/CoverageSummaryInfo.h</div><div>> ===================================================================</div><div>> --- /dev/null</div>
<div>> +++ tools/llvm-cov/CoverageSummaryInfo.h</div><div>> @@ -0,0 +1,133 @@</div><div>> +//===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// These structures are used to represent code coverage metrics</div><div>> +// for functions/files.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +</div><div>> +#ifndef COVERAGESUMMARYINFO_H</div><div>> +#define COVERAGESUMMARYINFO_H</div><div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "llvm/Support/raw_ostream.h"</div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Provides information about region coverage for a function/file</div><div><br></div><div>Comments should end in a period. You seem to have been consistent about</div>
<div>full sentences everywhere except for this file ;)</div><div><br></div><div>> +struct RegionCoverageInfo {</div><div>> +  /// \brief The number of regions that were executed at least once</div><div>> +  size_t Covered;</div>
<div>> +</div><div>> +  /// \brief The number of regions that weren't executed</div><div>> +  size_t NotCovered;</div><div>> +</div><div>> +  /// \brief The total number of regions in a function/file</div>
<div>> +  size_t NumRegions;</div><div>> +</div><div>> +  RegionCoverageInfo(size_t Covered, size_t NumRegions)</div><div>> +      : Covered(Covered), NotCovered(NumRegions - Covered),</div><div>> +        NumRegions(NumRegions) {}</div>
<div>> +</div><div>> +  bool isFullyCovered() const { return Covered == NumRegions; }</div><div>> +</div><div>> +  double getPercentCovered() const {</div><div>> +    return double(Covered) / double(NumRegions) * 100.0;</div>
<div>> +  }</div><div>> +};</div><div>> +</div><div>> +/// \brief Provides information about line coverage for a function/file</div><div>> +struct LineCoverageInfo {</div><div>> +  /// \brief The number of lines that were executed at least once</div>
<div>> +  size_t Covered;</div><div>> +</div><div>> +  /// \brief The number of lines that weren't executed</div><div>> +  size_t NotCovered;</div><div>> +</div><div>> +  /// \brief The number of lines that aren't code</div>
<div>> +  size_t NumNonCodeLines;</div><div><br></div><div>NumNon is a bit awkward to say. It's fairly obvious it's a number, so</div><div>how about just NonCodeLines?</div><div><br></div><div>> +</div><div>
> +  /// \brief The total number of lines in a function/file</div><div>> +  size_t NumLines;</div><div>> +</div><div>> +  LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines)</div><div>> +      : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered),</div>
<div>> +        NumNonCodeLines(NumNonCodeLines), NumLines(NumLines) {}</div><div>> +</div><div>> +  bool isFullyCovered() const {</div><div>> +    return Covered == (NumLines - NumNonCodeLines);</div><div>> +  }</div>
<div>> +</div><div>> +  double getPercentCovered() const {</div><div>> +    return double(Covered) / double(NumLines - NumNonCodeLines) * 100.0;</div><div>> +  }</div><div>> +};</div><div>> +</div><div>> +/// \brief Provides information about function coverage for a file</div>
<div>> +struct FunctionCoverageInfo {</div><div>> +  /// \brief The number of functions that have full</div><div>> +  /// region coverage</div><div>> +  size_t FullyCovered;</div><div>> +</div><div>> +  /// \brief The total number of functions in this file</div>
<div>> +  size_t NumFunctions;</div><div>> +</div><div>> +  FunctionCoverageInfo(size_t FullyCovered, size_t NumFunctions)</div><div>> +      : FullyCovered(FullyCovered), NumFunctions(NumFunctions) {}</div><div>
> +</div><div>> +  bool isFullyCovered() const { return FullyCovered == NumFunctions; }</div><div>> +</div><div>> +  double getPercentCovered() const {</div><div>> +    return double(FullyCovered) / double(NumFunctions) * 100.0;</div>
<div>> +  }</div><div>> +};</div><div>> +</div><div>> +/// \brief A summary of function's code coverage</div><div>> +struct FunctionCoverageSummary {</div><div>> +  StringRef Name;</div><div>> +  RegionCoverageInfo RegionCoverage;</div>
<div>> +  LineCoverageInfo LineCoverage;</div><div>> +</div><div>> +  FunctionCoverageSummary(StringRef Name,</div><div>> +                          const RegionCoverageInfo &RegionCoverage,</div><div>> +                          const LineCoverageInfo &LineCoverage)</div>
<div>> +      : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {</div><div>> +  }</div><div>> +</div><div>> +  /// \brief Compute the code coverage summary for the given function coverage</div>
<div>> +  /// mapping record.</div><div>> +  static FunctionCoverageSummary get(const FunctionCoverageMapping &Function);</div><div>> +};</div><div>> +</div><div>> +/// \brief A summary of file's code coverage</div>
<div>> +struct FileCoverageSummary {</div><div>> +  StringRef Name;</div><div>> +  RegionCoverageInfo RegionCoverage;</div><div>> +  LineCoverageInfo LineCoverage;</div><div>> +  FunctionCoverageInfo FunctionCoverage;</div>
<div>> +  /// \brief The summary of every function</div><div>> +  /// in this file.</div><div>> +  ArrayRef<FunctionCoverageSummary> FunctionSummaries;</div><div>> +</div><div>> +  FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage,</div>
<div>> +                      const LineCoverageInfo &LineCoverage,</div><div>> +                      const FunctionCoverageInfo &FunctionCoverage,</div><div>> +                      ArrayRef<FunctionCoverageSummary> FunctionSummaries)</div>
<div>> +      : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),</div><div>> +        FunctionCoverage(FunctionCoverage),</div><div>> +        FunctionSummaries(FunctionSummaries) {}</div><div>
> +</div><div>> +  /// \brief Compute the code coverage summary for a file</div><div>> +  static FileCoverageSummary</div><div>> +  get(StringRef Name, ArrayRef<FunctionCoverageSummary> FunctionSummaries);</div>
<div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>> +</div><div>> +#endif // COVERAGESUMMARYINFO_H</div><div>> Index: tools/llvm-cov/CoverageViewOptions.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/CoverageViewOptions.h</div><div>> @@ -0,0 +1,28 @@</div><div>> +//===- CoverageViewOptions.h - Code coverage display options -------------===//</div><div>> +//</div>
<div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef COVERAGEVIEWOPTIONS_H</div><div>> +#define COVERAGEVIEWOPTIONS_H</div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief The options for displaying the code coverage information.</div><div>> +struct CoverageViewOptions {</div><div>> +  bool Debug;</div>
<div>> +  bool Colors;</div><div>> +  bool ShowLineNumbers;</div><div>> +  bool ShowLineStats;</div><div>> +  bool ShowRegionMarkers;</div><div>> +  bool ShowLineStatsOrRegionMarkers;</div><div>> +  bool ShowExpandedRegions;</div>
<div>> +  bool ShowFunctionInstantiations;</div><div>> +};</div><div>> +}</div><div>> +</div><div>> +#endif // COVERAGEVIEWOPTIONS_H</div><div>> Index: tools/llvm-cov/FunctionCoverageMapping.h</div><div>
> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/FunctionCoverageMapping.h</div><div>> @@ -0,0 +1,50 @@</div><div>> +//===- FunctionCoverageMapping.h - Function coverage mapping record -------===//</div>
<div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// A structure that stores the coverage mapping record for a single function.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef FUNCTIONCOVERAGEMAPPING_H</div><div>> +#define FUNCTIONCOVERAGEMAPPING_H</div>
<div>> +</div><div>> +#include <string></div><div>> +#include <vector></div><div>> +#include "llvm/ADT/ArrayRef.h"</div><div>> +#include "llvm/ADT/StringRef.h"</div><div>> +#include "llvm/ProfileData/CoverageMapping.h"</div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Associates a source range with an execution count.</div><div>> +struct MappingRegion : public coverage::CounterMappingRegion {</div>
<div>> +  uint64_t ExecutionCount;</div><div>> +</div><div>> +  MappingRegion(const CounterMappingRegion &R, uint64_t ExecutionCount)</div><div>> +      : CounterMappingRegion(R), ExecutionCount(ExecutionCount) {}</div>
<div>> +};</div><div>> +</div><div>> +/// \brief Stores all the required information</div><div>> +/// about code coverage for a single function.</div><div>> +struct FunctionCoverageMapping {</div><div>> +  /// \brief Raw function name</div>
<div>> +  std::string Name;</div><div>> +  /// \brief Demangled function name</div><div>> +  std::string PrettyName;</div><div>> +  std::vector<std::string> Filenames;</div><div>> +  std::vector<MappingRegion> MappingRegions;</div>
<div>> +</div><div>> +  FunctionCoverageMapping(StringRef Name, ArrayRef<StringRef> Filenames)</div><div>> +      : Name(Name), PrettyName(Name),</div><div>> +        Filenames(Filenames.begin(), Filenames.end()) {}</div>
<div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>> +</div><div>> +#endif // FUNCTIONCOVERAGEMAPPING_H</div><div>> Index: tools/llvm-cov/LLVMBuild.txt</div><div>> ===================================================================</div>
<div>> --- tools/llvm-cov/LLVMBuild.txt</div><div>> +++ tools/llvm-cov/LLVMBuild.txt</div><div>> @@ -19,4 +19,4 @@</div><div>>  type = Tool</div><div>>  name = llvm-cov</div><div>>  parent = Tools</div><div>
> -required_libraries = Instrumentation</div><div>> +required_libraries = ProfileData Support Instrumentation</div><div>> Index: tools/llvm-cov/Makefile</div><div>> ===================================================================</div>
<div>> --- tools/llvm-cov/Makefile</div><div>> +++ tools/llvm-cov/Makefile</div><div>> @@ -9,7 +9,7 @@</div><div>></div><div>>  LEVEL := ../..</div><div>>  TOOLNAME := llvm-cov</div><div>> -LINK_COMPONENTS := core support</div>
<div>> +LINK_COMPONENTS := core support profiledata object</div><div>></div><div>>  # This tool has no plugins, optimize startup time.</div><div>>  TOOL_NO_EXPORTS := 1</div><div>> Index: tools/llvm-cov/SourceCoverageDataManager.cpp</div>
<div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceCoverageDataManager.cpp</div><div>> @@ -0,0 +1,55 @@</div><div>> +//===- SourceCoverageDataManager.cpp - Manager for source file coverage</div>
<div>> +// data-===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div>
<div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class separates and merges mapping regions for a specific source file.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "SourceCoverageDataManager.h"</div><div>> +</div><div>> +using namespace llvm;</div>
<div>> +using namespace coverage;</div><div>> +</div><div>> +void SourceCoverageDataManager::insert(const MappingRegion &Region) {</div><div>> +  SourceRange Range(Region.LineStart, Region.ColumnStart, Region.LineEnd,</div>
<div>> +                    Region.ColumnEnd);</div><div>> +  if (Region.Kind == CounterMappingRegion::SkippedRegion) {</div><div>> +    SkippedRegions.push_back(Range);</div><div>> +    return;</div><div>> +  }</div>
<div>> +  auto It = Regions.find(Range);</div><div>> +  if (It == Regions.end()) {</div><div>> +    Regions.insert(std::make_pair(Range, Region.ExecutionCount));</div><div>> +    return;</div><div>> +  }</div>
<div>> +  It->second += Region.ExecutionCount;</div><div>> +}</div><div>> +</div><div>> +ArrayRef<std::pair<SourceCoverageDataManager::SourceRange, uint64_t>></div><div>> +SourceCoverageDataManager::getSourceRegions() {</div>
<div>> +  if (!SortedRegions.empty())</div><div>> +    return SortedRegions;</div><div>> +</div><div>> +  SortedRegions.reserve(Regions.size());</div><div>> +  for (const auto &Region : Regions)</div><div>
> +    SortedRegions.push_back(std::make_pair(Region.first, Region.second));</div><div>> +</div><div>> +  // Get rid of the map</div><div>> +  Regions.clear();</div><div>> +</div><div>> +  // Sort the final source regions</div>
<div>> +  std::sort(SortedRegions.begin(), SortedRegions.end(),</div><div>> +            [](const std::pair<SourceRange, uint64_t> &LHS,</div><div>> +               const std::pair<SourceRange, uint64_t> &RHS) {</div>
<div>> +    return LHS.first < RHS.first;</div><div>> +  });</div><div>> +</div><div>> +  return SortedRegions;</div><div>> +}</div><div>> Index: tools/llvm-cov/SourceCoverageDataManager.h</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceCoverageDataManager.h</div><div>> @@ -0,0 +1,83 @@</div><div>> +//===- SourceCoverageDataManager.h - Manager for source file coverage data-===//</div><div>
> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class separates and merges mapping regions for a specific source file.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#ifndef SOURCECOVERAGEDATAMANAGER_H</div><div>> +#define SOURCECOVERAGEDATAMANAGER_H</div>
<div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "llvm/ProfileData/CoverageMapping.h"</div><div>> +#include <vector></div><div>> +#include <unordered_map></div>
<div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Partions mapping regions by their kind and sums</div><div>> +/// the execution counts of the regions that start at the same location.</div>
<div>> +class SourceCoverageDataManager {</div><div>> +public:</div><div>> +  struct SourceRange {</div><div>> +    unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;</div><div>> +</div><div>> +    SourceRange(unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,</div>
<div>> +                unsigned ColumnEnd)</div><div>> +        : LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),</div><div>> +          ColumnEnd(ColumnEnd) {}</div><div>> +</div><div>> +    bool operator==(const SourceRange &Other) const {</div>
<div>> +      return LineStart == Other.LineStart && ColumnStart == Other.ColumnStart &&</div><div>> +             LineEnd == Other.LineEnd && ColumnEnd == Other.ColumnEnd;</div><div>> +    }</div>
<div>> +</div><div>> +    bool operator<(const SourceRange &Other) const {</div><div>> +      if (LineStart == Other.LineStart)</div><div>> +        return ColumnStart < Other.ColumnStart;</div><div>
> +      return LineStart < Other.LineStart;</div><div>> +    }</div><div>> +</div><div>> +    bool contains(const SourceRange &Other) {</div><div>> +      if (LineStart > Other.LineStart ||</div>
<div>> +          (LineStart == Other.LineStart && ColumnStart > Other.ColumnStart))</div><div>> +        return false;</div><div>> +      if (LineEnd < Other.LineEnd ||</div><div>> +          (LineEnd == Other.LineEnd && ColumnEnd < Other.ColumnEnd))</div>
<div>> +        return false;</div><div>> +      return true;</div><div>> +    }</div><div>> +</div><div>> +    struct Hash {</div><div>> +      size_t operator()(const SourceRange &Range) const {</div>
<div>> +        return Range.LineStart + (Range.ColumnStart << 9) +</div><div>> +               (Range.LineEnd << 16) + (Range.ColumnEnd << 25);</div><div><br></div><div>Where do these numbers come from? This needs some comments at the very</div>
<div>least.</div><div><br></div><div>> +      }</div><div>> +    };</div><div>> +  };</div><div>> +</div><div>> +protected:</div><div>> +  std::unordered_map<SourceRange, uint64_t, SourceRange::Hash> Regions;</div>
<div>> +  std::vector<std::pair<SourceRange, uint64_t>> SortedRegions;</div><div>> +  std::vector<SourceRange> SkippedRegions;</div><div>> +</div><div>> +public:</div><div>> +  void insert(const MappingRegion &Region);</div>
<div>> +</div><div>> +  /// \brief Return the source ranges and execution counts</div><div>> +  /// obtained from the non-skipped mapping regions.</div><div>> +  ArrayRef<std::pair<SourceRange, uint64_t>> getSourceRegions();</div>
<div>> +</div><div>> +  /// \brief Return the source ranges obtained from the skipped mapping regions.</div><div>> +  ArrayRef<SourceRange> getSkippedRegions() const { return SkippedRegions; }</div><div>> +};</div>
<div>> +</div><div>> +} // namespace llvm</div><div>> +</div><div>> +#endif // SOURCECOVERAGEDATAMANAGER_H</div><div>> Index: tools/llvm-cov/SourceCoverageView.cpp</div><div>> ===================================================================</div>
<div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceCoverageView.cpp</div><div>> @@ -0,0 +1,390 @@</div><div>> +//===- SourceCoverageView.cpp - Code coverage view for source code --------===//</div><div>
> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div>
<div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +//</div><div>> +// This class implements rendering for code coverage of source code.</div><div>
> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div><div>> +#include "SourceCoverageView.h"</div><div>> +#include "llvm/ADT/SmallString.h"</div>
<div>> +</div><div>> +using namespace llvm;</div><div>> +</div><div>> +void SourceCoverageView::skipToLine(StringRef &Source, unsigned LineToFind) {</div><div>> +  if (LineToFind < 2)</div><div>> +    return;</div>
<div>> +</div><div>> +  unsigned Line = 1;</div><div>> +  for (size_t I = 0; I < Source.size();) {</div><div>> +    if (Source[I] != '\n') {</div><div>> +      ++I;</div><div>> +      continue;</div>
<div>> +    }</div><div>> +    ++Line, ++I;</div><div>> +    if (Line == LineToFind) {</div><div>> +      Source = Source.substr(I);</div><div>> +      return;</div><div>> +    }</div><div>> +  }</div>
<div>> +  Source = StringRef();</div><div>> +}</div><div>> +</div><div>> +StringRef SourceCoverageView::getNextLine(StringRef &Source) {</div><div>> +  for (size_t I = 0; I < Source.size(); ++I) {</div>
<div>> +    if (Source[I] == '\n') {</div><div>> +      auto Line = Source.substr(0, I);</div><div>> +      Source = Source.substr(I + 1);</div><div>> +      return Line;</div><div>> +    }</div><div>
> +  }</div><div>> +  // This must be the last line</div><div>> +  return Source;</div><div>> +}</div><div><br></div><div>These functions look pretty similar to llvm/Support/LineIterator's</div><div>behaviour. Can we avoid some code duplication by using that interface</div>
<div>instead?</div><div><br></div><div>> +</div><div>> +void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,</div><div>> +                                    ArrayRef<HighlightRange> Ranges) {</div>
<div>> +  if (Ranges.empty()) {</div><div>> +    OS << Line << "\n";</div><div>> +    return;</div><div>> +  }</div><div>> +  if (Line.empty())</div><div>> +    Line = " ";</div>
<div>> +</div><div>> +  unsigned PrevColumnStart = 0;</div><div>> +  unsigned Start = 1;</div><div>> +  for (const auto &Range : Ranges) {</div><div>> +    if (PrevColumnStart == Range.ColumnStart)</div>
<div>> +      continue;</div><div>> +</div><div>> +    // Show the unhighlighted part</div><div>> +    unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;</div><div>> +    OS << Line.substr(Start - 1, ColumnStart - Start);</div>
<div>> +</div><div>> +    // Show the highlighted part</div><div>> +    auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED</div><div>> +                                                          : raw_ostream::CYAN;</div>
<div>> +    OS.changeColor(Color, false, true);</div><div>> +    unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size() + 1);</div><div>> +    OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);</div>
<div>> +    Start = ColumnEnd;</div><div>> +    OS.resetColor();</div><div>> +  }</div><div>> +</div><div>> +  // Show the rest of the line</div><div>> +  OS << Line.substr(Start - 1, Line.size() - Start + 1);</div>
<div>> +  OS << "\n";</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {</div><div>> +  for (unsigned J = 0; J < I; ++J)</div>
<div>> +    OS << "  |";</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,</div><div>> +                                           raw_ostream &OS) {</div>
<div>> +  for (unsigned J = 1; J < Offset; ++J)</div><div>> +    OS << "  |";</div><div>> +  if (Offset != 0)</div><div>> +    OS.indent(2);</div><div>> +  for (unsigned I = 0; I < Length; ++I)</div>
<div>> +    OS << "-";</div><div>> +}</div><div>> +</div><div>> +void</div><div>> +SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,</div><div>> +                                             const LineCoverageInfo &Line) {</div>
<div>> +  if (!Line.isMapped()) {</div><div>> +    OS.indent(LineCoverageColumnWidth) << '|';</div><div>> +    return;</div><div>> +  }</div><div>> +  SmallString<32> Buffer;</div><div>> +  raw_svector_ostream BufferOS(Buffer);</div>
<div>> +  BufferOS << Line.ExecutionCount;</div><div>> +  auto Str = BufferOS.str();</div><div>> +  if (Line.hasMultipleRegions() && Options.Colors)</div><div>> +    OS.changeColor(raw_ostream::MAGENTA, false, false);</div>
<div>> +  // Trim</div><div>> +  Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));</div><div>> +  // Align to the right</div><div>> +  OS.indent(LineCoverageColumnWidth - Str.size()) << Str;</div>
<div>> +  OS.resetColor();</div><div>> +  OS << '|';</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,</div><div>> +                                                unsigned LineNo) {</div>
<div>> +  SmallString<32> Buffer;</div><div>> +  raw_svector_ostream BufferOS(Buffer);</div><div>> +  BufferOS << LineNo;</div><div>> +  auto Str = BufferOS.str();</div><div>> +  // Trim and align to the right</div>
<div>> +  Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));</div><div>> +  OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';</div><div>> +}</div><div>> +</div>
<div>> +void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,</div><div>> +                                             ArrayRef<RegionMarker> Regions) {</div><div>> +  SmallString<32> Buffer;</div>
<div>> +  raw_svector_ostream BufferOS(Buffer);</div><div>> +</div><div>> +  unsigned PrevColumn = 1;</div><div>> +  for (const auto &Region : Regions) {</div><div>> +    // Skip to the new region</div>
<div>> +    if (Region.Column > PrevColumn)</div><div>> +      OS.indent(Region.Column - PrevColumn);</div><div>> +    PrevColumn = Region.Column + 1;</div><div>> +    BufferOS << Region.ExecutionCount;</div>
<div>> +    StringRef Str = BufferOS.str();</div><div>> +    // Trim the execution count</div><div>> +    Str = Str.substr(0, std::min(Str.size(), (size_t)7));</div><div>> +    PrevColumn += Str.size();</div><div>
> +    OS << '^' << Str;</div><div>> +    Buffer.clear();</div><div>> +  }</div><div>> +  OS << "\n";</div><div>> +}</div><div>> +</div><div>> +/// \brief Insert a new highlighting range into the line's highlighting ranges</div>
<div>> +/// Return line's new highlighting ranges in result.</div><div>> +static void InsertHighlightRange(</div><div><br></div><div>Functions start with lower case letters, ie, insertHighlightRange.</div><div><br>
</div><div>> +    ArrayRef<SourceCoverageView::HighlightRange> Ranges,</div><div>> +    SourceCoverageView::HighlightRange RangeToInsert,</div><div>> +    SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {</div>
<div>> +  Result.clear();</div><div>> +  for (const auto &Range : Ranges) {</div><div>> +    if (!Range.overlaps(RangeToInsert)) {</div><div>> +      Result.push_back(Range);</div><div>> +      continue;</div>
<div>> +    }</div><div>> +</div><div>> +    if (RangeToInsert.contains(Range))</div><div>> +      continue;</div><div>> +    if (Range.contains(RangeToInsert)) {</div><div>> +      // Split into 0 - 2 ranges</div>
<div>> +      if (RangeToInsert.ColumnStart != Range.ColumnStart)</div><div>> +        Result.push_back(SourceCoverageView::HighlightRange(</div><div>> +            Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,</div>
<div>> +            Range.Kind));</div><div>> +      if (RangeToInsert.ColumnEnd != Range.ColumnEnd)</div><div>> +        Result.push_back(SourceCoverageView::HighlightRange(</div><div>> +            Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));</div>
<div>> +    } else if (Range.columnStartOverlaps(RangeToInsert)) {</div><div>> +      if (RangeToInsert.ColumnStart != Range.ColumnStart)</div><div>> +        Result.push_back(SourceCoverageView::HighlightRange(</div>
<div>> +            Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,</div><div>> +            Range.Kind));</div><div>> +    } else {</div><div>> +      if (RangeToInsert.ColumnEnd != Range.ColumnEnd)</div>
<div>> +        Result.push_back(SourceCoverageView::HighlightRange(</div><div>> +            Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));</div><div>> +    }</div><div>> +  }</div><div>> +  Result.push_back(RangeToInsert);</div>
<div>> +  std::sort(Result.begin(), Result.end());</div><div><br></div><div>Do we really need to sort here? AFAICT createHighlightRanges sorted</div><div>these already, so we should just be able to insert our range at the</div>
<div>right point above, no?</div><div><br></div><div>Similarly, I think this logic can be simplified if the input is</div><div>guaranteed to already be sorted.</div><div><br></div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::sortChildren() {</div>
<div>> +  for (auto &I : Children)</div><div>> +    I->sortChildren();</div><div>> +  std::sort(Children.begin(), Children.end(),</div><div>> +            [](const std::unique_ptr<SourceCoverageView> &LHS,</div>
<div>> +               const std::unique_ptr<SourceCoverageView> &RHS) {</div><div>> +    return LHS->ExpansionRegion < RHS->ExpansionRegion;</div><div>> +  });</div><div>> +}</div><div>> +</div>
<div>> +SourceCoverageView::HighlightRange</div><div>> +SourceCoverageView::getExpansionHighlightRange() const {</div><div>> +  return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,</div><div>
> +                        ExpansionRegion.ColumnEnd, HighlightRange::Expanded);</div><div>> +}</div><div>> +</div><div>> +template <typename T></div><div>> +ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,</div>
<div>> +                            unsigned LineNo) {</div><div>> +  auto PrevIdx = CurrentIdx;</div><div>> +  for (auto S = Items.size();</div><div>> +       CurrentIdx < S && Items[CurrentIdx].Line == LineNo; ++CurrentIdx)</div>
<div>> +    ;</div><div><br></div><div>This is simpler using "while".</div><div><br></div><div>> +  return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);</div><div>> +}</div><div>> +</div>
<div>> +void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {</div><div><br></div><div>This whole function is confusing. Notably, what is going on with</div><div>ChildIterator? You iterator over all of the children at the end of the</div>
<div>first iteration of the outer (LineCount) loop, and you check if that's</div><div>already happened for some of the intermediate steps.</div><div><br></div><div>At the very least, this needs some comments explaining what and why</div>
<div>different parts of the function are doing what they're doing. I suspect</div><div>it might be possible to reorganize it so it's clearer in the first place</div><div>though.</div><div><br></div><div>> +  sortChildren();</div>
<div>> +</div><div>> +  SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;</div><div>> +  auto ChildIterator = Children.begin();</div><div>> +  size_t CurrentHighlightRange = 0;</div><div>> +  size_t CurrentRegionMarker = 0;</div>
<div>> +</div><div>> +  StringRef Source = File.getBuffer();</div><div>> +  skipToLine(Source, LineStart);</div><div>> +  unsigned CombinedColumnWidth =</div><div>> +      (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +</div>
<div>> +      (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);</div><div>> +  unsigned DividerWidth = CombinedColumnWidth + 4;</div><div>> +</div><div>> +  for (size_t I = 0; I < LineCount; ++I) {</div>
<div>> +    unsigned LineNo = I + LineStart;</div><div>> +</div><div>> +    renderOffset(OS, Offset);</div><div>> +    if (Options.ShowLineStats)</div><div>> +      renderLineCoverageColumn(OS, LineStats[I]);</div>
<div>> +    if (Options.ShowLineNumbers)</div><div>> +      renderLineNumberColumn(OS, LineNo);</div><div>> +</div><div>> +    // Gather highlighting ranges</div><div>> +    auto LineHighlightRanges =</div>
<div>> +        gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);</div><div>> +    auto LineRanges = LineHighlightRanges;</div><div>> +    if (ChildIterator != Children.end() &&</div><div>> +        (*ChildIterator)->ExpansionRegion.LineStart == LineNo &&</div>
<div>> +        Options.Colors) {</div><div>> +      InsertHighlightRange(LineHighlightRanges,</div><div>> +                           (*ChildIterator)->getExpansionHighlightRange(),</div><div>> +                           AdjustedLineHighlightRanges);</div>
<div>> +      LineRanges = AdjustedLineHighlightRanges;</div><div>> +    }</div><div>> +</div><div>> +    // Display the source code for the current line</div><div>> +    StringRef Line = getNextLine(Source);</div>
<div>> +    renderLine(OS, Line, LineRanges);</div><div>> +</div><div>> +    // Show the region markers</div><div>> +    bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||</div><div>> +                       LineStats[I].hasMultipleRegions();</div>
<div>> +    auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);</div><div>> +    if (ShowMarkers && !LineMarkers.empty()) {</div><div>> +      renderOffset(OS, Offset);</div><div>> +      OS.indent(CombinedColumnWidth);</div>
<div>> +      renderRegionMarkers(OS, LineMarkers);</div><div>> +    }</div><div>> +</div><div>> +    // Show the embedded expanded child views</div><div>> +    for (bool FirstChildExpansion = true;</div><div>
> +         ChildIterator != Children.end() &&</div><div>> +             (*ChildIterator)->ExpansionRegion.LineStart == LineNo;</div><div>> +         ++ChildIterator) {</div><div>> +      const auto &Child = *ChildIterator;</div>
<div>> +      unsigned NewOffset = Offset + 1;</div><div>> +</div><div>> +      if (Child->isInstantiationSubView()) {</div><div>> +        if (FirstChildExpansion) {</div><div>> +          renderViewDivider(NewOffset, CombinedColumnWidth + 4, OS);</div>
<div>> +          OS << "\n";</div><div>> +        }</div><div>> +        renderOffset(OS, NewOffset);</div><div>> +        OS << ' ';</div><div>> +        if (Options.Colors)</div>
<div>> +          OS.changeColor(raw_ostream::CYAN, false, false);</div><div>> +        OS << Child->FunctionName << ":";</div><div>> +        OS.resetColor();</div><div>> +        OS << "\n";</div>
<div>> +      }</div><div>> +</div><div>> +      if (FirstChildExpansion)</div><div>> +        FirstChildExpansion = false;</div><div>> +      else if (Child->isExpansionSubView()) {</div><div>> +        // Re-render the current line</div>
<div>> +        InsertHighlightRange(LineHighlightRanges,</div><div>> +                             Child->getExpansionHighlightRange(),</div><div>> +                             AdjustedLineHighlightRanges);</div>
<div>> +        renderOffset(OS, Offset);</div><div>> +        OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));</div><div>> +        renderLine(OS, Line, AdjustedLineHighlightRanges);</div><div>> +      }</div>
<div>> +</div><div>> +      if (Child->isExpansionSubView()) {</div><div>> +        renderViewDivider(NewOffset, DividerWidth, OS);</div><div>> +        OS << "\n";</div><div>> +      }</div>
<div>> +      Child->render(OS, NewOffset);</div><div>> +      renderViewDivider(NewOffset, DividerWidth, OS);</div><div>> +      OS << "\n";</div><div>> +    }</div><div>> +  }</div><div>
> +}</div><div>> +</div><div>> +void</div><div>> +SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {</div><div>> +  LineStats.resize(LineCount);</div><div>> +  for (const auto &Region : Data.getSourceRegions()) {</div>
<div>> +    auto Value = Region.second;</div><div>> +    LineStats[Region.first.LineStart - LineStart].addRegionStartCount(Value);</div><div>> +    for (unsigned Line = Region.first.LineStart + 1;</div><div>> +         Line <= Region.first.LineEnd; ++Line)</div>
<div>> +      LineStats[Line - LineStart].addRegionCount(Value);</div><div>> +  }</div><div>> +</div><div>> +  // Reset the line stats for skipped regions</div><div>> +  for (const auto &Region : Data.getSkippedRegions()) {</div>
<div>> +    for (unsigned Line = Region.LineStart; Line <= Region.LineEnd; ++Line)</div><div>> +      LineStats[Line - LineStart] = LineCoverageInfo();</div><div>> +  }</div><div>> +}</div><div>> +</div>
<div>> +void</div><div>> +SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {</div><div>> +  auto Regions = Data.getSourceRegions();</div><div>> +  std::vector<bool> AlreadyHighlighted;</div>
<div>> +  AlreadyHighlighted.resize(Regions.size(), false);</div><div>> +</div><div>> +  for (size_t I = 0, S = Regions.size(); I < S; ++I) {</div><div>> +    const auto &Region = Regions[I];</div><div>
> +    auto Value = Region.second;</div><div>> +    auto SrcRange = Region.first;</div><div>> +    if (Value != 0)</div><div>> +      continue;</div><div>> +    if (AlreadyHighlighted[I])</div><div>> +      continue;</div>
<div>> +    for (size_t J = 0; J < S; ++J) {</div><div>> +      if (SrcRange.contains(Regions[J].first)) {</div><div>> +        AlreadyHighlighted[J] = true;</div><div>> +      }</div><div>> +    }</div>
<div>> +    if (Options.Debug)</div><div>> +      outs() << "Highlighted range " << Value << ", "</div><div>> +             << " " << SrcRange.LineStart << ":" << SrcRange.ColumnStart</div>
<div>> +             << " -> " << SrcRange.LineEnd << ":" << SrcRange.ColumnEnd << "\n";</div><div>> +    if (SrcRange.LineStart == SrcRange.LineEnd) {</div>
<div>> +      HighlightRanges.push_back(HighlightRange(</div><div>> +          SrcRange.LineStart, SrcRange.ColumnStart, SrcRange.ColumnEnd));</div><div>> +      continue;</div><div>> +    }</div><div>> +    HighlightRanges.push_back(</div>
<div>> +        HighlightRange(SrcRange.LineStart, SrcRange.ColumnStart,</div><div>> +                       std::numeric_limits<unsigned>::max()));</div><div>> +    HighlightRanges.push_back(</div><div>> +        HighlightRange(SrcRange.LineEnd, 1, SrcRange.ColumnEnd));</div>
<div>> +    for (unsigned Line = SrcRange.LineStart + 1; Line < SrcRange.LineEnd;</div><div>> +         ++Line) {</div><div>> +      HighlightRanges.push_back(</div><div>> +          HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));</div>
<div>> +    }</div><div>> +  }</div><div>> +</div><div>> +  std::sort(HighlightRanges.begin(), HighlightRanges.end());</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {</div>
<div>> +  for (const auto &Region : Data.getSourceRegions()) {</div><div>> +    if (Region.first.LineStart >= LineStart)</div><div>> +      Markers.push_back(RegionMarker(Region.first.LineStart,</div><div>
> +                                     Region.first.ColumnStart, Region.second));</div><div>> +  }</div><div>> +}</div><div>> +</div><div>> +void SourceCoverageView::load(SourceCoverageDataManager &Data) {</div>
<div>> +  if (Options.ShowLineStats)</div><div>> +    createLineCoverageInfo(Data);</div><div>> +  if (Options.Colors)</div><div>> +    createHighlightRanges(Data);</div><div>> +  if (Options.ShowRegionMarkers)</div>
<div>> +    createRegionMarkers(Data);</div><div>> +}</div><div>> Index: tools/llvm-cov/SourceCoverageView.h</div><div>> ===================================================================</div><div>> --- /dev/null</div>
<div>> +++ tools/llvm-cov/SourceCoverageView.h</div><div>> @@ -0,0 +1,215 @@</div><div>> +//===- SourceCoverageView.h - Code coverage view for source code ----------===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// This class implements rendering for code coverage of source code.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>
> +</div><div>> +#ifndef SOURCECOVERAGEVIEW_H</div><div>> +#define SOURCECOVERAGEVIEW_H</div><div>> +</div><div>> +#include "CoverageViewOptions.h"</div><div>> +#include "SourceCoverageDataManager.h"</div>
<div>> +#include "llvm/ProfileData/CoverageMapping.h"</div><div>> +#include "llvm/Support/MemoryBuffer.h"</div><div>> +#include <vector></div><div>> +</div><div>> +namespace llvm {</div>
<div>> +</div><div>> +/// \brief A code coverage view of a specific source file.</div><div>> +/// It can have embedded coverage views.</div><div>> +class SourceCoverageView {</div><div>> +public:</div><div>
> +  enum SubViewKind { View, ExpansionView, InstantiationView };</div><div>> +</div><div>> +  /// \brief Coverage information for a single line</div><div>> +  struct LineCoverageInfo {</div><div>> +    uint64_t ExecutionCount;</div>
<div>> +    unsigned RegionCount;</div><div>> +    bool mapped;</div><div><br></div><div>Why not capitalize mapped?</div><div><br></div><div>> +</div><div>> +    LineCoverageInfo() : ExecutionCount(0), RegionCount(0), mapped(false) {}</div>
<div>> +</div><div>> +    bool isMapped() const { return mapped; }</div><div>> +</div><div>> +    bool hasMultipleRegions() const { return RegionCount > 1; }</div><div>> +</div><div>> +    void addRegionStartCount(uint64_t Count) {</div>
<div>> +      mapped = true;</div><div>> +      ExecutionCount = Count;</div><div>> +      ++RegionCount;</div><div>> +    }</div><div>> +</div><div>> +    void addRegionCount(uint64_t Count) {</div><div>
> +      mapped = true;</div><div>> +      ExecutionCount = Count;</div><div>> +    }</div><div>> +  };</div><div>> +</div><div>> +  /// \brief A marker that points at the start</div><div>> +  /// of a specific mapping region.</div>
<div>> +  struct RegionMarker {</div><div>> +    unsigned Line, Column;</div><div>> +    uint64_t ExecutionCount;</div><div>> +</div><div>> +    RegionMarker(unsigned Line, unsigned Column, uint64_t Value)</div>
<div>> +        : Line(Line), Column(Column), ExecutionCount(Value) {}</div><div>> +  };</div><div>> +</div><div>> +  /// \brief A single line source range used to</div><div>> +  /// render highlighted text.</div>
<div>> +  struct HighlightRange {</div><div>> +    enum HighlightKind {</div><div>> +      /// The code that wasn't executed</div><div>> +      NotCovered,</div><div>> +</div><div>> +      /// The region of code that was expanded</div>
<div>> +      Expanded</div><div>> +    };</div><div>> +    HighlightKind Kind;</div><div>> +    unsigned Line;</div><div>> +    unsigned ColumnStart;</div><div>> +    unsigned ColumnEnd;</div><div>> +</div>
<div>> +    HighlightRange(unsigned Line, unsigned ColumnStart, unsigned ColumnEnd,</div><div>> +                   HighlightKind Kind = NotCovered)</div><div>> +        : Kind(Kind), Line(Line), ColumnStart(ColumnStart),</div>
<div>> +          ColumnEnd(ColumnEnd) {}</div><div>> +</div><div>> +    bool operator<(const HighlightRange &Other) const {</div><div>> +      if (Line == Other.Line)</div><div>> +        return ColumnStart < Other.ColumnStart;</div>
<div>> +      return Line < Other.Line;</div><div>> +    }</div><div>> +</div><div>> +    bool columnStartOverlaps(const HighlightRange &Other) const {</div><div>> +      return ColumnStart <= Other.ColumnStart && ColumnEnd > Other.ColumnStart;</div>
<div>> +    }</div><div>> +    bool columnEndOverlaps(const HighlightRange &Other) const {</div><div>> +      return ColumnEnd >= Other.ColumnEnd && ColumnStart < Other.ColumnEnd;</div><div>> +    }</div>
<div>> +    bool contains(const HighlightRange &Other) const {</div><div>> +      if (Line != Other.Line)</div><div>> +        return false;</div><div>> +      return ColumnStart <= Other.ColumnStart && ColumnEnd >= Other.ColumnEnd;</div>
<div>> +    }</div><div>> +</div><div>> +    bool overlaps(const HighlightRange &Other) const {</div><div>> +      if (Line != Other.Line)</div><div>> +        return false;</div><div>> +      return columnStartOverlaps(Other) || columnEndOverlaps(Other);</div>
<div>> +    }</div><div>> +  };</div><div>> +</div><div>> +private:</div><div>> +  const MemoryBuffer &File;</div><div>> +  const CoverageViewOptions &Options;</div><div>> +  unsigned LineStart, LineCount;</div>
<div>> +  SubViewKind Kind;</div><div>> +  coverage::CounterMappingRegion ExpansionRegion;</div><div>> +  std::vector<std::unique_ptr<SourceCoverageView>> Children;</div><div>> +  std::vector<LineCoverageInfo> LineStats;</div>
<div>> +  std::vector<HighlightRange> HighlightRanges;</div><div>> +  std::vector<RegionMarker> Markers;</div><div>> +  StringRef FunctionName;</div><div>> +</div><div>> +  /// \brief Create the line coverage information using the coverage data</div>
<div>> +  void createLineCoverageInfo(SourceCoverageDataManager &Data);</div><div>> +</div><div>> +  /// \brief Create the line highlighting ranges using the coverage data</div><div>> +  void createHighlightRanges(SourceCoverageDataManager &Data);</div>
<div>> +</div><div>> +  /// \brief Create the region markers using the coverage data</div><div>> +  void createRegionMarkers(SourceCoverageDataManager &Data);</div><div>> +</div><div>> +  /// \brief Sort children by the starting location.</div>
<div>> +  void sortChildren();</div><div>> +</div><div>> +  /// \brief Update the given source string to start at the given line</div><div>> +  void skipToLine(StringRef &Source, unsigned LineToFind);</div>
<div>> +</div><div>> +  /// \brief Return the current source line and update the source</div><div>> +  /// string to start from the next line</div><div>> +  StringRef getNextLine(StringRef &Source);</div><div>
> +</div><div>> +  /// \brief Return a highlight range for the expansion region of this view.</div><div>> +  HighlightRange getExpansionHighlightRange() const;</div><div>> +</div><div>> +  /// \brief Render a source line with highlighting.</div>
<div>> +  void renderLine(raw_ostream &OS, StringRef Line,</div><div>> +                  ArrayRef<HighlightRange> Ranges);</div><div>> +</div><div>> +  void renderOffset(raw_ostream &OS, unsigned I);</div>
<div>> +</div><div>> +  void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);</div><div>> +</div><div>> +  /// \brief Render the line's execution count column</div><div>> +  void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);</div>
<div>> +</div><div>> +  /// \brief Render the line number column</div><div>> +  void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);</div><div>> +</div><div>> +  /// \brief Render all the region's execution counts on a line</div>
<div>> +  void renderRegionMarkers(raw_ostream &OS, ArrayRef<RegionMarker> Regions);</div><div>> +</div><div>> +  static const unsigned LineCoverageColumnWidth = 7;</div><div>> +  static const unsigned LineNumberColumnWidth = 5;</div>
<div>> +</div><div>> +public:</div><div>> +  SourceCoverageView(const MemoryBuffer &File,</div><div>> +                     const CoverageViewOptions &Options)</div><div>> +      : File(File), Options(Options), LineStart(1), Kind(View),</div>
<div>> +        ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {</div><div>> +    LineCount = File.getBuffer().count('\n') + 1;</div><div>> +  }</div><div>> +</div><div>> +  SourceCoverageView(const MemoryBuffer &File,</div>
<div>> +                     const CoverageViewOptions &Options, unsigned LineStart,</div><div>> +                     unsigned LineEnd)</div><div>> +      : File(File), Options(Options), LineStart(LineStart),</div>
<div>> +        LineCount(LineEnd - LineStart + 1), Kind(View),</div><div>> +        ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {}</div><div>> +</div><div>> +  SourceCoverageView(SourceCoverageView &Parent, unsigned LineStart,</div>
<div>> +                     unsigned LineEnd, StringRef FunctionName)</div><div>> +      : File(Parent.File), Options(Parent.Options), LineStart(LineStart),</div><div>> +        LineCount(LineEnd - LineStart + 1), Kind(InstantiationView),</div>
<div>> +        ExpansionRegion(coverage::Counter(), 0, LineEnd, 0, LineEnd, 0),</div><div>> +        FunctionName(FunctionName) {}</div><div>> +</div><div>> +  SourceCoverageView(const MemoryBuffer &File,</div>
<div>> +                     const CoverageViewOptions &Options, unsigned LineStart,</div><div>> +                     unsigned LineEnd,</div><div>> +                     const coverage::CounterMappingRegion &ExpansionRegion)</div>
<div>> +      : File(File), Options(Options), LineStart(LineStart),</div><div>> +        LineCount(LineEnd - LineStart + 1), Kind(ExpansionView),</div><div>> +        ExpansionRegion(ExpansionRegion) {}</div><div>
> +</div><div>> +  const CoverageViewOptions &getOptions() const { return Options; }</div><div>> +</div><div>> +  bool isExpansionSubView() const { return Kind == ExpansionView; }</div><div>> +</div><div>
> +  bool isInstantiationSubView() const { return Kind == InstantiationView; }</div><div>> +</div><div>> +  void addChild(SourceCoverageView *view) {</div><div>> +    Children.push_back(std::unique_ptr<SourceCoverageView>(view));</div>
<div>> +  }</div><div><br></div><div>As I mentioned above, better to take a unique_ptr argument so that it's</div><div>obvious that ownership is passed.</div><div><br></div><div>> +</div><div>> +  /// \brief Print the code coverage information for a specific</div>
<div>> +  /// portion of a source file to the output stream.</div><div>> +  void render(raw_ostream &OS, unsigned Offset = 0);</div><div>> +</div><div>> +  /// \brief Load the coverage information required for rendering</div>
<div>> +  /// from the mapping regions in the data manager.</div><div>> +  void load(SourceCoverageDataManager &Data);</div><div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>> +</div>
<div>> +#endif // SOURCECOVERAGEVIEW_H</div><div>> Index: tools/llvm-cov/SourceLanguages.cpp</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>
> +++ tools/llvm-cov/SourceLanguages.cpp</div><div>> @@ -0,0 +1,93 @@</div><div>> +//===- SourceLanguages.cpp - Language specific operations  ---------------===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div>
<div>> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// These classes provide various language specific operations.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div><br>
</div><div>As far as I can tell, this really only provides "demangle", and I'm not</div><div>entirely convinced this is even a good idea. This is *very* brittle and</div><div>pretty awkward to extend, and really difficult to make general enough to</div>
<div>be valuable. How much is it really gaining us?</div><div><br></div><div>> +</div><div>> +#include "SourceLanguages.h"</div><div>> +#include "llvm/ADT/StringRef.h"</div><div>> +#include "llvm/Support/Path.h"</div>
<div>> +</div><div>> +using namespace llvm;</div><div>> +</div><div>> +SourceLanguages::SourceLanguages() {</div><div>> +  Languages[SourceLanguage::Unknown].reset(new SourceLanguage);</div><div>> +  Languages[SourceLanguage::CXX].reset(new CXXSourceLanguage);</div>
<div>> +  Languages[SourceLanguage::ObjectiveC].reset(new ObjectiveCSourceLanguage);</div><div>> +}</div><div>> +</div><div>> +SourceLanguage *SourceLanguages::get(const FunctionCoverageMapping &Function) {</div>
<div>> +  unsigned Scores[SourceLanguage::NumLanguages];</div><div>> +  for (unsigned I = 0; I < SourceLanguage::NumLanguages; ++I)</div><div>> +    Scores[I] = 0;</div><div>> +  for (unsigned I = 0; I < SourceLanguage::NumLanguages; ++I) {</div>
<div>> +    if (Languages[I]->matchMangledName(Function.Name))</div><div>> +      ++Scores[I];</div><div>> +    for (const auto &File : Function.Filenames) {</div><div>> +      if (Languages[I]->matchFileExtension(sys::path::extension(File)))</div>
<div>> +        ++Scores[I];</div><div>> +    }</div><div>> +  }</div><div>> +  unsigned BestScore = 0;</div><div>> +  SourceLanguage *Match = Languages[SourceLanguage::Unknown].get();</div><div>> +  for (unsigned I = 0; I < SourceLanguage::NumLanguages; ++I) {</div>
<div>> +    if (Scores[I] > BestScore) {</div><div>> +      BestScore = Scores[I];</div><div>> +      Match = Languages[I].get();</div><div>> +    }</div><div>> +  }</div><div>> +  return Match;</div>
<div>> +}</div><div>> +</div><div>> +#if !defined(_MSC_VER)</div><div>> +// Assume that __cxa_demangle is provided by libcxxabi (except for Windows).</div><div>> +extern "C" char *__cxa_demangle(const char *mangled_name, char *output_buffer,</div>
<div>> +                                size_t *length, int *status);</div><div>> +#endif</div><div>> +</div><div>> +void CXXSourceLanguage::demangle(std::string &Name) {</div><div>> +  StringRef NameRef(Name);</div>
<div>> +  std::string FileName;</div><div>> +  size_t ColonPos = NameRef.find_first_of(':');</div><div>> +  if (ColonPos != StringRef::npos) {</div><div>> +    FileName = std::string(Name.begin(), Name.begin() + ColonPos);</div>
<div>> +    Name = std::string(Name.begin() + ColonPos + 1, Name.end());</div><div>> +  }</div><div>> +#if !defined(_MSC_VER)</div><div>> +  int status = 0;</div><div>> +  char *DemangledName = __cxa_demangle(Name.c_str(), 0, 0, &status);</div>
<div>> +  if (status != 0)</div><div>> +    return;</div><div>> +  Name = DemangledName;</div><div>> +  free(DemangledName);</div><div>> +#endif</div><div>> +  if (FileName.empty())</div><div>> +    return;</div>
<div>> +  Name = FileName + ": " + Name;</div><div>> +}</div><div>> +</div><div>> +bool CXXSourceLanguage::matchMangledName(StringRef Name) {</div><div>> +  return Name.startswith("_Z");</div>
<div>> +}</div><div>> +</div><div>> +bool CXXSourceLanguage::matchFileExtension(StringRef Ext) {</div><div>> +  return Ext.equals_lower(".h") || Ext.equals_lower(".cpp") ||</div><div>> +         Ext.equals_lower(".cxx") || Ext.equals_lower(".hpp") ||</div>
<div>> +         Ext.equals_lower(".cc") || Ext.equals_lower("hxx");</div><div>> +}</div><div>> +</div><div>> +bool ObjectiveCSourceLanguage::matchMangledName(StringRef Name) {</div><div>> +  return Name.startswith("_c") || Name.startswith("_i");</div>
<div>> +}</div><div>> +</div><div>> +bool ObjectiveCSourceLanguage::matchFileExtension(StringRef Ext) {</div><div>> +  return Ext.equals_lower(".m") || Ext.equals_lower(".h") ||</div><div>> +         Ext.equals_lower(".mm");</div>
<div>> +}</div><div>> Index: tools/llvm-cov/SourceLanguages.h</div><div>> ===================================================================</div><div>> --- /dev/null</div><div>> +++ tools/llvm-cov/SourceLanguages.h</div>
<div>> @@ -0,0 +1,69 @@</div><div>> +//===- SourceLanguages.h - Language specific operations  -----------------===//</div><div>> +//</div><div>> +//                     The LLVM Compiler Infrastructure</div><div>
> +//</div><div>> +// This file is distributed under the University of Illinois Open Source</div><div>> +// License. See LICENSE.TXT for details.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div>
<div>> +//</div><div>> +// These classes provide various language specific operations.</div><div>> +//</div><div>> +//===----------------------------------------------------------------------===//</div><div>> +</div>
<div>> +#ifndef SOURCELANGUAGES_H</div><div>> +#define SOURCELANGUAGES_H</div><div>> +</div><div>> +#include "FunctionCoverageMapping.h"</div><div>> +#include "llvm/ADT/StringRef.h"</div>
<div>> +#include <string></div><div>> +</div><div>> +namespace llvm {</div><div>> +</div><div>> +/// \brief Interface for language specific operations.</div><div>> +class SourceLanguage {</div><div>
> +public:</div><div>> +  enum LanguageKind { Unknown, CXX, ObjectiveC, NumLanguages };</div><div>> +</div><div>> +  virtual void demangle(std::string &Name) {}</div><div>> +</div><div>> +  /// \brief Return true if the given name has this language's mangling.</div>
<div>> +  virtual bool matchMangledName(StringRef Name) { return false; }</div><div>> +</div><div>> +  /// \brief Return true if the given file extension</div><div>> +  /// is the file extension used by the source files of this language.</div>
<div>> +  virtual bool matchFileExtension(StringRef Ext) { return false; }</div><div>> +};</div><div>> +</div><div>> +/// \brief Manages all of the known source languages.</div><div>> +class SourceLanguages {</div>
<div>> +  std::unique_ptr<SourceLanguage> Languages[SourceLanguage::NumLanguages];</div><div>> +</div><div>> +public:</div><div>> +  SourceLanguages();</div><div>> +</div><div>> +  /// \brief Return the source language object for the given function.</div>
<div>> +  SourceLanguage *get(const FunctionCoverageMapping &Function);</div><div>> +};</div><div>> +</div><div>> +/// \brief Implementation of C++ language specific operations.</div><div>> +class CXXSourceLanguage : public SourceLanguage {</div>
<div>> +public:</div><div>> +  void demangle(std::string &Name) override;</div><div>> +</div><div>> +  bool matchMangledName(StringRef Name) override;</div><div>> +</div><div>> +  bool matchFileExtension(StringRef Ext) override;</div>
<div>> +};</div><div>> +</div><div>> +/// \brief Implementation of Objective C language specific operations.</div><div>> +class ObjectiveCSourceLanguage : public SourceLanguage {</div><div>> +public:</div><div>
> +  bool matchMangledName(StringRef Name) override;</div><div>> +</div><div>> +  bool matchFileExtension(StringRef Ext) override;</div><div>> +};</div><div>> +</div><div>> +} // namespace llvm</div><div>
> +</div><div>> +#endif // SOURCELANGUAGES_H</div><div>> Index: tools/llvm-cov/llvm-cov.cpp</div><div>> ===================================================================</div><div>> --- tools/llvm-cov/llvm-cov.cpp</div>
<div>> +++ tools/llvm-cov/llvm-cov.cpp</div><div>> @@ -11,9 +11,59 @@</div><div>>  //</div><div>>  //===----------------------------------------------------------------------===//</div><div>></div><div>> +#include "llvm/ADT/StringRef.h"</div>
<div>> +#include "llvm/Support/raw_ostream.h"</div><div>> +#include "llvm/Support/Path.h"</div><div>> +#include <string></div><div>> +</div><div>> +using namespace llvm;</div><div>
> +</div><div>> +/// \brief The main function for the invocation of the tool with a 'show'</div><div>> +/// command</div><div>> +int show_main(int argc, const char **argv);</div><div>> +</div><div>> +/// \brief The main function for the invocation of the tool with a 'report'</div>
<div>> +/// command</div><div>> +int report_main(int argc, const char **argv);</div><div><br></div><div>These comments are a bit confusing. I'd prefer "The main entry point for</div><div>the 'show' subcommand" and similarly for 'report'.</div>
<div><br></div><div>> +</div><div>>  /// \brief The main function for the gcov compatible coverage tool</div><div>>  int gcov_main(int argc, const char **argv);</div><div>></div><div>>  int main(int argc, const char **argv) {</div>
<div>> +  // If argv[0] is or ends with 'gcov', always be gcov compatible</div><div>> +  if (sys::path::stem(argv[0]).endswith_lower("gcov"))</div><div>> +    return gcov_main(argc, argv);</div>
<div>> +</div><div>> +  // Check if we are invoking a specific tool command.</div><div>> +  if (argc > 1) {</div><div>> +    int (*func)(int, const char **) = nullptr;</div><div>> +</div><div>> +    StringRef command = argv[1];</div>
<div>> +    if (command.equals_lower("show"))</div><div>> +      func = show_main;</div><div>> +    else if (command.equals_lower("report"))</div><div>> +      func = report_main;</div><div>
> +    else if (command.equals_lower("gcov"))</div><div>> +      func = gcov_main;</div><div>> +</div><div>> +    if (func) {</div><div>> +      std::string Invocation(std::string(argv[0]) + " " + argv[1]);</div>
<div>> +      argv[1] = Invocation.c_str();</div><div>> +      return func(argc - 1, argv + 1);</div><div>> +    }</div><div>> +  }</div><div>> +</div><div>> +  // Give a warning and fall back to gcov</div>
<div>> +  errs().changeColor(raw_ostream::RED);</div><div>> +  errs() << "warning: ";</div><div>> +  if (argc > 1)</div><div>> +    errs() << "Unrecognized command '" << argv[1] << "'.";</div>
<div>> +  else</div><div>> +    errs() << "No command given.";</div><div><br></div><div>If argc is 0, calling gcov_main isn't going to accomplish much (it needs</div><div>a filename at the very least) - maybe just spit out usage for that.</div>
<div><br></div><div>It would also be nice to avoid the "unrecognized command" prompt if the</div><div>argument starts with - or if it's a filename. There's diminishing</div><div>returns on how much effort to put in, but maybe only say unrecognized</div>
<div>command if the arg doesn't start with - and doesn't have a . in it?</div><div><br></div><div>> +  errs() << " Using the gcov compatible mode "</div><div>> +            "(this behaviour may be dropped in the future).";</div>
<div>> +  errs().resetColor();</div><div>> +  errs() << "\n";</div><div>> +</div><div>>    return gcov_main(argc, argv);</div><div>>  }</div><div><br></div></div><div class="gmail_extra"><br>
<br><div class="gmail_quote">On Fri, Jul 25, 2014 at 11:31 AM, Alex Lorenz <span dir="ltr"><<a href="mailto:arphaman@gmail.com" target="_blank">arphaman@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Split the patch and updated it to be based on <a href="http://reviews.llvm.org/D4673" target="_blank">http://reviews.llvm.org/D4673</a>.<br>
Added some minor fixes.<br>
<div class=""><br>
<a href="http://reviews.llvm.org/D4445" target="_blank">http://reviews.llvm.org/D4445</a><br>
<br>
Files:<br>
  tools/llvm-cov/CMakeLists.txt<br>
  tools/llvm-cov/CodeCoverage.cpp<br>
  tools/llvm-cov/CoverageFilters.cpp<br>
  tools/llvm-cov/CoverageFilters.h<br>
  tools/llvm-cov/CoverageReport.cpp<br>
  tools/llvm-cov/CoverageReport.h<br>
  tools/llvm-cov/CoverageSummary.cpp<br>
  tools/llvm-cov/CoverageSummary.h<br>
  tools/llvm-cov/CoverageSummaryInfo.cpp<br>
  tools/llvm-cov/CoverageSummaryInfo.h<br>
  tools/llvm-cov/CoverageViewOptions.h<br>
  tools/llvm-cov/FunctionCoverageMapping.h<br>
  tools/llvm-cov/LLVMBuild.txt<br>
  tools/llvm-cov/Makefile<br>
  tools/llvm-cov/SourceCoverageDataManager.cpp<br>
  tools/llvm-cov/SourceCoverageDataManager.h<br>
  tools/llvm-cov/SourceCoverageView.cpp<br>
  tools/llvm-cov/SourceCoverageView.h<br>
  tools/llvm-cov/SourceLanguages.cpp<br>
  tools/llvm-cov/SourceLanguages.h<br>
</div>  tools/llvm-cov/llvm-cov.cpp<br>
</blockquote></div><br></div>