[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