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