From 6dcc8f11e9bf92113e354c9f6b1ef622034dc761 Mon Sep 17 00:00:00 2001 From: Vitaly Buka Date: Sat, 12 Oct 2019 02:29:24 +0000 Subject: [PATCH] [sancov] Use LLVM Support library JSON writer in favor of individual implementation Summary: In this diff, I've replaced the individual implementation of `JSONWriter` with `json::OStream` provided by `llvm/Support/JSON.h`. Important Note: The output format of the JSON is considerably different compared to the original implementation. Important differences include: * New line for each entry in an array (should make diffs cleaner) * No space between keys and colon in attributed object entries. * Attributes with empty strings will now print the attribute name and a quote pair rather than excluding the attribute altogether Examples of these differences can be seen in the changes to the sancov tests which compare the JSON output. Patch by Douglas Gliner. Reviewers: kcc, filcab, phosek, morehouse, vitalybuka, metzman Subscribers: mehdi_amini, dexonsmith, llvm-commits Tags: #sanitizers, #llvm Differential Revision: https://reviews.llvm.org/D68752 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@374628 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/tools/sancov/merge.test | 105 ++++++----- test/tools/sancov/symbolize.test | 35 ++-- .../sancov/symbolize_noskip_dead_files.test | 45 ++--- tools/sancov/sancov.cpp | 167 ++++-------------- 4 files changed, 142 insertions(+), 210 deletions(-) diff --git a/test/tools/sancov/merge.test b/test/tools/sancov/merge.test index 4c95d7c0bb5..9c5ca9e6244 100644 --- a/test/tools/sancov/merge.test +++ b/test/tools/sancov/merge.test @@ -3,62 +3,81 @@ RUN: sancov -merge %p/Inputs/test-linux_x86_64.0.symcov| FileCheck --check-prefi RUN: sancov -merge %p/Inputs/test-linux_x86_64.0.symcov %p/Inputs/test-linux_x86_64.1.symcov| FileCheck --check-prefix=MERGE2 %s MERGE1: { -MERGE1-NEXT: "covered-points" : ["4e132b", "4e1472", "4e1520", "4e1553", "4e1586"], -MERGE1-NEXT: "binary-hash" : "BB3CDD5045AED83906F6ADCC1C4DAF7E2596A6B5", -MERGE1-NEXT: "point-symbol-info" : { -MERGE1-NEXT: "test/tools/sancov/Inputs/foo.cpp" : { -MERGE1-NEXT: "foo()" : { -MERGE1-NEXT: "4e178c" : "5:0" +MERGE1-NEXT: "covered-points": [ +MERGE1-NEXT: "4e132b", +MERGE1-NEXT: "4e1472", +MERGE1-NEXT: "4e1520", +MERGE1-NEXT: "4e1553", +MERGE1-NEXT: "4e1586" +MERGE1-NEXT: ], +MERGE1-NEXT: "binary-hash": "BB3CDD5045AED83906F6ADCC1C4DAF7E2596A6B5", +MERGE1-NEXT: "point-symbol-info": { +MERGE1-NEXT: "test/tools/sancov/Inputs/foo.cpp": { +MERGE1-NEXT: "foo()": { +MERGE1-NEXT: "4e178c": "5:0" MERGE1-NEXT: } MERGE1-NEXT: }, -MERGE1-NEXT: "test/tools/sancov/Inputs/test.cpp" : { -MERGE1-NEXT: "bar(std::string)" : { -MERGE1-NEXT: "4e132b" : "12:0" +MERGE1-NEXT: "test/tools/sancov/Inputs/test.cpp": { +MERGE1-NEXT: "bar(std::string)": { +MERGE1-NEXT: "4e132b": "12:0" MERGE1-NEXT: }, -MERGE1-NEXT: "main" : { -MERGE1-NEXT: "4e1472" : "14:0", -MERGE1-NEXT: "4e14c2" : "16:9", -MERGE1-NEXT: "4e1520" : "17:5", -MERGE1-NEXT: "4e1553" : "17:5", -MERGE1-NEXT: "4e1586" : "17:5", -MERGE1-NEXT: "4e1635" : "19:1", -MERGE1-NEXT: "4e1690" : "17:5" +MERGE1-NEXT: "main": { +MERGE1-NEXT: "4e1472": "14:0", +MERGE1-NEXT: "4e14c2": "16:9", +MERGE1-NEXT: "4e1520": "17:5", +MERGE1-NEXT: "4e1553": "17:5", +MERGE1-NEXT: "4e1586": "17:5", +MERGE1-NEXT: "4e1635": "19:1", +MERGE1-NEXT: "4e1690": "17:5" MERGE1-NEXT: } MERGE1-NEXT: } MERGE1-NEXT: } MERGE1-NEXT: } MERGE2: { -MERGE2-NEXT: "covered-points" : ["04e132b", "04e1472", "04e1520", "04e1553", "04e1586", "14e132b", "14e1472", "14e14c2", "14e1520", "14e1553", "14e1586", "14e178c"], -MERGE2-NEXT: "point-symbol-info" : { -MERGE2-NEXT: "test/tools/sancov/Inputs/foo.cpp" : { -MERGE2-NEXT: "foo()" : { -MERGE2-NEXT: "04e178c" : "5:0", -MERGE2-NEXT: "14e178c" : "5:0" +MERGE2-NEXT: "covered-points": [ +MERGE2-NEXT: "04e132b", +MERGE2-NEXT: "04e1472", +MERGE2-NEXT: "04e1520", +MERGE2-NEXT: "04e1553", +MERGE2-NEXT: "04e1586", +MERGE2-NEXT: "14e132b", +MERGE2-NEXT: "14e1472", +MERGE2-NEXT: "14e14c2", +MERGE2-NEXT: "14e1520", +MERGE2-NEXT: "14e1553", +MERGE2-NEXT: "14e1586", +MERGE2-NEXT: "14e178c" +MERGE2-NEXT: ], +MERGE2-NEXT: "binary-hash": "", +MERGE2-NEXT: "point-symbol-info": { +MERGE2-NEXT: "test/tools/sancov/Inputs/foo.cpp": { +MERGE2-NEXT: "foo()": { +MERGE2-NEXT: "04e178c": "5:0", +MERGE2-NEXT: "14e178c": "5:0" MERGE2-NEXT: } MERGE2-NEXT: }, -MERGE2-NEXT: "test/tools/sancov/Inputs/test.cpp" : { -MERGE2-NEXT: "bar(std::string)" : { -MERGE2-NEXT: "04e132b" : "12:0", -MERGE2-NEXT: "14e132b" : "12:0" +MERGE2-NEXT: "test/tools/sancov/Inputs/test.cpp": { +MERGE2-NEXT: "bar(std::string)": { +MERGE2-NEXT: "04e132b": "12:0", +MERGE2-NEXT: "14e132b": "12:0" MERGE2-NEXT: }, -MERGE2-NEXT: "main" : { -MERGE2-NEXT: "04e1472" : "14:0", -MERGE2-NEXT: "04e14c2" : "16:9", -MERGE2-NEXT: "04e1520" : "17:5", -MERGE2-NEXT: "04e1553" : "17:5", -MERGE2-NEXT: "04e1586" : "17:5", -MERGE2-NEXT: "04e1635" : "19:1", -MERGE2-NEXT: "04e1690" : "17:5", -MERGE2-NEXT: "14e1472" : "14:0", -MERGE2-NEXT: "14e14c2" : "16:9", -MERGE2-NEXT: "14e1520" : "17:5", -MERGE2-NEXT: "14e1553" : "17:5", -MERGE2-NEXT: "14e1586" : "17:5", -MERGE2-NEXT: "14e1635" : "19:1", -MERGE2-NEXT: "14e1690" : "17:5" +MERGE2-NEXT: "main": { +MERGE2-NEXT: "04e1472": "14:0", +MERGE2-NEXT: "04e14c2": "16:9", +MERGE2-NEXT: "04e1520": "17:5", +MERGE2-NEXT: "04e1553": "17:5", +MERGE2-NEXT: "04e1586": "17:5", +MERGE2-NEXT: "04e1635": "19:1", +MERGE2-NEXT: "04e1690": "17:5", +MERGE2-NEXT: "14e1472": "14:0", +MERGE2-NEXT: "14e14c2": "16:9", +MERGE2-NEXT: "14e1520": "17:5", +MERGE2-NEXT: "14e1553": "17:5", +MERGE2-NEXT: "14e1586": "17:5", +MERGE2-NEXT: "14e1635": "19:1", +MERGE2-NEXT: "14e1690": "17:5" MERGE2-NEXT: } MERGE2-NEXT: } MERGE2-NEXT: } MERGE2-NEXT: } - diff --git a/test/tools/sancov/symbolize.test b/test/tools/sancov/symbolize.test index 7d0ba3e470a..3cc426f919b 100644 --- a/test/tools/sancov/symbolize.test +++ b/test/tools/sancov/symbolize.test @@ -2,23 +2,28 @@ REQUIRES: x86_64-linux RUN: sancov -symbolize -strip_path_prefix="llvm/" %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck %s CHECK: { -CHECK-NEXT: "covered-points" : ["4e132b", "4e1472", "4e1520", "4e1553", "4e1586"], -CHECK-NEXT: "binary-hash" : "BB3CDD5045AED83906F6ADCC1C4DAF7E2596A6B5", -CHECK-NEXT: "point-symbol-info" : { -CHECK-NEXT: "test/tools/sancov/Inputs/test.cpp" : { -CHECK-NEXT: "bar(std::string)" : { -CHECK-NEXT: "4e132b" : "12:0" +CHECK-NEXT: "covered-points": [ +CHECK-NEXT: "4e132b", +CHECK-NEXT: "4e1472", +CHECK-NEXT: "4e1520", +CHECK-NEXT: "4e1553", +CHECK-NEXT: "4e1586" +CHECK-NEXT: ], +CHECK-NEXT: "binary-hash": "BB3CDD5045AED83906F6ADCC1C4DAF7E2596A6B5", +CHECK-NEXT: "point-symbol-info": { +CHECK-NEXT: "test/tools/sancov/Inputs/test.cpp": { +CHECK-NEXT: "bar(std::string)": { +CHECK-NEXT: "4e132b": "12:0" CHECK-NEXT: }, -CHECK-NEXT: "main" : { -CHECK-NEXT: "4e1472" : "14:0", -CHECK-NEXT: "4e14c2" : "16:9", -CHECK-NEXT: "4e1520" : "17:5", -CHECK-NEXT: "4e1553" : "17:5", -CHECK-NEXT: "4e1586" : "17:5", -CHECK-NEXT: "4e1635" : "19:1", -CHECK-NEXT: "4e1690" : "17:5" +CHECK-NEXT: "main": { +CHECK-NEXT: "4e1472": "14:0", +CHECK-NEXT: "4e14c2": "16:9", +CHECK-NEXT: "4e1520": "17:5", +CHECK-NEXT: "4e1553": "17:5", +CHECK-NEXT: "4e1586": "17:5", +CHECK-NEXT: "4e1635": "19:1", +CHECK-NEXT: "4e1690": "17:5" CHECK-NEXT: } CHECK-NEXT: } CHECK-NEXT: } CHECK-NEXT:} - diff --git a/test/tools/sancov/symbolize_noskip_dead_files.test b/test/tools/sancov/symbolize_noskip_dead_files.test index 60eff941b00..9ddf89cbf56 100644 --- a/test/tools/sancov/symbolize_noskip_dead_files.test +++ b/test/tools/sancov/symbolize_noskip_dead_files.test @@ -2,28 +2,33 @@ REQUIRES: x86_64-linux RUN: sancov -symbolize -skip-dead-files=0 -strip_path_prefix="llvm/" %p/Inputs/test-linux_x86_64 %p/Inputs/test-linux_x86_64.0.sancov | FileCheck %s CHECK: { -CHECK-NEXT: "covered-points" : ["4e132b", "4e1472", "4e1520", "4e1553", "4e1586"], -CHECK-NEXT: "binary-hash" : "BB3CDD5045AED83906F6ADCC1C4DAF7E2596A6B5", -CHECK-NEXT: "point-symbol-info" : { -CHECK-NEXT: "test/tools/sancov/Inputs/foo.cpp" : { -CHECK-NEXT: "foo()" : { -CHECK-NEXT: "4e178c" : "5:0" -CHECK-NEXT: } -CHECK-NEXT: }, -CHECK-NEXT: "test/tools/sancov/Inputs/test.cpp" : { -CHECK-NEXT: "bar(std::string)" : { -CHECK-NEXT: "4e132b" : "12:0" +CHECK-NEXT: "covered-points": [ +CHECK-NEXT: "4e132b", +CHECK-NEXT: "4e1472", +CHECK-NEXT: "4e1520", +CHECK-NEXT: "4e1553", +CHECK-NEXT: "4e1586" +CHECK-NEXT: ], +CHECK-NEXT: "binary-hash": "BB3CDD5045AED83906F6ADCC1C4DAF7E2596A6B5", +CHECK-NEXT: "point-symbol-info": { +CHECK-NEXT: "test/tools/sancov/Inputs/foo.cpp": { +CHECK-NEXT: "foo()": { +CHECK-NEXT: "4e178c": "5:0" +CHECK-NEXT: } +CHECK-NEXT: }, +CHECK-NEXT: "test/tools/sancov/Inputs/test.cpp": { +CHECK-NEXT: "bar(std::string)": { +CHECK-NEXT: "4e132b": "12:0" CHECK-NEXT: }, -CHECK-NEXT: "main" : { -CHECK-NEXT: "4e1472" : "14:0", -CHECK-NEXT: "4e14c2" : "16:9", -CHECK-NEXT: "4e1520" : "17:5", -CHECK-NEXT: "4e1553" : "17:5", -CHECK-NEXT: "4e1586" : "17:5", -CHECK-NEXT: "4e1635" : "19:1", -CHECK-NEXT: "4e1690" : "17:5" +CHECK-NEXT: "main": { +CHECK-NEXT: "4e1472": "14:0", +CHECK-NEXT: "4e14c2": "16:9", +CHECK-NEXT: "4e1520": "17:5", +CHECK-NEXT: "4e1553": "17:5", +CHECK-NEXT: "4e1586": "17:5", +CHECK-NEXT: "4e1635": "19:1", +CHECK-NEXT: "4e1690": "17:5" CHECK-NEXT: } CHECK-NEXT: } CHECK-NEXT: } CHECK-NEXT:} - diff --git a/tools/sancov/sancov.cpp b/tools/sancov/sancov.cpp index 9645183c2bc..e0cc8a7cf84 100644 --- a/tools/sancov/sancov.cpp +++ b/tools/sancov/sancov.cpp @@ -31,6 +31,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/MD5.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" @@ -284,87 +285,6 @@ static raw_ostream &operator<<(raw_ostream &OS, const CoverageStats &Stats) { return OS; } -// Helper for writing out JSON. Handles indents and commas using -// scope variables for objects and arrays. -class JSONWriter { -public: - JSONWriter(raw_ostream &Out) : OS(Out) {} - JSONWriter(const JSONWriter &) = delete; - ~JSONWriter() { OS << "\n"; } - - void operator<<(StringRef S) { printJSONStringLiteral(S, OS); } - - // Helper RAII class to output JSON objects. - class Object { - public: - Object(JSONWriter *W, raw_ostream &OS) : W(W), OS(OS) { - OS << "{"; - W->Indent++; - } - ~Object() { - W->Indent--; - OS << "\n"; - W->indent(); - OS << "}"; - } - - void key(StringRef Key) { - Index++; - if (Index > 0) - OS << ","; - OS << "\n"; - W->indent(); - printJSONStringLiteral(Key, OS); - OS << " : "; - } - - private: - JSONWriter *W; - raw_ostream &OS; - int Index = -1; - }; - - Object object() { return {this, OS}; } - - // Helper RAII class to output JSON arrays. - class Array { - public: - Array(raw_ostream &OS) : OS(OS) { OS << "["; } - ~Array() { OS << "]"; } - void next() { - Index++; - if (Index > 0) - OS << ", "; - } - - private: - raw_ostream &OS; - int Index = -1; - }; - - Array array() { return {OS}; } - -private: - void indent() { OS.indent(Indent * 2); } - - static void printJSONStringLiteral(StringRef S, raw_ostream &OS) { - if (S.find('"') == std::string::npos) { - OS << "\"" << S << "\""; - return; - } - OS << "\""; - for (char Ch : S.bytes()) { - if (Ch == '"') - OS << "\\"; - OS << Ch; - } - OS << "\""; - } - - raw_ostream &OS; - int Indent = 0; -}; - // Output symbolized information for coverage points in JSON. // Format: // { @@ -375,10 +295,9 @@ private: // } // } // } -static void operator<<(JSONWriter &W, +static void operator<<(json::OStream &W, const std::vector &Points) { // Group points by file. - auto ByFile(W.object()); std::map> PointsByFile; for (const auto &Point : Points) { for (const DILineInfo &Loc : Point.Locs) { @@ -388,10 +307,6 @@ static void operator<<(JSONWriter &W, for (const auto &P : PointsByFile) { std::string FileName = P.first; - ByFile.key(FileName); - - // Group points by function. - auto ByFn(W.object()); std::map> PointsByFn; for (auto PointPtr : P.second) { for (const DILineInfo &Loc : PointPtr->Locs) { @@ -399,54 +314,42 @@ static void operator<<(JSONWriter &W, } } - for (const auto &P : PointsByFn) { - std::string FunctionName = P.first; - std::set WrittenIds; - - ByFn.key(FunctionName); - - // Output : ":". - auto ById(W.object()); - for (const CoveragePoint *Point : P.second) { - for (const auto &Loc : Point->Locs) { - if (Loc.FileName != FileName || Loc.FunctionName != FunctionName) - continue; - if (WrittenIds.find(Point->Id) != WrittenIds.end()) - continue; - - WrittenIds.insert(Point->Id); - ById.key(Point->Id); - W << (utostr(Loc.Line) + ":" + utostr(Loc.Column)); - } + W.attributeObject(P.first, [&] { + // Group points by function. + for (const auto &P : PointsByFn) { + std::string FunctionName = P.first; + std::set WrittenIds; + + W.attributeObject(FunctionName, [&] { + for (const CoveragePoint *Point : P.second) { + for (const auto &Loc : Point->Locs) { + if (Loc.FileName != FileName || Loc.FunctionName != FunctionName) + continue; + if (WrittenIds.find(Point->Id) != WrittenIds.end()) + continue; + + // Output : ":". + WrittenIds.insert(Point->Id); + W.attribute(Point->Id, + (utostr(Loc.Line) + ":" + utostr(Loc.Column))); + } + } + }); } - } + }); } } -static void operator<<(JSONWriter &W, const SymbolizedCoverage &C) { - auto O(W.object()); - - { - O.key("covered-points"); - auto PointsArray(W.array()); - - for (const std::string &P : C.CoveredIds) { - PointsArray.next(); - W << P; - } - } - - { - if (!C.BinaryHash.empty()) { - O.key("binary-hash"); - W << C.BinaryHash; - } - } - - { - O.key("point-symbol-info"); - W << C.Points; - } +static void operator<<(json::OStream &W, const SymbolizedCoverage &C) { + W.object([&] { + W.attributeArray("covered-points", [&] { + for (const std::string &P : C.CoveredIds) { + W.value(P); + } + }); + W.attribute("binary-hash", C.BinaryHash); + W.attributeObject("point-symbol-info", [&] { W << C.Points; }); + }); } static std::string parseScalarString(yaml::Node *N) { @@ -1275,7 +1178,7 @@ int main(int Argc, char **Argv) { } case MergeAction: case SymbolizeAction: { // merge & symbolize are synonims. - JSONWriter W(outs()); + json::OStream W(outs(), 2); W << *Coverage; return 0; } -- 2.40.0