[llvm] r261287 - [sancov] Adding covered/uncovered tables to coverage report.

Mike Aizatsky via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 19 18:17:15 PST 2016


Looks like MSVC can't handle structure initialization within array index.
Extracted a variable to help it a little. Let's see if it does:

http://reviews.llvm.org/rL261396


On Fri, Feb 19, 2016 at 5:31 PM Sean Silva <chisophugis at gmail.com> wrote:

> 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/20160220/3caefaea/attachment.html>


More information about the llvm-commits mailing list