diff options
author | Brian Osman <brianosman@google.com> | 2017-08-09 09:25:39 -0400 |
---|---|---|
committer | Skia Commit-Bot <skia-commit-bot@chromium.org> | 2017-08-09 13:46:24 +0000 |
commit | 69fd008199989c5a5a96f992dcaa4089b63f490f (patch) | |
tree | 3bf276d326699010cb5e172d986e90e409ca7efd /tools/trace | |
parent | bca3b8d8e0eaca73e25ed44fd20d4a6a0376e4ce (diff) |
Added SkJSONWriter
This is a stand-alone helper class for writing properly
structured JSON to an SkWStream. It currently solves two
problems (although this CL only uses it in one context):
1) Performance. Writing out JSON this way is about 10x
faster than using JSONCPP. For the large amounts of data
generated by the tracing system, that's a big win.
2) Makes it easy to emit structured JSON from code that's
not fully centralized. We'd like to spit out JSON that
describes a GrContext, GrGpu, GrCaps, etc... Doing that
with simple string manipulation is complex, and spreads
this logic over all those functions. Using JSONCPP adds
yet another (large) third party library dependency (that
we only build into our own tools right now).
This went through several revisions. I originally planned
it as a stateful SkString wrapper, so the user could just
build their JSON as a string. That's O(N^2), though,
because SkString grows by a (small) constant amount. Even
using a better growth strategy still means needing RAM
for all the resulting text, which is usually pointless.
This version has a constant memory cost, so writing huge
amounts of JSON to disk (tracing a long DM run can emit
100's of MBs) doesn't stress resources.
Bug: skia:
Change-Id: Ia716524b246db0f97d332da60d2ce9903069e748
Reviewed-on: https://skia-review.googlesource.com/31204
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@chromium.org>
Diffstat (limited to 'tools/trace')
-rw-r--r-- | tools/trace/SkChromeTracingTracer.cpp | 134 | ||||
-rw-r--r-- | tools/trace/SkChromeTracingTracer.h | 5 |
2 files changed, 75 insertions, 64 deletions
diff --git a/tools/trace/SkChromeTracingTracer.cpp b/tools/trace/SkChromeTracingTracer.cpp index fd9e8d1c3d..ced4f74fa9 100644 --- a/tools/trace/SkChromeTracingTracer.cpp +++ b/tools/trace/SkChromeTracingTracer.cpp @@ -6,6 +6,7 @@ */ #include "SkChromeTracingTracer.h" +#include "SkJSONWriter.h" #include "SkThreadID.h" #include "SkTraceEvent.h" #include "SkOSFile.h" @@ -86,31 +87,38 @@ void SkChromeTracingTracer::updateTraceEventDuration(const uint8_t* categoryEnab traceEvent->fClockEnd = std::chrono::high_resolution_clock::now().time_since_epoch().count(); } -static Json::Value trace_value_to_json(uint64_t argValue, uint8_t argType) { +static void trace_value_to_json(SkJSONWriter* writer, uint64_t argValue, uint8_t argType) { skia::tracing_internals::TraceValueUnion value; value.as_uint = argValue; switch (argType) { case TRACE_VALUE_TYPE_BOOL: - return Json::Value(value.as_bool); + writer->appendBool(value.as_bool); + break; case TRACE_VALUE_TYPE_UINT: - return Json::Value(static_cast<Json::UInt64>(value.as_uint)); + writer->appendU64(value.as_uint); + break; case TRACE_VALUE_TYPE_INT: - return Json::Value(static_cast<Json::Int64>(value.as_uint)); + writer->appendS64(value.as_int); + break; case TRACE_VALUE_TYPE_DOUBLE: - return Json::Value(value.as_double); + writer->appendDouble(value.as_double); + break; case TRACE_VALUE_TYPE_POINTER: - return Json::Value(SkStringPrintf("%p", value.as_pointer).c_str()); + writer->appendPointer(value.as_pointer); + break; case TRACE_VALUE_TYPE_STRING: case TRACE_VALUE_TYPE_COPY_STRING: - return Json::Value(value.as_string); + writer->appendString(value.as_string); + break; default: - return Json::Value("<unknown type>"); + writer->appendString("<unknown type>"); + break; } } -Json::Value SkChromeTracingTracer::traceEventToJson(const TraceEvent& traceEvent, - BaseTypeResolver* baseTypeResolver) { +void SkChromeTracingTracer::traceEventToJson(SkJSONWriter* writer, const TraceEvent& traceEvent, + BaseTypeResolver* baseTypeResolver) { // We track the original (creation time) "name" of each currently live object, so we can // automatically insert "base_name" fields in object snapshot events. if (TRACE_EVENT_PHASE_CREATE_OBJECT == traceEvent.fPhase) { @@ -121,89 +129,91 @@ Json::Value SkChromeTracingTracer::traceEventToJson(const TraceEvent& traceEvent baseTypeResolver->remove(traceEvent.fID); } - Json::Value json; + writer->beginObject(); + char phaseString[2] = { traceEvent.fPhase, 0 }; - json["ph"] = phaseString; - json["name"] = traceEvent.fName; - json["cat"] = traceEvent.fCategory; + writer->appendString("ph", phaseString); + writer->appendString("name", traceEvent.fName); + writer->appendString("cat", traceEvent.fCategory); if (0 != traceEvent.fID) { // IDs are (almost) always pointers - json["id"] = SkStringPrintf("%p", traceEvent.fID).c_str(); + writer->appendPointer("id", reinterpret_cast<void*>(traceEvent.fID)); } // Convert nanoseconds to microseconds (standard time unit for tracing JSON files) - json["ts"] = static_cast<double>(traceEvent.fClockBegin) * 1E-3; + writer->appendDouble("ts", static_cast<double>(traceEvent.fClockBegin) * 1E-3); if (0 != traceEvent.fClockEnd) { - json["dur"] = static_cast<double>(traceEvent.fClockEnd - traceEvent.fClockBegin) * 1E-3; + double dur = static_cast<double>(traceEvent.fClockEnd - traceEvent.fClockBegin) * 1E-3; + writer->appendDouble("dur", dur); } - json["tid"] = static_cast<Json::Int64>(traceEvent.fThreadID); + writer->appendS64("tid", traceEvent.fThreadID); // Trace events *must* include a process ID, but for internal tools this isn't particularly // important (and certainly not worth adding a cross-platform API to get it). - json["pid"] = 0; + writer->appendS32("pid", 0); if (traceEvent.fNumArgs) { - Json::Value args; + writer->beginObject("args"); + + bool addedSnapshot = false; + if (TRACE_EVENT_PHASE_SNAPSHOT_OBJECT == traceEvent.fPhase && + baseTypeResolver->find(traceEvent.fID) && + 0 != strcmp(*baseTypeResolver->find(traceEvent.fID), traceEvent.fName)) { + // Special handling for snapshots where the name differs from creation. + writer->beginObject("snapshot"); + writer->appendString("base_type", *baseTypeResolver->find(traceEvent.fID)); + addedSnapshot = true; + } + for (int i = 0; i < traceEvent.fNumArgs; ++i) { - Json::Value argValue = trace_value_to_json(traceEvent.fArgValues[i], - traceEvent.fArgTypes[i]); + // TODO: Skip '#' + writer->appendName(traceEvent.fArgNames[i]); + if (traceEvent.fArgNames[i] && '#' == traceEvent.fArgNames[i][0]) { - // Interpret #foo as an ID reference - Json::Value idRef; - idRef["id_ref"] = argValue; - args[traceEvent.fArgNames[i] + 1] = idRef; + writer->beginObject(); + writer->appendName("id_ref"); + trace_value_to_json(writer, traceEvent.fArgValues[i], traceEvent.fArgTypes[i]); + writer->endObject(); } else { - args[traceEvent.fArgNames[i]] = argValue; + trace_value_to_json(writer, traceEvent.fArgValues[i], traceEvent.fArgTypes[i]); } } - if (TRACE_EVENT_PHASE_SNAPSHOT_OBJECT == traceEvent.fPhase && - baseTypeResolver->find(traceEvent.fID) && - 0 != strcmp(*baseTypeResolver->find(traceEvent.fID), traceEvent.fName)) { - // Special handling for snapshots where the name differs from creation. - // We start with args = { "snapshot": "Object info" } - - // Inject base_type. args = { "snapshot": "Object Info", "base_type": "BaseFoo" } - args["base_type"] = *baseTypeResolver->find(traceEvent.fID); - - // Wrap this up in a new dict, again keyed by "snapshot". The inner "snapshot" is now - // arbitrary, but we don't have a better name (and the outer key *must* be snapshot). - // snapshot = { "snapshot": { "snapshot": "Object Info", "base_type": "BaseFoo" } } - Json::Value snapshot; - snapshot["snapshot"] = args; - - // Now insert that whole thing as the event's args. - // { "name": "DerivedFoo", "id": "0x12345678, ... - // "args": { "snapshot": { "snapshot": "Object Info", "base_type": "BaseFoo" } } - // }, ... - json["args"] = snapshot; - } else { - json["args"] = args; + + if (addedSnapshot) { + writer->endObject(); } + + writer->endObject(); } - return json; + + writer->endObject(); } void SkChromeTracingTracer::flush() { SkAutoMutexAcquire lock(fMutex); - Json::Value root(Json::arrayValue); + SkString dirname = SkOSPath::Dirname(fFilename.c_str()); + if (!dirname.isEmpty() && !sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) { + if (!sk_mkdir(dirname.c_str())) { + SkDebugf("Failed to create directory."); + } + } + + SkFILEWStream fileStream(fFilename.c_str()); + SkJSONWriter writer(&fileStream, SkJSONWriter::Mode::kFast); + writer.beginArray(); + BaseTypeResolver baseTypeResolver; for (int i = 0; i < fBlocks.count(); ++i) { for (int j = 0; j < kEventsPerBlock; ++j) { - root.append(this->traceEventToJson(fBlocks[i][j], &baseTypeResolver)); + this->traceEventToJson(&writer, fBlocks[i][j], &baseTypeResolver); } } for (int i = 0; i < fEventsInCurBlock; ++i) { - root.append(this->traceEventToJson(fCurBlock[i], &baseTypeResolver)); + this->traceEventToJson(&writer, fCurBlock[i], &baseTypeResolver); } - SkString dirname = SkOSPath::Dirname(fFilename.c_str()); - if (!dirname.isEmpty() && !sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) { - if (!sk_mkdir(dirname.c_str())) { - SkDebugf("Failed to create directory."); - } - } - SkFILEWStream stream(fFilename.c_str()); - stream.writeText(Json::FastWriter().write(root).c_str()); - stream.flush(); + writer.endArray(); + writer.flush(); + fileStream.flush(); } diff --git a/tools/trace/SkChromeTracingTracer.h b/tools/trace/SkChromeTracingTracer.h index 987031641b..c6411bb69f 100644 --- a/tools/trace/SkChromeTracingTracer.h +++ b/tools/trace/SkChromeTracingTracer.h @@ -10,11 +10,12 @@ #include "SkEventTracer.h" #include "SkEventTracingPriv.h" -#include "SkJSONCPP.h" #include "SkSpinlock.h" #include "SkString.h" #include "SkTHash.h" +class SkJSONWriter; + /** * A SkEventTracer implementation that logs events to JSON for viewing with chrome://tracing. */ @@ -80,7 +81,7 @@ private: typedef SkTHashMap<uint64_t, const char*> BaseTypeResolver; TraceEvent* appendEvent(const TraceEvent&); - Json::Value traceEventToJson(const TraceEvent&, BaseTypeResolver* baseTypeResolver); + void traceEventToJson(SkJSONWriter*, const TraceEvent&, BaseTypeResolver* baseTypeResolver); SkString fFilename; SkSpinlock fMutex; |