llvm-cov: add code coverage tool that's based on coverage mapping format and clang...
authorAlex Lorenz <arphaman@gmail.com>
Fri, 22 Aug 2014 22:56:03 +0000 (22:56 +0000)
committerAlex Lorenz <arphaman@gmail.com>
Fri, 22 Aug 2014 22:56:03 +0000 (22:56 +0000)
This commit expands llvm-cov's functionality by adding support for a new code coverage
tool that uses LLVM's coverage mapping format and clang's instrumentation based profiling.
The gcov compatible tool can be invoked by supplying the 'gcov' command as the first argument,
or by modifying the tool's name to end with 'gcov'.

Differential Revision: http://reviews.llvm.org/D4445

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@216300 91177308-0d34-0410-b5e6-96231b3b80d8

32 files changed:
lib/ProfileData/CoverageMappingReader.cpp
test/tools/llvm-cov/Inputs/README
test/tools/llvm-cov/Inputs/highlightedRanges.covmapping [new file with mode: 0644]
test/tools/llvm-cov/Inputs/highlightedRanges.profdata [new file with mode: 0644]
test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping [new file with mode: 0644]
test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata [new file with mode: 0644]
test/tools/llvm-cov/Inputs/regionMarkers.covmapping [new file with mode: 0644]
test/tools/llvm-cov/Inputs/regionMarkers.profdata [new file with mode: 0644]
test/tools/llvm-cov/showHighlightedRanges.cpp [new file with mode: 0644]
test/tools/llvm-cov/showLineExecutionCounts.cpp [new file with mode: 0644]
test/tools/llvm-cov/showRegionMarkers.cpp [new file with mode: 0644]
tools/llvm-cov/CMakeLists.txt
tools/llvm-cov/CodeCoverage.cpp [new file with mode: 0644]
tools/llvm-cov/CoverageFilters.cpp [new file with mode: 0644]
tools/llvm-cov/CoverageFilters.h [new file with mode: 0644]
tools/llvm-cov/CoverageReport.cpp [new file with mode: 0644]
tools/llvm-cov/CoverageReport.h [new file with mode: 0644]
tools/llvm-cov/CoverageSummary.cpp [new file with mode: 0644]
tools/llvm-cov/CoverageSummary.h [new file with mode: 0644]
tools/llvm-cov/CoverageSummaryInfo.cpp [new file with mode: 0644]
tools/llvm-cov/CoverageSummaryInfo.h [new file with mode: 0644]
tools/llvm-cov/CoverageViewOptions.h [new file with mode: 0644]
tools/llvm-cov/FunctionCoverageMapping.h [new file with mode: 0644]
tools/llvm-cov/LLVMBuild.txt
tools/llvm-cov/Makefile
tools/llvm-cov/RenderingSupport.h [new file with mode: 0644]
tools/llvm-cov/SourceCoverageDataManager.cpp [new file with mode: 0644]
tools/llvm-cov/SourceCoverageDataManager.h [new file with mode: 0644]
tools/llvm-cov/SourceCoverageView.cpp [new file with mode: 0644]
tools/llvm-cov/SourceCoverageView.h [new file with mode: 0644]
tools/llvm-cov/TestingSupport.cpp [new file with mode: 0644]
tools/llvm-cov/llvm-cov.cpp

