[llvm] r261287 - [sancov] Adding covered/uncovered tables to coverage report.
Mike Aizatsky via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 18 16:26:20 PST 2016
Author: aizatsky
Date: Thu Feb 18 18:26:20 2016
New Revision: 261287
URL: http://llvm.org/viewvc/llvm-project?rev=261287&view=rev
Log:
[sancov] Adding covered/uncovered tables to coverage report.
Summary:
This change adds 3 tables to html report:
- list of covered files with number of functions covered.
- list of not covered files
- list of not covered functions.
I tried to put most coverage-calculating functionality into
SourceCoverageData.
Differential Revision: http://reviews.llvm.org/D17421
Modified:
llvm/trunk/tools/sancov/sancov.cc
Modified: llvm/trunk/tools/sancov/sancov.cc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/sancov/sancov.cc?rev=261287&r1=261286&r2=261287&view=diff
==============================================================================
--- llvm/trunk/tools/sancov/sancov.cc (original)
+++ llvm/trunk/tools/sancov/sancov.cc Thu Feb 18 18:26:20 2016
@@ -25,7 +25,9 @@
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/Binary.h"
+#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ErrorOr.h"
@@ -93,7 +95,7 @@ static cl::opt<bool> ClUseDefaultBlackli
"use_default_blacklist", cl::init(true), cl::Hidden,
cl::desc("Controls if default blacklist should be used."));
-static const char *const DefaultBlacklist = "fun:__sanitizer_*";
+static const char *const DefaultBlacklistStr = "fun:__sanitizer_*";
// --------- FORMAT SPECIFICATION ---------
@@ -106,7 +108,7 @@ static const uint32_t BinCoverageMagic =
static const uint32_t Bitness32 = 0xFFFFFF32;
static const uint32_t Bitness64 = 0xFFFFFF64;
-// ---------
+// --------- ERROR HANDLING ---------
static void Fail(const llvm::Twine &E) {
errs() << "Error: " << E << "\n";
@@ -138,6 +140,23 @@ static void FailIfEmpty(const std::uniqu
Fail(Message);
}
+// ---------
+
+// Produces std::map<K, std::vector<E>> grouping input
+// elements by FuncTy result.
+template <class RangeTy, class FuncTy>
+static inline auto group_by(const RangeTy &R, FuncTy F)
+ -> std::map<typename std::decay<decltype(F(*R.begin()))>::type,
+ std::vector<typename std::decay<decltype(*R.begin())>::type>> {
+ std::map<typename std::decay<decltype(F(*R.begin()))>::type,
+ std::vector<typename std::decay<decltype(*R.begin())>::type>>
+ Result;
+ for (const auto &E : R) {
+ Result[F(E)].push_back(E);
+ }
+ return Result;
+}
+
template <typename T>
static void readInts(const char *Start, const char *End,
std::set<uint64_t> *Ints) {
@@ -155,8 +174,18 @@ struct FileLoc {
uint32_t Line;
};
-struct FunctionLoc {
- bool operator<(const FunctionLoc &RHS) const {
+struct FileFn {
+ bool operator<(const FileFn &RHS) const {
+ return std::tie(FileName, FunctionName) <
+ std::tie(RHS.FileName, RHS.FunctionName);
+ }
+
+ std::string FileName;
+ std::string FunctionName;
+};
+
+struct FnLoc {
+ bool operator<(const FnLoc &RHS) const {
return std::tie(Loc, FunctionName) < std::tie(RHS.Loc, RHS.FunctionName);
}
@@ -181,51 +210,86 @@ static std::unique_ptr<symbolize::LLVMSy
new symbolize::LLVMSymbolizer(SymbolizerOptions));
}
-// Compute [FileLoc -> FunctionName] map for given addresses.
-static std::map<FileLoc, std::string>
-computeFunctionsMap(std::string ObjectFile, const std::set<uint64_t> &Addrs) {
- std::map<FileLoc, std::string> Fns;
+// A DILineInfo with address.
+struct AddrInfo : public DILineInfo {
+ uint64_t Addr;
- auto Symbolizer(createSymbolizer());
-
- // Fill in Fns map.
- for (auto Addr : Addrs) {
- auto InliningInfo = Symbolizer->symbolizeInlinedCode(ObjectFile, Addr);
- FailIfError(InliningInfo);
- for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
- auto FrameInfo = InliningInfo->getFrame(I);
- SmallString<256> FileName(FrameInfo.FileName);
- sys::path::remove_dots(FileName, /* remove_dot_dot */ true);
- FileLoc Loc = {FileName.str(), FrameInfo.Line};
- Fns[Loc] = FrameInfo.FunctionName;
- }
+ AddrInfo(const DILineInfo &DI, uint64_t Addr) : DILineInfo(DI), Addr(Addr) {
+ FileName = normalizeFilename(FileName);
}
- return Fns;
-}
+private:
+ static std::string normalizeFilename(std::string FileName) {
+ SmallString<256> S(FileName);
+ sys::path::remove_dots(S, /* remove_dot_dot */ true);
+ return S.str().str();
+ }
+};
-// Compute functions for given addresses. It keeps only the first
-// occurence of a function within a file.
-std::set<FunctionLoc> computeFunctionLocs(std::string ObjectFile,
- const std::set<uint64_t> &Addrs) {
- std::map<FileLoc, std::string> Fns = computeFunctionsMap(ObjectFile, Addrs);
+class Blacklists {
+public:
+ Blacklists()
+ : DefaultBlacklist(createDefaultBlacklist()),
+ UserBlacklist(createUserBlacklist()) {}
+
+ bool isBlacklisted(const DILineInfo &DI) {
+ if (DefaultBlacklist && DefaultBlacklist->inSection("fun", DI.FunctionName))
+ return true;
+ if (DefaultBlacklist && DefaultBlacklist->inSection("src", DI.FileName))
+ return true;
+ if (UserBlacklist && UserBlacklist->inSection("fun", DI.FunctionName))
+ return true;
+ if (UserBlacklist && UserBlacklist->inSection("src", DI.FileName))
+ return true;
+ return false;
+ }
- std::set<FunctionLoc> Result;
- std::string LastFileName;
- std::set<std::string> ProcessedFunctions;
+private:
+ static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() {
+ if (!ClUseDefaultBlacklist)
+ return std::unique_ptr<SpecialCaseList>();
+ std::unique_ptr<MemoryBuffer> MB =
+ MemoryBuffer::getMemBuffer(DefaultBlacklistStr);
+ std::string Error;
+ auto Blacklist = SpecialCaseList::create(MB.get(), Error);
+ FailIfNotEmpty(Error);
+ return Blacklist;
+ }
+
+ static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
+ if (ClBlacklist.empty())
+ return std::unique_ptr<SpecialCaseList>();
- for (const auto &P : Fns) {
- std::string FileName = P.first.FileName;
- std::string FunctionName = P.second;
+ return SpecialCaseList::createOrDie({{ClBlacklist}});
+ }
+ std::unique_ptr<SpecialCaseList> DefaultBlacklist;
+ std::unique_ptr<SpecialCaseList> UserBlacklist;
+};
- if (LastFileName != FileName)
- ProcessedFunctions.clear();
- LastFileName = FileName;
+// Collect all debug info for given addresses.
+static std::vector<AddrInfo> getAddrInfo(std::string ObjectFile,
+ const std::set<uint64_t> &Addrs,
+ bool InlinedCode) {
+ std::vector<AddrInfo> Result;
+ auto Symbolizer(createSymbolizer());
+ Blacklists B;
- if (!ProcessedFunctions.insert(FunctionName).second)
+ for (auto Addr : Addrs) {
+ auto LineInfo = Symbolizer->symbolizeCode(ObjectFile, Addr);
+ FailIfError(LineInfo);
+ if (B.isBlacklisted(*LineInfo))
continue;
-
- Result.insert(FunctionLoc{P.first, P.second});
+ Result.push_back(AddrInfo(*LineInfo, Addr));
+ if (InlinedCode) {
+ auto InliningInfo = Symbolizer->symbolizeInlinedCode(ObjectFile, Addr);
+ FailIfError(InliningInfo);
+ for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
+ auto FrameInfo = InliningInfo->getFrame(I);
+ if (B.isBlacklisted(FrameInfo))
+ continue;
+ Result.push_back(AddrInfo(FrameInfo, Addr));
+ }
+ }
}
return Result;
@@ -247,13 +311,11 @@ findSanitizerCovFunctions(const object::
if (Name == "__sanitizer_cov" || Name == "__sanitizer_cov_with_check" ||
Name == "__sanitizer_cov_trace_func_enter") {
- Result.insert(AddressOrErr.get());
+ if (!(Symbol.getFlags() & object::BasicSymbolRef::SF_Undefined))
+ Result.insert(AddressOrErr.get());
}
}
- if (Result.empty())
- Fail("__sanitizer_cov* functions not found");
-
return Result;
}
@@ -296,6 +358,8 @@ static void getObjectCoveragePoints(cons
FailIfEmpty(MIA, "no instruction analysis info for target " + TripleName);
auto SanCovAddrs = findSanitizerCovFunctions(O);
+ if (SanCovAddrs.empty())
+ Fail("__sanitizer_cov* functions not found");
for (const auto Section : O.sections()) {
if (Section.isVirtual() || !Section.isText()) // llvm-objdump does the same.
@@ -305,9 +369,6 @@ static void getObjectCoveragePoints(cons
if (!SectSize)
continue;
- StringRef SectionName;
- FailIfError(Section.getName(SectionName));
-
StringRef BytesStr;
FailIfError(Section.getContents(BytesStr));
ArrayRef<uint8_t> Bytes(reinterpret_cast<const uint8_t *>(BytesStr.data()),
@@ -322,92 +383,67 @@ static void getObjectCoveragePoints(cons
Size = 1;
continue;
}
+ uint64_t Addr = Index + SectionAddr;
+ // Sanitizer coverage uses the address of the next instruction - 1.
+ uint64_t CovPoint = Addr + Size - 1;
uint64_t Target;
if (MIA->isCall(Inst) &&
- MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target)) {
- if (SanCovAddrs.find(Target) != SanCovAddrs.end()) {
- // Sanitizer coverage uses the address of the next instruction - 1.
- Addrs->insert(Index + SectionAddr + Size - 1);
- }
- }
+ MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) &&
+ SanCovAddrs.find(Target) != SanCovAddrs.end())
+ Addrs->insert(CovPoint);
}
}
}
-static void getArchiveCoveragePoints(const object::Archive &A,
- std::set<uint64_t> *Addrs) {
+static void
+visitObjectFiles(const object::Archive &A,
+ std::function<void(const object::ObjectFile &)> Fn) {
for (auto &ErrorOrChild : A.children()) {
FailIfError(ErrorOrChild);
const object::Archive::Child &C = *ErrorOrChild;
ErrorOr<std::unique_ptr<object::Binary>> ChildOrErr = C.getAsBinary();
FailIfError(ChildOrErr);
- if (object::ObjectFile *O =
- dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
- getObjectCoveragePoints(*O, Addrs);
+ if (auto *O = dyn_cast<object::ObjectFile>(&*ChildOrErr.get()))
+ Fn(*O);
else
FailIfError(object::object_error::invalid_file_type);
}
}
-// Locate addresses of all coverage points in a file. Coverage point
-// is defined as the 'address of instruction following __sanitizer_cov
-// call - 1'.
-std::set<uint64_t> getCoveragePoints(std::string FileName) {
- std::set<uint64_t> Result;
-
+static void
+visitObjectFiles(std::string FileName,
+ std::function<void(const object::ObjectFile &)> Fn) {
ErrorOr<object::OwningBinary<object::Binary>> BinaryOrErr =
object::createBinary(FileName);
FailIfError(BinaryOrErr);
object::Binary &Binary = *BinaryOrErr.get().getBinary();
if (object::Archive *A = dyn_cast<object::Archive>(&Binary))
- getArchiveCoveragePoints(*A, &Result);
+ visitObjectFiles(*A, Fn);
else if (object::ObjectFile *O = dyn_cast<object::ObjectFile>(&Binary))
- getObjectCoveragePoints(*O, &Result);
+ Fn(*O);
else
FailIfError(object::object_error::invalid_file_type);
-
- return Result;
-}
-
-static std::unique_ptr<SpecialCaseList> createDefaultBlacklist() {
- if (!ClUseDefaultBlacklist)
- return std::unique_ptr<SpecialCaseList>();
- std::unique_ptr<MemoryBuffer> MB =
- MemoryBuffer::getMemBuffer(DefaultBlacklist);
- std::string Error;
- auto Blacklist = SpecialCaseList::create(MB.get(), Error);
- FailIfNotEmpty(Error);
- return Blacklist;
}
-static std::unique_ptr<SpecialCaseList> createUserBlacklist() {
- if (ClBlacklist.empty())
- return std::unique_ptr<SpecialCaseList>();
-
- return SpecialCaseList::createOrDie({{ClBlacklist}});
+std::set<uint64_t> findSanitizerCovFunctions(std::string FileName) {
+ std::set<uint64_t> Result;
+ visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
+ auto Addrs = findSanitizerCovFunctions(O);
+ Result.insert(Addrs.begin(), Addrs.end());
+ });
+ return Result;
}
-static void printFunctionLocs(const std::set<FunctionLoc> &FnLocs,
- raw_ostream &OS) {
- std::unique_ptr<SpecialCaseList> DefaultBlacklist = createDefaultBlacklist();
- std::unique_ptr<SpecialCaseList> UserBlacklist = createUserBlacklist();
-
- for (const FunctionLoc &FnLoc : FnLocs) {
- if (DefaultBlacklist &&
- DefaultBlacklist->inSection("fun", FnLoc.FunctionName))
- continue;
- if (DefaultBlacklist &&
- DefaultBlacklist->inSection("src", FnLoc.Loc.FileName))
- continue;
- if (UserBlacklist && UserBlacklist->inSection("fun", FnLoc.FunctionName))
- continue;
- if (UserBlacklist && UserBlacklist->inSection("src", FnLoc.Loc.FileName))
- continue;
-
- OS << stripPathPrefix(FnLoc.Loc.FileName) << ":" << FnLoc.Loc.Line << " "
- << FnLoc.FunctionName << "\n";
- }
+// Locate addresses of all coverage points in a file. Coverage point
+// is defined as the 'address of instruction following __sanitizer_cov
+// call - 1'.
+std::set<uint64_t> getCoveragePoints(std::string FileName) {
+ std::set<uint64_t> Result;
+ visitObjectFiles(FileName, [&](const object::ObjectFile &O) {
+ getObjectCoveragePoints(O, &Result);
+ });
+ return Result;
}
static std::string escapeHtml(const std::string &S) {
@@ -438,28 +474,6 @@ static std::string escapeHtml(const std:
return Result;
}
-// Computes a map file_name->{line_number}
-static std::map<std::string, std::set<int>>
-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(ObjectFile, Addr);
- FailIfError(InliningInfo);
- for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
- auto FrameInfo = InliningInfo->getFrame(I);
- SmallString<256> FileName(FrameInfo.FileName);
- sys::path::remove_dots(FileName, /* remove_dot_dot */ true);
- FileLines[FileName.str()].insert(FrameInfo.Line);
- }
- }
-
- return FileLines;
-}
-
static ErrorOr<bool> isCoverageFile(std::string FileName) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
MemoryBuffer::getFile(FileName);
@@ -547,83 +561,193 @@ class CoverageData {
}
}
- void printReport(std::string ObjectFile, raw_ostream &OS) {
- // file_name -> set of covered lines;
- std::map<std::string, std::set<int>> CoveredFileLines =
- getFileLines(ObjectFile, *Addrs);
- std::map<std::string, std::set<int>> CoveragePoints =
- getFileLines(ObjectFile, getCoveragePoints(ObjectFile));
+protected:
+ explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)
+ : Addrs(std::move(Addrs)) {}
- // TOC
- OS << "<ul>\n";
- for (auto It : CoveredFileLines) {
- auto FileName = It.first;
- OS << "<li><a href=\"#" << escapeHtml(FileName) << "\">"
- << stripPathPrefix(FileName) << "</a></li>\n";
+ friend class CoverageDataWithObjectFile;
+
+ std::unique_ptr<std::set<uint64_t>> Addrs;
+};
+
+// Coverage data translated into source code line-level information.
+// Fetches debug info in constructor and calculates various information per
+// request.
+class SourceCoverageData {
+public:
+ enum LineStatus {
+ // coverage information for the line is not available.
+ // default value in maps.
+ UNKNOWN = 0,
+ // the line is fully covered.
+ COVERED = 1,
+ // the line is fully uncovered.
+ NOT_COVERED = 2,
+ // some points in the line a covered, some are not.
+ MIXED = 3
+ };
+
+ SourceCoverageData(std::string ObjectFile, const std::set<uint64_t> &Addrs) {
+ std::set<uint64_t> AllCovPoints = getCoveragePoints(ObjectFile);
+
+ if (!std::includes(AllCovPoints.begin(), AllCovPoints.end(), Addrs.begin(),
+ Addrs.end())) {
+ Fail("Coverage points in binary and .sancov file do not match.");
+ }
+
+ AllAddrInfo = getAddrInfo(ObjectFile, AllCovPoints, true);
+ CovAddrInfo = getAddrInfo(ObjectFile, Addrs, true);
+ }
+
+ // Compute number of functions hit/total in a file.
+ // file_name -> <fn_coverage, all_fn_coverage>
+ std::map<std::string, std::pair<size_t, size_t>> computeFileFnCoverage() {
+ std::map<std::string, std::pair<size_t, size_t>> FileCoverage;
+ auto AllCovPointsByFile =
+ group_by(AllAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
+ auto CovPointByFile =
+ group_by(CovAddrInfo, [](const AddrInfo &AI) { return AI.FileName; });
+
+ for (auto P : AllCovPointsByFile) {
+ const std::string &FileName = P.first;
+ const auto &AllCovInfo = P.second;
+
+ auto AllFns = group_by(
+ AllCovInfo, [](const AddrInfo &AI) { return AI.FunctionName; });
+ size_t AllCoverage = AllFns.size();
+ size_t Coverage = 0;
+
+ auto It = CovPointByFile.find(FileName);
+ if (It != CovPointByFile.end()) {
+ const auto &CovInfo = It->second;
+ auto Fns = group_by(CovInfo,
+ [](const AddrInfo &AI) { return AI.FunctionName; });
+ Coverage = Fns.size();
+ }
+ FileCoverage[FileName] = std::make_pair(Coverage, AllCoverage);
}
- OS << "</ul>\n";
+ return FileCoverage;
+ }
- // Source
- for (auto It : CoveredFileLines) {
- auto FileName = It.first;
- auto Lines = It.second;
- auto CovLines = CoveragePoints[FileName];
+ // line_number -> line_status.
+ typedef std::map<int, LineStatus> LineStatusMap;
+ // file_name -> LineStatusMap
+ typedef std::map<std::string, LineStatusMap> FileLineStatusMap;
- OS << "<a name=\"" << escapeHtml(FileName) << "\"></a>\n";
- OS << "<h2>" << stripPathPrefix(FileName) << "</h2>\n";
- ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
- MemoryBuffer::getFile(FileName);
- if (!BufOrErr) {
- OS << "Error reading file: " << FileName << " : "
- << BufOrErr.getError().message() << "("
- << BufOrErr.getError().value() << ")\n";
- continue;
+ // fills in the {file_name -> {line_no -> status}} map.
+ FileLineStatusMap computeLineStatusMap() {
+ FileLineStatusMap StatusMap;
+
+ auto AllLocs = group_by(AllAddrInfo, [](const AddrInfo &AI) {
+ return FileLoc{AI.FileName, AI.Line};
+ });
+ auto CovLocs = group_by(CovAddrInfo, [](const AddrInfo &AI) {
+ return FileLoc{AI.FileName, AI.Line};
+ });
+
+ for (const auto &P : AllLocs) {
+ const FileLoc &Loc = P.first;
+ auto I = CovLocs.find(Loc);
+
+ if (I == CovLocs.end()) {
+ StatusMap[Loc.FileName][Loc.Line] = NOT_COVERED;
+ } else {
+ StatusMap[Loc.FileName][Loc.Line] =
+ (I->second.size() == P.second.size()) ? COVERED : MIXED;
}
+ }
+ return StatusMap;
+ }
- OS << "<pre>\n";
- for (line_iterator I = line_iterator(*BufOrErr.get(), false);
- !I.is_at_eof(); ++I) {
- OS << "<span ";
- if (Lines.find(I.line_number()) != Lines.end())
- OS << "class=covered";
- else if (CovLines.find(I.line_number()) != CovLines.end())
- OS << "class=notcovered";
- OS << ">";
- OS << escapeHtml(*I) << "</span>\n";
+ std::set<FileFn> computeCoveredFunctions() const {
+ std::set<FileFn> Fns;
+ auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+
+ for (const auto &P : CovFns) {
+ Fns.insert(P.first);
+ }
+ return Fns;
+ }
+
+ std::set<FileFn> computeNotCoveredFunctions() const {
+ std::set<FileFn> Fns;
+
+ auto AllFns = group_by(AllAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+ auto CovFns = group_by(CovAddrInfo, [](const AddrInfo &AI) {
+ return FileFn{AI.FileName, AI.FunctionName};
+ });
+
+ for (const auto &P : AllFns) {
+ if (CovFns.find(P.first) == CovFns.end()) {
+ Fns.insert(P.first);
}
- OS << "</pre>\n";
}
+ return Fns;
}
- // Print list of covered functions.
- // Line format: <file_name>:<line> <function_name>
- void printCoveredFunctions(std::string ObjectFile, raw_ostream &OS) {
- printFunctionLocs(computeFunctionLocs(ObjectFile, *Addrs), OS);
+ typedef std::map<FileLoc, std::set<std::string>> FunctionLocs;
+ // finds first line number in a file for each function.
+ FunctionLocs resolveFunctions(const std::set<FileFn> &Fns) const {
+ std::vector<AddrInfo> FnAddrs;
+ for (const auto &AI : AllAddrInfo) {
+ if (Fns.find(FileFn{AI.FileName, AI.FunctionName}) != Fns.end())
+ FnAddrs.push_back(AI);
+ }
+
+ auto GroupedAddrs = group_by(FnAddrs, [](const AddrInfo &AI) {
+ return FnLoc{FileLoc{AI.FileName, AI.Line}, AI.FunctionName};
+ });
+
+ FunctionLocs Result;
+ std::string LastFileName;
+ std::set<std::string> ProcessedFunctions;
+
+ for (const auto &P : GroupedAddrs) {
+ const FnLoc &Loc = P.first;
+ std::string FileName = Loc.Loc.FileName;
+ std::string FunctionName = Loc.FunctionName;
+
+ if (LastFileName != FileName)
+ ProcessedFunctions.clear();
+ LastFileName = FileName;
+
+ if (!ProcessedFunctions.insert(FunctionName).second)
+ continue;
+
+ Result[FileLoc{FileName, Loc.Loc.Line}].insert(FunctionName);
+ }
+ return Result;
}
- // Print list of not covered functions.
- // Line format: <file_name>:<line> <function_name>
- void printNotCoveredFunctions(std::string ObjectFile, raw_ostream &OS) {
- std::set<FunctionLoc> AllFns =
- computeFunctionLocs(ObjectFile, getCoveragePoints(ObjectFile));
- std::set<FunctionLoc> CoveredFns = computeFunctionLocs(ObjectFile, *Addrs);
-
- std::set<FunctionLoc> NotCoveredFns;
- std::set_difference(AllFns.begin(), AllFns.end(), CoveredFns.begin(),
- CoveredFns.end(),
- std::inserter(NotCoveredFns, NotCoveredFns.end()));
- printFunctionLocs(NotCoveredFns, OS);
+ std::set<std::string> files() const {
+ std::set<std::string> Files;
+ for (const auto &AI : AllAddrInfo) {
+ Files.insert(AI.FileName);
+ }
+ return Files;
}
private:
- explicit CoverageData(std::unique_ptr<std::set<uint64_t>> Addrs)
- : Addrs(std::move(Addrs)) {}
-
- std::unique_ptr<std::set<uint64_t>> Addrs;
+ std::vector<AddrInfo> AllAddrInfo;
+ std::vector<AddrInfo> CovAddrInfo;
};
+static void printFunctionLocs(const SourceCoverageData::FunctionLocs &FnLocs,
+ raw_ostream &OS) {
+ for (const auto &Fns : FnLocs) {
+ for (const auto &Fn : Fns.second) {
+ OS << stripPathPrefix(Fns.first.FileName) << ":" << Fns.first.Line << " "
+ << Fn << "\n";
+ }
+ }
+}
+
// Holder for coverage data + filename of corresponding object file.
-class CoverageDataWithObjectFile {
+class CoverageDataWithObjectFile : public CoverageData {
public:
static ErrorOr<std::unique_ptr<CoverageDataWithObjectFile>>
readAndMerge(std::string ObjectFile,
@@ -638,25 +762,145 @@ public:
std::string object_file() const { return ObjectFile; }
+ // Print list of covered functions.
+ // Line format: <file_name>:<line> <function_name>
void printCoveredFunctions(raw_ostream &OS) const {
- Coverage->printCoveredFunctions(ObjectFile, OS);
+ SourceCoverageData SCovData(ObjectFile, *Addrs);
+ auto CoveredFns = SCovData.computeCoveredFunctions();
+ printFunctionLocs(SCovData.resolveFunctions(CoveredFns), OS);
}
+ // Print list of not covered functions.
+ // Line format: <file_name>:<line> <function_name>
void printNotCoveredFunctions(raw_ostream &OS) const {
- Coverage->printNotCoveredFunctions(ObjectFile, OS);
+ SourceCoverageData SCovData(ObjectFile, *Addrs);
+ auto NotCoveredFns = SCovData.computeNotCoveredFunctions();
+ printFunctionLocs(SCovData.resolveFunctions(NotCoveredFns), OS);
}
void printReport(raw_ostream &OS) const {
- Coverage->printReport(ObjectFile, OS);
+ SourceCoverageData SCovData(ObjectFile, *Addrs);
+ auto LineStatusMap = SCovData.computeLineStatusMap();
+
+ // file_name -> [file_fn].
+ auto NotCoveredFns = SCovData.computeNotCoveredFunctions();
+ auto NotCoveredFnMap = group_by(
+ NotCoveredFns, [](const FileFn &FileFn) { return FileFn.FileName; });
+ // file_loc -> set[function_name]
+ auto NotCoveredFnByLoc = SCovData.resolveFunctions(NotCoveredFns);
+ auto FileFnCoverage = SCovData.computeFileFnCoverage();
+
+ // TOC
+
+ // Covered Files.
+ OS << "<details open><summary>Covered Files</summary>\n";
+ OS << "<table>\n";
+ OS << "<tr><th>File</th><th>Hit Fns %</th>";
+ OS << "<th>Hit (Total) Fns</th></tr>\n";
+ for (auto FileName : SCovData.files()) {
+ std::pair<size_t, size_t> FC = FileFnCoverage[FileName];
+ if (FC.first == 0)
+ continue;
+ size_t CovPct = FC.second == 0 ? 100 : 100 * FC.first / FC.second;
+
+ OS << "<tr><td><a href=\"#" << escapeHtml(FileName) << "\">"
+ << stripPathPrefix(FileName) << "</a></td>"
+ << "<td>" << CovPct << "%</td>"
+ << "<td>" << FC.first << " (" << FC.second << ")"
+ << "</tr>\n";
+ }
+ OS << "</table>\n";
+ OS << "</details>\n";
+
+ // Not covered files.
+ OS << "<details><summary>Not Covered Files</summary>\n";
+ OS << "<table>\n";
+ for (auto FileName : SCovData.files()) {
+ std::pair<size_t, size_t> FC = FileFnCoverage[FileName];
+ if (FC.first == 0)
+ OS << "<tr><td>" << stripPathPrefix(FileName) << "</td>\n";
+ }
+ OS << "</table>\n";
+ OS << "</details>\n";
+
+ // Source
+ for (auto FileName : SCovData.files()) {
+ std::pair<size_t, size_t> FC = FileFnCoverage[FileName];
+ if (FC.first == 0)
+ continue;
+ OS << "<a name=\"" << escapeHtml(FileName) << "\"></a>\n";
+ OS << "<h2>" << stripPathPrefix(FileName) << "</h2>\n";
+
+ auto NotCoveredFns = NotCoveredFnMap.find(FileName);
+ if (NotCoveredFns != NotCoveredFnMap.end()) {
+ OS << "<details open><summary>Not Covered Functions</summary>";
+ OS << "<table>\n";
+ for (auto FileFn : NotCoveredFns->second) {
+ OS << "<tr><td>";
+ OS << "<a href=\"#"
+ << escapeHtml(FileName + ":: " + FileFn.FunctionName) << "\">";
+ OS << escapeHtml(FileFn.FunctionName) << "</a>";
+ OS << "</td></tr>\n";
+ }
+ OS << "</table></details>\n";
+ }
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
+ MemoryBuffer::getFile(FileName);
+ if (!BufOrErr) {
+ OS << "Error reading file: " << FileName << " : "
+ << BufOrErr.getError().message() << "("
+ << BufOrErr.getError().value() << ")\n";
+ continue;
+ }
+
+ OS << "<pre>\n";
+ const auto &LineStatuses = LineStatusMap[FileName];
+ for (line_iterator I = line_iterator(*BufOrErr.get(), false);
+ !I.is_at_eof(); ++I) {
+ uint32_t Line = I.line_number();
+ { // generate anchors (if any);
+ FileLoc Loc = FileLoc{FileName, Line};
+ auto It = NotCoveredFnByLoc.find(Loc);
+ if (It != NotCoveredFnByLoc.end()) {
+ for (std::string Fn : It->second) {
+ OS << "<a name=\"" << escapeHtml(FileName + ":: " + Fn)
+ << "\"></a>";
+ };
+ }
+ }
+
+ OS << "<span ";
+ auto LIT = LineStatuses.find(I.line_number());
+ auto Status = (LIT != LineStatuses.end()) ? LIT->second
+ : SourceCoverageData::UNKNOWN;
+ switch (Status) {
+ case SourceCoverageData::UNKNOWN:
+ OS << "class=unknown";
+ break;
+ case SourceCoverageData::COVERED:
+ OS << "class=covered";
+ break;
+ case SourceCoverageData::NOT_COVERED:
+ OS << "class=notcovered";
+ break;
+ case SourceCoverageData::MIXED:
+ OS << "class=mixed";
+ break;
+ }
+ OS << ">";
+ OS << escapeHtml(*I) << "</span>\n";
+ }
+ OS << "</pre>\n";
+ }
}
private:
CoverageDataWithObjectFile(std::string ObjectFile,
std::unique_ptr<CoverageData> Coverage)
- : ObjectFile(std::move(ObjectFile)), Coverage(std::move(Coverage)) {}
-
+ : CoverageData(std::move(Coverage->Addrs)),
+ ObjectFile(std::move(ObjectFile)) {}
const std::string ObjectFile;
- const std::unique_ptr<CoverageData> Coverage;
};
// Multiple coverage files data organized by object file.
@@ -687,13 +931,11 @@ public:
}
}
- // 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) {
+ // Object file => list of corresponding coverage file names.
+ auto CoverageByObjFile = group_by(CovFiles, [&](std::string FileName) {
auto ShortFileName = llvm::sys::path::filename(FileName);
auto Ok = SancovRegex.match(ShortFileName, &Components);
if (!Ok) {
@@ -706,13 +948,24 @@ public:
if (Iter == ObjFiles.end()) {
Fail("Object file for coverage not found: " + FileName);
}
- auto ObjectFile = Iter->second;
- CoverageByObjFile[ObjectFile].push_back(FileName);
- }
+ return Iter->second;
+ });
// Read coverage.
std::vector<std::unique_ptr<CoverageDataWithObjectFile>> MergedCoverage;
for (const auto &Pair : CoverageByObjFile) {
+ if (findSanitizerCovFunctions(Pair.first).empty()) {
+ for (auto FileName : Pair.second) {
+ CovFiles.erase(FileName);
+ }
+
+ errs()
+ << "Ignoring " << Pair.first
+ << " and its coverage because __sanitizer_cov* functions were not "
+ "found.\n";
+ continue;
+ }
+
auto DataOrError =
CoverageDataWithObjectFile::readAndMerge(Pair.first, Pair.second);
FailIfError(DataOrError);
@@ -745,6 +998,8 @@ public:
OS << "<style>\n";
OS << ".covered { background: #7F7; }\n";
OS << ".notcovered { background: #F77; }\n";
+ OS << "summary { font-weight: bold; }\n";
+ OS << "details > summary + * { margin-left: 1em; }\n";
OS << "</style>\n";
OS << "<title>" << Title << "</title>\n";
OS << "</head>\n";
@@ -752,14 +1007,6 @@ public:
// 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) {
@@ -780,6 +1027,17 @@ public:
CovData->printReport(OS);
}
+ // About
+ OS << "<details><summary>About</summary>\n";
+ OS << "Coverage files:<ul>";
+ for (auto InputFile : CoverageFiles) {
+ llvm::sys::fs::file_status Status;
+ llvm::sys::fs::status(InputFile, Status);
+ OS << "<li>" << stripPathPrefix(InputFile) << " ("
+ << Status.getLastModificationTime().str() << ")</li>\n";
+ }
+ OS << "</ul></details>\n";
+
OS << "</body>\n";
OS << "</html>\n";
}
More information about the llvm-commits
mailing list