blob: 530c3c346b21407564e30624ead3ce2dbc1c780f [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "include/core/SkStream.h"
9#include "include/private/SkThreadID.h"
10#include "src/core/SkOSFile.h"
11#include "src/core/SkTraceEvent.h"
12#include "src/utils/SkJSONWriter.h"
13#include "src/utils/SkOSPath.h"
14#include "tools/trace/ChromeTracingTracer.h"
Brian Osman53136aa2017-07-20 15:43:35 -040015
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 {
Mike Kleineb678fc2019-03-20 12:01:47 -050025 uint8_t fArgType;
Brian Osmanc7f26762017-08-16 10:31:29 -040026 const char* fArgName;
Mike Kleineb678fc2019-03-20 12:01:47 -050027 uint64_t fArgValue;
Brian Osmanc7f26762017-08-16 10:31:29 -040028};
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 {
Mike Kleineb678fc2019-03-20 12:01:47 -050033 char fPhase;
34 uint8_t fNumArgs;
Brian Osmanc7f26762017-08-16 10:31:29 -040035 uint32_t fSize;
36
37 const char* fName;
Brian Osmanc7f26762017-08-16 10:31:29 -040038 // TODO: Merge fID and fClockEnd (never used together)
Mike Kleineb678fc2019-03-20 12:01:47 -050039 uint64_t fID;
40 uint64_t fClockBegin;
41 uint64_t fClockEnd;
Brian Osmanc7f26762017-08-16 10:31:29 -040042 SkThreadID fThreadID;
43
44 TraceEvent* next() {
45 return reinterpret_cast<TraceEvent*>(reinterpret_cast<char*>(this) + fSize);
46 }
Mike Kleineb678fc2019-03-20 12:01:47 -050047 TraceEventArg* args() { return reinterpret_cast<TraceEventArg*>(this + 1); }
48 char* stringTable() { return reinterpret_cast<char*>(this->args() + fNumArgs); }
Brian Osmanc7f26762017-08-16 10:31:29 -040049};
50
Mike Kleineb678fc2019-03-20 12:01:47 -050051} // namespace
Brian Osmanc7f26762017-08-16 10:31:29 -040052
Mike Kleineb678fc2019-03-20 12:01:47 -050053ChromeTracingTracer::ChromeTracingTracer(const char* filename) : fFilename(filename) {
Brian Osmanc7f26762017-08-16 10:31:29 -040054 this->createBlock();
Brian Osmanbc8150f2017-07-24 11:38:01 -040055}
56
Mike Kleineb678fc2019-03-20 12:01:47 -050057ChromeTracingTracer::~ChromeTracingTracer() { this->flush(); }
Brian Osmanbc8150f2017-07-24 11:38:01 -040058
Mike Kleineb678fc2019-03-20 12:01:47 -050059void ChromeTracingTracer::createBlock() {
60 fCurBlock.fBlock = BlockPtr(new uint8_t[kBlockSize]);
Brian Osmanc7f26762017-08-16 10:31:29 -040061 fCurBlock.fEventsInBlock = 0;
Mike Kleineb678fc2019-03-20 12:01:47 -050062 fCurBlockUsed = 0;
Brian Osmanbc8150f2017-07-24 11:38:01 -040063}
64
Mike Kleineb678fc2019-03-20 12:01:47 -050065SkEventTracer::Handle ChromeTracingTracer::appendEvent(const void* data, size_t size) {
Brian Osmanc7f26762017-08-16 10:31:29 -040066 SkASSERT(size > 0 && size <= kBlockSize);
67
Herb Derbya1b7be62019-05-09 16:59:18 -040068 SkAutoSpinlock lock(fMutex);
Brian Osmanc7f26762017-08-16 10:31:29 -040069 if (fCurBlockUsed + size > kBlockSize) {
Brian Osmanbc8150f2017-07-24 11:38:01 -040070 fBlocks.push_back(std::move(fCurBlock));
Brian Osmanc7f26762017-08-16 10:31:29 -040071 this->createBlock();
Brian Osmanbc8150f2017-07-24 11:38:01 -040072 }
Brian Osmanc7f26762017-08-16 10:31:29 -040073 memcpy(fCurBlock.fBlock.get() + fCurBlockUsed, data, size);
74 Handle handle = reinterpret_cast<Handle>(fCurBlock.fBlock.get() + fCurBlockUsed);
75 fCurBlockUsed += size;
76 fCurBlock.fEventsInBlock++;
77 return handle;
Brian Osmanbc8150f2017-07-24 11:38:01 -040078}
79
Mike Kleineb678fc2019-03-20 12:01:47 -050080SkEventTracer::Handle ChromeTracingTracer::addTraceEvent(char phase,
81 const uint8_t* categoryEnabledFlag,
82 const char* name,
83 uint64_t id,
84 int numArgs,
85 const char** argNames,
86 const uint8_t* argTypes,
87 const uint64_t* argValues,
88 uint8_t flags) {
Brian Osmande6e5bf2017-07-21 15:30:14 -040089 // TODO: Respect flags (or assert). INSTANT events encode scope in flags, should be stored
90 // using "s" key in JSON. COPY flag should be supported or rejected.
91
Brian Osmanc7f26762017-08-16 10:31:29 -040092 // Figure out how much extra storage we need for copied strings
93 int size = static_cast<int>(sizeof(TraceEvent) + numArgs * sizeof(TraceEventArg));
Brian Osmanbc8150f2017-07-24 11:38:01 -040094 for (int i = 0; i < numArgs; ++i) {
Brian Osmanbc8150f2017-07-24 11:38:01 -040095 if (TRACE_VALUE_TYPE_COPY_STRING == argTypes[i]) {
96 skia::tracing_internals::TraceValueUnion value;
Brian Osmande6e5bf2017-07-21 15:30:14 -040097 value.as_uint = argValues[i];
Brian Osmanc7f26762017-08-16 10:31:29 -040098 size += strlen(value.as_string) + 1;
Brian Osman53136aa2017-07-20 15:43:35 -040099 }
Brian Osman53136aa2017-07-20 15:43:35 -0400100 }
Brian Osmanbc8150f2017-07-24 11:38:01 -0400101
Brian Osman3fa10432018-10-24 13:22:32 -0400102 size = SkAlign8(size);
103
Brian Osmanc7f26762017-08-16 10:31:29 -0400104 SkSTArray<128, uint8_t, true> storage;
Mike Kleineb678fc2019-03-20 12:01:47 -0500105 uint8_t* storagePtr = storage.push_back_n(size);
Brian Osmanc7f26762017-08-16 10:31:29 -0400106
Mike Kleineb678fc2019-03-20 12:01:47 -0500107 TraceEvent* traceEvent = reinterpret_cast<TraceEvent*>(storagePtr);
108 traceEvent->fPhase = phase;
109 traceEvent->fNumArgs = numArgs;
110 traceEvent->fSize = size;
111 traceEvent->fName = name;
112 traceEvent->fID = id;
Brian Osmanc7f26762017-08-16 10:31:29 -0400113 traceEvent->fClockBegin = std::chrono::steady_clock::now().time_since_epoch().count();
Mike Kleineb678fc2019-03-20 12:01:47 -0500114 traceEvent->fClockEnd = 0;
115 traceEvent->fThreadID = SkGetThreadID();
Brian Osmanc7f26762017-08-16 10:31:29 -0400116
Mike Kleineb678fc2019-03-20 12:01:47 -0500117 TraceEventArg* traceEventArgs = traceEvent->args();
118 char* stringTableBase = traceEvent->stringTable();
119 char* stringTable = stringTableBase;
Brian Osmanc7f26762017-08-16 10:31:29 -0400120 for (int i = 0; i < numArgs; ++i) {
121 traceEventArgs[i].fArgName = argNames[i];
122 traceEventArgs[i].fArgType = argTypes[i];
123 if (TRACE_VALUE_TYPE_COPY_STRING == argTypes[i]) {
124 // Just write an offset into the arguments array
Mike Kleineb678fc2019-03-20 12:01:47 -0500125 traceEventArgs[i].fArgValue = stringTable - stringTableBase;
Brian Osmanc7f26762017-08-16 10:31:29 -0400126
127 // Copy string into our buffer (and advance)
128 skia::tracing_internals::TraceValueUnion value;
129 value.as_uint = argValues[i];
130 while (*value.as_string) {
131 *stringTable++ = *value.as_string++;
132 }
133 *stringTable++ = 0;
134 } else {
135 traceEventArgs[i].fArgValue = argValues[i];
136 }
137 }
138
139 return this->appendEvent(storagePtr, size);
Brian Osman53136aa2017-07-20 15:43:35 -0400140}
141
Mike Kleineb678fc2019-03-20 12:01:47 -0500142void ChromeTracingTracer::updateTraceEventDuration(const uint8_t* categoryEnabledFlag,
143 const char* name,
144 SkEventTracer::Handle handle) {
Brian Osmanbc8150f2017-07-24 11:38:01 -0400145 // We could probably get away with not locking here, but let's be totally safe.
Herb Derbya1b7be62019-05-09 16:59:18 -0400146 SkAutoSpinlock lock(fMutex);
147 TraceEvent* traceEvent = reinterpret_cast<TraceEvent*>(handle);
Mike Kleineb678fc2019-03-20 12:01:47 -0500148 traceEvent->fClockEnd = std::chrono::steady_clock::now().time_since_epoch().count();
Brian Osmanbc8150f2017-07-24 11:38:01 -0400149}
150
Mike Kleineb678fc2019-03-20 12:01:47 -0500151static void trace_value_to_json(SkJSONWriter* writer,
152 uint64_t argValue,
153 uint8_t argType,
154 const char* stringTableBase) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400155 skia::tracing_internals::TraceValueUnion value;
156 value.as_uint = argValue;
157
158 switch (argType) {
Mike Kleineb678fc2019-03-20 12:01:47 -0500159 case TRACE_VALUE_TYPE_BOOL: writer->appendBool(value.as_bool); break;
160 case TRACE_VALUE_TYPE_UINT: writer->appendU64(value.as_uint); break;
161 case TRACE_VALUE_TYPE_INT: writer->appendS64(value.as_int); break;
162 case TRACE_VALUE_TYPE_DOUBLE: writer->appendDouble(value.as_double); break;
163 case TRACE_VALUE_TYPE_POINTER: writer->appendPointer(value.as_pointer); break;
164 case TRACE_VALUE_TYPE_STRING: writer->appendString(value.as_string); break;
Brian Osmanc7f26762017-08-16 10:31:29 -0400165 case TRACE_VALUE_TYPE_COPY_STRING:
166 writer->appendString(stringTableBase + value.as_uint);
167 break;
Mike Kleineb678fc2019-03-20 12:01:47 -0500168 default: writer->appendString("<unknown type>"); break;
Brian Osmanb6705c22017-08-01 10:23:38 -0400169 }
170}
171
Brian Osmanb6f82122017-08-16 16:56:04 -0400172namespace {
173
174struct TraceEventSerializationState {
175 TraceEventSerializationState(uint64_t clockOffset)
176 : fClockOffset(clockOffset), fNextThreadID(0) {}
177
178 int getShortThreadID(SkThreadID id) {
179 if (int* shortIDPtr = fShortThreadIDMap.find(id)) {
180 return *shortIDPtr;
181 }
182 int shortID = fNextThreadID++;
183 fShortThreadIDMap.set(id, shortID);
184 return shortID;
185 }
186
Mike Kleineb678fc2019-03-20 12:01:47 -0500187 uint64_t fClockOffset;
Brian Osmanb6f82122017-08-16 16:56:04 -0400188 SkTHashMap<uint64_t, const char*> fBaseTypeResolver;
Mike Kleineb678fc2019-03-20 12:01:47 -0500189 int fNextThreadID;
190 SkTHashMap<SkThreadID, int> fShortThreadIDMap;
Brian Osmanb6f82122017-08-16 16:56:04 -0400191};
192
Mike Kleineb678fc2019-03-20 12:01:47 -0500193} // namespace
Brian Osmanc7f26762017-08-16 10:31:29 -0400194
Mike Kleineb678fc2019-03-20 12:01:47 -0500195static void trace_event_to_json(SkJSONWriter* writer,
196 TraceEvent* traceEvent,
Brian Osmanb6f82122017-08-16 16:56:04 -0400197 TraceEventSerializationState* serializationState) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400198 // We track the original (creation time) "name" of each currently live object, so we can
199 // automatically insert "base_name" fields in object snapshot events.
Brian Osmanb6f82122017-08-16 16:56:04 -0400200 auto baseTypeResolver = &(serializationState->fBaseTypeResolver);
Brian Osmanc7f26762017-08-16 10:31:29 -0400201 if (TRACE_EVENT_PHASE_CREATE_OBJECT == traceEvent->fPhase) {
202 SkASSERT(nullptr == baseTypeResolver->find(traceEvent->fID));
203 baseTypeResolver->set(traceEvent->fID, traceEvent->fName);
204 } else if (TRACE_EVENT_PHASE_DELETE_OBJECT == traceEvent->fPhase) {
205 SkASSERT(nullptr != baseTypeResolver->find(traceEvent->fID));
206 baseTypeResolver->remove(traceEvent->fID);
Brian Osmanb6705c22017-08-01 10:23:38 -0400207 }
208
Brian Osman69fd0082017-08-09 09:25:39 -0400209 writer->beginObject();
210
Mike Kleineb678fc2019-03-20 12:01:47 -0500211 char phaseString[2] = {traceEvent->fPhase, 0};
Brian Osman69fd0082017-08-09 09:25:39 -0400212 writer->appendString("ph", phaseString);
Brian Osmanc7f26762017-08-16 10:31:29 -0400213 writer->appendString("name", traceEvent->fName);
Brian Osmanc7f26762017-08-16 10:31:29 -0400214 if (0 != traceEvent->fID) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400215 // IDs are (almost) always pointers
Brian Osmanc7f26762017-08-16 10:31:29 -0400216 writer->appendPointer("id", reinterpret_cast<void*>(traceEvent->fID));
Brian Osmanb6705c22017-08-01 10:23:38 -0400217 }
Brian Osmanb6f82122017-08-16 16:56:04 -0400218
219 // Offset timestamps to reduce JSON length, then convert nanoseconds to microseconds
220 // (standard time unit for tracing JSON files).
Mike Kleineb678fc2019-03-20 12:01:47 -0500221 uint64_t relativeTimestamp =
222 static_cast<int64_t>(traceEvent->fClockBegin - serializationState->fClockOffset);
Brian Osmanb500ef72019-06-17 16:14:45 -0400223 writer->appendDouble("ts", static_cast<double>(relativeTimestamp) * 1E-3);
Brian Osmanc7f26762017-08-16 10:31:29 -0400224 if (0 != traceEvent->fClockEnd) {
225 double dur = static_cast<double>(traceEvent->fClockEnd - traceEvent->fClockBegin) * 1E-3;
Brian Osmanb500ef72019-06-17 16:14:45 -0400226 writer->appendDouble("dur", dur);
Brian Osmanbc8150f2017-07-24 11:38:01 -0400227 }
Brian Osmanb6f82122017-08-16 16:56:04 -0400228
229 writer->appendS64("tid", serializationState->getShortThreadID(traceEvent->fThreadID));
Brian Osmanbc8150f2017-07-24 11:38:01 -0400230 // Trace events *must* include a process ID, but for internal tools this isn't particularly
231 // important (and certainly not worth adding a cross-platform API to get it).
Brian Osman69fd0082017-08-09 09:25:39 -0400232 writer->appendS32("pid", 0);
Brian Osmanb6705c22017-08-01 10:23:38 -0400233
Brian Osmanc7f26762017-08-16 10:31:29 -0400234 if (traceEvent->fNumArgs) {
Brian Osman69fd0082017-08-09 09:25:39 -0400235 writer->beginObject("args");
Mike Kleineb678fc2019-03-20 12:01:47 -0500236 const char* stringTable = traceEvent->stringTable();
237 bool addedSnapshot = false;
Brian Osmanc7f26762017-08-16 10:31:29 -0400238 if (TRACE_EVENT_PHASE_SNAPSHOT_OBJECT == traceEvent->fPhase &&
Mike Kleineb678fc2019-03-20 12:01:47 -0500239 baseTypeResolver->find(traceEvent->fID) &&
240 0 != strcmp(*baseTypeResolver->find(traceEvent->fID), traceEvent->fName)) {
Brian Osmanb6705c22017-08-01 10:23:38 -0400241 // Special handling for snapshots where the name differs from creation.
Brian Osman69fd0082017-08-09 09:25:39 -0400242 writer->beginObject("snapshot");
Brian Osmanc7f26762017-08-16 10:31:29 -0400243 writer->appendString("base_type", *baseTypeResolver->find(traceEvent->fID));
Brian Osman69fd0082017-08-09 09:25:39 -0400244 addedSnapshot = true;
Brian Osmanb6705c22017-08-01 10:23:38 -0400245 }
Brian Osman69fd0082017-08-09 09:25:39 -0400246
Brian Osmanc7f26762017-08-16 10:31:29 -0400247 for (int i = 0; i < traceEvent->fNumArgs; ++i) {
248 const TraceEventArg* arg = traceEvent->args() + i;
Brian Osman69fd0082017-08-09 09:25:39 -0400249 // TODO: Skip '#'
Brian Osmanc7f26762017-08-16 10:31:29 -0400250 writer->appendName(arg->fArgName);
Brian Osman69fd0082017-08-09 09:25:39 -0400251
Brian Osmanc7f26762017-08-16 10:31:29 -0400252 if (arg->fArgName && '#' == arg->fArgName[0]) {
Brian Osman69fd0082017-08-09 09:25:39 -0400253 writer->beginObject();
254 writer->appendName("id_ref");
Brian Osmanc7f26762017-08-16 10:31:29 -0400255 trace_value_to_json(writer, arg->fArgValue, arg->fArgType, stringTable);
Brian Osman69fd0082017-08-09 09:25:39 -0400256 writer->endObject();
257 } else {
Brian Osmanc7f26762017-08-16 10:31:29 -0400258 trace_value_to_json(writer, arg->fArgValue, arg->fArgType, stringTable);
Brian Osman69fd0082017-08-09 09:25:39 -0400259 }
260 }
261
262 if (addedSnapshot) {
263 writer->endObject();
264 }
265
266 writer->endObject();
Brian Osmanbc8150f2017-07-24 11:38:01 -0400267 }
Brian Osman69fd0082017-08-09 09:25:39 -0400268
269 writer->endObject();
Brian Osman53136aa2017-07-20 15:43:35 -0400270}
271
Mike Kleineb678fc2019-03-20 12:01:47 -0500272void ChromeTracingTracer::flush() {
Herb Derbya1b7be62019-05-09 16:59:18 -0400273 SkAutoSpinlock lock(fMutex);
Brian Osmanbc8150f2017-07-24 11:38:01 -0400274
Brian Osman53136aa2017-07-20 15:43:35 -0400275 SkString dirname = SkOSPath::Dirname(fFilename.c_str());
Brian Osmanbc8150f2017-07-24 11:38:01 -0400276 if (!dirname.isEmpty() && !sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) {
Brian Osman53136aa2017-07-20 15:43:35 -0400277 if (!sk_mkdir(dirname.c_str())) {
278 SkDebugf("Failed to create directory.");
279 }
280 }
Brian Osman69fd0082017-08-09 09:25:39 -0400281
282 SkFILEWStream fileStream(fFilename.c_str());
Mike Kleineb678fc2019-03-20 12:01:47 -0500283 SkJSONWriter writer(&fileStream, SkJSONWriter::Mode::kFast);
Brian Osman69fd0082017-08-09 09:25:39 -0400284 writer.beginArray();
285
Brian Osmanb6f82122017-08-16 16:56:04 -0400286 uint64_t clockOffset = 0;
287 if (fBlocks.count() > 0) {
288 clockOffset = reinterpret_cast<TraceEvent*>(fBlocks[0].fBlock.get())->fClockBegin;
289 } else if (fCurBlock.fEventsInBlock > 0) {
290 clockOffset = reinterpret_cast<TraceEvent*>(fCurBlock.fBlock.get())->fClockBegin;
291 }
292
293 TraceEventSerializationState serializationState(clockOffset);
Brian Osman69fd0082017-08-09 09:25:39 -0400294
Mike Kleineb678fc2019-03-20 12:01:47 -0500295 auto event_block_to_json = [](SkJSONWriter* writer,
296 const TraceEventBlock& block,
Brian Osmanb6f82122017-08-16 16:56:04 -0400297 TraceEventSerializationState* serializationState) {
Brian Osmanc7f26762017-08-16 10:31:29 -0400298 TraceEvent* traceEvent = reinterpret_cast<TraceEvent*>(block.fBlock.get());
299 for (int i = 0; i < block.fEventsInBlock; ++i) {
Brian Osmanb6f82122017-08-16 16:56:04 -0400300 trace_event_to_json(writer, traceEvent, serializationState);
Brian Osmanc7f26762017-08-16 10:31:29 -0400301 traceEvent = traceEvent->next();
Brian Osman69fd0082017-08-09 09:25:39 -0400302 }
Brian Osmanc7f26762017-08-16 10:31:29 -0400303 };
Brian Osman69fd0082017-08-09 09:25:39 -0400304
Brian Osmanc7f26762017-08-16 10:31:29 -0400305 for (int i = 0; i < fBlocks.count(); ++i) {
Brian Osmanb6f82122017-08-16 16:56:04 -0400306 event_block_to_json(&writer, fBlocks[i], &serializationState);
Brian Osman69fd0082017-08-09 09:25:39 -0400307 }
Brian Osmanb6f82122017-08-16 16:56:04 -0400308 event_block_to_json(&writer, fCurBlock, &serializationState);
Brian Osman69fd0082017-08-09 09:25:39 -0400309
310 writer.endArray();
311 writer.flush();
312 fileStream.flush();
Brian Osman53136aa2017-07-20 15:43:35 -0400313}