index f10ae05..8d1508b 100644 (file)
@@ -289,18 +289,6 @@ ObjectFileCoverageMappingReader::ObjectFileCoverageMappingReader(
     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 @@ struct SectionData {
 
 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 @@ std::error_code readCoverageMappingData(
         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 @@ std::error_code readCoverageMappingData(
   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 @@ std::error_code ObjectFileCoverageMappingReader::readHeader() {
   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 2cfb191..3773ba3 100644 (file)
@@ -1,7 +1,21 @@
 These inputs were pre-generated to allow for easier testing of llvm-cov.
 
-test.gcno and test.gcda were create by running clang:
-  clang++ -g -ftest-coverage -fprofile-arcs test.cpp
+The files used to test the gcov compatible code coverage tool were generated
+using the following method:
 
-test.cpp.gcov was created by running gcov 4.2.1:
-  gcov test.cpp
+  test.gcno and test.gcda were create by running clang:
+    clang++ -g -ftest-coverage -fprofile-arcs test.cpp
+
+  test.cpp.gcov was created by running gcov 4.2.1:
+    gcov test.cpp
+
+The 'covmapping' files that are used to test llvm-cov contain raw sections
+with the coverage mapping data generated by the compiler and linker. They are
+created by running clang and llvm-cov:
+  clang++ -fprofile-instr-generate -fcoverage-mapping -o test test.cpp
+  llvm-cov convert-for-testing -o test.covmapping test
+
+The 'profdata' files were generated by running an instrumented version of the
+program and merging the raw profile data using llvm-profdata.
+  ./test
+  llvm-profdata merge -o test.profdata default.profraw
diff --git a/test/tools/llvm-cov/Inputs/highlightedRanges.covmapping b/test/tools/llvm-cov/Inputs/highlightedRanges.covmapping
new file mode 100644 (file)
index 0000000..20eb0d7
Binary files /dev/null and b/test/tools/llvm-cov/Inputs/highlightedRanges.covmapping differ
diff --git a/test/tools/llvm-cov/Inputs/highlightedRanges.profdata b/test/tools/llvm-cov/Inputs/highlightedRanges.profdata
new file mode 100644 (file)
index 0000000..b465b00
Binary files /dev/null and b/test/tools/llvm-cov/Inputs/highlightedRanges.profdata differ
diff --git a/test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping b/test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping
new file mode 100644 (file)
index 0000000..5cfa169
Binary files /dev/null and b/test/tools/llvm-cov/Inputs/lineExecutionCounts.covmapping differ
diff --git a/test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata b/test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata
new file mode 100644 (file)
index 0000000..8712227
Binary files /dev/null and b/test/tools/llvm-cov/Inputs/lineExecutionCounts.profdata differ
diff --git a/test/tools/llvm-cov/Inputs/regionMarkers.covmapping b/test/tools/llvm-cov/Inputs/regionMarkers.covmapping
new file mode 100644 (file)
index 0000000..3ebcb07
Binary files /dev/null and b/test/tools/llvm-cov/Inputs/regionMarkers.covmapping differ
diff --git a/test/tools/llvm-cov/Inputs/regionMarkers.profdata b/test/tools/llvm-cov/Inputs/regionMarkers.profdata
new file mode 100644 (file)
index 0000000..8712227
Binary files /dev/null and b/test/tools/llvm-cov/Inputs/regionMarkers.profdata differ
diff --git a/test/tools/llvm-cov/showHighlightedRanges.cpp b/test/tools/llvm-cov/showHighlightedRanges.cpp
new file mode 100644 (file)
index 0000000..7c38fff
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: llvm-cov show %S/Inputs/highlightedRanges.covmapping -instr-profile %S/Inputs/highlightedRanges.profdata -dump -filename-equivalence %s | FileCheck %s
+
+void func() {
+  return;
+  int i = 0;                     // CHECK: Highlighted line [[@LINE]], 3 -> 12
+}
+
+void func2(int x) {
+  if(x > 5) {
+    while(x >= 9) {
+      return;
+      --x;                       // CHECK: Highlighted line [[@LINE]], 7 -> 10
+    }
+    int i = 0;                   // CHECK: Highlighted line [[@LINE]], 5 -> 14
+  }
+}
+
+void test() {
+  int x = 0;
+
+  if (x) {                       // CHECK: Highlighted line [[@LINE]], 10 -> ?
+    x = 0;                       // CHECK: Highlighted line [[@LINE]], 1 -> ?
+  } else {                       // CHECK: Highlighted line [[@LINE]], 1 -> 4
+    x = 1;
+  }
+
+                                  // CHECK: Highlighted line [[@LINE+1]], 26 -> 29
+  for (int i = 0; i < 0; ++i) {   // CHECK: Highlighted line [[@LINE]], 31 -> ?
+    x = 1;                        // CHECK: Highlighted line [[@LINE]], 1 -> ?
+  }                               // CHECK: Highlighted line [[@LINE]], 1 -> 4
+
+  x = x < 10 ? x +
+               1
+             : x - 1;             // CHECK: Highlighted line [[@LINE]], 16 -> 21
+  x = x > 10 ? x +                // CHECK: Highlighted line [[@LINE]], 16 -> ?
+               1                  // CHECK: Highlighted line [[@LINE]], 1 -> 17
+             : x - 1;
+}
+
+int main() {
+  test();
+  func();
+  func2(9);
+  return 0;
+}
diff --git a/test/tools/llvm-cov/showLineExecutionCounts.cpp b/test/tools/llvm-cov/showLineExecutionCounts.cpp
new file mode 100644 (file)
index 0000000..abcf5a3
--- /dev/null
@@ -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]]|}
diff --git a/test/tools/llvm-cov/showRegionMarkers.cpp b/test/tools/llvm-cov/showRegionMarkers.cpp
new file mode 100644 (file)
index 0000000..9866144
--- /dev/null
@@ -0,0 +1,23 @@
+// RUN: llvm-cov show %S/Inputs/regionMarkers.covmapping -instr-profile %S/Inputs/regionMarkers.profdata -show-regions -dump -filename-equivalence %s | FileCheck %s
+
+int main() {                      // CHECK: Marker at [[@LINE]]:12 = 1
+  int x = 0;
+
+  if (x) {                        // CHECK: Marker at [[@LINE]]:10 = 0
+    x = 0;
+  } else {                        // CHECK: Marker at [[@LINE]]:10 = 1
+    x = 1;
+  }
+                                  // CHECK: Marker at [[@LINE+2]]:19 = 101
+                                  // CHECK: Marker at [[@LINE+1]]:28 = 100
+  for (int i = 0; i < 100; ++i) { // CHECK: Marker at [[@LINE]]:33 = 100
+    x = 1;
+  }
+                                  // CHECK: Marker at [[@LINE+1]]:16 = 1
+  x = x < 10 ? x + 1 : x - 1;     // CHECK: Marker at [[@LINE]]:24 = 0
+  x = x > 10 ?
+        x - 1:                    // CHECK: Marker at [[@LINE]]:9 = 0
+        x + 1;                    // CHECK: Marker at [[@LINE]]:9 = 1
+
+  return 0;
+}
index e4c10ea..54d9ece 100644 (file)
@@ -1,6 +1,14 @@
-set(LLVM_LINK_COMPONENTS core support )
+set(LLVM_LINK_COMPONENTS core support object profiledata)
 
 add_llvm_tool(llvm-cov
   llvm-cov.cpp
   gcov.cpp
+  CodeCoverage.cpp
+  CoverageFilters.cpp
+  CoverageReport.cpp
+  CoverageSummary.cpp
+  CoverageSummaryInfo.cpp
+  SourceCoverageDataManager.cpp
+  SourceCoverageView.cpp
+  TestingSupport.cpp
   )
diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp
new file mode 100644 (file)
index 0000000..a5e039c
--- /dev/null
@@ -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);
+}
diff --git a/tools/llvm-cov/CoverageFilters.cpp b/tools/llvm-cov/CoverageFilters.cpp
new file mode 100644 (file)
index 0000000..3732d72
--- /dev/null
@@ -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;
+}
diff --git a/tools/llvm-cov/CoverageFilters.h b/tools/llvm-cov/CoverageFilters.h
new file mode 100644 (file)
index 0000000..b356377
--- /dev/null
@@ -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
diff --git a/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp
new file mode 100644 (file)
index 0000000..2efa8e0
--- /dev/null
@@ -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)
+      << format("%*zd", FileReportColumns[2], File.RegionCoverage.NotCovered);
+  Options.colored_ostream(OS,
+                          determineCoveragePercentageColor(File.RegionCoverage))
+      << format("%*.2f", FileReportColumns[3] - 1,
+                File.RegionCoverage.getPercentCovered()) << '%';
+  OS << format("%*zd", FileReportColumns[4],
+               File.FunctionCoverage.NumFunctions);
+  Options.colored_ostream(
+      OS, determineCoveragePercentageColor(File.FunctionCoverage))
+      << 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)
+      << format("%*zd", FunctionReportColumns[2],
+                Function.RegionCoverage.NotCovered);
+  Options.colored_ostream(
+      OS, determineCoveragePercentageColor(Function.RegionCoverage))
+      << 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)
+      << format("%*zd", FunctionReportColumns[5],
+                Function.LineCoverage.NotCovered);
+  Options.colored_ostream(
+      OS, determineCoveragePercentageColor(Function.LineCoverage))
+      << 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);
+}
diff --git a/tools/llvm-cov/CoverageReport.h b/tools/llvm-cov/CoverageReport.h
new file mode 100644 (file)
index 0000000..e8d34f2
--- /dev/null
@@ -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
diff --git a/tools/llvm-cov/CoverageSummary.cpp b/tools/llvm-cov/CoverageSummary.cpp
new file mode 100644 (file)
index 0000000..e32f341
--- /dev/null
@@ -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>());
+}
diff --git a/tools/llvm-cov/CoverageSummary.h b/tools/llvm-cov/CoverageSummary.h
new file mode 100644 (file)
index 0000000..bf4be08
--- /dev/null
@@ -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
diff --git a/tools/llvm-cov/CoverageSummaryInfo.cpp b/tools/llvm-cov/CoverageSummaryInfo.cpp
new file mode 100644 (file)
index 0000000..765bb3e
--- /dev/null
@@ -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);
+}
diff --git a/tools/llvm-cov/CoverageSummaryInfo.h b/tools/llvm-cov/CoverageSummaryInfo.h
new file mode 100644 (file)
index 0000000..710108d
--- /dev/null
@@ -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
diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h
new file mode 100644 (file)
index 0000000..94b55fe
--- /dev/null
@@ -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
diff --git a/tools/llvm-cov/FunctionCoverageMapping.h b/tools/llvm-cov/FunctionCoverageMapping.h
new file mode 100644 (file)
index 0000000..d171483
--- /dev/null
@@ -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 87e00d1..d6eb74d 100644 (file)
@@ -19,4 +19,4 @@
 type = Tool
 name = llvm-cov
 parent = Tools
