xref: /aosp_15_r20/external/skia/tools/trace/ChromeTracingTracer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2017 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkThreadID.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkOSFile.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkTraceEvent.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkJSONWriter.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkOSPath.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "tools/trace/ChromeTracingTracer.h"
15*c8dee2aaSAndroid Build Coastguard Worker 
16*c8dee2aaSAndroid Build Coastguard Worker #include <chrono>
17*c8dee2aaSAndroid Build Coastguard Worker 
18*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
19*c8dee2aaSAndroid Build Coastguard Worker 
20*c8dee2aaSAndroid Build Coastguard Worker namespace {
21*c8dee2aaSAndroid Build Coastguard Worker 
22*c8dee2aaSAndroid Build Coastguard Worker /**
23*c8dee2aaSAndroid Build Coastguard Worker  * All events have a fixed block of information (TraceEvent), plus variable length payload:
24*c8dee2aaSAndroid Build Coastguard Worker  * {TraceEvent} {TraceEventArgs} {Inline Payload}
25*c8dee2aaSAndroid Build Coastguard Worker  */
26*c8dee2aaSAndroid Build Coastguard Worker struct TraceEventArg {
27*c8dee2aaSAndroid Build Coastguard Worker     uint8_t     fArgType;
28*c8dee2aaSAndroid Build Coastguard Worker     const char* fArgName;
29*c8dee2aaSAndroid Build Coastguard Worker     uint64_t    fArgValue;
30*c8dee2aaSAndroid Build Coastguard Worker };
31*c8dee2aaSAndroid Build Coastguard Worker 
32*c8dee2aaSAndroid Build Coastguard Worker // These fields are ordered to minimize size due to alignment. Argument types could be packed
33*c8dee2aaSAndroid Build Coastguard Worker // better, but very few events have many arguments, so the net loss is pretty small.
34*c8dee2aaSAndroid Build Coastguard Worker struct TraceEvent {
35*c8dee2aaSAndroid Build Coastguard Worker     char     fPhase;
36*c8dee2aaSAndroid Build Coastguard Worker     uint8_t  fNumArgs;
37*c8dee2aaSAndroid Build Coastguard Worker     uint32_t fSize;
38*c8dee2aaSAndroid Build Coastguard Worker 
39*c8dee2aaSAndroid Build Coastguard Worker     const char* fName;
40*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Merge fID and fClockEnd (never used together)
41*c8dee2aaSAndroid Build Coastguard Worker     uint64_t   fID;
42*c8dee2aaSAndroid Build Coastguard Worker     uint64_t   fClockBegin;
43*c8dee2aaSAndroid Build Coastguard Worker     uint64_t   fClockEnd;
44*c8dee2aaSAndroid Build Coastguard Worker     SkThreadID fThreadID;
45*c8dee2aaSAndroid Build Coastguard Worker 
next__anon75d6dbb40111::TraceEvent46*c8dee2aaSAndroid Build Coastguard Worker     TraceEvent* next() {
47*c8dee2aaSAndroid Build Coastguard Worker         return reinterpret_cast<TraceEvent*>(reinterpret_cast<char*>(this) + fSize);
48*c8dee2aaSAndroid Build Coastguard Worker     }
args__anon75d6dbb40111::TraceEvent49*c8dee2aaSAndroid Build Coastguard Worker     TraceEventArg* args() { return reinterpret_cast<TraceEventArg*>(this + 1); }
stringTable__anon75d6dbb40111::TraceEvent50*c8dee2aaSAndroid Build Coastguard Worker     char*          stringTable() { return reinterpret_cast<char*>(this->args() + fNumArgs); }
51*c8dee2aaSAndroid Build Coastguard Worker };
52*c8dee2aaSAndroid Build Coastguard Worker 
53*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
54*c8dee2aaSAndroid Build Coastguard Worker 
ChromeTracingTracer(const char * filename)55*c8dee2aaSAndroid Build Coastguard Worker ChromeTracingTracer::ChromeTracingTracer(const char* filename) : fFilename(filename) {
56*c8dee2aaSAndroid Build Coastguard Worker     this->createBlock();
57*c8dee2aaSAndroid Build Coastguard Worker }
58*c8dee2aaSAndroid Build Coastguard Worker 
~ChromeTracingTracer()59*c8dee2aaSAndroid Build Coastguard Worker ChromeTracingTracer::~ChromeTracingTracer() { this->flush(); }
60*c8dee2aaSAndroid Build Coastguard Worker 
createBlock()61*c8dee2aaSAndroid Build Coastguard Worker void ChromeTracingTracer::createBlock() {
62*c8dee2aaSAndroid Build Coastguard Worker     fCurBlock.fBlock         = BlockPtr(new uint8_t[kBlockSize]);
63*c8dee2aaSAndroid Build Coastguard Worker     fCurBlock.fEventsInBlock = 0;
64*c8dee2aaSAndroid Build Coastguard Worker     fCurBlockUsed            = 0;
65*c8dee2aaSAndroid Build Coastguard Worker }
66*c8dee2aaSAndroid Build Coastguard Worker 
appendEvent(const void * data,size_t size)67*c8dee2aaSAndroid Build Coastguard Worker SkEventTracer::Handle ChromeTracingTracer::appendEvent(const void* data, size_t size) {
68*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(size > 0 && size <= kBlockSize);
69*c8dee2aaSAndroid Build Coastguard Worker 
70*c8dee2aaSAndroid Build Coastguard Worker     SkAutoSpinlock lock(fMutex);
71*c8dee2aaSAndroid Build Coastguard Worker     if (fCurBlockUsed + size > kBlockSize) {
72*c8dee2aaSAndroid Build Coastguard Worker         fBlocks.push_back(std::move(fCurBlock));
73*c8dee2aaSAndroid Build Coastguard Worker         this->createBlock();
74*c8dee2aaSAndroid Build Coastguard Worker     }
75*c8dee2aaSAndroid Build Coastguard Worker     memcpy(fCurBlock.fBlock.get() + fCurBlockUsed, data, size);
76*c8dee2aaSAndroid Build Coastguard Worker     Handle handle = reinterpret_cast<Handle>(fCurBlock.fBlock.get() + fCurBlockUsed);
77*c8dee2aaSAndroid Build Coastguard Worker     fCurBlockUsed += size;
78*c8dee2aaSAndroid Build Coastguard Worker     fCurBlock.fEventsInBlock++;
79*c8dee2aaSAndroid Build Coastguard Worker     return handle;
80*c8dee2aaSAndroid Build Coastguard Worker }
81*c8dee2aaSAndroid Build Coastguard Worker 
addTraceEvent(char phase,const uint8_t * categoryEnabledFlag,const char * name,uint64_t id,int numArgs,const char ** argNames,const uint8_t * argTypes,const uint64_t * argValues,uint8_t flags)82*c8dee2aaSAndroid Build Coastguard Worker SkEventTracer::Handle ChromeTracingTracer::addTraceEvent(char            phase,
83*c8dee2aaSAndroid Build Coastguard Worker                                                          const uint8_t*  categoryEnabledFlag,
84*c8dee2aaSAndroid Build Coastguard Worker                                                          const char*     name,
85*c8dee2aaSAndroid Build Coastguard Worker                                                          uint64_t        id,
86*c8dee2aaSAndroid Build Coastguard Worker                                                          int             numArgs,
87*c8dee2aaSAndroid Build Coastguard Worker                                                          const char**    argNames,
88*c8dee2aaSAndroid Build Coastguard Worker                                                          const uint8_t*  argTypes,
89*c8dee2aaSAndroid Build Coastguard Worker                                                          const uint64_t* argValues,
90*c8dee2aaSAndroid Build Coastguard Worker                                                          uint8_t         flags) {
91*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Respect flags (or assert). INSTANT events encode scope in flags, should be stored
92*c8dee2aaSAndroid Build Coastguard Worker     // using "s" key in JSON. COPY flag should be supported or rejected.
93*c8dee2aaSAndroid Build Coastguard Worker 
94*c8dee2aaSAndroid Build Coastguard Worker     // Figure out how much extra storage we need for copied strings
95*c8dee2aaSAndroid Build Coastguard Worker     int size = static_cast<int>(sizeof(TraceEvent) + numArgs * sizeof(TraceEventArg));
96*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < numArgs; ++i) {
97*c8dee2aaSAndroid Build Coastguard Worker         if (TRACE_VALUE_TYPE_COPY_STRING == argTypes[i]) {
98*c8dee2aaSAndroid Build Coastguard Worker             size += strlen(skia_private::TraceValueAsString(argValues[i])) + 1;
99*c8dee2aaSAndroid Build Coastguard Worker         }
100*c8dee2aaSAndroid Build Coastguard Worker     }
101*c8dee2aaSAndroid Build Coastguard Worker 
102*c8dee2aaSAndroid Build Coastguard Worker     size = SkAlign8(size);
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker     STArray<128, uint8_t, true> storage;
105*c8dee2aaSAndroid Build Coastguard Worker     uint8_t* storagePtr = storage.push_back_n(size);
106*c8dee2aaSAndroid Build Coastguard Worker 
107*c8dee2aaSAndroid Build Coastguard Worker     TraceEvent* traceEvent  = reinterpret_cast<TraceEvent*>(storagePtr);
108*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fPhase      = phase;
109*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fNumArgs    = numArgs;
110*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fSize       = size;
111*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fName       = name;
112*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fID         = id;
113*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fClockBegin = std::chrono::steady_clock::now().time_since_epoch().count();
114*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fClockEnd   = 0;
115*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fThreadID   = SkGetThreadID();
116*c8dee2aaSAndroid Build Coastguard Worker 
117*c8dee2aaSAndroid Build Coastguard Worker     TraceEventArg* traceEventArgs  = traceEvent->args();
118*c8dee2aaSAndroid Build Coastguard Worker     char*          stringTableBase = traceEvent->stringTable();
119*c8dee2aaSAndroid Build Coastguard Worker     char*          stringTable     = stringTableBase;
120*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < numArgs; ++i) {
121*c8dee2aaSAndroid Build Coastguard Worker         traceEventArgs[i].fArgName = argNames[i];
122*c8dee2aaSAndroid Build Coastguard Worker         traceEventArgs[i].fArgType = argTypes[i];
123*c8dee2aaSAndroid Build Coastguard Worker         if (TRACE_VALUE_TYPE_COPY_STRING == argTypes[i]) {
124*c8dee2aaSAndroid Build Coastguard Worker             // Just write an offset into the arguments array
125*c8dee2aaSAndroid Build Coastguard Worker             traceEventArgs[i].fArgValue = stringTable - stringTableBase;
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker             // Copy string into our buffer (and advance)
128*c8dee2aaSAndroid Build Coastguard Worker             const char* valueStr = skia_private::TraceValueAsString(argValues[i]);
129*c8dee2aaSAndroid Build Coastguard Worker             while (*valueStr) {
130*c8dee2aaSAndroid Build Coastguard Worker                 *stringTable++ = *valueStr++;
131*c8dee2aaSAndroid Build Coastguard Worker             }
132*c8dee2aaSAndroid Build Coastguard Worker             *stringTable++ = 0;
133*c8dee2aaSAndroid Build Coastguard Worker         } else {
134*c8dee2aaSAndroid Build Coastguard Worker             traceEventArgs[i].fArgValue = argValues[i];
135*c8dee2aaSAndroid Build Coastguard Worker         }
136*c8dee2aaSAndroid Build Coastguard Worker     }
137*c8dee2aaSAndroid Build Coastguard Worker 
138*c8dee2aaSAndroid Build Coastguard Worker     return this->appendEvent(storagePtr, size);
139*c8dee2aaSAndroid Build Coastguard Worker }
140*c8dee2aaSAndroid Build Coastguard Worker 
updateTraceEventDuration(const uint8_t * categoryEnabledFlag,const char * name,SkEventTracer::Handle handle)141*c8dee2aaSAndroid Build Coastguard Worker void ChromeTracingTracer::updateTraceEventDuration(const uint8_t*        categoryEnabledFlag,
142*c8dee2aaSAndroid Build Coastguard Worker                                                    const char*           name,
143*c8dee2aaSAndroid Build Coastguard Worker                                                    SkEventTracer::Handle handle) {
144*c8dee2aaSAndroid Build Coastguard Worker     // We could probably get away with not locking here, but let's be totally safe.
145*c8dee2aaSAndroid Build Coastguard Worker     SkAutoSpinlock lock(fMutex);
146*c8dee2aaSAndroid Build Coastguard Worker     TraceEvent*    traceEvent = reinterpret_cast<TraceEvent*>(handle);
147*c8dee2aaSAndroid Build Coastguard Worker     traceEvent->fClockEnd         = std::chrono::steady_clock::now().time_since_epoch().count();
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker 
trace_value_to_json(SkJSONWriter * writer,uint64_t argValue,uint8_t argType,const char * stringTableBase)150*c8dee2aaSAndroid Build Coastguard Worker static void trace_value_to_json(SkJSONWriter* writer,
151*c8dee2aaSAndroid Build Coastguard Worker                                 uint64_t      argValue,
152*c8dee2aaSAndroid Build Coastguard Worker                                 uint8_t       argType,
153*c8dee2aaSAndroid Build Coastguard Worker                                 const char*   stringTableBase) {
154*c8dee2aaSAndroid Build Coastguard Worker     switch (argType) {
155*c8dee2aaSAndroid Build Coastguard Worker         case TRACE_VALUE_TYPE_BOOL:   writer->appendBool(argValue); break;
156*c8dee2aaSAndroid Build Coastguard Worker         case TRACE_VALUE_TYPE_UINT:   writer->appendU64(argValue); break;
157*c8dee2aaSAndroid Build Coastguard Worker         case TRACE_VALUE_TYPE_INT:    writer->appendS64(static_cast<int64_t>(argValue)); break;
158*c8dee2aaSAndroid Build Coastguard Worker         case TRACE_VALUE_TYPE_DOUBLE: writer->appendDouble(sk_bit_cast<double>(argValue)); break;
159*c8dee2aaSAndroid Build Coastguard Worker         case TRACE_VALUE_TYPE_POINTER:
160*c8dee2aaSAndroid Build Coastguard Worker             writer->appendPointer(skia_private::TraceValueAsPointer(argValue));
161*c8dee2aaSAndroid Build Coastguard Worker             break;
162*c8dee2aaSAndroid Build Coastguard Worker         case TRACE_VALUE_TYPE_STRING:
163*c8dee2aaSAndroid Build Coastguard Worker             writer->appendCString(skia_private::TraceValueAsString(argValue));
164*c8dee2aaSAndroid Build Coastguard Worker             break;
165*c8dee2aaSAndroid Build Coastguard Worker         case TRACE_VALUE_TYPE_COPY_STRING:
166*c8dee2aaSAndroid Build Coastguard Worker             // See addTraceEvent(), the value in _COPY_STRING events is replaced with an offset
167*c8dee2aaSAndroid Build Coastguard Worker             writer->appendCString(stringTableBase + argValue);
168*c8dee2aaSAndroid Build Coastguard Worker             break;
169*c8dee2aaSAndroid Build Coastguard Worker         default: writer->appendNString("<unknown type>"); break;
170*c8dee2aaSAndroid Build Coastguard Worker     }
171*c8dee2aaSAndroid Build Coastguard Worker }
172*c8dee2aaSAndroid Build Coastguard Worker 
173*c8dee2aaSAndroid Build Coastguard Worker namespace {
174*c8dee2aaSAndroid Build Coastguard Worker 
175*c8dee2aaSAndroid Build Coastguard Worker struct TraceEventSerializationState {
TraceEventSerializationState__anon75d6dbb40211::TraceEventSerializationState176*c8dee2aaSAndroid Build Coastguard Worker     TraceEventSerializationState(uint64_t clockOffset)
177*c8dee2aaSAndroid Build Coastguard Worker             : fClockOffset(clockOffset), fNextThreadID(0) {}
178*c8dee2aaSAndroid Build Coastguard Worker 
getShortThreadID__anon75d6dbb40211::TraceEventSerializationState179*c8dee2aaSAndroid Build Coastguard Worker     int getShortThreadID(SkThreadID id) {
180*c8dee2aaSAndroid Build Coastguard Worker         if (int* shortIDPtr = fShortThreadIDMap.find(id)) {
181*c8dee2aaSAndroid Build Coastguard Worker             return *shortIDPtr;
182*c8dee2aaSAndroid Build Coastguard Worker         }
183*c8dee2aaSAndroid Build Coastguard Worker         int shortID = fNextThreadID++;
184*c8dee2aaSAndroid Build Coastguard Worker         fShortThreadIDMap.set(id, shortID);
185*c8dee2aaSAndroid Build Coastguard Worker         return shortID;
186*c8dee2aaSAndroid Build Coastguard Worker     }
187*c8dee2aaSAndroid Build Coastguard Worker 
188*c8dee2aaSAndroid Build Coastguard Worker     uint64_t                        fClockOffset;
189*c8dee2aaSAndroid Build Coastguard Worker     THashMap<uint64_t, const char*> fBaseTypeResolver;
190*c8dee2aaSAndroid Build Coastguard Worker     int                             fNextThreadID;
191*c8dee2aaSAndroid Build Coastguard Worker     THashMap<SkThreadID, int>       fShortThreadIDMap;
192*c8dee2aaSAndroid Build Coastguard Worker };
193*c8dee2aaSAndroid Build Coastguard Worker 
194*c8dee2aaSAndroid Build Coastguard Worker }  // namespace
195*c8dee2aaSAndroid Build Coastguard Worker 
trace_event_to_json(SkJSONWriter * writer,TraceEvent * traceEvent,TraceEventSerializationState * serializationState)196*c8dee2aaSAndroid Build Coastguard Worker static void trace_event_to_json(SkJSONWriter*                 writer,
197*c8dee2aaSAndroid Build Coastguard Worker                                 TraceEvent*                   traceEvent,
198*c8dee2aaSAndroid Build Coastguard Worker                                 TraceEventSerializationState* serializationState) {
199*c8dee2aaSAndroid Build Coastguard Worker     // We track the original (creation time) "name" of each currently live object, so we can
200*c8dee2aaSAndroid Build Coastguard Worker     // automatically insert "base_name" fields in object snapshot events.
201*c8dee2aaSAndroid Build Coastguard Worker     auto baseTypeResolver = &(serializationState->fBaseTypeResolver);
202*c8dee2aaSAndroid Build Coastguard Worker     if (TRACE_EVENT_PHASE_CREATE_OBJECT == traceEvent->fPhase) {
203*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(nullptr == baseTypeResolver->find(traceEvent->fID));
204*c8dee2aaSAndroid Build Coastguard Worker         baseTypeResolver->set(traceEvent->fID, traceEvent->fName);
205*c8dee2aaSAndroid Build Coastguard Worker     } else if (TRACE_EVENT_PHASE_DELETE_OBJECT == traceEvent->fPhase) {
206*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(nullptr != baseTypeResolver->find(traceEvent->fID));
207*c8dee2aaSAndroid Build Coastguard Worker         baseTypeResolver->remove(traceEvent->fID);
208*c8dee2aaSAndroid Build Coastguard Worker     }
209*c8dee2aaSAndroid Build Coastguard Worker 
210*c8dee2aaSAndroid Build Coastguard Worker     writer->beginObject();
211*c8dee2aaSAndroid Build Coastguard Worker 
212*c8dee2aaSAndroid Build Coastguard Worker     writer->appendString("ph", &traceEvent->fPhase, 1);
213*c8dee2aaSAndroid Build Coastguard Worker     writer->appendCString("name", traceEvent->fName);
214*c8dee2aaSAndroid Build Coastguard Worker     if (0 != traceEvent->fID) {
215*c8dee2aaSAndroid Build Coastguard Worker         // IDs are (almost) always pointers
216*c8dee2aaSAndroid Build Coastguard Worker         writer->appendPointer("id", reinterpret_cast<void*>(traceEvent->fID));
217*c8dee2aaSAndroid Build Coastguard Worker     }
218*c8dee2aaSAndroid Build Coastguard Worker 
219*c8dee2aaSAndroid Build Coastguard Worker     // Offset timestamps to reduce JSON length, then convert nanoseconds to microseconds
220*c8dee2aaSAndroid Build Coastguard Worker     // (standard time unit for tracing JSON files).
221*c8dee2aaSAndroid Build Coastguard Worker     uint64_t relativeTimestamp =
222*c8dee2aaSAndroid Build Coastguard Worker             static_cast<int64_t>(traceEvent->fClockBegin - serializationState->fClockOffset);
223*c8dee2aaSAndroid Build Coastguard Worker     writer->appendDouble("ts", static_cast<double>(relativeTimestamp) * 1E-3);
224*c8dee2aaSAndroid Build Coastguard Worker     if (0 != traceEvent->fClockEnd) {
225*c8dee2aaSAndroid Build Coastguard Worker         double dur = static_cast<double>(traceEvent->fClockEnd - traceEvent->fClockBegin) * 1E-3;
226*c8dee2aaSAndroid Build Coastguard Worker         writer->appendDouble("dur", dur);
227*c8dee2aaSAndroid Build Coastguard Worker     }
228*c8dee2aaSAndroid Build Coastguard Worker 
229*c8dee2aaSAndroid Build Coastguard Worker     writer->appendS64("tid", serializationState->getShortThreadID(traceEvent->fThreadID));
230*c8dee2aaSAndroid Build Coastguard Worker     // Trace events *must* include a process ID, but for internal tools this isn't particularly
231*c8dee2aaSAndroid Build Coastguard Worker     // important (and certainly not worth adding a cross-platform API to get it).
232*c8dee2aaSAndroid Build Coastguard Worker     writer->appendS32("pid", 0);
233*c8dee2aaSAndroid Build Coastguard Worker 
234*c8dee2aaSAndroid Build Coastguard Worker     if (traceEvent->fNumArgs) {
235*c8dee2aaSAndroid Build Coastguard Worker         writer->beginObject("args");
236*c8dee2aaSAndroid Build Coastguard Worker         const char* stringTable   = traceEvent->stringTable();
237*c8dee2aaSAndroid Build Coastguard Worker         bool        addedSnapshot = false;
238*c8dee2aaSAndroid Build Coastguard Worker         if (TRACE_EVENT_PHASE_SNAPSHOT_OBJECT == traceEvent->fPhase &&
239*c8dee2aaSAndroid Build Coastguard Worker             baseTypeResolver->find(traceEvent->fID) &&
240*c8dee2aaSAndroid Build Coastguard Worker             0 != strcmp(*baseTypeResolver->find(traceEvent->fID), traceEvent->fName)) {
241*c8dee2aaSAndroid Build Coastguard Worker             // Special handling for snapshots where the name differs from creation.
242*c8dee2aaSAndroid Build Coastguard Worker             writer->beginObject("snapshot");
243*c8dee2aaSAndroid Build Coastguard Worker             writer->appendCString("base_type", *baseTypeResolver->find(traceEvent->fID));
244*c8dee2aaSAndroid Build Coastguard Worker             addedSnapshot = true;
245*c8dee2aaSAndroid Build Coastguard Worker         }
246*c8dee2aaSAndroid Build Coastguard Worker 
247*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < traceEvent->fNumArgs; ++i) {
248*c8dee2aaSAndroid Build Coastguard Worker             const TraceEventArg* arg = traceEvent->args() + i;
249*c8dee2aaSAndroid Build Coastguard Worker             // TODO: Skip '#'
250*c8dee2aaSAndroid Build Coastguard Worker             writer->appendName(arg->fArgName);
251*c8dee2aaSAndroid Build Coastguard Worker 
252*c8dee2aaSAndroid Build Coastguard Worker             if (arg->fArgName && '#' == arg->fArgName[0]) {
253*c8dee2aaSAndroid Build Coastguard Worker                 writer->beginObject();
254*c8dee2aaSAndroid Build Coastguard Worker                 writer->appendName("id_ref");
255*c8dee2aaSAndroid Build Coastguard Worker                 trace_value_to_json(writer, arg->fArgValue, arg->fArgType, stringTable);
256*c8dee2aaSAndroid Build Coastguard Worker                 writer->endObject();
257*c8dee2aaSAndroid Build Coastguard Worker             } else {
258*c8dee2aaSAndroid Build Coastguard Worker                 trace_value_to_json(writer, arg->fArgValue, arg->fArgType, stringTable);
259*c8dee2aaSAndroid Build Coastguard Worker             }
260*c8dee2aaSAndroid Build Coastguard Worker         }
261*c8dee2aaSAndroid Build Coastguard Worker 
262*c8dee2aaSAndroid Build Coastguard Worker         if (addedSnapshot) {
263*c8dee2aaSAndroid Build Coastguard Worker             writer->endObject();
264*c8dee2aaSAndroid Build Coastguard Worker         }
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker         writer->endObject();
267*c8dee2aaSAndroid Build Coastguard Worker     }
268*c8dee2aaSAndroid Build Coastguard Worker 
269*c8dee2aaSAndroid Build Coastguard Worker     writer->endObject();
270*c8dee2aaSAndroid Build Coastguard Worker }
271*c8dee2aaSAndroid Build Coastguard Worker 
flush()272*c8dee2aaSAndroid Build Coastguard Worker void ChromeTracingTracer::flush() {
273*c8dee2aaSAndroid Build Coastguard Worker     SkAutoSpinlock lock(fMutex);
274*c8dee2aaSAndroid Build Coastguard Worker 
275*c8dee2aaSAndroid Build Coastguard Worker     SkString dirname = SkOSPath::Dirname(fFilename.c_str());
276*c8dee2aaSAndroid Build Coastguard Worker     if (!dirname.isEmpty() && !sk_exists(dirname.c_str(), kWrite_SkFILE_Flag)) {
277*c8dee2aaSAndroid Build Coastguard Worker         if (!sk_mkdir(dirname.c_str())) {
278*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("Failed to create directory.");
279*c8dee2aaSAndroid Build Coastguard Worker         }
280*c8dee2aaSAndroid Build Coastguard Worker     }
281*c8dee2aaSAndroid Build Coastguard Worker 
282*c8dee2aaSAndroid Build Coastguard Worker     SkFILEWStream fileStream(fFilename.c_str());
283*c8dee2aaSAndroid Build Coastguard Worker     SkJSONWriter  writer(&fileStream, SkJSONWriter::Mode::kFast);
284*c8dee2aaSAndroid Build Coastguard Worker     writer.beginArray();
285*c8dee2aaSAndroid Build Coastguard Worker 
286*c8dee2aaSAndroid Build Coastguard Worker     uint64_t clockOffset = 0;
287*c8dee2aaSAndroid Build Coastguard Worker     if (fBlocks.size() > 0) {
288*c8dee2aaSAndroid Build Coastguard Worker         clockOffset = reinterpret_cast<TraceEvent*>(fBlocks[0].fBlock.get())->fClockBegin;
289*c8dee2aaSAndroid Build Coastguard Worker     } else if (fCurBlock.fEventsInBlock > 0) {
290*c8dee2aaSAndroid Build Coastguard Worker         clockOffset = reinterpret_cast<TraceEvent*>(fCurBlock.fBlock.get())->fClockBegin;
291*c8dee2aaSAndroid Build Coastguard Worker     }
292*c8dee2aaSAndroid Build Coastguard Worker 
293*c8dee2aaSAndroid Build Coastguard Worker     TraceEventSerializationState serializationState(clockOffset);
294*c8dee2aaSAndroid Build Coastguard Worker 
295*c8dee2aaSAndroid Build Coastguard Worker     auto event_block_to_json = [](SkJSONWriter*                 writer,
296*c8dee2aaSAndroid Build Coastguard Worker                                   const TraceEventBlock&        block,
297*c8dee2aaSAndroid Build Coastguard Worker                                   TraceEventSerializationState* serializationState) {
298*c8dee2aaSAndroid Build Coastguard Worker         TraceEvent* traceEvent = reinterpret_cast<TraceEvent*>(block.fBlock.get());
299*c8dee2aaSAndroid Build Coastguard Worker         for (int i = 0; i < block.fEventsInBlock; ++i) {
300*c8dee2aaSAndroid Build Coastguard Worker             trace_event_to_json(writer, traceEvent, serializationState);
301*c8dee2aaSAndroid Build Coastguard Worker             traceEvent = traceEvent->next();
302*c8dee2aaSAndroid Build Coastguard Worker         }
303*c8dee2aaSAndroid Build Coastguard Worker     };
304*c8dee2aaSAndroid Build Coastguard Worker 
305*c8dee2aaSAndroid Build Coastguard Worker     for (int i = 0; i < fBlocks.size(); ++i) {
306*c8dee2aaSAndroid Build Coastguard Worker         event_block_to_json(&writer, fBlocks[i], &serializationState);
307*c8dee2aaSAndroid Build Coastguard Worker     }
308*c8dee2aaSAndroid Build Coastguard Worker     event_block_to_json(&writer, fCurBlock, &serializationState);
309*c8dee2aaSAndroid Build Coastguard Worker 
310*c8dee2aaSAndroid Build Coastguard Worker     writer.endArray();
311*c8dee2aaSAndroid Build Coastguard Worker     writer.flush();
312*c8dee2aaSAndroid Build Coastguard Worker     fileStream.flush();
313*c8dee2aaSAndroid Build Coastguard Worker }
314