aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/trace
diff options
context:
space:
mode:
authorGravatar Brian Osman <brianosman@google.com>2017-08-09 09:25:39 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-08-09 13:46:24 +0000
commit69fd008199989c5a5a96f992dcaa4089b63f490f (patch)
tree3bf276d326699010cb5e172d986e90e409ca7efd /tools/trace
parentbca3b8d8e0eaca73e25ed44fd20d4a6a0376e4ce (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.cpp134
-rw-r--r--tools/trace/SkChromeTracingTracer.h5
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;