[llvm] r257824 - [sancov] html report

Mike Aizatsky via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 14 14:34:12 PST 2016


Author: aizatsky
Date: Thu Jan 14 16:34:11 2016
New Revision: 257824

URL: http://llvm.org/viewvc/llvm-project?rev=257824&view=rev
Log:
[sancov] html report

Differential Revision: http://reviews.llvm.org/D16161

Added:
    llvm/trunk/test/tools/sancov/html-report.test
Modified:
    llvm/trunk/tools/sancov/sancov.cc

Added: llvm/trunk/test/tools/sancov/html-report.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/sancov/html-report.test?rev=257824&view=auto
==============================================================================
--- llvm/trunk/test/tools/sancov/html-report.test (added)
+++ llvm/trunk/test/tools/sancov/html-report.test Thu Jan 14 16:34:11 2016
@@ -0,0 +1,6 @@
+REQUIRES: x86_64-linux
+RUN: sancov -obj %p/Inputs/test-linux_x86_64 -html-report %p/Inputs/test-linux_x86_64.sancov | FileCheck %s
+
+// It's very difficult to test html report. Do basic smoke check.
+CHECK: {{<a name=".*/Inputs/test.cpp">}}
+

Modified: llvm/trunk/tools/sancov/sancov.cc
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/sancov/sancov.cc?rev=257824&r1=257823&r2=257824&view=diff
==============================================================================
--- llvm/trunk/tools/sancov/sancov.cc (original)
+++ llvm/trunk/tools/sancov/sancov.cc Thu Jan 14 16:34:11 2016
@@ -55,7 +55,8 @@ namespace {
 enum ActionType {
   PrintAction,
   CoveredFunctionsAction,
-  NotCoveredFunctionsAction
+  NotCoveredFunctionsAction,
+  HtmlReportAction
 };
 
 cl::opt<ActionType> Action(
@@ -65,6 +66,8 @@ cl::opt<ActionType> Action(
                           "Print all covered funcions."),
                clEnumValN(NotCoveredFunctionsAction, "not-covered-functions",
                           "Print all not covered funcions."),
+               clEnumValN(HtmlReportAction, "html-report",
+                          "Print HTML coverage report."),
                clEnumValEnd));
 
 static cl::list<std::string> ClInputFiles(cl::Positional, cl::OneOrMore,
@@ -167,19 +170,24 @@ std::string stripPathPrefix(std::string
   return Path.substr(Pos + ClStripPathPrefix.size());
 }
 
+static std::unique_ptr<symbolize::LLVMSymbolizer> createSymbolizer() {
+  symbolize::LLVMSymbolizer::Options SymbolizerOptions;
+  SymbolizerOptions.Demangle = ClDemangle;
+  SymbolizerOptions.UseSymbolTable = true;
+  return std::unique_ptr<symbolize::LLVMSymbolizer>(
+      new symbolize::LLVMSymbolizer(SymbolizerOptions));
+}
+
 // Compute [FileLoc -> FunctionName] map for given addresses.
 static std::map<FileLoc, std::string>
 computeFunctionsMap(const std::set<uint64_t> &Addrs) {
   std::map<FileLoc, std::string> Fns;
 
-  symbolize::LLVMSymbolizer::Options SymbolizerOptions;
-  SymbolizerOptions.Demangle = ClDemangle;
-  SymbolizerOptions.UseSymbolTable = true;
-  symbolize::LLVMSymbolizer Symbolizer(SymbolizerOptions);
+  auto Symbolizer(createSymbolizer());
 
   // Fill in Fns map.
   for (auto Addr : Addrs) {
-    auto InliningInfo = Symbolizer.symbolizeInlinedCode(ClBinaryName, Addr);
+    auto InliningInfo = Symbolizer->symbolizeInlinedCode(ClBinaryName, Addr);
     FailIfError(InliningInfo);
     for (uint32_t I = 0; I < InliningInfo->getNumberOfFrames(); ++I) {
       auto FrameInfo = InliningInfo->getFrame(I);
@@ -398,6 +406,56 @@ static void printFunctionLocs(const std:
   }
 }
 
+static std::string escapeHtml(const std::string &S) {
+  std::string Result;
+  Result.reserve(S.size());
+  for (char Ch : S) {
+    switch (Ch) {
+    case '&':
+      Result.append("&");
+      break;
+    case '\'':
+      Result.append("'");
+      break;
+    case '"':
+      Result.append(""");
+      break;
+    case '<':
+      Result.append("<");
+      break;
+    case '>':
+      Result.append(">");
+      break;
+    default:
+      Result.push_back(Ch);
+      break;
+    }
+  }
+  return Result;
+}
+
+// Computes a map file_name->{line_number}
+static std::map<std::string, std::set<int>>
+getFileLines(const std::set<uint64_t> &Addrs) {
+  std::map<std::string, std::set<int>> FileLines;
+
+  auto Symbolizer(createSymbolizer());
+
+  // Fill in FileLines map.
+  for (auto Addr : Addrs) {
+    auto InliningInfo = Symbolizer->symbolizeInlinedCode(ClBinaryName, Addr);
+    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;
+}
+
 class CoverageData {
  public:
   // Read single file coverage data.
@@ -471,6 +529,82 @@ class CoverageData {
     }
   }
 
+  void printReport(raw_ostream &OS) {
+    // file_name -> set of covered lines;
+    std::map<std::string, std::set<int>> CoveredFileLines =
+        getFileLines(*Addrs);
+    std::map<std::string, std::set<int>> CoveragePoints =
+        getFileLines(getCoveragePoints(ClBinaryName));
+
+    std::string Title = stripPathPrefix(ClBinaryName) + " Coverage Report";
+
+    OS << "<html>\n";
+    OS << "<head>\n";
+
+    // Stylesheet
+    OS << "<style>\n";
+    OS << ".covered { background: #7F7; }\n";
+    OS << ".notcovered { background: #F77; }\n";
+    OS << "</style>\n";
+    OS << "<title>" << Title << "</title>\n";
+    OS << "</head>\n";
+    OS << "<body>\n";
+
+    // Title
+    OS << "<h1>" << Title << "</h1>\n";
+    OS << "<p>Coverage files: ";
+    for (auto InputFile : ClInputFiles) {
+      llvm::sys::fs::file_status Status;
+      llvm::sys::fs::status(InputFile, Status);
+      OS << stripPathPrefix(InputFile) << " ("
+         << Status.getLastModificationTime().str() << ")";
+    }
+    OS << "</p>\n";
+
+    // TOC
+    OS << "<ul>\n";
+    for (auto It : CoveredFileLines) {
+      auto FileName = It.first;
+      OS << "<li><a href=\"#" << escapeHtml(FileName) << "\">"
+         << stripPathPrefix(FileName) << "</a></li>\n";
+    }
+    OS << "</ul>\n";
+
+    // Source
+    for (auto It : CoveredFileLines) {
+      auto FileName = It.first;
+      auto Lines = It.second;
+      auto CovLines = CoveragePoints[FileName];
+
+      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;
+      }
+
+      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";
+      }
+      OS << "</pre>\n";
+    }
+
+    OS << "</body>\n";
+    OS << "</html>\n";
+  }
+
   // Print list of covered functions.
   // Line format: <file_name>:<line> <function_name>
   void printCoveredFunctions(raw_ostream &OS) {
@@ -527,6 +661,10 @@ int main(int argc, char **argv) {
     CovData.get()->printNotCoveredFunctions(outs());
     return 0;
   }
+  case HtmlReportAction: {
+    CovData.get()->printReport(outs());
+    return 0;
+  }
   }
 
   llvm_unreachable("unsupported action");




More information about the llvm-commits mailing list