blob: f1f201fe7b6a243625c4af9f3678ebca9e6ba013 [file] [log] [blame]
Brian Osman53136aa2017-07-20 15:43:35 -04001/*
2 * Copyright 2017 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkChromeTracingTracer.h"
Brian Osman69fd0082017-08-09 09:25:39 -04009#include "SkJSONWriter.h"
Brian Osman53136aa2017-07-20 15:43:35 -040010#include "SkThreadID.h"
11#include "SkTraceEvent.h"
12#include "SkOSFile.h"
13#include "SkOSPath.h"
14#include "SkStream.h"
15
16#include <chrono>
17
Brian Osmanc7f26762017-08-16 10:31:29 -040018namespace {
19
20/**
21 * All events have a fixed block of information (TraceEvent), plus variable length payload:
22 * {TraceEvent} {TraceEventArgs} {Inline Payload}
23 */
24struct TraceEventArg {
25 uint8_t fArgType;
26 const char* fArgName;
27 uint64_t fArgValue;
28};
29
30// These fields are ordered to minimize size due to alignment. Argument types could be packed
31// better, but very few events have many arguments, so the net loss is pretty small.
32struct TraceEvent {
33 char fPhase;
34 uint8_t fNumArgs;
35 uint32_t fSize;
36
37 const char* fName;
Brian Osmanc7f26762017-08-16 10:31:29 -040038 // TODO: Merge fID and fClockEnd (never used together)
39 uint64_t fID;
40 uint64_t fClockBegin;
41 uint64_t fClockEnd;
42 SkThreadID fThreadID;
43
44 TraceEvent* next() {
45 return reinterpret_cast<TraceEvent*>(reinterpret_cast<char*>(this) + fSize);
46 }
47 TraceEventArg* args() {
48 return reinterpret_cast<TraceEventArg*>(this + 1);
49 }
50 char* stringTable() {
51 return reinterpret_cast<char*>(this->args() + fNumArgs);
52 }
53};
54
55}
56
Brian Osmanbc8150f2017-07-24 11:38:01 -040057SkChromeTracingTracer::SkChromeTracingTracer(const char* filename) : fFilename(filename) {
Brian Osmanc7f26762017-08-16 10:31:29 -040058 this->createBlock();
Brian Osmanbc8150f2017-07-24 11:38:01 -040059}
60
61SkChromeTracingTracer::~SkChromeTracingTracer() {
62 this->flush();
63}
64
Brian Osmanc7f26762017-08-16 10:31:29 -040065void SkChromeTracingTracer::createBlock() {
66 fCurBlock.fBlock = BlockPtr(new uint8_t[kBlockSize]);
67 fCurBlock.fEventsInBlock = 0;
68 fCurBlockUsed = 0;
Brian Osmanbc8150f2017-07-24 11:38:01 -040069}
70
Brian Osmanc7f26762017-08-16 10:31:29 -040071SkEventTracer::Handle SkChromeTracingTracer::appendEvent(const void* data, size_t size) {
72 SkASSERT(size > 0 && size <= kBlockSize);
73
Brian Osmanbc8150f2017-07-24 11:38:01 -040074 SkAutoMutexAcquire lock(fMutex);
Brian Osmanc7f26762017-08-16 10:31:29 -040075 if (fCurBlockUsed + size > kBlockSize) {
Brian Osmanbc8150f2017-07-24 11:38:01 -040076 fBlocks.push_back(std::move(fCurBlock));
Brian Osmanc7f26762017-08-16 10:31:29 -040077 this->createBlock();
Brian Osmanbc8150f2017-07-24 11:38:01 -040078 }
Brian Osmanc7f26762017-08-16 10:31:29 -040079 memcpy(fCurBlock.fBlock.get() + fCurBlockUsed, data, size);
80 Handle handle = reinterpret_cast<Handle>(fCurBlock.fBlock.get() + fCurBlockUsed);
81 fCurBlockUsed += size;
82 fCurBlock.fEventsInBlock++;
83 return handle;
Brian Osmanbc8150f2017-07-24 11:38:01 -040084}
85
Brian Osman53136aa2017-07-20 15:43:35 -040086SkEventTracer::Handle SkChromeTracingTracer::addTraceEvent(char phase,
87 const uint8_t* categoryEnabledFlag,
88 const char* name,
89 uint64_t id,
90 int numArgs,
91 const char** argNames,
92 const uint8_t* argTypes,
93 const uint64_t* argValues,
94 uint8_t flags) {
Brian Osmande6e5bf2017-07-21 15:30:14 -040095 // TODO: Respect flags (or assert). INSTANT events encode scope in flags, should be stored
96 // using "s" key in JSON. COPY flag should be supported or rejected.
97
Brian Osmanc7f26762017-08-16 10:31:29 -040098 // Figure out how much extra storage we need for copied strings
99 int size = static_cast<int>(sizeof(TraceEvent) + numArgs * sizeof(TraceEventArg));
Brian Osmanbc8150f2017-07-24 11:38:01 -0400100 for (int i = 0; i < numArgs; ++i) {
Brian Osmanbc8150f2017-07-24 11:38:01 -0400101 if (TRACE_VALUE_TYPE_COPY_STRING == argTypes[i]) {
102 skia::tracing_internals::TraceValueUnion value;
Brian Osmande6e5bf2017-07-21 15:30:14 -0400103 value.as_uint = argValues[i];
Brian Osmanc7f26762017-08-16 10:31:29 -0400104 size += strlen(value.as_string) + 1;
Brian Osman53136aa2017-07-20 15:43:35 -0400105 }
Brian Osman53136aa2017-07-20 15:43:35 -0400106 }
Brian Osmanbc8150f2017-07-24 11:38:01 -0400107
Brian Osman3fa10432018-10-24 13:22:32 -0400108 size = SkAlign8(size);
109
Brian Osmanc7f26762017-08-16 10:31:29 -0400110 SkSTArray<128, uint8_t, true> storage;
111 uint8_t* storagePtr = storage.push_back_n(size);
112
113 TraceEvent* traceEvent = reinterpret_cast<TraceEvent*>(storagePtr);
114 traceEvent->fPhase = phase;
115 traceEvent->fNumArgs = numArgs;
116 traceEvent->fSize = size;
117 traceEvent->fName = name;
Brian Osmanc7f26762017-08-16 10:31:29 -0400118 traceEvent->fID = id;
119 traceEvent->fClockBegin = std::chrono::steady_clock::now().time_since_epoch().count();
120 traceEvent->fClockEnd = 0;
121 traceEvent->fThreadID = SkGetThreadID();
122
123 TraceEventArg* traceEventArgs = traceEvent->args();
124 char* stringTableBase = traceEvent->stringTable();
125 char* stringTable = stringTableBase;
126 for (int i = 0; i < numArgs; ++i) {
127 traceEventArgs[i].fArgName = argNames[i];
128 traceEventArgs[i].fArgType = argTypes[i];
129 if (TRACE_VALUE_TYPE_COPY_STRING == argTypes[i]) {
130 // Just write an offset into the arguments array
131 traceEventArgs[i].fArgValue = stringTable - stringTableBase;
132
133 // Copy string into our buffer (and advance)
134 skia::tracing_internals::TraceValueUnion value;
135 value.as_uint = argValues[i];
136 while (*value.as_string) {
137 *stringTable++ = *value.as_string++;
138 }
139 *stringTable++ = 0;
140 } else {
141 traceEventArgs[i].fArgValue = argValues[i];
142 }
143 }
144
145 return this->appendEvent(storagePtr, size);
Brian Osman53136aa2017-07-20 15:43:35 -0400146}
147
148void SkChromeTracingTracer::updateTraceEventDuration(const uint8_t* categoryEnabledFlag,
149 const char* name,
150 SkEventTracer::Handle handle) {
Brian Osmanbc8150f2017-07-24 11:38:01 -0400151 // We could probably get away with not locking here, but let's be totally safe.
152 SkAutoMutexAcquire lock(fMutex);
153 TraceEvent* traceEvent = reinterpret_cast<TraceEvent*>(handle);
Brian Osmanc7f26762017-08-16 10:31:29 -0400154 traceEvent->fClockEnd = std::chrono::steady_clock::now().time_since_epoch().count();
Brian Osmanbc8150f2017-07-24 11:38:01 -0400155}
156
Brian Osmanc7f26762017-08-16 10:31:29 -0400157static void trace_value_to_json(SkJSONWriter* writer, uint64_t argValue, uint8_t argType,
158 const char* stringTableBase) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400159 skia::tracing_internals::TraceValueUnion value;
160 value.as_uint = argValue;
161
162 switch (argType) {
163 case TRACE_VALUE_TYPE_BOOL:
Brian Osman69fd0082017-08-09 09:25:39 -0400164 writer->appendBool(value.as_bool);
165 break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400166 case TRACE_VALUE_TYPE_UINT:
Brian Osman69fd0082017-08-09 09:25:39 -0400167 writer->appendU64(value.as_uint);
168 break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400169 case TRACE_VALUE_TYPE_INT:
Brian Osman69fd0082017-08-09 09:25:39 -0400170 writer->appendS64(value.as_int);
171 break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400172 case TRACE_VALUE_TYPE_DOUBLE:
Brian Osman69fd0082017-08-09 09:25:39 -0400173 writer->appendDouble(value.as_double);
174 break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400175 case TRACE_VALUE_TYPE_POINTER:
Brian Osman69fd0082017-08-09 09:25:39 -0400176 writer->appendPointer(value.as_pointer);
177 break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400178 case TRACE_VALUE_TYPE_STRING:
Brian Osman69fd0082017-08-09 09:25:39 -0400179 writer->appendString(value.as_string);
180 break;
Brian Osmanc7f26762017-08-16 10:31:29 -0400181 case TRACE_VALUE_TYPE_COPY_STRING:
182 writer->appendString(stringTableBase + value.as_uint);
183 break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400184 default:
Brian Osman69fd0082017-08-09 09:25:39 -0400185 writer->appendString("<unknown type>");
186 break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400187 }
188}
189
Brian Osmanb6f82122017-08-16 16:56:04 -0400190namespace {
191
192struct TraceEventSerializationState {
193 TraceEventSerializationState(uint64_t clockOffset)
194 : fClockOffset(clockOffset), fNextThreadID(0) {}
195
196 int getShortThreadID(SkThreadID id) {
197 if (int* shortIDPtr = fShortThreadIDMap.find(id)) {
198 return *shortIDPtr;
199 }
200 int shortID = fNextThreadID++;
201 fShortThreadIDMap.set(id, shortID);
202 return shortID;
203 }
204
205 uint64_t fClockOffset;
206 SkTHashMap<uint64_t, const char*> fBaseTypeResolver;
207 int fNextThreadID;
208 SkTHashMap<SkThreadID, int> fShortThreadIDMap;
209};
210
211}
Brian Osmanc7f26762017-08-16 10:31:29 -0400212
213static void trace_event_to_json(SkJSONWriter* writer, TraceEvent* traceEvent,
Brian Osmanb6f82122017-08-16 16:56:04 -0400214 TraceEventSerializationState* serializationState) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400215 // We track the original (creation time) "name" of each currently live object, so we can
216 // automatically insert "base_name" fields in object snapshot events.
Brian Osmanb6f82122017-08-16 16:56:04 -0400217 auto baseTypeResolver = &(serializationState->fBaseTypeResolver);
Brian Osmanc7f26762017-08-16 10:31:29 -0400218 if (TRACE_EVENT_PHASE_CREATE_OBJECT == traceEvent->fPhase) {
219 SkASSERT(nullptr == baseTypeResolver->find(traceEvent->fID));
220 baseTypeResolver->set(traceEvent->fID, traceEvent->fName);
221 } else if (TRACE_EVENT_PHASE_DELETE_OBJECT == traceEvent->fPhase) {
222 SkASSERT(nullptr != baseTypeResolver->find(traceEvent->fID));
223 baseTypeResolver->remove(traceEvent->fID);
Brian Osmanb6705c22017-08-01 10:23:38 -0400224 }
225
Brian Osman69fd0082017-08-09 09:25:39 -0400226 writer->beginObject();
227
Brian Osmanc7f26762017-08-16 10:31:29 -0400228 char phaseString[2] = { traceEvent->fPhase, 0 };
Brian Osman69fd0082017-08-09 09:25:39 -0400229 writer->appendString("ph", phaseString);
Brian Osmanc7f26762017-08-16 10:31:29 -0400230 writer->appendString("name", traceEvent->fName);
Brian Osmanc7f26762017-08-16 10:31:29 -0400231 if (0 != traceEvent->fID) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400232 // IDs are (almost) always pointers
Brian Osmanc7f26762017-08-16 10:31:29 -0400233 writer->appendPointer("id", reinterpret_cast<void*>(traceEvent->fID));
Brian Osmanb6705c22017-08-01 10:23:38 -0400234 }
Brian Osmanb6f82122017-08-16 16:56:04 -0400235
236 // Offset timestamps to reduce JSON length, then convert nanoseconds to microseconds
237 // (standard time unit for tracing JSON files).
238 uint64_t relativeTimestamp = static_cast<int64_t>(traceEvent->fClockBegin -
239 serializationState->fClockOffset);
240 writer->appendDoubleDigits("ts", static_cast<double>(relativeTimestamp) * 1E-3, 3);
Brian Osmanc7f26762017-08-16 10:31:29 -0400241 if (0 != traceEvent->fClockEnd) {
242 double dur = static_cast<double>(traceEvent->fClockEnd - traceEvent->fClockBegin) * 1E-3;
Brian Osmanb6f82122017-08-16 16:56:04 -0400243 writer->appendDoubleDigits("dur", dur, 3);
Brian Osmanbc8150f2017-07-24 11:38:01 -0400244 }
Brian Osmanb6f82122017-08-16 16:56:04 -0400245
246 writer->appendS64("tid", serializationState->getShortThreadID(traceEvent->fThreadID));
Brian Osmanbc8150f2017-07-24 11:38:01 -0400247 // Trace events *must* include a process ID, but for internal tools this isn't particularly
248 // important (and certainly not worth adding a cross-platform API to get it).
Brian Osman69fd0082017-08-09 09:25:39 -0400249 writer->appendS32("pid", 0);
Brian Osmanb6705c22017-08-01 10:23:38 -0400250
Brian Osmanc7f26762017-08-16 10:31:29 -0400251 if (traceEvent->fNumArgs) {
Brian Osman69fd0082017-08-09 09:25:39 -0400252 writer->beginObject("args");
Brian Osmanc7f26762017-08-16 10:31:29 -0400253 const char* stringTable = traceEvent->stringTable();
Brian Osman69fd0082017-08-09 09:25:39 -0400254 bool addedSnapshot = false;
Brian Osmanc7f26762017-08-16 10:31:29 -0400255 if (TRACE_EVENT_PHASE_SNAPSHOT_OBJECT == traceEvent->fPhase &&
256 baseTypeResolver->find(traceEvent->fID) &&
257 0 != strcmp(*baseTypeResolver->find(traceEvent->fID), traceEvent->fName)) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400258 // Special handling for snapshots where the name differs from creation.
Brian Osman69fd0082017-08-09 09:25:39 -0400259 writer->beginObject("snapshot");
Brian Osmanc7f26762017-08-16 10:31:29 -0400260 writer->appendString("base_type", *baseTypeResolver->find(traceEvent->fID));
Brian Osman69fd0082017-08-09 09:25:39 -0400261 addedSnapshot = true;
Brian Osmanb6705c22017-08-01 10:23:38 -0400262 }
Brian Osman69fd0082017-08-09 09:25:39 -0400263
Brian Osmanc7f26762017-08-16 10:31:29 -0400264 for (int i = 0; i < traceEvent->fNumArgs; ++i) {
265 const TraceEventArg* arg = traceEvent->args() + i;
Brian Osman69fd0082017-08-09 09:25:39 -0400266 // TODO: Skip '#'
Brian Osmanc7f26762017-08-16 10:31:29 -0400267 writer->appendName(arg->fArgName);
Brian Osman69fd0082017-08-09 09:25:39 -0400268
Brian Osmanc7f26762017-08-16 10:31:29 -0400269 if (arg->fArgName && '#' == arg->fArgName[0]) {
Brian Osman69fd0082017-08-09 09:25:39 -0400270 writer->beginObject();
271 writer->appendName("id_ref");
Brian Osmanc7f26762017-08-16 10:31:29 -0400272 trace_value_to_json(writer, arg->fArgValue, arg->fArgType, stringTable);
Brian Osman69fd0082017-08-09 09:25:39 -0400273 writer->endObject();
274 } else {
Brian Osmanc7f26762017-08-16 10:31:29 -0400275 trace_value_to_json(writer, arg->fArgValue, arg->fArgType, stringTable);
Brian Osman69fd0082017-08-09 09:25:39 -0400276 }
277 }
278
279 if (addedSnapshot) {
280 writer->endObject();
281 }
282
283 writer->endObject();
Brian Osmanbc8150f2017-07-24 11:38:01 -0400284 }
Brian Osman69fd0082017-08-09 09:25:39 -0400285
286 writer->endObject();
Brian Osman53136aa2017-07-20 15:43:35 -0400287}
288
Brian Osman53136aa2017-07-20 15:43:35 -0400289void SkChromeTracingTracer::flush() {
Brian Osmanbc8150f2017-07-24 11:38:01 -0400290 SkAutoMutexAcquire lock(fMutex);
291
Brian Osman53136aa2017-07-20 15:43:35 -0400292 SkString dirname = SkOSPath::Dirname(fFilename.c_str());
Brian Osmanbc8150f2017-07-24 11:38:01 -0400293 if (!dirname.isEmpty() && !sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) {
Brian Osman53136aa2017-07-20 15:43:35 -0400294 if (!sk_mkdir(dirname.c_str())) {
295 SkDebugf("Failed to create directory.");
296 }
297 }
Brian Osman69fd0082017-08-09 09:25:39 -0400298
299 SkFILEWStream fileStream(fFilename.c_str());
300 SkJSONWriter writer(&fileStream, SkJSONWriter::Mode::kFast);
301 writer.beginArray();
302
Brian Osmanb6f82122017-08-16 16:56:04 -0400303 uint64_t clockOffset = 0;
304 if (fBlocks.count() > 0) {
305 clockOffset = reinterpret_cast<TraceEvent*>(fBlocks[0].fBlock.get())->fClockBegin;
306 } else if (fCurBlock.fEventsInBlock > 0) {
307 clockOffset = reinterpret_cast<TraceEvent*>(fCurBlock.fBlock.get())->fClockBegin;
308 }
309
310 TraceEventSerializationState serializationState(clockOffset);
Brian Osman69fd0082017-08-09 09:25:39 -0400311
Brian Osmanc7f26762017-08-16 10:31:29 -0400312 auto event_block_to_json = [](SkJSONWriter* writer, const TraceEventBlock& block,
Brian Osmanb6f82122017-08-16 16:56:04 -0400313 TraceEventSerializationState* serializationState) {
Brian Osmanc7f26762017-08-16 10:31:29 -0400314 TraceEvent* traceEvent = reinterpret_cast<TraceEvent*>(block.fBlock.get());
315 for (int i = 0; i < block.fEventsInBlock; ++i) {
Brian Osmanb6f82122017-08-16 16:56:04 -0400316 trace_event_to_json(writer, traceEvent, serializationState);
Brian Osmanc7f26762017-08-16 10:31:29 -0400317 traceEvent = traceEvent->next();
Brian Osman69fd0082017-08-09 09:25:39 -0400318 }
Brian Osmanc7f26762017-08-16 10:31:29 -0400319 };
Brian Osman69fd0082017-08-09 09:25:39 -0400320
Brian Osmanc7f26762017-08-16 10:31:29 -0400321 for (int i = 0; i < fBlocks.count(); ++i) {
Brian Osmanb6f82122017-08-16 16:56:04 -0400322 event_block_to_json(&writer, fBlocks[i], &serializationState);
Brian Osman69fd0082017-08-09 09:25:39 -0400323 }
Brian Osmanb6f82122017-08-16 16:56:04 -0400324 event_block_to_json(&writer, fCurBlock, &serializationState);
Brian Osman69fd0082017-08-09 09:25:39 -0400325
326 writer.endArray();
327 writer.flush();
328 fileStream.flush();
Brian Osman53136aa2017-07-20 15:43:35 -0400329}