-required_libraries = Instrumentation
+required_libraries = ProfileData Support Instrumentation
index efed6cc..6e32b4d 100644 (file)
@@ -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
diff --git a/tools/llvm-cov/RenderingSupport.h b/tools/llvm-cov/RenderingSupport.h
new file mode 100644 (file)
index 0000000..619a8a6
--- /dev/null
@@ -0,0 +1,59 @@
+//===- 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"
+#include <utility>
+
+namespace llvm {
+
+/// \brief A helper class that resets the output stream's color if needed
+/// when destroyed.
+class ColoredRawOstream {
+  ColoredRawOstream(const ColoredRawOstream &OS) LLVM_DELETED_FUNCTION;
+
+public:
+  raw_ostream &OS;
+  bool IsColorUsed;
+
+  ColoredRawOstream(raw_ostream &OS, bool IsColorUsed)
+      : OS(OS), IsColorUsed(IsColorUsed) {}
+
+  ColoredRawOstream(ColoredRawOstream &&Other)
+      : OS(Other.OS), IsColorUsed(Other.IsColorUsed) {
+    // Reset the other IsColorUsed so that the other object won't reset the
+    // color when destroyed.
+    Other.IsColorUsed = false;
+  }
+
+  ~ColoredRawOstream() {
+    if (IsColorUsed)
+      OS.resetColor();
+  }
+};
+
+template <typename T>
+inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) {
+  return OS.OS << std::forward<T>(Value);
+}
+
+/// \brief Change the color of the output stream if the `IsColorUsed` flag
+/// is true. Returns an object that resets the color when destroyed.
+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
diff --git a/tools/llvm-cov/SourceCoverageDataManager.cpp b/tools/llvm-cov/SourceCoverageDataManager.cpp
new file mode 100644 (file)
index 0000000..03382b2
--- /dev/null
@@ -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;
+}
diff --git a/tools/llvm-cov/SourceCoverageDataManager.h b/tools/llvm-cov/SourceCoverageDataManager.h
new file mode 100644 (file)
index 0000000..f389cad
--- /dev/null
@@ -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
diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp
new file mode 100644 (file)
index 0000000..7f33b77
--- /dev/null
@@ -0,0 +1,411 @@
+//===- 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 (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());
+
+  if (Options.Debug) {
+    for (const auto &Range : HighlightRanges) {
+      outs() << "Highlighted line " << Range.Line << ", " << Range.ColumnStart
+             << " -> ";
+      if (Range.ColumnEnd == std::numeric_limits<unsigned>::max()) {
+        outs() << "?\n";
+      } else {
+        outs() << Range.ColumnEnd << "\n";
+      }
+    }
+  }
+}
+
+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));
+  }
+
+  if (Options.Debug) {
+    for (const auto &Marker : Markers) {
+      outs() << "Marker at " << Marker.Line << ":" << Marker.Column << " = "
+             << Marker.ExecutionCount << "\n";
+    }
+  }
+}
+
+void SourceCoverageView::load(SourceCoverageDataManager &Data) {
+  if (Options.ShowLineStats)
+    createLineCoverageInfo(Data);
+  if (Options.Colors)
+    createHighlightRanges(Data);
+  if (Options.ShowRegionMarkers)
+    createRegionMarkers(Data);
+}
diff --git a/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h
new file mode 100644 (file)
index 0000000..ddfc690
--- /dev/null
@@ -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
diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp
new file mode 100644 (file)
index 0000000..8f580cf
--- /dev/null
@@ -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 75fa392..9c9cdef 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-/// \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);
 }