[PATCH] Code coverage tool that uses the new coverage mapping format and clang's instrumentation based profiling data

Justin Bogner mail at justinbogner.com
Fri Aug 22 14:10:33 PDT 2014


Alex Lorenz <arphaman at gmail.com> writes:
> I've added a testcase, please take a look at the setup.

Yep, this style of test is what I had in mind. A few more to make sure
the ranges are accurate and this is good to go. I've also included the
comments I made to you in person about the colored ostream below.

>
> Files:
>   lib/ProfileData/CoverageMappingReader.cpp
>   test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping
>   test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata
>   test/tools/llvm-cov/showLineExecutionCounts.cpp
>   tools/llvm-cov/CMakeLists.txt
>   tools/llvm-cov/CodeCoverage.cpp
>   tools/llvm-cov/CoverageFilters.cpp
>   tools/llvm-cov/CoverageFilters.h
>   tools/llvm-cov/CoverageReport.cpp
>   tools/llvm-cov/CoverageReport.h
>   tools/llvm-cov/CoverageSummary.cpp
>   tools/llvm-cov/CoverageSummary.h
>   tools/llvm-cov/CoverageSummaryInfo.cpp
>   tools/llvm-cov/CoverageSummaryInfo.h
>   tools/llvm-cov/CoverageViewOptions.h
>   tools/llvm-cov/FunctionCoverageMapping.h
>   tools/llvm-cov/LLVMBuild.txt
>   tools/llvm-cov/Makefile
>   tools/llvm-cov/RenderingSupport.h
>   tools/llvm-cov/SourceCoverageDataManager.cpp
>   tools/llvm-cov/SourceCoverageDataManager.h
>   tools/llvm-cov/SourceCoverageView.cpp
>   tools/llvm-cov/SourceCoverageView.h
>   tools/llvm-cov/TestingSupport.cpp
>   tools/llvm-cov/llvm-cov.cpp
>
> Index: lib/ProfileData/CoverageMappingReader.cpp
> ===================================================================
> --- lib/ProfileData/CoverageMappingReader.cpp
> +++ lib/ProfileData/CoverageMappingReader.cpp
> @@ -289,18 +289,6 @@
>      Object = std::move(File.get());
>  }
>  
> -ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader(
> -    std::unique_ptr<MemoryBuffer> &ObjectBuffer, sys::fs::file_magic Type)
> -    : CurrentRecord(0) {
> -  auto File = object::ObjectFile::createObjectFile(
> -      ObjectBuffer->getMemBufferRef(), Type);
> -  if (!File)
> -    error(File.getError());
> -  else
> -    Object = OwningBinary<ObjectFile>(std::move(File.get()),
> -                                      std::move(ObjectBuffer));
> -}
> -
>  namespace {
>  /// \brief The coverage mapping data for a single function.
>  /// It points to the function's name.
> @@ -347,19 +335,11 @@
>  
>  template <typename T>
>  std::error_code readCoverageMappingData(
> -    SectionRef &ProfileNames, SectionRef &CoverageMapping,
> +    SectionData &ProfileNames, StringRef Data,
>      std::vector<ObjectFileCoverageMappingReader::ProfileMappingRecord> &Records,
>      std::vector<StringRef> &Filenames) {
>    llvm::DenseSet<T> UniqueFunctionMappingData;
>  
> -  // Get the contents of the given sections.
> -  StringRef Data;
> -  if (auto Err = CoverageMapping.getContents(Data))
> -    return Err;
> -  SectionData ProfileNamesData;
> -  if (auto Err = ProfileNamesData.load(ProfileNames))
> -    return Err;
> -
>    // Read the records in the coverage data section.
>    while (!Data.empty()) {
>      if (Data.size() < sizeof(CoverageMappingTURecord<T>))
> @@ -418,9 +398,9 @@
>          continue;
>        UniqueFunctionMappingData.insert(MappingRecord.FunctionNamePtr);
>        StringRef FunctionName;
> -      if (auto Err = ProfileNamesData.get(MappingRecord.FunctionNamePtr,
> -                                          MappingRecord.FunctionNameSize,
> -                                          FunctionName))
> +      if (auto Err =
> +              ProfileNames.get(MappingRecord.FunctionNamePtr,
> +                               MappingRecord.FunctionNameSize, FunctionName))
>          return Err;
>        Records.push_back(ObjectFileCoverageMappingReader::ProfileMappingRecord(
>            Version, FunctionName, MappingRecord.FunctionHash, Mapping,
> @@ -431,6 +411,63 @@
>    return instrprof_error::success;
>  }
>  
> +static const char *TestingFormatMagic = "llvmcovmtestdata";
> +
> +static std::error_code decodeTestingFormat(StringRef Data,
> +                                           SectionData &ProfileNames,
> +                                           StringRef &CoverageMapping) {
> +  Data = Data.substr(StringRef(TestingFormatMagic).size());
> +  if (Data.size() < 1)
> +    return instrprof_error::truncated;
> +  unsigned N = 0;
> +  auto ProfileNamesSize =
> +      decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N);
> +  if (N > Data.size())
> +    return instrprof_error::malformed;
> +  Data = Data.substr(N);
> +  if (Data.size() < 1)
> +    return instrprof_error::truncated;
> +  N = 0;
> +  ProfileNames.Address =
> +      decodeULEB128(reinterpret_cast<const uint8_t *>(Data.data()), &N);
> +  if (N > Data.size())
> +    return instrprof_error::malformed;
> +  Data = Data.substr(N);
> +  if (Data.size() < ProfileNamesSize)
> +    return instrprof_error::malformed;
> +  ProfileNames.Data = Data.substr(0, ProfileNamesSize);
> +  CoverageMapping = Data.substr(ProfileNamesSize);
> +  return instrprof_error::success;
> +}
> +
> +ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader(
> +    std::unique_ptr<MemoryBuffer> &ObjectBuffer, sys::fs::file_magic Type)
> +    : CurrentRecord(0) {
> +  if (ObjectBuffer->getBuffer().startswith(TestingFormatMagic)) {
> +    // This is a special format used for testing.
> +    SectionData ProfileNames;
> +    StringRef CoverageMapping;
> +    if (auto Err = decodeTestingFormat(ObjectBuffer->getBuffer(), ProfileNames,
> +                                       CoverageMapping)) {
> +      error(Err);
> +      return;
> +    }
> +    error(readCoverageMappingData<uint64_t>(ProfileNames, CoverageMapping,
> +                                            MappingRecords, Filenames));
> +    Object = OwningBinary<ObjectFile>(std::unique_ptr<ObjectFile>(),
> +                                      std::move(ObjectBuffer));
> +    return;
> +  }
> +
> +  auto File = object::ObjectFile::createObjectFile(
> +      ObjectBuffer->getMemBufferRef(), Type);
> +  if (!File)
> +    error(File.getError());
> +  else
> +    Object = OwningBinary<ObjectFile>(std::move(File.get()),
> +                                      std::move(ObjectBuffer));
> +}
> +
>  std::error_code ObjectFileCoverageMappingReader::readHeader() {
>    ObjectFile *OF = Object.getBinary().get();
>    if (!OF)
> @@ -457,13 +494,21 @@
>    if (FoundSectionCount != 2)
>      return error(instrprof_error::bad_header);
>  
> +  // Get the contents of the given sections.
> +  StringRef Data;
> +  if (auto Err = CoverageMapping.getContents(Data))
> +    return Err;
> +  SectionData ProfileNamesData;
> +  if (auto Err = ProfileNamesData.load(ProfileNames))
> +    return Err;
> +
>    // Load the data from the found sections.
>    std::error_code Err;
>    if (BytesInAddress == 4)
> -    Err = readCoverageMappingData<uint32_t>(ProfileNames, CoverageMapping,
> +    Err = readCoverageMappingData<uint32_t>(ProfileNamesData, Data,
>                                              MappingRecords, Filenames);
>    else
> -    Err = readCoverageMappingData<uint64_t>(ProfileNames, CoverageMapping,
> +    Err = readCoverageMappingData<uint64_t>(ProfileNamesData, Data,
>                                              MappingRecords, Filenames);
>    if (Err)
>      return error(Err);
> Index: test/tools/llvm-cov/showLineExecutionCounts.cpp
> ===================================================================
> --- /dev/null
> +++ test/tools/llvm-cov/showLineExecutionCounts.cpp
> @@ -0,0 +1,22 @@
> +// RUN: llvm-cov show %S/Inputs/lineExecutionCounts.covmapping -instr-profile %S/Inputs/lineExecutionCounts.profdata -no-colors -filename-equivalence %s | FileCheck %s
> +
> +int main() {                             // CHECK:   1| [[@LINE]]|int main(
> +  int x = 0;                             // CHECK:   1| [[@LINE]]|  int x
> +                                         // CHECK:   1| [[@LINE]]|
> +  if (x) {                               // CHECK:   0| [[@LINE]]|  if (x)
> +    x = 0;                               // CHECK:   0| [[@LINE]]|    x = 0
> +  } else {                               // CHECK:   1| [[@LINE]]|  } else
> +    x = 1;                               // CHECK:   1| [[@LINE]]|    x = 1
> +  }                                      // CHECK:   1| [[@LINE]]|  }
> +                                         // CHECK:   1| [[@LINE]]|
> +  for (int i = 0; i < 100; ++i) {        // CHECK: 100| [[@LINE]]|  for (
> +    x = 1;                               // CHECK: 100| [[@LINE]]|    x = 1
> +  }                                      // CHECK: 100| [[@LINE]]|  }
> +                                         // CHECK:   1| [[@LINE]]|
> +  x = x < 10 ? x + 1 : x - 1;            // CHECK:   0| [[@LINE]]|  x =
> +  x = x > 10 ?                           // CHECK:   1| [[@LINE]]|  x =
> +        x - 1:                           // CHECK:   0| [[@LINE]]|        x
> +        x + 1;                           // CHECK:   1| [[@LINE]]|        x
> +                                         // CHECK:   1| [[@LINE]]|
> +  return 0;                              // CHECK:   1| [[@LINE]]|  return
> +}                                        // CHECK:   1| [[@LINE]]|}
> Index: tools/llvm-cov/CMakeLists.txt
> ===================================================================
> --- tools/llvm-cov/CMakeLists.txt
> +++ tools/llvm-cov/CMakeLists.txt
> @@ -1,6 +1,13 @@
> -set(LLVM_LINK_COMPONENTS core support )
> +set(LLVM_LINK_COMPONENTS core support object)
>  
>  add_llvm_tool(llvm-cov
>    llvm-cov.cpp
>    gcov.cpp
> +  CodeCoverage.cpp
> +  CoverageFilters.cpp
> +  CoverageReport.cpp
> +  CoverageSummary.cpp
> +  CoverageSummaryInfo.cpp
> +  SourceCoverageDataManager.cpp
> +  SourceCoverageView.cpp
>    )
> Index: tools/llvm-cov/CodeCoverage.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CodeCoverage.cpp
> @@ -0,0 +1,709 @@
> +//===- CodeCoverage.cpp - Coverage tool based on profiling instrumentation-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// The 'CodeCoverageTool' class implements a command line tool to analyze and
> +// report coverage information using the profiling instrumentation and code
> +// coverage mapping.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "FunctionCoverageMapping.h"
> +#include "RenderingSupport.h"
> +#include "CoverageViewOptions.h"
> +#include "CoverageFilters.h"
> +#include "SourceCoverageDataManager.h"
> +#include "SourceCoverageView.h"
> +#include "CoverageSummary.h"
> +#include "CoverageReport.h"
> +#include "llvm/ADT/StringRef.h"
> +#include "llvm/ADT/SmallString.h"
> +#include "llvm/ADT/SmallSet.h"
> +#include "llvm/ADT/DenseSet.h"
> +#include "llvm/ProfileData/InstrProfReader.h"
> +#include "llvm/ProfileData/CoverageMapping.h"
> +#include "llvm/ProfileData/CoverageMappingReader.h"
> +#include "llvm/Support/CommandLine.h"
> +#include "llvm/Support/FileSystem.h"
> +#include "llvm/Support/ManagedStatic.h"
> +#include "llvm/Support/MemoryObject.h"
> +#include "llvm/Support/Format.h"
> +#include "llvm/Support/Path.h"
> +#include "llvm/Support/Signals.h"
> +#include "llvm/Support/PrettyStackTrace.h"
> +#include <system_error>
> +#include <functional>
> +
> +using namespace llvm;
> +using namespace coverage;
> +
> +namespace {
> +/// \brief Distribute the functions into instantiation sets.
> +/// An instantiation set is a collection of functions
> +/// that have the same source code, e.g.
> +/// template functions specializations.
> +class FunctionInstantiationSetCollector {
> +  ArrayRef<FunctionCoverageMapping> FunctionMappings;
> +  typedef uint64_t KeyType;
> +  typedef std::vector<const FunctionCoverageMapping *> SetType;
> +  std::unordered_map<uint64_t, SetType> InstantiatedFunctions;
> +
> +  static KeyType getKey(const MappingRegion &R) {
> +    return uint64_t(R.LineStart) | uint64_t(R.ColumnStart) << 32;
> +  }
> +
> +public:
> +  void insert(const FunctionCoverageMapping &Function, unsigned FileID) {
> +    KeyType Key = 0;
> +    for (const auto &R : Function.MappingRegions) {
> +      if (R.FileID == FileID) {
> +        Key = getKey(R);
> +        break;
> +      }
> +    }
> +    auto I = InstantiatedFunctions.find(Key);
> +    if (I == InstantiatedFunctions.end()) {
> +      SetType Set;
> +      Set.push_back(&Function);
> +      InstantiatedFunctions.insert(std::make_pair(Key, Set));
> +    } else
> +      I->second.push_back(&Function);
> +  }
> +
> +  std::unordered_map<KeyType, SetType>::iterator begin() {
> +    return InstantiatedFunctions.begin();
> +  }
> +
> +  std::unordered_map<KeyType, SetType>::iterator end() {
> +    return InstantiatedFunctions.end();
> +  }
> +};
> +
> +/// \brief The implementation of the coverage tool.
> +class CodeCoverageTool {
> +public:
> +  enum Command {
> +    /// \brief The show command.
> +    Show,
> +    /// \brief The report command.
> +    Report
> +  };
> +
> +  /// \brief Print the error message to the error output stream.
> +  void error(const Twine &Message, StringRef Whence = "");
> +
> +  /// \brief Return a memory buffer for the given source file.
> +  ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
> +
> +  /// \brief Return true if two filepaths refer to the same file.
> +  bool equivalentFiles(StringRef A, StringRef B);
> +
> +  /// \brief Collect a set of function's file ids which correspond to the
> +  /// given source file. Return false if the set is empty.
> +  bool gatherInterestingFileIDs(StringRef SourceFile,
> +                                const FunctionCoverageMapping &Function,
> +                                SmallSet<unsigned, 8> &InterestingFileIDs);
> +
> +  /// \brief Find the file id which is not an expanded file id.
> +  bool findMainViewFileID(StringRef SourceFile,
> +                          const FunctionCoverageMapping &Function,
> +                          unsigned &MainViewFileID);
> +
> +  bool findMainViewFileID(const FunctionCoverageMapping &Function,
> +                          unsigned &MainViewFileID);
> +
> +  /// \brief Create a source view which shows coverage for an expansion
> +  /// of a file.
> +  void createExpansionSubView(const MappingRegion &ExpandedRegion,
> +                              const FunctionCoverageMapping &Function,
> +                              SourceCoverageView &Parent);
> +
> +  void createExpansionSubViews(SourceCoverageView &View, unsigned ViewFileID,
> +                               const FunctionCoverageMapping &Function);
> +
> +  /// \brief Create a source view which shows coverage for an instantiation
> +  /// of a funciton.
> +  void createInstantiationSubView(StringRef SourceFile,
> +                                  const FunctionCoverageMapping &Function,
> +                                  SourceCoverageView &View);
> +
> +  /// \brief Create the main source view of a particular source file.
> +  /// Return true if this particular source file is not covered.
> +  bool
> +  createSourceFileView(StringRef SourceFile, SourceCoverageView &View,
> +                       ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
> +                       bool UseOnlyRegionsInMainFile = false);
> +
> +  /// \brief Load the coverage mapping data. Return true if an error occured.
> +  bool load();
> +
> +  int run(Command Cmd, int argc, const char **argv);
> +
> +  typedef std::function<int(int, const char **)> CommandLineParserType;
> +
> +  int show(int argc, const char **argv,
> +           CommandLineParserType commandLineParser);
> +
> +  int report(int argc, const char **argv,
> +             CommandLineParserType commandLineParser);
> +
> +  StringRef ObjectFilename;
> +  CoverageViewOptions ViewOpts;
> +  std::unique_ptr<IndexedInstrProfReader> PGOReader;
> +  CoverageFiltersMatchAll Filters;
> +  std::vector<std::string> SourceFiles;
> +  std::vector<std::pair<std::string, std::unique_ptr<MemoryBuffer>>>
> +      LoadedSourceFiles;
> +  std::vector<FunctionCoverageMapping> FunctionMappingRecords;
> +  bool CompareFilenamesOnly;
> +};
> +}
> +
> +void CodeCoverageTool::error(const Twine &Message, StringRef Whence) {
> +  errs() << "error: ";
> +  if (!Whence.empty())
> +    errs() << Whence << ": ";
> +  errs() << Message << "\n";
> +}
> +
> +ErrorOr<const MemoryBuffer &>
> +CodeCoverageTool::getSourceFile(StringRef SourceFile) {
> +  SmallString<256> Path(SourceFile);
> +  sys::fs::make_absolute(Path);
> +  for (const auto &Files : LoadedSourceFiles) {
> +    if (sys::fs::equivalent(Path.str(), Files.first)) {
> +      return *Files.second;
> +    }
> +  }
> +  auto Buffer = MemoryBuffer::getFile(SourceFile);
> +  if (auto EC = Buffer.getError()) {
> +    error(EC.message(), SourceFile);
> +    return EC;
> +  }
> +  LoadedSourceFiles.push_back(std::make_pair(
> +      std::string(Path.begin(), Path.end()), std::move(Buffer.get())));
> +  return *LoadedSourceFiles.back().second;
> +}
> +
> +/// \brief Return a line start - line end range which contains
> +/// all the mapping regions of a given function with a particular file id.
> +std::pair<unsigned, unsigned>
> +findExpandedFileInterestingLineRange(unsigned FileID,
> +                                     const FunctionCoverageMapping &Function) {
> +  unsigned LineStart = std::numeric_limits<unsigned>::max();
> +  unsigned LineEnd = 0;
> +  for (const auto &Region : Function.MappingRegions) {
> +    if (Region.FileID != FileID)
> +      continue;
> +    LineStart = std::min(Region.LineStart, LineStart);
> +    LineEnd = std::max(Region.LineEnd, LineEnd);
> +  }
> +  return std::make_pair(LineStart, LineEnd);
> +}
> +
> +bool CodeCoverageTool::equivalentFiles(StringRef A, StringRef B) {
> +  if (CompareFilenamesOnly)
> +    return sys::path::filename(A).equals_lower(sys::path::filename(B));
> +  return sys::fs::equivalent(A, B);
> +}
> +
> +bool CodeCoverageTool::gatherInterestingFileIDs(
> +    StringRef SourceFile, const FunctionCoverageMapping &Function,
> +    SmallSet<unsigned, 8> &InterestingFileIDs) {
> +  bool Interesting = false;
> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
> +    if (equivalentFiles(SourceFile, Function.Filenames[I])) {
> +      InterestingFileIDs.insert(I);
> +      Interesting = true;
> +    }
> +  }
> +  return Interesting;
> +}
> +
> +bool
> +CodeCoverageTool::findMainViewFileID(StringRef SourceFile,
> +                                     const FunctionCoverageMapping &Function,
> +                                     unsigned &MainViewFileID) {
> +  llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
> +  llvm::SmallVector<bool, 8> FilenameEquivalence(Function.Filenames.size(),
> +                                                 false);
> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
> +    if (equivalentFiles(SourceFile, Function.Filenames[I]))
> +      FilenameEquivalence[I] = true;
> +  }
> +  for (const auto &Region : Function.MappingRegions) {
> +    if (Region.Kind == MappingRegion::ExpansionRegion &&
> +        FilenameEquivalence[Region.FileID])
> +      IsExpandedFile[Region.ExpandedFileID] = true;
> +  }
> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
> +    if (!FilenameEquivalence[I] || IsExpandedFile[I])
> +      continue;
> +    MainViewFileID = I;
> +    return false;
> +  }
> +  return true;
> +}
> +
> +bool
> +CodeCoverageTool::findMainViewFileID(const FunctionCoverageMapping &Function,
> +                                     unsigned &MainViewFileID) {
> +  llvm::SmallVector<bool, 8> IsExpandedFile(Function.Filenames.size(), false);
> +  for (const auto &Region : Function.MappingRegions) {
> +    if (Region.Kind == MappingRegion::ExpansionRegion)
> +      IsExpandedFile[Region.ExpandedFileID] = true;
> +  }
> +  for (unsigned I = 0, E = Function.Filenames.size(); I < E; ++I) {
> +    if (IsExpandedFile[I])
> +      continue;
> +    MainViewFileID = I;
> +    return false;
> +  }
> +  return true;
> +}
> +
> +void CodeCoverageTool::createExpansionSubView(
> +    const MappingRegion &ExpandedRegion,
> +    const FunctionCoverageMapping &Function, SourceCoverageView &Parent) {
> +  auto ExpandedLines = findExpandedFileInterestingLineRange(
> +      ExpandedRegion.ExpandedFileID, Function);
> +  if (ViewOpts.Debug)
> +    llvm::outs() << "Expansion of " << ExpandedRegion.ExpandedFileID << ":"
> +                 << ExpandedLines.first << " -> " << ExpandedLines.second
> +                 << " @ " << ExpandedRegion.FileID << ", "
> +                 << ExpandedRegion.LineStart << ":"
> +                 << ExpandedRegion.ColumnStart << "\n";
> +  auto SourceBuffer =
> +      getSourceFile(Function.Filenames[ExpandedRegion.ExpandedFileID]);
> +  if (!SourceBuffer)
> +    return;
> +  auto SubView = llvm::make_unique<SourceCoverageView>(
> +      SourceBuffer.get(), Parent.getOptions(), ExpandedLines.first,
> +      ExpandedLines.second, ExpandedRegion);
> +  SourceCoverageDataManager RegionManager;
> +  for (const auto &Region : Function.MappingRegions) {
> +    if (Region.FileID == ExpandedRegion.ExpandedFileID)
> +      RegionManager.insert(Region);
> +  }
> +  SubView->load(RegionManager);
> +  createExpansionSubViews(*SubView, ExpandedRegion.ExpandedFileID, Function);
> +  Parent.addChild(std::move(SubView));
> +}
> +
> +void CodeCoverageTool::createExpansionSubViews(
> +    SourceCoverageView &View, unsigned ViewFileID,
> +    const FunctionCoverageMapping &Function) {
> +  if (!ViewOpts.ShowExpandedRegions)
> +    return;
> +  for (const auto &Region : Function.MappingRegions) {
> +    if (Region.Kind != CounterMappingRegion::ExpansionRegion)
> +      continue;
> +    if (Region.FileID != ViewFileID)
> +      continue;
> +    createExpansionSubView(Region, Function, View);
> +  }
> +}
> +
> +void CodeCoverageTool::createInstantiationSubView(
> +    StringRef SourceFile, const FunctionCoverageMapping &Function,
> +    SourceCoverageView &View) {
> +  SourceCoverageDataManager RegionManager;
> +  SmallSet<unsigned, 8> InterestingFileIDs;
> +  if (!gatherInterestingFileIDs(SourceFile, Function, InterestingFileIDs))
> +    return;
> +  // Get the interesting regions
> +  for (const auto &Region : Function.MappingRegions) {
> +    if (InterestingFileIDs.count(Region.FileID))
> +      RegionManager.insert(Region);
> +  }
> +  View.load(RegionManager);
> +  unsigned MainFileID;
> +  if (findMainViewFileID(SourceFile, Function, MainFileID))
> +    return;
> +  createExpansionSubViews(View, MainFileID, Function);
> +}
> +
> +bool CodeCoverageTool::createSourceFileView(
> +    StringRef SourceFile, SourceCoverageView &View,
> +    ArrayRef<FunctionCoverageMapping> FunctionMappingRecords,
> +    bool UseOnlyRegionsInMainFile) {
> +  SourceCoverageDataManager RegionManager;
> +  FunctionInstantiationSetCollector InstantiationSetCollector;
> +
> +  for (const auto &Function : FunctionMappingRecords) {
> +    unsigned MainFileID;
> +    if (findMainViewFileID(SourceFile, Function, MainFileID))
> +      continue;
> +    SmallSet<unsigned, 8> InterestingFileIDs;
> +    if (UseOnlyRegionsInMainFile) {
> +      InterestingFileIDs.insert(MainFileID);
> +    } else if (!gatherInterestingFileIDs(SourceFile, Function,
> +                                         InterestingFileIDs))
> +      continue;
> +    // Get the interesting regions
> +    for (const auto &Region : Function.MappingRegions) {
> +      if (InterestingFileIDs.count(Region.FileID))
> +        RegionManager.insert(Region);
> +    }
> +    InstantiationSetCollector.insert(Function, MainFileID);
> +    createExpansionSubViews(View, MainFileID, Function);
> +  }
> +  if (RegionManager.getSourceRegions().empty())
> +    return true;
> +  View.load(RegionManager);
> +  // Show instantiations
> +  if (!ViewOpts.ShowFunctionInstantiations)
> +    return false;
> +  for (const auto &InstantiationSet : InstantiationSetCollector) {
> +    if (InstantiationSet.second.size() < 2)
> +      continue;
> +    auto InterestingRange = findExpandedFileInterestingLineRange(
> +        InstantiationSet.second.front()->MappingRegions.front().FileID,
> +        *InstantiationSet.second.front());
> +    for (auto Function : InstantiationSet.second) {
> +      auto SubView = llvm::make_unique<SourceCoverageView>(
> +          View, InterestingRange.first, InterestingRange.second,
> +          Function->PrettyName);
> +      createInstantiationSubView(SourceFile, *Function, *SubView);
> +      View.addChild(std::move(SubView));
> +    }
> +  }
> +  return false;
> +}
> +
> +bool CodeCoverageTool::load() {
> +  auto CounterMappingBuff = MemoryBuffer::getFileOrSTDIN(ObjectFilename);
> +  if (auto EC = CounterMappingBuff.getError()) {
> +    error(EC.message(), ObjectFilename);
> +    return true;
> +  }
> +  ObjectFileCoverageMappingReader MappingReader(CounterMappingBuff.get());
> +  if (auto EC = MappingReader.readHeader()) {
> +    error(EC.message(), ObjectFilename);
> +    return true;
> +  }
> +
> +  std::vector<uint64_t> Counts;
> +  for (const auto &I : MappingReader) {
> +    FunctionCoverageMapping Function(I.FunctionName, I.Filenames);
> +
> +    // Create the mapping regions with evaluated execution counts
> +    Counts.clear();
> +    PGOReader->getFunctionCounts(Function.Name, I.FunctionHash, Counts);
> +
> +    // Get the biggest referenced counters
> +    bool RegionError = false;
> +    CounterMappingContext Ctx(I.Expressions, Counts);
> +    for (const auto &R : I.MappingRegions) {
> +      // Compute the values of mapped regions
> +      if (ViewOpts.Debug) {
> +        outs() << "File " << R.FileID << "| " << R.LineStart << ":"
> +               << R.ColumnStart << " -> " << R.LineEnd << ":" << R.ColumnEnd
> +               << " = ";
> +        Ctx.dump(R.Count);
> +        if (R.Kind == CounterMappingRegion::ExpansionRegion) {
> +          outs() << " (Expanded file id = " << R.ExpandedFileID << ") ";
> +        }
> +        outs() << "\n";
> +      }
> +      std::error_code Error;
> +      Function.MappingRegions.push_back(
> +          MappingRegion(R, Ctx.evaluate(R.Count, Error)));
> +      if (Error && !RegionError) {
> +        colored_ostream(errs(), raw_ostream::RED)
> +            << "error: Regions and counters don't match in a function '"
> +            << Function.PrettyName << "' (re-run the instrumented binary).";
> +        errs() << "\n";
> +        RegionError = true;
> +      }
> +    }
> +
> +    if (RegionError || !Filters.matches(Function))
> +      continue;
> +
> +    FunctionMappingRecords.push_back(Function);
> +  }
> +  return false;
> +}
> +
> +int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
> +  // Print a stack trace if we signal out.
> +  sys::PrintStackTraceOnErrorSignal();
> +  PrettyStackTraceProgram X(argc, argv);
> +  llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
> +
> +  cl::list<std::string> InputSourceFiles(
> +      cl::Positional, cl::desc("<Source files>"), cl::ZeroOrMore);
> +
> +  cl::opt<std::string> PGOFilename(
> +      "instr-profile", cl::Required,
> +      cl::desc(
> +          "File with the profile data obtained after an instrumented run"));
> +
> +  cl::opt<bool> DebugDump("dump", cl::Optional,
> +                          cl::desc("Show internal debug dump"));
> +
> +  cl::opt<bool> FilenameEquivalence(
> +      "filename-equivalence", cl::Optional,
> +      cl::desc("Compare the filenames instead of full filepaths"));
> +
> +  cl::OptionCategory FilteringCategory("Function filtering options");
> +
> +  cl::list<std::string> NameFilters(
> +      "name", cl::Optional,
> +      cl::desc("Show code coverage only for functions with the given name"),
> +      cl::ZeroOrMore, cl::cat(FilteringCategory));
> +
> +  cl::list<std::string> NameRegexFilters(
> +      "name-regex", cl::Optional,
> +      cl::desc("Show code coverage only for functions that match the given "
> +               "regular expression"),
> +      cl::ZeroOrMore, cl::cat(FilteringCategory));
> +
> +  cl::opt<double> RegionCoverageLtFilter(
> +      "region-coverage-lt", cl::Optional,
> +      cl::desc("Show code coverage only for functions with region coverage "
> +               "less than the given threshold"),
> +      cl::cat(FilteringCategory));
> +
> +  cl::opt<double> RegionCoverageGtFilter(
> +      "region-coverage-gt", cl::Optional,
> +      cl::desc("Show code coverage only for functions with region coverage "
> +               "greater than the given threshold"),
> +      cl::cat(FilteringCategory));
> +
> +  cl::opt<double> LineCoverageLtFilter(
> +      "line-coverage-lt", cl::Optional,
> +      cl::desc("Show code coverage only for functions with line coverage less "
> +               "than the given threshold"),
> +      cl::cat(FilteringCategory));
> +
> +  cl::opt<double> LineCoverageGtFilter(
> +      "line-coverage-gt", cl::Optional,
> +      cl::desc("Show code coverage only for functions with line coverage "
> +               "greater than the given threshold"),
> +      cl::cat(FilteringCategory));
> +
> +  auto commandLineParser = [&, this](int argc, const char **argv) -> int {
> +    cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
> +    ViewOpts.Debug = DebugDump;
> +    CompareFilenamesOnly = FilenameEquivalence;
> +
> +    if (auto EC = IndexedInstrProfReader::create(PGOFilename, PGOReader)) {
> +      error(EC.message(), PGOFilename);
> +      return 1;
> +    }
> +
> +    // Create the function filters
> +    if (!NameFilters.empty() || !NameRegexFilters.empty()) {
> +      auto NameFilterer = new CoverageFilters;
> +      for (const auto &Name : NameFilters)
> +        NameFilterer->push_back(llvm::make_unique<NameCoverageFilter>(Name));
> +      for (const auto &Regex : NameRegexFilters)
> +        NameFilterer->push_back(
> +            llvm::make_unique<NameRegexCoverageFilter>(Regex));
> +      Filters.push_back(std::unique_ptr<CoverageFilter>(NameFilterer));
> +    }
> +    if (RegionCoverageLtFilter.getNumOccurrences() ||
> +        RegionCoverageGtFilter.getNumOccurrences() ||
> +        LineCoverageLtFilter.getNumOccurrences() ||
> +        LineCoverageGtFilter.getNumOccurrences()) {
> +      auto StatFilterer = new CoverageFilters;
> +      if (RegionCoverageLtFilter.getNumOccurrences())
> +        StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
> +            RegionCoverageFilter::LessThan, RegionCoverageLtFilter));
> +      if (RegionCoverageGtFilter.getNumOccurrences())
> +        StatFilterer->push_back(llvm::make_unique<RegionCoverageFilter>(
> +            RegionCoverageFilter::GreaterThan, RegionCoverageGtFilter));
> +      if (LineCoverageLtFilter.getNumOccurrences())
> +        StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
> +            LineCoverageFilter::LessThan, LineCoverageLtFilter));
> +      if (LineCoverageGtFilter.getNumOccurrences())
> +        StatFilterer->push_back(llvm::make_unique<LineCoverageFilter>(
> +            RegionCoverageFilter::GreaterThan, LineCoverageGtFilter));
> +      Filters.push_back(std::unique_ptr<CoverageFilter>(StatFilterer));
> +    }
> +
> +    SourceFiles = InputSourceFiles;
> +    return 0;
> +  };
> +
> +  // Parse the object filename
> +  if (argc > 1) {
> +    StringRef Arg(argv[1]);
> +    if (Arg.equals_lower("-help") || Arg.equals_lower("-version")) {
> +      cl::ParseCommandLineOptions(2, argv, "LLVM code coverage tool\n");
> +      return 0;
> +    }
> +    ObjectFilename = Arg;
> +
> +    argv[1] = argv[0];
> +    --argc;
> +    ++argv;
> +  } else {
> +    errs() << sys::path::filename(argv[0]) << ": No executable file given!\n";
> +    return 1;
> +  }
> +
> +  switch (Cmd) {
> +  case Show:
> +    return show(argc, argv, commandLineParser);
> +  case Report:
> +    return report(argc, argv, commandLineParser);
> +  }
> +  return 0;
> +}
> +
> +int CodeCoverageTool::show(int argc, const char **argv,
> +                           CommandLineParserType commandLineParser) {
> +
> +  cl::OptionCategory ViewCategory("Viewing options");
> +
> +  cl::opt<bool> ShowLineExecutionCounts(
> +      "show-line-counts", cl::Optional,
> +      cl::desc("Show the execution counts for each line"), cl::init(true),
> +      cl::cat(ViewCategory));
> +
> +  cl::opt<bool> ShowRegions(
> +      "show-regions", cl::Optional,
> +      cl::desc("Show the execution counts for each region"),
> +      cl::cat(ViewCategory));
> +
> +  cl::opt<bool> ShowBestLineRegionsCounts(
> +      "show-line-counts-or-regions", cl::Optional,
> +      cl::desc("Show the execution counts for each line, or the execution "
> +               "counts for each region on lines that have multiple regions"),
> +      cl::cat(ViewCategory));
> +
> +  cl::opt<bool> ShowExpansions("show-expansions", cl::Optional,
> +                               cl::desc("Show expanded source regions"),
> +                               cl::cat(ViewCategory));
> +
> +  cl::opt<bool> ShowInstantiations("show-instantiations", cl::Optional,
> +                                   cl::desc("Show function instantiations"),
> +                                   cl::cat(ViewCategory));
> +
> +  cl::opt<bool> NoColors("no-colors", cl::Optional,
> +                         cl::desc("Don't show text colors"), cl::init(false),
> +                         cl::cat(ViewCategory));
> +
> +  auto Err = commandLineParser(argc, argv);
> +  if (Err)
> +    return Err;
> +
> +  ViewOpts.Colors = !NoColors;
> +  ViewOpts.ShowLineNumbers = true;
> +  ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 ||
> +                           !ShowRegions || ShowBestLineRegionsCounts;
> +  ViewOpts.ShowRegionMarkers = ShowRegions || ShowBestLineRegionsCounts;
> +  ViewOpts.ShowLineStatsOrRegionMarkers = ShowBestLineRegionsCounts;
> +  ViewOpts.ShowExpandedRegions = ShowExpansions;
> +  ViewOpts.ShowFunctionInstantiations = ShowInstantiations;
> +
> +  if (load())
> +    return 1;
> +
> +  if (!Filters.empty()) {
> +    // Show functions
> +    for (const auto &Function : FunctionMappingRecords) {
> +      unsigned MainFileID;
> +      if (findMainViewFileID(Function, MainFileID))
> +        continue;
> +      StringRef SourceFile = Function.Filenames[MainFileID];
> +      std::unique_ptr<SourceCoverageView> mainView;
> +      auto SourceBuffer = getSourceFile(SourceFile);
> +      if (!SourceBuffer)
> +        return 1;
> +      auto Range = findExpandedFileInterestingLineRange(MainFileID, Function);
> +      mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts,
> +                                            Range.first, Range.second));
> +      createSourceFileView(SourceFile, *mainView, Function, true);
> +      ViewOpts.colored_ostream(outs(), raw_ostream::CYAN)
> +          << Function.PrettyName << " from " << SourceFile << ":";
> +      outs() << "\n";
> +      mainView->render(outs());
> +      if (FunctionMappingRecords.size() > 1)
> +        outs() << "\n";
> +    }
> +    return 0;
> +  }
> +
> +  // Show files
> +  bool ShowFilenames = SourceFiles.size() != 1;
> +
> +  if (SourceFiles.empty()) {
> +    // Get the source files from the function coverage mapping
> +    std::set<StringRef> UniqueFilenames;
> +    for (const auto &Function : FunctionMappingRecords) {
> +      for (const auto &Filename : Function.Filenames)
> +        UniqueFilenames.insert(Filename);
> +    }
> +    for (const auto &Filename : UniqueFilenames)
> +      SourceFiles.push_back(Filename);
> +  }
> +
> +  for (const auto &SourceFile : SourceFiles) {
> +    std::unique_ptr<SourceCoverageView> mainView;
> +    auto SourceBuffer = getSourceFile(SourceFile);
> +    if (!SourceBuffer)
> +      return 1;
> +    mainView.reset(new SourceCoverageView(SourceBuffer.get(), ViewOpts));
> +    if (createSourceFileView(SourceFile, *mainView, FunctionMappingRecords)) {
> +      ViewOpts.colored_ostream(outs(), raw_ostream::RED)
> +          << "warning: The file '" << SourceFile << "' isn't covered.";
> +      outs() << "\n";
> +      continue;
> +    }
> +
> +    if (ShowFilenames) {
> +      ViewOpts.colored_ostream(outs(), raw_ostream::CYAN) << SourceFile << ":";
> +      outs() << "\n";
> +    }
> +    mainView->render(outs());
> +    if (SourceFiles.size() > 1)
> +      outs() << "\n";
> +  }
> +
> +  return 0;
> +}
> +
> +int CodeCoverageTool::report(int argc, const char **argv,
> +                             CommandLineParserType commandLineParser) {
> +  cl::opt<bool> NoColors("no-colors", cl::Optional,
> +                         cl::desc("Don't show text colors"), cl::init(false));
> +
> +  auto Err = commandLineParser(argc, argv);
> +  if (Err)
> +    return Err;
> +
> +  ViewOpts.Colors = !NoColors;
> +
> +  if (load())
> +    return 1;
> +
> +  CoverageSummary Summarizer;
> +  Summarizer.createSummaries(FunctionMappingRecords);
> +  CoverageReport Report(ViewOpts, Summarizer);
> +  if (SourceFiles.empty() && Filters.empty()) {
> +    Report.renderFileReports(llvm::outs());
> +    return 0;
> +  }
> +
> +  Report.renderFunctionReports(llvm::outs());
> +  return 0;
> +}
> +
> +int show_main(int argc, const char **argv) {
> +  CodeCoverageTool Tool;
> +  return Tool.run(CodeCoverageTool::Show, argc, argv);
> +}
> +
> +int report_main(int argc, const char **argv) {
> +  CodeCoverageTool Tool;
> +  return Tool.run(CodeCoverageTool::Report, argc, argv);
> +}
> Index: tools/llvm-cov/CoverageFilters.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageFilters.cpp
> @@ -0,0 +1,57 @@
> +//===- CoverageFilters.cpp - Function coverage mapping filters ------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// These classes provide filtering for function coverage mapping records.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "CoverageFilters.h"
> +#include "CoverageSummaryInfo.h"
> +#include "llvm/Support/Regex.h"
> +
> +using namespace llvm;
> +
> +bool NameCoverageFilter::matches(const FunctionCoverageMapping &Function) {
> +  StringRef FuncName = Function.PrettyName;
> +  return FuncName.find(Name) != StringRef::npos;
> +}
> +
> +bool NameRegexCoverageFilter::matches(const FunctionCoverageMapping &Function) {
> +  return llvm::Regex(Regex).match(Function.PrettyName);
> +}
> +
> +bool RegionCoverageFilter::matches(const FunctionCoverageMapping &Function) {
> +  return PassesThreshold(FunctionCoverageSummary::get(Function)
> +                             .RegionCoverage.getPercentCovered());
> +}
> +
> +bool LineCoverageFilter::matches(const FunctionCoverageMapping &Function) {
> +  return PassesThreshold(
> +      FunctionCoverageSummary::get(Function).LineCoverage.getPercentCovered());
> +}
> +
> +void CoverageFilters::push_back(std::unique_ptr<CoverageFilter> Filter) {
> +  Filters.push_back(std::move(Filter));
> +}
> +
> +bool CoverageFilters::matches(const FunctionCoverageMapping &Function) {
> +  for (const auto &Filter : Filters) {
> +    if (Filter->matches(Function))
> +      return true;
> +  }
> +  return false;
> +}
> +
> +bool CoverageFiltersMatchAll::matches(const FunctionCoverageMapping &Function) {
> +  for (const auto &Filter : Filters) {
> +    if (!Filter->matches(Function))
> +      return false;
> +  }
> +  return true;
> +}
> Index: tools/llvm-cov/CoverageFilters.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageFilters.h
> @@ -0,0 +1,125 @@
> +//===- CoverageFilters.h - Function coverage mapping filters --------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// These classes provide filtering for function coverage mapping records.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_COVERAGEFILTERS_H
> +#define LLVM_COV_COVERAGEFILTERS_H
> +
> +#include "FunctionCoverageMapping.h"
> +#include <vector>
> +#include <memory>
> +
> +namespace llvm {
> +
> +/// \brief Matches specific functions that pass the requirement of this filter.
> +class CoverageFilter {
> +public:
> +  virtual ~CoverageFilter() {}
> +
> +  /// \brief Return true if the function passes the requirements of this filter.
> +  virtual bool matches(const FunctionCoverageMapping &Function) { return true; }
> +};
> +
> +/// \brief Matches functions that contain a specific string in their name.
> +class NameCoverageFilter : public CoverageFilter {
> +  StringRef Name;
> +
> +public:
> +  NameCoverageFilter(StringRef Name) : Name(Name) {}
> +
> +  bool matches(const FunctionCoverageMapping &Function) override;
> +};
> +
> +/// \brief Matches functions whose name matches a certain regular expression.
> +class NameRegexCoverageFilter : public CoverageFilter {
> +  StringRef Regex;
> +
> +public:
> +  NameRegexCoverageFilter(StringRef Regex) : Regex(Regex) {}
> +
> +  bool matches(const FunctionCoverageMapping &Function) override;
> +};
> +
> +/// \brief Matches numbers that pass a certain threshold.
> +template <typename T> class StatisticThresholdFilter {
> +public:
> +  enum Operation { LessThan, GreaterThan };
> +
> +protected:
> +  Operation Op;
> +  T Threshold;
> +
> +  StatisticThresholdFilter(Operation Op, T Threshold)
> +      : Op(Op), Threshold(Threshold) {}
> +
> +  /// \brief Return true if the given number is less than
> +  /// or greater than the certain threshold.
> +  bool PassesThreshold(T Value) const {
> +    switch (Op) {
> +    case LessThan:
> +      return Value < Threshold;
> +    case GreaterThan:
> +      return Value > Threshold;
> +    }
> +    return false;
> +  }
> +};
> +
> +/// \brief Matches functions whose region coverage percentage
> +/// is above/below a certain percentage.
> +class RegionCoverageFilter : public CoverageFilter,
> +                             public StatisticThresholdFilter<double> {
> +public:
> +  RegionCoverageFilter(Operation Op, double Threshold)
> +      : StatisticThresholdFilter(Op, Threshold) {}
> +
> +  bool matches(const FunctionCoverageMapping &Function) override;
> +};
> +
> +/// \brief Matches functions whose line coverage percentage
> +/// is above/below a certain percentage.
> +class LineCoverageFilter : public CoverageFilter,
> +                           public StatisticThresholdFilter<double> {
> +public:
> +  LineCoverageFilter(Operation Op, double Threshold)
> +      : StatisticThresholdFilter(Op, Threshold) {}
> +
> +  bool matches(const FunctionCoverageMapping &Function) override;
> +};
> +
> +/// \brief A collection of filters.
> +/// Matches functions that match any filters contained
> +/// in an instance of this class.
> +class CoverageFilters : public CoverageFilter {
> +protected:
> +  std::vector<std::unique_ptr<CoverageFilter>> Filters;
> +
> +public:
> +  /// \brief Append a filter to this collection.
> +  void push_back(std::unique_ptr<CoverageFilter> Filter);
> +
> +  bool empty() const { return Filters.empty(); }
> +
> +  bool matches(const FunctionCoverageMapping &Function) override;
> +};
> +
> +/// \brief A collection of filters.
> +/// Matches functions that match all of the filters contained
> +/// in an instance of this class.
> +class CoverageFiltersMatchAll : public CoverageFilters {
> +public:
> +  bool matches(const FunctionCoverageMapping &Function) override;
> +};
> +
> +} // namespace llvm
> +
> +#endif // LLVM_COV_COVERAGEFILTERS_H
> Index: tools/llvm-cov/CoverageReport.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageReport.cpp
> @@ -0,0 +1,201 @@
> +//===- CoverageReport.cpp - Code coverage report -------------------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class implements rendering of a code coverage report.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "CoverageReport.h"
> +#include "CoverageSummary.h"
> +#include "RenderingSupport.h"
> +#include "llvm/Support/Format.h"
> +#include "llvm/Support/FileSystem.h"
> +
> +using namespace llvm;
> +namespace {
> +/// \brief Helper struct which prints trimmed and aligned columns.
> +struct Column {
> +  enum TrimKind { NoTrim, LeftTrim, RightTrim };
> +
> +  enum AlignmentKind { LeftAlignment, RightAlignment };
> +
> +  StringRef Str;
> +  unsigned Width;
> +  TrimKind Trim;
> +  AlignmentKind Alignment;
> +
> +  Column(StringRef Str, unsigned Width)
> +      : Str(Str), Width(Width), Trim(NoTrim), Alignment(LeftAlignment) {}
> +
> +  Column &set(TrimKind Value) {
> +    Trim = Value;
> +    return *this;
> +  }
> +
> +  Column &set(AlignmentKind Value) {
> +    Alignment = Value;
> +    return *this;
> +  }
> +
> +  void render(raw_ostream &OS) const;
> +};
> +raw_ostream &operator<<(raw_ostream &OS, const Column &Value) {
> +  Value.render(OS);
> +  return OS;
> +}
> +}
> +
> +void Column::render(raw_ostream &OS) const {
> +  if (Str.size() <= Width) {
> +    if (Alignment == RightAlignment) {
> +      OS.indent(Width - Str.size());
> +      OS << Str;
> +      return;
> +    }
> +    OS << Str;
> +    OS.indent(Width - Str.size());
> +    return;
> +  }
> +
> +  switch (Trim) {
> +  case NoTrim:
> +    OS << Str.substr(0, Width);
> +    break;
> +  case LeftTrim:
> +    OS << "..." << Str.substr(Str.size() - Width + 3);
> +    break;
> +  case RightTrim:
> +    OS << Str.substr(0, Width - 3) << "...";
> +    break;
> +  }
> +}
> +
> +static Column column(StringRef Str, unsigned Width) {
> +  return Column(Str, Width);
> +}
> +
> +template <typename T>
> +static Column column(StringRef Str, unsigned Width, const T &Value) {
> +  return Column(Str, Width).set(Value);
> +}
> +
> +static const unsigned FileReportColumns[] = {25, 10, 8, 8, 10, 8};
> +static const unsigned FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
> +
> +/// \brief Prints a horizontal divider which spans across the given columns.
> +template <typename T, size_t N>
> +static void renderDivider(T (&Columns)[N], raw_ostream &OS) {
> +  unsigned Length = 0;
> +  for (unsigned I = 0; I < N; ++I)
> +    Length += Columns[I];
> +  for (unsigned I = 0; I < Length; ++I)
> +    OS << '-';
> +}
> +
> +/// \brief Return the color which correponds to the coverage
> +/// percentage of a certain metric.
> +template <typename T>
> +static raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
> +  if (Info.isFullyCovered())
> +    return raw_ostream::GREEN;
> +  return Info.getPercentCovered() >= 80.0 ? raw_ostream::YELLOW
> +                                          : raw_ostream::RED;
> +}
> +
> +void CoverageReport::render(const FileCoverageSummary &File, raw_ostream &OS) {
> +  OS << column(File.Name, FileReportColumns[0], Column::LeftTrim)
> +     << format("%*zd", FileReportColumns[1], File.RegionCoverage.NumRegions);
> +  (Options.colored_ostream(OS, File.RegionCoverage.isFullyCovered()
> +                                   ? raw_ostream::GREEN
> +                                   : raw_ostream::RED)).OS
> +      << format("%*zd", FileReportColumns[2], File.RegionCoverage.NotCovered);
> +  (Options.colored_ostream(
> +       OS, determineCoveragePercentageColor(File.RegionCoverage))).OS
> +      << format("%*.2f", FileReportColumns[3] - 1,
> +                File.RegionCoverage.getPercentCovered()) << '%';
> +  OS << format("%*zd", FileReportColumns[4],
> +               File.FunctionCoverage.NumFunctions);
> +  (Options.colored_ostream(
> +       OS, determineCoveragePercentageColor(File.FunctionCoverage))).OS
> +      << format("%*.2f", FileReportColumns[5] - 1,
> +                File.FunctionCoverage.getPercentCovered()) << '%';
> +  OS << "\n";
> +}
> +
> +void CoverageReport::render(const FunctionCoverageSummary &Function,
> +                            raw_ostream &OS) {
> +  OS << column(Function.Name, FunctionReportColumns[0], Column::RightTrim)
> +     << format("%*zd", FunctionReportColumns[1],
> +               Function.RegionCoverage.NumRegions);
> +  (Options.colored_ostream(OS, Function.RegionCoverage.isFullyCovered()
> +                                   ? raw_ostream::GREEN
> +                                   : raw_ostream::RED)).OS
> +      << format("%*zd", FunctionReportColumns[2],
> +                Function.RegionCoverage.NotCovered);
> +  (Options.colored_ostream(
> +       OS, determineCoveragePercentageColor(Function.RegionCoverage))).OS
> +      << format("%*.2f", FunctionReportColumns[3] - 1,
> +                Function.RegionCoverage.getPercentCovered()) << '%';
> +  OS << format("%*zd", FunctionReportColumns[4],
> +               Function.LineCoverage.NumLines);
> +  (Options.colored_ostream(OS, Function.LineCoverage.isFullyCovered()
> +                                   ? raw_ostream::GREEN
> +                                   : raw_ostream::RED)).OS
> +      << format("%*zd", FunctionReportColumns[5],
> +                Function.LineCoverage.NotCovered);
> +  (Options.colored_ostream(
> +       OS, determineCoveragePercentageColor(Function.LineCoverage))).OS
> +      << format("%*.2f", FunctionReportColumns[6] - 1,
> +                Function.LineCoverage.getPercentCovered()) << '%';
> +  OS << "\n";
> +}
> +
> +void CoverageReport::renderFunctionReports(raw_ostream &OS) {
> +  bool isFirst = true;
> +  for (const auto &File : Summary.getFileSummaries()) {
> +    if (isFirst)
> +      isFirst = false;
> +    else
> +      OS << "\n";
> +    OS << "File '" << File.Name << "':\n";
> +    OS << column("Name", FunctionReportColumns[0])
> +       << column("Regions", FunctionReportColumns[1], Column::RightAlignment)
> +       << column("Miss", FunctionReportColumns[2], Column::RightAlignment)
> +       << column("Cover", FunctionReportColumns[3], Column::RightAlignment)
> +       << column("Lines", FunctionReportColumns[4], Column::RightAlignment)
> +       << column("Miss", FunctionReportColumns[5], Column::RightAlignment)
> +       << column("Cover", FunctionReportColumns[6], Column::RightAlignment);
> +    OS << "\n";
> +    renderDivider(FunctionReportColumns, OS);
> +    OS << "\n";
> +    for (const auto &Function : File.FunctionSummaries)
> +      render(Function, OS);
> +    renderDivider(FunctionReportColumns, OS);
> +    OS << "\n";
> +    render(FunctionCoverageSummary("TOTAL", File.RegionCoverage,
> +                                   File.LineCoverage),
> +           OS);
> +  }
> +}
> +
> +void CoverageReport::renderFileReports(raw_ostream &OS) {
> +  OS << column("Filename", FileReportColumns[0])
> +     << column("Regions", FileReportColumns[1], Column::RightAlignment)
> +     << column("Miss", FileReportColumns[2], Column::RightAlignment)
> +     << column("Cover", FileReportColumns[3], Column::RightAlignment)
> +     << column("Functions", FileReportColumns[4], Column::RightAlignment)
> +     << column("Cover", FileReportColumns[5], Column::RightAlignment) << "\n";
> +  renderDivider(FileReportColumns, OS);
> +  OS << "\n";
> +  for (const auto &File : Summary.getFileSummaries())
> +    render(File, OS);
> +  renderDivider(FileReportColumns, OS);
> +  OS << "\n";
> +  render(Summary.getCombinedFileSummaries(), OS);
> +}
> Index: tools/llvm-cov/CoverageReport.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageReport.h
> @@ -0,0 +1,40 @@
> +//===- CoverageReport.h - Code coverage report ---------------------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class implements rendering of a code coverage report.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_COVERAGEREPORT_H
> +#define LLVM_COV_COVERAGEREPORT_H
> +
> +#include "CoverageViewOptions.h"
> +#include "CoverageSummary.h"
> +
> +namespace llvm {
> +
> +/// \brief Displays the code coverage report.
> +class CoverageReport {
> +  const CoverageViewOptions &Options;
> +  CoverageSummary &Summary;
> +
> +  void render(const FileCoverageSummary &File, raw_ostream &OS);
> +  void render(const FunctionCoverageSummary &Function, raw_ostream &OS);
> +
> +public:
> +  CoverageReport(const CoverageViewOptions &Options, CoverageSummary &Summary)
> +      : Options(Options), Summary(Summary) {}
> +
> +  void renderFunctionReports(raw_ostream &OS);
> +
> +  void renderFileReports(raw_ostream &OS);
> +};
> +}
> +
> +#endif // LLVM_COV_COVERAGEREPORT_H
> Index: tools/llvm-cov/CoverageSummary.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageSummary.cpp
> @@ -0,0 +1,92 @@
> +//===- CoverageSummary.cpp - Code coverage summary ------------------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class implements data management and rendering for the code coverage
> +// summaries of all files and functions.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "CoverageSummary.h"
> +#include "llvm/Support/FileSystem.h"
> +#include "llvm/Support/Format.h"
> +
> +using namespace llvm;
> +
> +unsigned CoverageSummary::getFileID(StringRef Filename) {
> +  for (unsigned I = 0, E = Filenames.size(); I < E; ++I) {
> +    if (sys::fs::equivalent(Filenames[I], Filename))
> +      return I;
> +  }
> +  Filenames.push_back(Filename);
> +  return Filenames.size() - 1;
> +}
> +
> +void
> +CoverageSummary::createSummaries(ArrayRef<FunctionCoverageMapping> Functions) {
> +  std::vector<std::pair<unsigned, size_t>> FunctionFileIDs;
> +
> +  FunctionFileIDs.resize(Functions.size());
> +  for (size_t I = 0, E = Functions.size(); I < E; ++I) {
> +    StringRef Filename = Functions[I].Filenames[0];
> +    FunctionFileIDs[I] = std::make_pair(getFileID(Filename), I);
> +  }
> +
> +  // Sort the function records by file ids
> +  std::sort(FunctionFileIDs.begin(), FunctionFileIDs.end(),
> +            [](const std::pair<unsigned, size_t> &lhs,
> +               const std::pair<unsigned, size_t> &rhs) {
> +    return lhs.first < rhs.first;
> +  });
> +
> +  // Create function summaries in a sorted order (by file ids)
> +  FunctionSummaries.reserve(Functions.size());
> +  for (size_t I = 0, E = Functions.size(); I < E; ++I)
> +    FunctionSummaries.push_back(
> +        FunctionCoverageSummary::get(Functions[FunctionFileIDs[I].second]));
> +
> +  // Create file summaries
> +  size_t CurrentSummary = 0;
> +  for (unsigned FileID = 0; FileID < Filenames.size(); ++FileID) {
> +    // Gather the relevant functions summaries
> +    auto PrevSummary = CurrentSummary;
> +    while (CurrentSummary < FunctionSummaries.size() &&
> +           FunctionFileIDs[CurrentSummary].first == FileID)
> +      ++CurrentSummary;
> +    ArrayRef<FunctionCoverageSummary> LocalSummaries(
> +        FunctionSummaries.data() + PrevSummary,
> +        FunctionSummaries.data() + CurrentSummary);
> +    if (LocalSummaries.empty())
> +      continue;
> +
> +    FileSummaries.push_back(
> +        FileCoverageSummary::get(Filenames[FileID], LocalSummaries));
> +  }
> +}
> +
> +FileCoverageSummary CoverageSummary::getCombinedFileSummaries() {
> +  size_t NumRegions = 0, CoveredRegions = 0;
> +  size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
> +  size_t NumFunctionsCovered = 0, NumFunctions = 0;
> +  for (const auto &File : FileSummaries) {
> +    NumRegions += File.RegionCoverage.NumRegions;
> +    CoveredRegions += File.RegionCoverage.Covered;
> +
> +    NumLines += File.LineCoverage.NumLines;
> +    NonCodeLines += File.LineCoverage.NonCodeLines;
> +    CoveredLines += File.LineCoverage.Covered;
> +
> +    NumFunctionsCovered += File.FunctionCoverage.FullyCovered;
> +    NumFunctions += File.FunctionCoverage.NumFunctions;
> +  }
> +  return FileCoverageSummary(
> +      "TOTAL", RegionCoverageInfo(CoveredRegions, NumRegions),
> +      LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
> +      FunctionCoverageInfo(NumFunctionsCovered, NumFunctions),
> +      ArrayRef<FunctionCoverageSummary>());
> +}
> Index: tools/llvm-cov/CoverageSummary.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageSummary.h
> @@ -0,0 +1,45 @@
> +//===- CoverageSummary.h - Code coverage summary --------------------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class implements data management and rendering for the code coverage
> +// summaries of all files and functions.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_COVERAGESUMMARY_H
> +#define LLVM_COV_COVERAGESUMMARY_H
> +
> +#include "CoverageSummaryInfo.h"
> +#include <vector>
> +
> +namespace llvm {
> +
> +/// \brief Manager for the function and file code coverage summaries.
> +class CoverageSummary {
> +  std::vector<StringRef> Filenames;
> +  std::vector<FunctionCoverageSummary> FunctionSummaries;
> +  std::vector<std::pair<unsigned, unsigned>> FunctionSummariesFileIDs;
> +  std::vector<FileCoverageSummary> FileSummaries;
> +
> +  unsigned getFileID(StringRef Filename);
> +
> +public:
> +  void createSummaries(ArrayRef<FunctionCoverageMapping> Functions);
> +
> +  ArrayRef<FileCoverageSummary> getFileSummaries() { return FileSummaries; }
> +
> +  FileCoverageSummary getCombinedFileSummaries();
> +
> +  void render(const FunctionCoverageSummary &Summary, raw_ostream &OS);
> +
> +  void render(raw_ostream &OS);
> +};
> +}
> +
> +#endif // LLVM_COV_COVERAGESUMMARY_H
> Index: tools/llvm-cov/CoverageSummaryInfo.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageSummaryInfo.cpp
> @@ -0,0 +1,95 @@
> +//===- CoverageSummaryInfo.cpp - Coverage summary for function/file -------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// These structures are used to represent code coverage metrics
> +// for functions/files.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "CoverageSummaryInfo.h"
> +
> +using namespace llvm;
> +using namespace coverage;
> +
> +FunctionCoverageSummary
> +FunctionCoverageSummary::get(const FunctionCoverageMapping &Function) {
> +  // Compute the region coverage
> +  size_t NumCodeRegions = 0, CoveredRegions = 0;
> +  for (auto &Region : Function.MappingRegions) {
> +    if (Region.Kind != CounterMappingRegion::CodeRegion)
> +      continue;
> +    ++NumCodeRegions;
> +    if (Region.ExecutionCount != 0)
> +      ++CoveredRegions;
> +  }
> +
> +  // Compute the line coverage
> +  size_t NumLines = 0, CoveredLines = 0;
> +  for (unsigned FileID = 0, E = Function.Filenames.size(); FileID < E;
> +       ++FileID) {
> +    // Find the line start and end of the function's source code
> +    // in that particular file
> +    unsigned LineStart = std::numeric_limits<unsigned>::max();
> +    unsigned LineEnd = 0;
> +    for (auto &Region : Function.MappingRegions) {
> +      if (Region.FileID != FileID)
> +        continue;
> +      LineStart = std::min(LineStart, Region.LineStart);
> +      LineEnd = std::max(LineEnd, Region.LineEnd);
> +    }
> +    unsigned LineCount = LineEnd - LineStart + 1;
> +
> +    // Get counters
> +    llvm::SmallVector<uint64_t, 16> ExecutionCounts;
> +    ExecutionCounts.resize(LineCount, 0);
> +    for (auto &Region : Function.MappingRegions) {
> +      if (Region.FileID != FileID)
> +        continue;
> +      // Ignore the lines that were skipped by the preprocessor.
> +      auto ExecutionCount = Region.ExecutionCount;
> +      if (Region.Kind == MappingRegion::SkippedRegion) {
> +        LineCount -= Region.LineEnd - Region.LineStart + 1;
> +        ExecutionCount = 1;
> +      }
> +      for (unsigned I = Region.LineStart; I <= Region.LineEnd; ++I)
> +        ExecutionCounts[I - LineStart] = ExecutionCount;
> +    }
> +    CoveredLines += LineCount - std::count(ExecutionCounts.begin(),
> +                                           ExecutionCounts.end(), 0);
> +    NumLines += LineCount;
> +  }
> +  return FunctionCoverageSummary(
> +      Function.PrettyName, RegionCoverageInfo(CoveredRegions, NumCodeRegions),
> +      LineCoverageInfo(CoveredLines, 0, NumLines));
> +}
> +
> +FileCoverageSummary
> +FileCoverageSummary::get(StringRef Name,
> +                         ArrayRef<FunctionCoverageSummary> FunctionSummaries) {
> +  size_t NumRegions = 0, CoveredRegions = 0;
> +  size_t NumLines = 0, NonCodeLines = 0, CoveredLines = 0;
> +  size_t NumFunctionsCovered = 0;
> +  for (const auto &Func : FunctionSummaries) {
> +    CoveredRegions += Func.RegionCoverage.Covered;
> +    NumRegions += Func.RegionCoverage.NumRegions;
> +
> +    CoveredLines += Func.LineCoverage.Covered;
> +    NonCodeLines += Func.LineCoverage.NonCodeLines;
> +    NumLines += Func.LineCoverage.NumLines;
> +
> +    if (Func.RegionCoverage.isFullyCovered())
> +      ++NumFunctionsCovered;
> +  }
> +
> +  return FileCoverageSummary(
> +      Name, RegionCoverageInfo(CoveredRegions, NumRegions),
> +      LineCoverageInfo(CoveredLines, NonCodeLines, NumLines),
> +      FunctionCoverageInfo(NumFunctionsCovered, FunctionSummaries.size()),
> +      FunctionSummaries);
> +}
> Index: tools/llvm-cov/CoverageSummaryInfo.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageSummaryInfo.h
> @@ -0,0 +1,131 @@
> +//===- CoverageSummaryInfo.h - Coverage summary for function/file ---------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// These structures are used to represent code coverage metrics
> +// for functions/files.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_COVERAGESUMMARYINFO_H
> +#define LLVM_COV_COVERAGESUMMARYINFO_H
> +
> +#include "FunctionCoverageMapping.h"
> +#include "llvm/Support/raw_ostream.h"
> +
> +namespace llvm {
> +
> +/// \brief Provides information about region coverage for a function/file.
> +struct RegionCoverageInfo {
> +  /// \brief The number of regions that were executed at least once.
> +  size_t Covered;
> +
> +  /// \brief The number of regions that weren't executed.
> +  size_t NotCovered;
> +
> +  /// \brief The total number of regions in a function/file.
> +  size_t NumRegions;
> +
> +  RegionCoverageInfo(size_t Covered, size_t NumRegions)
> +      : Covered(Covered), NotCovered(NumRegions - Covered),
> +        NumRegions(NumRegions) {}
> +
> +  bool isFullyCovered() const { return Covered == NumRegions; }
> +
> +  double getPercentCovered() const {
> +    return double(Covered) / double(NumRegions) * 100.0;
> +  }
> +};
> +
> +/// \brief Provides information about line coverage for a function/file.
> +struct LineCoverageInfo {
> +  /// \brief The number of lines that were executed at least once.
> +  size_t Covered;
> +
> +  /// \brief The number of lines that weren't executed.
> +  size_t NotCovered;
> +
> +  /// \brief The number of lines that aren't code.
> +  size_t NonCodeLines;
> +
> +  /// \brief The total number of lines in a function/file.
> +  size_t NumLines;
> +
> +  LineCoverageInfo(size_t Covered, size_t NumNonCodeLines, size_t NumLines)
> +      : Covered(Covered), NotCovered(NumLines - NumNonCodeLines - Covered),
> +        NonCodeLines(NumNonCodeLines), NumLines(NumLines) {}
> +
> +  bool isFullyCovered() const { return Covered == (NumLines - NonCodeLines); }
> +
> +  double getPercentCovered() const {
> +    return double(Covered) / double(NumLines - NonCodeLines) * 100.0;
> +  }
> +};
> +
> +/// \brief Provides information about function coverage for a file.
> +struct FunctionCoverageInfo {
> +  /// \brief The number of functions that have full
> +  /// region coverage.
> +  size_t FullyCovered;
> +
> +  /// \brief The total number of functions in this file.
> +  size_t NumFunctions;
> +
> +  FunctionCoverageInfo(size_t FullyCovered, size_t NumFunctions)
> +      : FullyCovered(FullyCovered), NumFunctions(NumFunctions) {}
> +
> +  bool isFullyCovered() const { return FullyCovered == NumFunctions; }
> +
> +  double getPercentCovered() const {
> +    return double(FullyCovered) / double(NumFunctions) * 100.0;
> +  }
> +};
> +
> +/// \brief A summary of function's code coverage.
> +struct FunctionCoverageSummary {
> +  StringRef Name;
> +  RegionCoverageInfo RegionCoverage;
> +  LineCoverageInfo LineCoverage;
> +
> +  FunctionCoverageSummary(StringRef Name,
> +                          const RegionCoverageInfo &RegionCoverage,
> +                          const LineCoverageInfo &LineCoverage)
> +      : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {
> +  }
> +
> +  /// \brief Compute the code coverage summary for the given function coverage
> +  /// mapping record.
> +  static FunctionCoverageSummary get(const FunctionCoverageMapping &Function);
> +};
> +
> +/// \brief A summary of file's code coverage.
> +struct FileCoverageSummary {
> +  StringRef Name;
> +  RegionCoverageInfo RegionCoverage;
> +  LineCoverageInfo LineCoverage;
> +  FunctionCoverageInfo FunctionCoverage;
> +  /// \brief The summary of every function
> +  /// in this file.
> +  ArrayRef<FunctionCoverageSummary> FunctionSummaries;
> +
> +  FileCoverageSummary(StringRef Name, const RegionCoverageInfo &RegionCoverage,
> +                      const LineCoverageInfo &LineCoverage,
> +                      const FunctionCoverageInfo &FunctionCoverage,
> +                      ArrayRef<FunctionCoverageSummary> FunctionSummaries)
> +      : Name(Name), RegionCoverage(RegionCoverage), LineCoverage(LineCoverage),
> +        FunctionCoverage(FunctionCoverage),
> +        FunctionSummaries(FunctionSummaries) {}
> +
> +  /// \brief Compute the code coverage summary for a file.
> +  static FileCoverageSummary
> +  get(StringRef Name, ArrayRef<FunctionCoverageSummary> FunctionSummaries);
> +};
> +
> +} // namespace llvm
> +
> +#endif // LLVM_COV_COVERAGESUMMARYINFO_H
> Index: tools/llvm-cov/CoverageViewOptions.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/CoverageViewOptions.h
> @@ -0,0 +1,36 @@
> +//===- CoverageViewOptions.h - Code coverage display options -------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H
> +#define LLVM_COV_COVERAGEVIEWOPTIONS_H
> +
> +#include "RenderingSupport.h"
> +
> +namespace llvm {
> +
> +/// \brief The options for displaying the code coverage information.
> +struct CoverageViewOptions {
> +  bool Debug;
> +  bool Colors;
> +  bool ShowLineNumbers;
> +  bool ShowLineStats;
> +  bool ShowRegionMarkers;
> +  bool ShowLineStatsOrRegionMarkers;
> +  bool ShowExpandedRegions;
> +  bool ShowFunctionInstantiations;
> +
> +  /// \brief Change the output's stream color if the colors are enabled.
> +  ColoredRawOstream colored_ostream(raw_ostream &OS,
> +                                    raw_ostream::Colors Color) const {
> +    return llvm::colored_ostream(OS, Color, Colors);
> +  }
> +};
> +}
> +
> +#endif // LLVM_COV_COVERAGEVIEWOPTIONS_H
> Index: tools/llvm-cov/FunctionCoverageMapping.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/FunctionCoverageMapping.h
> @@ -0,0 +1,50 @@
> +//===- FunctionCoverageMapping.h - Function coverage mapping record -------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// A structure that stores the coverage mapping record for a single function.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
> +#define LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
> +
> +#include <string>
> +#include <vector>
> +#include "llvm/ADT/ArrayRef.h"
> +#include "llvm/ADT/StringRef.h"
> +#include "llvm/ProfileData/CoverageMapping.h"
> +
> +namespace llvm {
> +
> +/// \brief Associates a source range with an execution count.
> +struct MappingRegion : public coverage::CounterMappingRegion {
> +  uint64_t ExecutionCount;
> +
> +  MappingRegion(const CounterMappingRegion &R, uint64_t ExecutionCount)
> +      : CounterMappingRegion(R), ExecutionCount(ExecutionCount) {}
> +};
> +
> +/// \brief Stores all the required information
> +/// about code coverage for a single function.
> +struct FunctionCoverageMapping {
> +  /// \brief Raw function name.
> +  std::string Name;
> +  /// \brief Demangled function name.
> +  std::string PrettyName;
> +  std::vector<std::string> Filenames;
> +  std::vector<MappingRegion> MappingRegions;
> +
> +  FunctionCoverageMapping(StringRef Name, ArrayRef<StringRef> Filenames)
> +      : Name(Name), PrettyName(Name),
> +        Filenames(Filenames.begin(), Filenames.end()) {}
> +};
> +
> +} // namespace llvm
> +
> +#endif // LLVM_COV_FUNCTIONCOVERAGEMAPPING_H
> Index: tools/llvm-cov/LLVMBuild.txt
> ===================================================================
> --- tools/llvm-cov/LLVMBuild.txt
> +++ tools/llvm-cov/LLVMBuild.txt
> @@ -19,4 +19,4 @@
>  type = Tool
>  name = llvm-cov
>  parent = Tools
> -required_libraries = Instrumentation
> +required_libraries = ProfileData Support Instrumentation
> Index: tools/llvm-cov/Makefile
> ===================================================================
> --- tools/llvm-cov/Makefile
> +++ tools/llvm-cov/Makefile
> @@ -9,7 +9,7 @@
>  
>  LEVEL := ../..
>  TOOLNAME := llvm-cov
> -LINK_COMPONENTS := core support
> +LINK_COMPONENTS := core support profiledata object
>  
>  # This tool has no plugins, optimize startup time.
>  TOOL_NO_EXPORTS := 1
> Index: tools/llvm-cov/RenderingSupport.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/RenderingSupport.h
> @@ -0,0 +1,48 @@
> +//===- RenderingSupport.h - output stream rendering support functions  ----===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_RENDERINGSUPPORT_H
> +#define LLVM_COV_RENDERINGSUPPORT_H
> +
> +#include "llvm/Support/raw_ostream.h"
> +
> +namespace llvm {
> +
> +struct ColoredRawOstream {
> +  raw_ostream &OS;
> +  bool IsColorUsed;
> +
> +  ColoredRawOstream(raw_ostream &OS, bool IsColorUsed)
> +      : OS(OS), IsColorUsed(IsColorUsed) {}
> +
> +  ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION;

We usually make deleted functions private (in case the host compiler
doesn't have "= delete")

> +
> +  ColoredRawOstream(ColoredRawOstream &&OS)
> +      : OS(OS.OS), IsColorUsed(OS.IsColorUsed) {
> +    OS.IsColorUsed = false;
> +  }

Please rename the OS parameter, the shadowing is confusing here. I'd
probably call it RHS.

> +
> +  ~ColoredRawOstream() {
> +    if (IsColorUsed)
> +      OS.resetColor();
> +  }
> +
> +  operator raw_ostream &() const { return OS; }

Rather than an implicit conversion, you can use a templated operator<<
with perfect forwarding. This will make some of the uses of this type
less awkward, as well.

> +};
> +
> +inline ColoredRawOstream colored_ostream(raw_ostream &OS,
> +                                         raw_ostream::Colors Color,
> +                                         bool IsColorUsed = false) {
> +  if (IsColorUsed)
> +    OS.changeColor(Color);
> +  return ColoredRawOstream(OS, IsColorUsed);
> +}
> +}
> +
> +#endif // LLVM_COV_RENDERINGSUPPORT_H
> Index: tools/llvm-cov/SourceCoverageDataManager.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/SourceCoverageDataManager.cpp
> @@ -0,0 +1,57 @@
> +//===- SourceCoverageDataManager.cpp - Manager for source file coverage
> +// data-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class separates and merges mapping regions for a specific source file.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "SourceCoverageDataManager.h"
> +
> +using namespace llvm;
> +using namespace coverage;
> +
> +void SourceCoverageDataManager::insert(const MappingRegion &Region) {
> +  SourceRange Range(Region.LineStart, Region.ColumnStart, Region.LineEnd,
> +                    Region.ColumnEnd);
> +  if (Region.Kind == CounterMappingRegion::SkippedRegion) {
> +    SkippedRegions.push_back(Range);
> +    return;
> +  }
> +  Regions.push_back(std::make_pair(Range, Region.ExecutionCount));
> +}
> +
> +ArrayRef<std::pair<SourceCoverageDataManager::SourceRange, uint64_t>>
> +SourceCoverageDataManager::getSourceRegions() {
> +  if (Uniqued || Regions.size() <= 1)
> +    return Regions;
> +
> +  // Sort.
> +  std::sort(Regions.begin(), Regions.end(),
> +            [](const std::pair<SourceRange, uint64_t> &LHS,
> +               const std::pair<SourceRange, uint64_t> &RHS) {
> +    return LHS.first < RHS.first;
> +  });
> +
> +  // Merge duplicate source ranges and sum their execution counts.
> +  auto Prev = Regions.begin();
> +  for (auto I = Prev + 1, E = Regions.end(); I != E; ++I) {
> +    if (I->first == Prev->first) {
> +      Prev->second += I->second;
> +      continue;
> +    }
> +    ++Prev;
> +    *Prev = *I;
> +  }
> +  ++Prev;
> +  Regions.erase(Prev, Regions.end());
> +
> +  Uniqued = true;
> +  return Regions;
> +}
> Index: tools/llvm-cov/SourceCoverageDataManager.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/SourceCoverageDataManager.h
> @@ -0,0 +1,79 @@
> +//===- SourceCoverageDataManager.h - Manager for source file coverage data-===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class separates and merges mapping regions for a specific source file.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
> +#define LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
> +
> +#include "FunctionCoverageMapping.h"
> +#include "llvm/ProfileData/CoverageMapping.h"
> +#include "llvm/ADT/Hashing.h"
> +#include <vector>
> +#include <unordered_map>
> +
> +namespace llvm {
> +
> +/// \brief Partions mapping regions by their kind and sums
> +/// the execution counts of the regions that start at the same location.
> +class SourceCoverageDataManager {
> +public:
> +  struct SourceRange {
> +    unsigned LineStart, ColumnStart, LineEnd, ColumnEnd;
> +
> +    SourceRange(unsigned LineStart, unsigned ColumnStart, unsigned LineEnd,
> +                unsigned ColumnEnd)
> +        : LineStart(LineStart), ColumnStart(ColumnStart), LineEnd(LineEnd),
> +          ColumnEnd(ColumnEnd) {}
> +
> +    bool operator==(const SourceRange &Other) const {
> +      return LineStart == Other.LineStart && ColumnStart == Other.ColumnStart &&
> +             LineEnd == Other.LineEnd && ColumnEnd == Other.ColumnEnd;
> +    }
> +
> +    bool operator<(const SourceRange &Other) const {
> +      if (LineStart == Other.LineStart)
> +        return ColumnStart < Other.ColumnStart;
> +      return LineStart < Other.LineStart;
> +    }
> +
> +    bool contains(const SourceRange &Other) {
> +      if (LineStart > Other.LineStart ||
> +          (LineStart == Other.LineStart && ColumnStart > Other.ColumnStart))
> +        return false;
> +      if (LineEnd < Other.LineEnd ||
> +          (LineEnd == Other.LineEnd && ColumnEnd < Other.ColumnEnd))
> +        return false;
> +      return true;
> +    }
> +  };
> +
> +protected:
> +  std::vector<std::pair<SourceRange, uint64_t>> Regions;
> +  std::vector<SourceRange> SkippedRegions;
> +  bool Uniqued;
> +
> +public:
> +  SourceCoverageDataManager() : Uniqued(false) {}
> +
> +  void insert(const MappingRegion &Region);
> +
> +  /// \brief Return the source ranges and execution counts
> +  /// obtained from the non-skipped mapping regions.
> +  ArrayRef<std::pair<SourceRange, uint64_t>> getSourceRegions();
> +
> +  /// \brief Return the source ranges obtained from the skipped mapping regions.
> +  ArrayRef<SourceRange> getSkippedRegions() const { return SkippedRegions; }
> +};
> +
> +} // namespace llvm
> +
> +#endif // LLVM_COV_SOURCECOVERAGEDATAMANAGER_H
> Index: tools/llvm-cov/SourceCoverageView.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/SourceCoverageView.cpp
> @@ -0,0 +1,396 @@
> +//===- SourceCoverageView.cpp - Code coverage view for source code --------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class implements rendering for code coverage of source code.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "SourceCoverageView.h"
> +#include "llvm/ADT/SmallString.h"
> +#include "llvm/Support/LineIterator.h"
> +
> +using namespace llvm;
> +
> +void SourceCoverageView::renderLine(raw_ostream &OS, StringRef Line,
> +                                    ArrayRef<HighlightRange> Ranges) {
> +  if (Ranges.empty()) {
> +    OS << Line << "\n";
> +    return;
> +  }
> +  if (Line.empty())
> +    Line = " ";
> +
> +  unsigned PrevColumnStart = 0;
> +  unsigned Start = 1;
> +  for (const auto &Range : Ranges) {
> +    if (PrevColumnStart == Range.ColumnStart)
> +      continue;
> +
> +    // Show the unhighlighted part
> +    unsigned ColumnStart = PrevColumnStart = Range.ColumnStart;
> +    OS << Line.substr(Start - 1, ColumnStart - Start);
> +
> +    // Show the highlighted part
> +    auto Color = Range.Kind == HighlightRange::NotCovered ? raw_ostream::RED
> +                                                          : raw_ostream::CYAN;
> +    OS.changeColor(Color, false, true);
> +    unsigned ColumnEnd = std::min(Range.ColumnEnd, (unsigned)Line.size() + 1);
> +    OS << Line.substr(ColumnStart - 1, ColumnEnd - ColumnStart);
> +    Start = ColumnEnd;
> +    OS.resetColor();
> +  }
> +
> +  // Show the rest of the line
> +  OS << Line.substr(Start - 1, Line.size() - Start + 1);
> +  OS << "\n";
> +}
> +
> +void SourceCoverageView::renderOffset(raw_ostream &OS, unsigned I) {
> +  for (unsigned J = 0; J < I; ++J)
> +    OS << "  |";
> +}
> +
> +void SourceCoverageView::renderViewDivider(unsigned Offset, unsigned Length,
> +                                           raw_ostream &OS) {
> +  for (unsigned J = 1; J < Offset; ++J)
> +    OS << "  |";
> +  if (Offset != 0)
> +    OS.indent(2);
> +  for (unsigned I = 0; I < Length; ++I)
> +    OS << "-";
> +}
> +
> +void
> +SourceCoverageView::renderLineCoverageColumn(raw_ostream &OS,
> +                                             const LineCoverageInfo &Line) {
> +  if (!Line.isMapped()) {
> +    OS.indent(LineCoverageColumnWidth) << '|';
> +    return;
> +  }
> +  SmallString<32> Buffer;
> +  raw_svector_ostream BufferOS(Buffer);
> +  BufferOS << Line.ExecutionCount;
> +  auto Str = BufferOS.str();
> +  // Trim
> +  Str = Str.substr(0, std::min(Str.size(), (size_t)LineCoverageColumnWidth));
> +  // Align to the right
> +  OS.indent(LineCoverageColumnWidth - Str.size());
> +  colored_ostream(OS, raw_ostream::MAGENTA,
> +                  Line.hasMultipleRegions() && Options.Colors)
> +      << Str;
> +  OS << '|';
> +}
> +
> +void SourceCoverageView::renderLineNumberColumn(raw_ostream &OS,
> +                                                unsigned LineNo) {
> +  SmallString<32> Buffer;
> +  raw_svector_ostream BufferOS(Buffer);
> +  BufferOS << LineNo;
> +  auto Str = BufferOS.str();
> +  // Trim and align to the right
> +  Str = Str.substr(0, std::min(Str.size(), (size_t)LineNumberColumnWidth));
> +  OS.indent(LineNumberColumnWidth - Str.size()) << Str << '|';
> +}
> +
> +void SourceCoverageView::renderRegionMarkers(raw_ostream &OS,
> +                                             ArrayRef<RegionMarker> Regions) {
> +  SmallString<32> Buffer;
> +  raw_svector_ostream BufferOS(Buffer);
> +
> +  unsigned PrevColumn = 1;
> +  for (const auto &Region : Regions) {
> +    // Skip to the new region
> +    if (Region.Column > PrevColumn)
> +      OS.indent(Region.Column - PrevColumn);
> +    PrevColumn = Region.Column + 1;
> +    BufferOS << Region.ExecutionCount;
> +    StringRef Str = BufferOS.str();
> +    // Trim the execution count
> +    Str = Str.substr(0, std::min(Str.size(), (size_t)7));
> +    PrevColumn += Str.size();
> +    OS << '^' << Str;
> +    Buffer.clear();
> +  }
> +  OS << "\n";
> +}
> +
> +/// \brief Insert a new highlighting range into the line's highlighting ranges
> +/// Return line's new highlighting ranges in result.
> +static void insertHighlightRange(
> +    ArrayRef<SourceCoverageView::HighlightRange> Ranges,
> +    SourceCoverageView::HighlightRange RangeToInsert,
> +    SmallVectorImpl<SourceCoverageView::HighlightRange> &Result) {
> +  Result.clear();
> +  size_t I = 0;
> +  auto E = Ranges.size();
> +  for (; I < E; ++I) {
> +    if (RangeToInsert.ColumnStart < Ranges[I].ColumnEnd) {
> +      const auto &Range = Ranges[I];
> +      bool NextRangeContainsInserted = false;
> +      // If the next range starts before the inserted range, move the end of the
> +      // next range to the start of the inserted range.
> +      if (Range.ColumnStart < RangeToInsert.ColumnStart) {
> +        if (RangeToInsert.ColumnStart != Range.ColumnStart)
> +          Result.push_back(SourceCoverageView::HighlightRange(
> +              Range.Line, Range.ColumnStart, RangeToInsert.ColumnStart,
> +              Range.Kind));
> +        // If the next range also ends after the inserted range, keep this range
> +        // and create a new range that starts at the inserted range and ends
> +        // at the next range later.
> +        if (Range.ColumnEnd > RangeToInsert.ColumnEnd)
> +          NextRangeContainsInserted = true;
> +      }
> +      if (!NextRangeContainsInserted) {
> +        ++I;
> +        // Ignore ranges that are contained in inserted range
> +        while (I < E && RangeToInsert.contains(Ranges[I]))
> +          ++I;
> +      }
> +      break;
> +    }
> +    Result.push_back(Ranges[I]);
> +  }
> +  Result.push_back(RangeToInsert);
> +  // If the next range starts before the inserted range end, move the start
> +  // of the next range to the end of the inserted range.
> +  if (I < E && Ranges[I].ColumnStart < RangeToInsert.ColumnEnd) {
> +    const auto &Range = Ranges[I];
> +    if (RangeToInsert.ColumnEnd != Range.ColumnEnd)
> +      Result.push_back(SourceCoverageView::HighlightRange(
> +          Range.Line, RangeToInsert.ColumnEnd, Range.ColumnEnd, Range.Kind));
> +    ++I;
> +  }
> +  // Add the remaining ranges that are located after the inserted range
> +  for (; I < E; ++I)
> +    Result.push_back(Ranges[I]);
> +}
> +
> +void SourceCoverageView::sortChildren() {
> +  for (auto &I : Children)
> +    I->sortChildren();
> +  std::sort(Children.begin(), Children.end(),
> +            [](const std::unique_ptr<SourceCoverageView> &LHS,
> +               const std::unique_ptr<SourceCoverageView> &RHS) {
> +    return LHS->ExpansionRegion < RHS->ExpansionRegion;
> +  });
> +}
> +
> +SourceCoverageView::HighlightRange
> +SourceCoverageView::getExpansionHighlightRange() const {
> +  return HighlightRange(ExpansionRegion.LineStart, ExpansionRegion.ColumnStart,
> +                        ExpansionRegion.ColumnEnd, HighlightRange::Expanded);
> +}
> +
> +template <typename T>
> +ArrayRef<T> gatherLineItems(size_t &CurrentIdx, const std::vector<T> &Items,
> +                            unsigned LineNo) {
> +  auto PrevIdx = CurrentIdx;
> +  auto E = Items.size();
> +  while (CurrentIdx < E && Items[CurrentIdx].Line == LineNo)
> +    ++CurrentIdx;
> +  return ArrayRef<T>(Items.data() + PrevIdx, CurrentIdx - PrevIdx);
> +}
> +
> +ArrayRef<std::unique_ptr<SourceCoverageView>>
> +gatherLineSubViews(size_t &CurrentIdx,
> +                   ArrayRef<std::unique_ptr<SourceCoverageView>> Items,
> +                   unsigned LineNo) {
> +  auto PrevIdx = CurrentIdx;
> +  auto E = Items.size();
> +  while (CurrentIdx < E &&
> +         Items[CurrentIdx]->getSubViewsExpansionLine() == LineNo)
> +    ++CurrentIdx;
> +  return ArrayRef<std::unique_ptr<SourceCoverageView>>(Items.data() + PrevIdx,
> +                                                       CurrentIdx - PrevIdx);
> +}
> +
> +void SourceCoverageView::render(raw_ostream &OS, unsigned Offset) {
> +  // Make sure that the children are in sorted order.
> +  sortChildren();
> +
> +  SmallVector<HighlightRange, 8> AdjustedLineHighlightRanges;
> +  size_t CurrentChild = 0;
> +  size_t CurrentHighlightRange = 0;
> +  size_t CurrentRegionMarker = 0;
> +
> +  line_iterator Lines(File);
> +  // Advance the line iterator to the first line.
> +  while (Lines.line_number() < LineStart)
> +    ++Lines;
> +
> +  // The width of the leading columns
> +  unsigned CombinedColumnWidth =
> +      (Options.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
> +      (Options.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
> +  // The width of the line that is used to divide between the view and the
> +  // subviews.
> +  unsigned DividerWidth = CombinedColumnWidth + 4;
> +
> +  for (size_t I = 0; I < LineCount; ++I) {
> +    unsigned LineNo = I + LineStart;
> +
> +    // Gather the child subviews that are visible on this line.
> +    auto LineSubViews = gatherLineSubViews(CurrentChild, Children, LineNo);
> +
> +    renderOffset(OS, Offset);
> +    if (Options.ShowLineStats)
> +      renderLineCoverageColumn(OS, LineStats[I]);
> +    if (Options.ShowLineNumbers)
> +      renderLineNumberColumn(OS, LineNo);
> +
> +    // Gather highlighting ranges.
> +    auto LineHighlightRanges =
> +        gatherLineItems(CurrentHighlightRange, HighlightRanges, LineNo);
> +    auto LineRanges = LineHighlightRanges;
> +    // Highlight the expansion range if there is an expansion subview on this
> +    // line.
> +    if (!LineSubViews.empty() && LineSubViews.front()->isExpansionSubView() &&
> +        Options.Colors) {
> +      insertHighlightRange(LineHighlightRanges,
> +                           LineSubViews.front()->getExpansionHighlightRange(),
> +                           AdjustedLineHighlightRanges);
> +      LineRanges = AdjustedLineHighlightRanges;
> +    }
> +
> +    // Display the source code for the current line.
> +    StringRef Line = *Lines;
> +    // Check if the line is empty, as line_iterator skips blank lines.
> +    if (LineNo < Lines.line_number())
> +      Line = "";
> +    else if (!Lines.is_at_eof())
> +      ++Lines;
> +    renderLine(OS, Line, LineRanges);
> +
> +    // Show the region markers.
> +    bool ShowMarkers = !Options.ShowLineStatsOrRegionMarkers ||
> +                       LineStats[I].hasMultipleRegions();
> +    auto LineMarkers = gatherLineItems(CurrentRegionMarker, Markers, LineNo);
> +    if (ShowMarkers && !LineMarkers.empty()) {
> +      renderOffset(OS, Offset);
> +      OS.indent(CombinedColumnWidth);
> +      renderRegionMarkers(OS, LineMarkers);
> +    }
> +
> +    // Show the line's expanded child subviews.
> +    bool FirstChildExpansion = true;
> +    if (LineSubViews.empty())
> +      continue;
> +    unsigned NewOffset = Offset + 1;
> +    renderViewDivider(NewOffset, DividerWidth, OS);
> +    OS << "\n";
> +    for (const auto &Child : LineSubViews) {
> +      // If this subview shows a function instantiation, render the function's
> +      // name.
> +      if (Child->isInstantiationSubView()) {
> +        renderOffset(OS, NewOffset);
> +        OS << ' ';
> +        Options.colored_ostream(OS, raw_ostream::CYAN) << Child->FunctionName
> +                                                       << ":";
> +        OS << "\n";
> +      } else {
> +        if (!FirstChildExpansion) {
> +          // Re-render the current line and highlight the expansion range for
> +          // this
> +          // subview.
> +          insertHighlightRange(LineHighlightRanges,
> +                               Child->getExpansionHighlightRange(),
> +                               AdjustedLineHighlightRanges);
> +          renderOffset(OS, Offset);
> +          OS.indent(CombinedColumnWidth + (Offset == 0 ? 0 : 1));
> +          renderLine(OS, Line, AdjustedLineHighlightRanges);
> +          renderViewDivider(NewOffset, DividerWidth, OS);
> +          OS << "\n";
> +        } else
> +          FirstChildExpansion = false;
> +      }
> +      // Render the child subview
> +      Child->render(OS, NewOffset);
> +      renderViewDivider(NewOffset, DividerWidth, OS);
> +      OS << "\n";
> +    }
> +  }
> +}
> +
> +void
> +SourceCoverageView::createLineCoverageInfo(SourceCoverageDataManager &Data) {
> +  LineStats.resize(LineCount);
> +  for (const auto &Region : Data.getSourceRegions()) {
> +    auto Value = Region.second;
> +    LineStats[Region.first.LineStart - LineStart].addRegionStartCount(Value);
> +    for (unsigned Line = Region.first.LineStart + 1;
> +         Line <= Region.first.LineEnd; ++Line)
> +      LineStats[Line - LineStart].addRegionCount(Value);
> +  }
> +
> +  // Reset the line stats for skipped regions.
> +  for (const auto &Region : Data.getSkippedRegions()) {
> +    for (unsigned Line = Region.LineStart; Line <= Region.LineEnd; ++Line)
> +      LineStats[Line - LineStart] = LineCoverageInfo();
> +  }
> +}
> +
> +void
> +SourceCoverageView::createHighlightRanges(SourceCoverageDataManager &Data) {
> +  auto Regions = Data.getSourceRegions();
> +  std::vector<bool> AlreadyHighlighted;
> +  AlreadyHighlighted.resize(Regions.size(), false);
> +
> +  for (size_t I = 0, S = Regions.size(); I < S; ++I) {
> +    const auto &Region = Regions[I];
> +    auto Value = Region.second;
> +    auto SrcRange = Region.first;
> +    if (Value != 0)
> +      continue;
> +    if (AlreadyHighlighted[I])
> +      continue;
> +    for (size_t J = 0; J < S; ++J) {
> +      if (SrcRange.contains(Regions[J].first)) {
> +        AlreadyHighlighted[J] = true;
> +      }
> +    }
> +    if (Options.Debug)
> +      outs() << "Highlighted range " << Value << ", "
> +             << " " << SrcRange.LineStart << ":" << SrcRange.ColumnStart
> +             << " -> " << SrcRange.LineEnd << ":" << SrcRange.ColumnEnd << "\n";
> +    if (SrcRange.LineStart == SrcRange.LineEnd) {
> +      HighlightRanges.push_back(HighlightRange(
> +          SrcRange.LineStart, SrcRange.ColumnStart, SrcRange.ColumnEnd));
> +      continue;
> +    }
> +    HighlightRanges.push_back(
> +        HighlightRange(SrcRange.LineStart, SrcRange.ColumnStart,
> +                       std::numeric_limits<unsigned>::max()));
> +    HighlightRanges.push_back(
> +        HighlightRange(SrcRange.LineEnd, 1, SrcRange.ColumnEnd));
> +    for (unsigned Line = SrcRange.LineStart + 1; Line < SrcRange.LineEnd;
> +         ++Line) {
> +      HighlightRanges.push_back(
> +          HighlightRange(Line, 1, std::numeric_limits<unsigned>::max()));
> +    }
> +  }
> +
> +  std::sort(HighlightRanges.begin(), HighlightRanges.end());
> +}
> +
> +void SourceCoverageView::createRegionMarkers(SourceCoverageDataManager &Data) {
> +  for (const auto &Region : Data.getSourceRegions()) {
> +    if (Region.first.LineStart >= LineStart)
> +      Markers.push_back(RegionMarker(Region.first.LineStart,
> +                                     Region.first.ColumnStart, Region.second));
> +  }
> +}
> +
> +void SourceCoverageView::load(SourceCoverageDataManager &Data) {
> +  if (Options.ShowLineStats)
> +    createLineCoverageInfo(Data);
> +  if (Options.Colors)
> +    createHighlightRanges(Data);
> +  if (Options.ShowRegionMarkers)
> +    createRegionMarkers(Data);
> +}
> Index: tools/llvm-cov/SourceCoverageView.h
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/SourceCoverageView.h
> @@ -0,0 +1,213 @@
> +//===- SourceCoverageView.h - Code coverage view for source code ----------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This class implements rendering for code coverage of source code.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef LLVM_COV_SOURCECOVERAGEVIEW_H
> +#define LLVM_COV_SOURCECOVERAGEVIEW_H
> +
> +#include "CoverageViewOptions.h"
> +#include "SourceCoverageDataManager.h"
> +#include "llvm/ProfileData/CoverageMapping.h"
> +#include "llvm/Support/MemoryBuffer.h"
> +#include <vector>
> +
> +namespace llvm {
> +
> +/// \brief A code coverage view of a specific source file.
> +/// It can have embedded coverage views.
> +class SourceCoverageView {
> +public:
> +  enum SubViewKind { View, ExpansionView, InstantiationView };
> +
> +  /// \brief Coverage information for a single line.
> +  struct LineCoverageInfo {
> +    uint64_t ExecutionCount;
> +    unsigned RegionCount;
> +    bool Mapped;
> +
> +    LineCoverageInfo() : ExecutionCount(0), RegionCount(0), Mapped(false) {}
> +
> +    bool isMapped() const { return Mapped; }
> +
> +    bool hasMultipleRegions() const { return RegionCount > 1; }
> +
> +    void addRegionStartCount(uint64_t Count) {
> +      Mapped = true;
> +      ExecutionCount = Count;
> +      ++RegionCount;
> +    }
> +
> +    void addRegionCount(uint64_t Count) {
> +      Mapped = true;
> +      ExecutionCount = Count;
> +    }
> +  };
> +
> +  /// \brief A marker that points at the start
> +  /// of a specific mapping region.
> +  struct RegionMarker {
> +    unsigned Line, Column;
> +    uint64_t ExecutionCount;
> +
> +    RegionMarker(unsigned Line, unsigned Column, uint64_t Value)
> +        : Line(Line), Column(Column), ExecutionCount(Value) {}
> +  };
> +
> +  /// \brief A single line source range used to
> +  /// render highlighted text.
> +  struct HighlightRange {
> +    enum HighlightKind {
> +      /// The code that wasn't executed.
> +      NotCovered,
> +
> +      /// The region of code that was expanded.
> +      Expanded
> +    };
> +    HighlightKind Kind;
> +    unsigned Line;
> +    unsigned ColumnStart;
> +    unsigned ColumnEnd;
> +
> +    HighlightRange(unsigned Line, unsigned ColumnStart, unsigned ColumnEnd,
> +                   HighlightKind Kind = NotCovered)
> +        : Kind(Kind), Line(Line), ColumnStart(ColumnStart),
> +          ColumnEnd(ColumnEnd) {}
> +
> +    bool operator<(const HighlightRange &Other) const {
> +      if (Line == Other.Line)
> +        return ColumnStart < Other.ColumnStart;
> +      return Line < Other.Line;
> +    }
> +
> +    bool columnStartOverlaps(const HighlightRange &Other) const {
> +      return ColumnStart <= Other.ColumnStart && ColumnEnd > Other.ColumnStart;
> +    }
> +    bool columnEndOverlaps(const HighlightRange &Other) const {
> +      return ColumnEnd >= Other.ColumnEnd && ColumnStart < Other.ColumnEnd;
> +    }
> +    bool contains(const HighlightRange &Other) const {
> +      if (Line != Other.Line)
> +        return false;
> +      return ColumnStart <= Other.ColumnStart && ColumnEnd >= Other.ColumnEnd;
> +    }
> +
> +    bool overlaps(const HighlightRange &Other) const {
> +      if (Line != Other.Line)
> +        return false;
> +      return columnStartOverlaps(Other) || columnEndOverlaps(Other);
> +    }
> +  };
> +
> +private:
> +  const MemoryBuffer &File;
> +  const CoverageViewOptions &Options;
> +  unsigned LineStart, LineCount;
> +  SubViewKind Kind;
> +  coverage::CounterMappingRegion ExpansionRegion;
> +  std::vector<std::unique_ptr<SourceCoverageView>> Children;
> +  std::vector<LineCoverageInfo> LineStats;
> +  std::vector<HighlightRange> HighlightRanges;
> +  std::vector<RegionMarker> Markers;
> +  StringRef FunctionName;
> +
> +  /// \brief Create the line coverage information using the coverage data.
> +  void createLineCoverageInfo(SourceCoverageDataManager &Data);
> +
> +  /// \brief Create the line highlighting ranges using the coverage data.
> +  void createHighlightRanges(SourceCoverageDataManager &Data);
> +
> +  /// \brief Create the region markers using the coverage data.
> +  void createRegionMarkers(SourceCoverageDataManager &Data);
> +
> +  /// \brief Sort children by the starting location.
> +  void sortChildren();
> +
> +  /// \brief Return a highlight range for the expansion region of this view.
> +  HighlightRange getExpansionHighlightRange() const;
> +
> +  /// \brief Render a source line with highlighting.
> +  void renderLine(raw_ostream &OS, StringRef Line,
> +                  ArrayRef<HighlightRange> Ranges);
> +
> +  void renderOffset(raw_ostream &OS, unsigned I);
> +
> +  void renderViewDivider(unsigned Offset, unsigned Length, raw_ostream &OS);
> +
> +  /// \brief Render the line's execution count column.
> +  void renderLineCoverageColumn(raw_ostream &OS, const LineCoverageInfo &Line);
> +
> +  /// \brief Render the line number column.
> +  void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo);
> +
> +  /// \brief Render all the region's execution counts on a line.
> +  void renderRegionMarkers(raw_ostream &OS, ArrayRef<RegionMarker> Regions);
> +
> +  static const unsigned LineCoverageColumnWidth = 7;
> +  static const unsigned LineNumberColumnWidth = 5;
> +
> +public:
> +  SourceCoverageView(const MemoryBuffer &File,
> +                     const CoverageViewOptions &Options)
> +      : File(File), Options(Options), LineStart(1), Kind(View),
> +        ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {
> +    LineCount = File.getBuffer().count('\n') + 1;
> +  }
> +
> +  SourceCoverageView(const MemoryBuffer &File,
> +                     const CoverageViewOptions &Options, unsigned LineStart,
> +                     unsigned LineEnd)
> +      : File(File), Options(Options), LineStart(LineStart),
> +        LineCount(LineEnd - LineStart + 1), Kind(View),
> +        ExpansionRegion(coverage::Counter(), 0, 0, 0, 0, 0) {}
> +
> +  SourceCoverageView(SourceCoverageView &Parent, unsigned LineStart,
> +                     unsigned LineEnd, StringRef FunctionName)
> +      : File(Parent.File), Options(Parent.Options), LineStart(LineStart),
> +        LineCount(LineEnd - LineStart + 1), Kind(InstantiationView),
> +        ExpansionRegion(coverage::Counter(), 0, LineEnd, 0, LineEnd, 0),
> +        FunctionName(FunctionName) {}
> +
> +  SourceCoverageView(const MemoryBuffer &File,
> +                     const CoverageViewOptions &Options, unsigned LineStart,
> +                     unsigned LineEnd,
> +                     const coverage::CounterMappingRegion &ExpansionRegion)
> +      : File(File), Options(Options), LineStart(LineStart),
> +        LineCount(LineEnd - LineStart + 1), Kind(ExpansionView),
> +        ExpansionRegion(ExpansionRegion) {}
> +
> +  const CoverageViewOptions &getOptions() const { return Options; }
> +
> +  bool isExpansionSubView() const { return Kind == ExpansionView; }
> +
> +  bool isInstantiationSubView() const { return Kind == InstantiationView; }
> +
> +  /// \brief Return the line number after which the subview expansion is shown.
> +  unsigned getSubViewsExpansionLine() const {
> +    return ExpansionRegion.LineStart;
> +  }
> +
> +  void addChild(std::unique_ptr<SourceCoverageView> View) {
> +    Children.push_back(std::move(View));
> +  }
> +
> +  /// \brief Print the code coverage information for a specific
> +  /// portion of a source file to the output stream.
> +  void render(raw_ostream &OS, unsigned Offset = 0);
> +
> +  /// \brief Load the coverage information required for rendering
> +  /// from the mapping regions in the data manager.
> +  void load(SourceCoverageDataManager &Data);
> +};
> +
> +} // namespace llvm
> +
> +#endif // LLVM_COV_SOURCECOVERAGEVIEW_H
> Index: tools/llvm-cov/TestingSupport.cpp
> ===================================================================
> --- /dev/null
> +++ tools/llvm-cov/TestingSupport.cpp
> @@ -0,0 +1,92 @@
> +//===- TestingSupport.cpp - Convert objects files into test files --------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#include "llvm/Object/ObjectFile.h"
> +#include "llvm/Support/raw_ostream.h"
> +#include "llvm/Support/LEB128.h"
> +#include "llvm/Support/CommandLine.h"
> +#include "llvm/Support/ManagedStatic.h"
> +#include "llvm/Support/MemoryObject.h"
> +#include "llvm/Support/Signals.h"
> +#include "llvm/Support/PrettyStackTrace.h"
> +#include <system_error>
> +#include <functional>
> +
> +using namespace llvm;
> +using namespace object;
> +
> +int convert_for_testing_main(int argc, const char **argv) {
> +  sys::PrintStackTraceOnErrorSignal();
> +  PrettyStackTraceProgram X(argc, argv);
> +  llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
> +
> +  cl::opt<std::string> InputSourceFile(cl::Positional, cl::Required,
> +                                       cl::desc("<Source file>"));
> +
> +  cl::opt<std::string> OutputFilename(
> +      "o", cl::Required,
> +      cl::desc(
> +          "File with the profile data obtained after an instrumented run"));
> +
> +  cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
> +
> +  auto ObjErr = llvm::object::ObjectFile::createObjectFile(InputSourceFile);
> +  if (auto Err = ObjErr.getError()) {
> +    errs() << "error: " << Err.message() << "\n";
> +    return 1;
> +  }
> +  ObjectFile *OF = ObjErr.get().getBinary().get();
> +  auto BytesInAddress = OF->getBytesInAddress();
> +  if (BytesInAddress != 8) {
> +    errs() << "error: 64 bit binary expected\n";
> +    return 1;
> +  }
> +
> +  // Look for the sections that we are interested in.
> +  int FoundSectionCount = 0;
> +  SectionRef ProfileNames, CoverageMapping;
> +  for (const auto &Section : OF->sections()) {
> +    StringRef Name;
> +    if (Section.getName(Name))
> +      return 1;
> +    if (Name == "__llvm_prf_names") {
> +      ProfileNames = Section;
> +    } else if (Name == "__llvm_covmap") {
> +      CoverageMapping = Section;
> +    } else
> +      continue;
> +    ++FoundSectionCount;
> +  }
> +  if (FoundSectionCount != 2)
> +    return 1;
> +
> +  // Get the contents of the given sections.
> +  StringRef CoverageMappingData;
> +  uint64_t ProfileNamesAddress;
> +  StringRef ProfileNamesData;
> +  if (CoverageMapping.getContents(CoverageMappingData) ||
> +      ProfileNames.getAddress(ProfileNamesAddress) ||
> +      ProfileNames.getContents(ProfileNamesData))
> +    return 1;
> +
> +  int FD;
> +  if (auto Err =
> +          sys::fs::openFileForWrite(OutputFilename, FD, sys::fs::F_None)) {
> +    errs() << "error: " << Err.message() << "\n";
> +    return 1;
> +  }
> +
> +  raw_fd_ostream OS(FD, true);
> +  OS << "llvmcovmtestdata";
> +  encodeULEB128(ProfileNamesData.size(), OS);
> +  encodeULEB128(ProfileNamesAddress, OS);
> +  OS << ProfileNamesData << CoverageMappingData;
> +
> +  return 0;
> +}
> Index: tools/llvm-cov/llvm-cov.cpp
> ===================================================================
> --- tools/llvm-cov/llvm-cov.cpp
> +++ tools/llvm-cov/llvm-cov.cpp
> @@ -11,9 +11,63 @@
>  //
>  //===----------------------------------------------------------------------===//
>  
> -/// \brief The main function for the gcov compatible coverage tool
> +#include "llvm/ADT/StringRef.h"
> +#include "llvm/Support/raw_ostream.h"
> +#include "llvm/Support/Path.h"
> +#include <string>
> +
> +using namespace llvm;
> +
> +/// \brief The main entry point for the 'show' subcommand.
> +int show_main(int argc, const char **argv);
> +
> +/// \brief The main entry point for the 'report' subcommand.
> +int report_main(int argc, const char **argv);
> +
> +/// \brief The main entry point for the 'convert-for-testing' subcommand.
> +int convert_for_testing_main(int argc, const char **argv);
> +
> +/// \brief The main entry point for the gcov compatible coverage tool.
>  int gcov_main(int argc, const char **argv);
>  
>  int main(int argc, const char **argv) {
> +  // If argv[0] is or ends with 'gcov', always be gcov compatible
> +  if (sys::path::stem(argv[0]).endswith_lower("gcov"))
> +    return gcov_main(argc, argv);
> +
> +  // Check if we are invoking a specific tool command.
> +  if (argc > 1) {
> +    int (*func)(int, const char **) = nullptr;
> +
> +    StringRef command = argv[1];
> +    if (command.equals_lower("show"))
> +      func = show_main;
> +    else if (command.equals_lower("report"))
> +      func = report_main;
> +    else if (command.equals_lower("convert-for-testing"))
> +      func = convert_for_testing_main;
> +    else if (command.equals_lower("gcov"))
> +      func = gcov_main;
> +
> +    if (func) {
> +      std::string Invocation(std::string(argv[0]) + " " + argv[1]);
> +      argv[1] = Invocation.c_str();
> +      return func(argc - 1, argv + 1);
> +    }
> +  }
> +
> +  // Give a warning and fall back to gcov
> +  errs().changeColor(raw_ostream::RED);
> +  errs() << "warning:";
> +  // Assume that argv[1] wasn't a command when it stats with a '-' or is a
> +  // filename (i.e. contains a '.')
> +  if (argc > 1 && !StringRef(argv[1]).startswith("-") &&
> +      StringRef(argv[1]).find(".") == StringRef::npos)
> +    errs() << " Unrecognized command '" << argv[1] << "'.";
> +  errs() << " Using the gcov compatible mode "
> +            "(this behaviour may be dropped in the future).";
> +  errs().resetColor();
> +  errs() << "\n";
> +
>    return gcov_main(argc, argv);
>  }



More information about the llvm-commits mailing list