[llvm] r261287 - [sancov] Adding covered/uncovered tables to coverage report.
Sean Silva via llvm-commits
llvm-commits at lists.llvm.org
Fri Feb 19 17:31:50 PST 2016
On Thu, Feb 18, 2016 at 4:26 PM, Mike Aizatsky via llvm-commits <
llvm-commits at lists.llvm.org> wrote:
> 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);
>
This looks like it is causing an MSVC warning:
http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/421/steps/build/logs/warnings%20%281%29
(view as text)
<http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/421/steps/build/logs/warnings%20(1)/text>
C:\Buildbot\Slave\llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast\llvm.src\tools\sancov\sancov.cc(724):
warning C4709: comma operator within array index expression
The warning doesn't really make sense to me. I assume that if it were
treating the ',' as a comma operator that a test would fail or it would
fail to compile?
-- Sean Silva
> + }
> + 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";
> }
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20160219/c0b84d06/attachment-0001.html>
More information about the llvm-commits
mailing list