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.
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>))
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,
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)
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);
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
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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]]|}
--- /dev/null
+// 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;
+}
-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
)
--- /dev/null
+//===- 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);
+}
--- /dev/null
+//===- 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;
+}
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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);
+}
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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>());
+}
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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);
+}
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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
type = Tool
name = llvm-cov
parent = Tools
-required_libraries = Instrumentation
+required_libraries = ProfileData Support Instrumentation
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
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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;
+}
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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);
+}
--- /dev/null
+//===- 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
--- /dev/null
+//===- 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;
+}
//
//===----------------------------------------------------------------------===//
-/// \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);
}