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