[llvm] r260628 - [sancov] improved object files handling.
Mike Aizatsky via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 11 16:29:07 PST 2016
Author: aizatsky
Date: Thu Feb 11 18:29:07 2016
New Revision: 260628
URL: http://llvm.org/viewvc/llvm-project?rev=260628&view=rev
Log:
[sancov] improved object files handling.
Multi-dso programs result in multiple coverage files dumped of the form
'<module_name>.<pid>.sancov'. When analyzing these coverage files it is
important to use correct corresponding object file.
This change removes the "-obj" sancov flag and lets user specify object
file names alongside coverage files. Sancov tool would match them using
<module_name> part of coverage file and short file name of the object
file.
Corresponding changes:
- compiler-rt: http://reviews.llvm.org/D17171
- docs: http://reviews.llvm.org/D17175
Differential Revision: http://reviews.llvm.org/D17169
Added:
llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.0.sancov
- copied, changed from r260627, llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov
llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.1.sancov
- copied, changed from r260627, llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov
Removed:
llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov
llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov
Modified:
llvm/trunk/test/tools/sancov/blacklist.test
llvm/trunk/test/tools/sancov/covered_functions.test
llvm/trunk/test/tools/sancov/html-report.test
llvm/trunk/test/tools/sancov/not_covered_functions.test
llvm/trunk/test/tools/sancov/print.test
llvm/trunk/tools/sancov/sancov.cc
Removed: llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov?rev=260627&view=auto
==============================================================================
Binary files llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov (original) and llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov (removed) differ
Copied: llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.0.sancov (from r260627, llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov)
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.0.sancov?p2=llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.0.sancov&p1=llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov&r1=260627&r2=260628&rev=260628&view=diff
==============================================================================
(empty)
Copied: llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.1.sancov (from r260627, llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov)
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.1.sancov?p2=llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.1.sancov&p1=llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64-1.sancov&r1=260627&r2=260628&rev=260628&view=diff
==============================================================================
(empty)
Removed: llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov?rev=260627&view=auto
==============================================================================
Binary files llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov (original) and llvm/trunk/test/tools/sancov/Inputs/test-linux_x86_64.sancov (removed) differ
Modified: llvm/trunk/test/tools/sancov/blacklist.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/blacklist.test?rev=260628&r1=260627&r2=260628&view=diff
==============================================================================
--- llvm/trunk/test/tools/sancov/blacklist.test (original)
+++ llvm/trunk/test/tools/sancov/blacklist.test Thu Feb 11 18:29:07 2016
@@ -1,5 +1,5 @@
REQUIRES: x86_64-linux
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -covered-functions -blacklist %p/Inputs/blacklist.txt %p/Inputs/test-linux_x86_64.sancov | FileCheck %s
+RUN: sancov -covered-functions -blacklist %p/Inputs/blacklist.txt %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck %s
CHECK-NOT: Inputs{{[/\\]}}test.cpp:12 bar(std::string)
CHECK: Inputs{{[/\\]}}test.cpp:14 main
Modified: llvm/trunk/test/tools/sancov/covered_functions.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/covered_functions.test?rev=260628&r1=260627&r2=260628&view=diff
==============================================================================
--- llvm/trunk/test/tools/sancov/covered_functions.test (original)
+++ llvm/trunk/test/tools/sancov/covered_functions.test Thu Feb 11 18:29:07 2016
@@ -1,7 +1,7 @@
REQUIRES: x86_64-linux
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -covered-functions %p/Inputs/test-linux_x86_64.sancov | FileCheck %s
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -covered-functions -strip_path_prefix=Inputs/ %p/Inputs/test-linux_x86_64.sancov | FileCheck --check-prefix=STRIP_PATH %s
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -demangle=0 -covered-functions %p/Inputs/test-linux_x86_64.sancov | FileCheck --check-prefix=NO_DEMANGLE %s
+RUN: sancov -covered-functions %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck %s
+RUN: sancov -covered-functions -strip_path_prefix=Inputs/ %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck --check-prefix=STRIP_PATH %s
+RUN: sancov -demangle=0 -covered-functions %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck --check-prefix=NO_DEMANGLE %s
CHECK: Inputs{{[/\\]}}test.cpp:12 bar(std::string)
CHECK: Inputs{{[/\\]}}test.cpp:14 main
Modified: llvm/trunk/test/tools/sancov/html-report.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/html-report.test?rev=260628&r1=260627&r2=260628&view=diff
==============================================================================
--- llvm/trunk/test/tools/sancov/html-report.test (original)
+++ llvm/trunk/test/tools/sancov/html-report.test Thu Feb 11 18:29:07 2016
@@ -1,5 +1,5 @@
REQUIRES: x86_64-linux
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -html-report %p/Inputs/test-linux_x86_64.sancov | FileCheck %s
+RUN: sancov -html-report %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck %s
// It's very difficult to test html report. Do basic smoke check.
CHECK: {{<a name=".*/Inputs/test.cpp">}}
Modified: llvm/trunk/test/tools/sancov/not_covered_functions.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/not_covered_functions.test?rev=260628&r1=260627&r2=260628&view=diff
==============================================================================
--- llvm/trunk/test/tools/sancov/not_covered_functions.test (original)
+++ llvm/trunk/test/tools/sancov/not_covered_functions.test Thu Feb 11 18:29:07 2016
@@ -1,6 +1,6 @@
REQUIRES: x86_64-linux
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -not-covered-functions %p/Inputs/test-linux_x86_64.sancov | FileCheck %s
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -not-covered-functions %p/Inputs/test-linux_x86_64-1.sancov | FileCheck --check-prefix=CHECK1 --allow-empty %s
+RUN: sancov -not-covered-functions %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck %s
+RUN: sancov -not-covered-functions %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.1.sancov | FileCheck --check-prefix=CHECK1 --allow-empty %s
CHECK: Inputs{{[/\\]}}foo.cpp:5 foo()
CHECK-NOT: {{.*__sanitizer.*}}
Modified: llvm/trunk/test/tools/sancov/print.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/print.test?rev=260628&r1=260627&r2=260628&view=diff
==============================================================================
--- llvm/trunk/test/tools/sancov/print.test (original)
+++ llvm/trunk/test/tools/sancov/print.test Thu Feb 11 18:29:07 2016
@@ -1,5 +1,5 @@
REQUIRES: x86_64-linux
-RUN: sancov -obj %p/Inputs/test-linux_x86_64 -print %p/Inputs/test-linux_x86_64.sancov | FileCheck %s
+RUN: sancov -print %p/Inputs/test-linux_x86_64.0.sancov | FileCheck %s
CHECK: 0x4dbe2b
CHECK: 0x4dbf72
Modified: llvm/trunk/tools/sancov/sancov.cc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/sancov/sancov.cc?rev=260628&r1=260627&r2=260628&view=diff
==============================================================================
--- llvm/trunk/tools/sancov/sancov.cc (original)
+++ llvm/trunk/tools/sancov/sancov.cc Thu Feb 11 18:29:07 2016
@@ -11,6 +11,7 @@
// coverage.
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Twine.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
@@ -34,6 +35,7 @@
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Regex.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/SpecialCaseList.h"
#include "llvm/Support/TargetRegistry.h"
@@ -44,6 +46,7 @@
#include <set>
#include <stdio.h>
#include <string>
+#include <utility>
#include <vector>
using namespace llvm;
@@ -70,12 +73,9 @@ cl::opt<ActionType> Action(
"Print HTML coverage report."),
clEnumValEnd));
-static cl::list<std::string> ClInputFiles(cl::Positional, cl::OneOrMore,
- cl::desc("<filenames...>"));
-
-static cl::opt<std::string>
- ClBinaryName("obj", cl::Required,
- cl::desc("Path to object file to be symbolized"));
+static cl::list<std::string>
+ ClInputFiles(cl::Positional, cl::OneOrMore,
+ cl::desc("(<binary file>|<.sancov file>)..."));
static cl::opt<bool>
ClDemangle("demangle", cl::init(true),
@@ -108,6 +108,11 @@ static const uint32_t Bitness64 = 0xFFFF
// ---------
+static void Fail(const llvm::Twine &E) {
+ errs() << "Error: " << E << "\n";
+ exit(1);
+}
+
static void FailIfError(std::error_code Error) {
if (!Error)
return;
@@ -119,11 +124,10 @@ template <typename T> static void FailIf
FailIfError(E.getError());
}
-static void FailIfNotEmpty(const std::string &E) {
- if (E.empty())
+static void FailIfNotEmpty(const llvm::Twine &E) {
+ if (E.str().empty())
return;
- errs() << "Error: " << E << "\n";
- exit(1);
+ Fail(E);
}
template <typename T>
@@ -131,8 +135,7 @@ static void FailIfEmpty(const std::uniqu
const std::string &Message) {
if (Ptr.get())
return;
- errs() << "Error: " << Message << "\n";
- exit(1);
+ Fail(Message);
}
template <typename T>
@@ -180,14 +183,14 @@ static std::unique_ptr<symbolize::LLVMSy
// Compute [FileLoc -> FunctionName] map for given addresses.
static std::map<FileLoc, std::string>
-computeFunctionsMap(const std::set<uint64_t> &Addrs) {
+computeFunctionsMap(std::string ObjectFile, const std::set<uint64_t> &Addrs) {
std::map<FileLoc, std::string> Fns;
auto Symbolizer(createSymbolizer());
// Fill in Fns map.
for (auto Addr : Addrs) {
- auto InliningInfo = Symbolizer->symbolizeInlinedCode(ClBinaryName, Addr);
+ auto InliningInfo = Symbolizer->symbolizeInlinedCode(ObjectFile, Addr);
FailIfError(InliningInfo);
for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
auto FrameInfo = InliningInfo->getFrame(I);
@@ -203,8 +206,9 @@ computeFunctionsMap(const std::set<uint6
// Compute functions for given addresses. It keeps only the first
// occurence of a function within a file.
-std::set<FunctionLoc> computeFunctionLocs(const std::set<uint64_t> &Addrs) {
- std::map<FileLoc, std::string> Fns = computeFunctionsMap(Addrs);
+std::set<FunctionLoc> computeFunctionLocs(std::string ObjectFile,
+ const std::set<uint64_t> &Addrs) {
+ std::map<FileLoc, std::string> Fns = computeFunctionsMap(ObjectFile, Addrs);
std::set<FunctionLoc> Result;
std::string LastFileName;
@@ -248,7 +252,7 @@ findSanitizerCovFunctions(const object::
}
if (Result.empty())
- FailIfNotEmpty("__sanitizer_cov* functions not found");
+ Fail("__sanitizer_cov* functions not found");
return Result;
}
@@ -436,14 +440,14 @@ static std::string escapeHtml(const std:
// Computes a map file_name->{line_number}
static std::map<std::string, std::set<int>>
-getFileLines(const std::set<uint64_t> &Addrs) {
+getFileLines(std::string ObjectFile, const std::set<uint64_t> &Addrs) {
std::map<std::string, std::set<int>> FileLines;
auto Symbolizer(createSymbolizer());
// Fill in FileLines map.
for (auto Addr : Addrs) {
- auto InliningInfo = Symbolizer->symbolizeInlinedCode(ClBinaryName, Addr);
+ auto InliningInfo = Symbolizer->symbolizeInlinedCode(ObjectFile, Addr);
FailIfError(InliningInfo);
for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
auto FrameInfo = InliningInfo->getFrame(I);
@@ -456,44 +460,58 @@ getFileLines(const std::set<uint64_t> &A
return FileLines;
}
+static ErrorOr<bool> isCoverageFile(std::string FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return BufOrErr.getError();
+ std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
+ if (Buf->getBufferSize() < 8) {
+ return false;
+ }
+ const FileHeader *Header =
+ reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
+ return Header->Magic == BinCoverageMagic;
+}
+
class CoverageData {
public:
// Read single file coverage data.
- static ErrorOr<std::unique_ptr<CoverageData>> read(std::string FileName) {
- ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
- MemoryBuffer::getFile(FileName);
- if (!BufOrErr)
- return BufOrErr.getError();
- std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
- if (Buf->getBufferSize() < 8) {
- errs() << "File too small (<8): " << Buf->getBufferSize();
- return make_error_code(errc::illegal_byte_sequence);
- }
- const FileHeader *Header =
- reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
-
- if (Header->Magic != BinCoverageMagic) {
- errs() << "Wrong magic: " << Header->Magic;
- return make_error_code(errc::illegal_byte_sequence);
- }
-
- auto Addrs = llvm::make_unique<std::set<uint64_t>>();
-
- switch (Header->Bitness) {
- case Bitness64:
- readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
- Addrs.get());
- break;
- case Bitness32:
- readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
- Addrs.get());
- break;
- default:
- errs() << "Unsupported bitness: " << Header->Bitness;
- return make_error_code(errc::illegal_byte_sequence);
- }
+ static ErrorOr<std::unique_ptr<CoverageData>> read(std::string FileName) {
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr)
+ return BufOrErr.getError();
+ std::unique_ptr<MemoryBuffer> Buf = std::move(BufOrErr.get());
+ if (Buf->getBufferSize() < 8) {
+ errs() << "File too small (<8): " << Buf->getBufferSize();
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+ const FileHeader *Header =
+ reinterpret_cast<const FileHeader *>(Buf->getBufferStart());
+
+ if (Header->Magic != BinCoverageMagic) {
+ errs() << "Wrong magic: " << Header->Magic;
+ return make_error_code(errc::illegal_byte_sequence);
+ }
+
+ auto Addrs = llvm::make_unique<std::set<uint64_t>>();
+
+ switch (Header->Bitness) {
+ case Bitness64:
+ readInts<uint64_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ case Bitness32:
+ readInts<uint32_t>(Buf->getBufferStart() + 8, Buf->getBufferEnd(),
+ Addrs.get());
+ break;
+ default:
+ errs() << "Unsupported bitness: " << Header->Bitness;
+ return make_error_code(errc::illegal_byte_sequence);
+ }
- return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
+ return std::unique_ptr<CoverageData>(new CoverageData(std::move(Addrs)));
}
// Merge multiple coverage data together.
@@ -529,37 +547,12 @@ class CoverageData {
}
}
- void printReport(raw_ostream &OS) {
+ void printReport(std::string ObjectFile, raw_ostream &OS) {
// file_name -> set of covered lines;
std::map<std::string, std::set<int>> CoveredFileLines =
- getFileLines(*Addrs);
+ getFileLines(ObjectFile, *Addrs);
std::map<std::string, std::set<int>> CoveragePoints =
- getFileLines(getCoveragePoints(ClBinaryName));
-
- std::string Title = stripPathPrefix(ClBinaryName) + " Coverage Report";
-
- OS << "<html>\n";
- OS << "<head>\n";
-
- // Stylesheet
- OS << "<style>\n";
- OS << ".covered { background: #7F7; }\n";
- OS << ".notcovered { background: #F77; }\n";
- OS << "</style>\n";
- OS << "<title>" << Title << "</title>\n";
- OS << "</head>\n";
- OS << "<body>\n";
-
- // Title
- OS << "<h1>" << Title << "</h1>\n";
- OS << "<p>Coverage files: ";
- for (auto InputFile : ClInputFiles) {
- llvm::sys::fs::file_status Status;
- llvm::sys::fs::status(InputFile, Status);
- OS << stripPathPrefix(InputFile) << " ("
- << Status.getLastModificationTime().str() << ")";
- }
- OS << "</p>\n";
+ getFileLines(ObjectFile, getCoveragePoints(ObjectFile));
// TOC
OS << "<ul>\n";
@@ -576,7 +569,7 @@ class CoverageData {
auto Lines = It.second;
auto CovLines = CoveragePoints[FileName];
- OS << "<a name=\"" << escapeHtml(FileName) << "\"> </a>\n";
+ OS << "<a name=\"" << escapeHtml(FileName) << "\"></a>\n";
OS << "<h2>" << stripPathPrefix(FileName) << "</h2>\n";
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFile(FileName);
@@ -600,23 +593,20 @@ class CoverageData {
}
OS << "</pre>\n";
}
-
- OS << "</body>\n";
- OS << "</html>\n";
}
// Print list of covered functions.
// Line format: <file_name>:<line> <function_name>
- void printCoveredFunctions(raw_ostream &OS) {
- printFunctionLocs(computeFunctionLocs(*Addrs), OS);
+ void printCoveredFunctions(std::string ObjectFile, raw_ostream &OS) {
+ printFunctionLocs(computeFunctionLocs(ObjectFile, *Addrs), OS);
}
// Print list of not covered functions.
// Line format: <file_name>:<line> <function_name>
- void printNotCoveredFunctions(raw_ostream &OS) {
+ void printNotCoveredFunctions(std::string ObjectFile, raw_ostream &OS) {
std::set<FunctionLoc> AllFns =
- computeFunctionLocs(getCoveragePoints(ClBinaryName));
- std::set<FunctionLoc> CoveredFns = computeFunctionLocs(*Addrs);
+ computeFunctionLocs(ObjectFile, getCoveragePoints(ObjectFile));
+ std::set<FunctionLoc> CoveredFns = computeFunctionLocs(ObjectFile, *Addrs);
std::set<FunctionLoc> NotCoveredFns;
std::set_difference(AllFns.begin(), AllFns.end(), CoveredFns.begin(),
@@ -631,6 +621,185 @@ private:
std::unique_ptr<std::set<uint64_t>> Addrs;
};
+
+// Holder for coverage data + filename of corresponding object file.
+class CoverageDataWithObjectFile {
+public:
+ static ErrorOr<std::unique_ptr<CoverageDataWithObjectFile>>
+ readAndMerge(std::string ObjectFile,
+ const std::vector<std::string> &FileNames) {
+ auto MergedDataOrError = CoverageData::readAndMerge(FileNames);
+ if (!MergedDataOrError)
+ return MergedDataOrError.getError();
+ return std::unique_ptr<CoverageDataWithObjectFile>(
+ new CoverageDataWithObjectFile(ObjectFile,
+ std::move(MergedDataOrError.get())));
+ }
+
+ std::string object_file() const { return ObjectFile; }
+
+ void printCoveredFunctions(raw_ostream &OS) const {
+ Coverage->printCoveredFunctions(ObjectFile, OS);
+ }
+
+ void printNotCoveredFunctions(raw_ostream &OS) const {
+ Coverage->printNotCoveredFunctions(ObjectFile, OS);
+ }
+
+ void printReport(raw_ostream &OS) const {
+ Coverage->printReport(ObjectFile, OS);
+ }
+
+private:
+ CoverageDataWithObjectFile(std::string ObjectFile,
+ std::unique_ptr<CoverageData> Coverage)
+ : ObjectFile(std::move(ObjectFile)), Coverage(std::move(Coverage)) {}
+
+ const std::string ObjectFile;
+ const std::unique_ptr<CoverageData> Coverage;
+};
+
+// Multiple coverage files data organized by object file.
+class CoverageDataSet {
+public:
+ static ErrorOr<std::unique_ptr<CoverageDataSet>>
+ readCmdArguments(std::vector<std::string> FileNames) {
+ // Short name => file name.
+ std::map<std::string, std::string> ObjFiles;
+ std::string FirstObjFile;
+ std::set<std::string> CovFiles;
+
+ // Partition input values into coverage/object files.
+ for (const auto &FileName : FileNames) {
+ auto ErrorOrIsCoverage = isCoverageFile(FileName);
+ FailIfError(ErrorOrIsCoverage);
+ if (ErrorOrIsCoverage.get()) {
+ CovFiles.insert(FileName);
+ } else {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ if (ObjFiles.find(ShortFileName) != ObjFiles.end()) {
+ Fail("Duplicate binary file with a short name: " + ShortFileName);
+ }
+
+ ObjFiles[ShortFileName] = FileName;
+ if (FirstObjFile.empty())
+ FirstObjFile = FileName;
+ }
+ }
+
+ // Object file => list of corresponding coverage files.
+ std::map<std::string, std::vector<std::string>> CoverageByObjFile;
+ Regex SancovRegex("(.*)\\.[0-9]+\\.sancov");
+ SmallVector<StringRef, 2> Components;
+
+ // Group coverage files by object file.
+ for (const auto &FileName : CovFiles) {
+ auto ShortFileName = llvm::sys::path::filename(FileName);
+ auto Ok = SancovRegex.match(ShortFileName, &Components);
+ if (!Ok) {
+ Fail("Can't match coverage file name against "
+ "<module_name>.<pid>.sancov pattern: " +
+ FileName);
+ }
+
+ auto Iter = ObjFiles.find(Components[1]);
+ if (Iter == ObjFiles.end()) {
+ Fail("Object file for coverage not found: " + FileName);
+ }
+ auto ObjectFile = Iter->second;
+ CoverageByObjFile[ObjectFile].push_back(FileName);
+ }
+
+ // Read coverage.
+ std::vector<std::unique_ptr<CoverageDataWithObjectFile>> MergedCoverage;
+ for (const auto &Pair : CoverageByObjFile) {
+ auto DataOrError =
+ CoverageDataWithObjectFile::readAndMerge(Pair.first, Pair.second);
+ FailIfError(DataOrError);
+ MergedCoverage.push_back(std::move(DataOrError.get()));
+ }
+
+ return std::unique_ptr<CoverageDataSet>(
+ new CoverageDataSet(FirstObjFile, &MergedCoverage, CovFiles));
+ }
+
+ void printCoveredFunctions(raw_ostream &OS) const {
+ for (const auto &Cov : Coverage) {
+ Cov->printCoveredFunctions(OS);
+ }
+ }
+
+ void printNotCoveredFunctions(raw_ostream &OS) const {
+ for (const auto &Cov : Coverage) {
+ Cov->printNotCoveredFunctions(OS);
+ }
+ }
+
+ void printReport(raw_ostream &OS) const {
+ std::string Title = stripPathPrefix(MainObjFile) + " Coverage Report";
+
+ OS << "<html>\n";
+ OS << "<head>\n";
+
+ // Stylesheet
+ OS << "<style>\n";
+ OS << ".covered { background: #7F7; }\n";
+ OS << ".notcovered { background: #F77; }\n";
+ OS << "</style>\n";
+ OS << "<title>" << Title << "</title>\n";
+ OS << "</head>\n";
+ OS << "<body>\n";
+
+ // Title
+ OS << "<h1>" << Title << "</h1>\n";
+ OS << "<p>Coverage files: ";
+ for (auto InputFile : CoverageFiles) {
+ llvm::sys::fs::file_status Status;
+ llvm::sys::fs::status(InputFile, Status);
+ OS << stripPathPrefix(InputFile) << " ("
+ << Status.getLastModificationTime().str() << ") ";
+ }
+ OS << "</p>\n";
+
+ // Modules TOC.
+ if (Coverage.size() > 1) {
+ for (const auto &CovData : Coverage) {
+ OS << "<li><a href=\"#module_" << escapeHtml(CovData->object_file())
+ << "\">" << llvm::sys::path::filename(CovData->object_file())
+ << "</a></li>\n";
+ }
+ }
+
+ for (const auto &CovData : Coverage) {
+ if (Coverage.size() > 1) {
+ OS << "<h2>" << llvm::sys::path::filename(CovData->object_file())
+ << "</h2>\n";
+ }
+ OS << "<a name=\"module_" << escapeHtml(CovData->object_file())
+ << "\"></a>\n";
+ CovData->printReport(OS);
+ }
+
+ OS << "</body>\n";
+ OS << "</html>\n";
+ }
+
+ bool empty() const { return Coverage.empty(); }
+
+private:
+ explicit CoverageDataSet(
+ const std::string &MainObjFile,
+ std::vector<std::unique_ptr<CoverageDataWithObjectFile>> *Data,
+ const std::set<std::string> &CoverageFiles)
+ : MainObjFile(MainObjFile), CoverageFiles(CoverageFiles) {
+ Data->swap(this->Coverage);
+ }
+
+ const std::string MainObjFile;
+ std::vector<std::unique_ptr<CoverageDataWithObjectFile>> Coverage;
+ const std::set<std::string> CoverageFiles;
+};
+
} // namespace
int main(int argc, char **argv) {
@@ -645,27 +814,35 @@ int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv, "Sanitizer Coverage Processing Tool");
- auto CovData = CoverageData::readAndMerge(ClInputFiles);
- FailIfError(CovData);
-
- switch (Action) {
- case PrintAction: {
+ // -print doesn't need object files.
+ if (Action == PrintAction) {
+ auto CovData = CoverageData::readAndMerge(ClInputFiles);
+ FailIfError(CovData);
CovData.get()->printAddrs(outs());
return 0;
}
+
+ auto CovDataSet = CoverageDataSet::readCmdArguments(ClInputFiles);
+ FailIfError(CovDataSet);
+
+ if (CovDataSet.get()->empty()) {
+ Fail("No coverage files specified.");
+ }
+
+ switch (Action) {
case CoveredFunctionsAction: {
- CovData.get()->printCoveredFunctions(outs());
+ CovDataSet.get()->printCoveredFunctions(outs());
return 0;
}
case NotCoveredFunctionsAction: {
- CovData.get()->printNotCoveredFunctions(outs());
+ CovDataSet.get()->printNotCoveredFunctions(outs());
return 0;
}
case HtmlReportAction: {
- CovData.get()->printReport(outs());
+ CovDataSet.get()->printReport(outs());
return 0;
}
+ case PrintAction:
+ llvm_unreachable("unsupported action");
}
-
- llvm_unreachable("unsupported action");
}
More information about the llvm-commits
mailing list