/* * Copyright 2017 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkChromeTracingTracer.h" #include "SkThreadID.h" #include "SkTraceEvent.h" #include "SkOSFile.h" #include "SkOSPath.h" #include "SkStream.h" #include SkChromeTracingTracer::SkChromeTracingTracer(const char* filename) : fFilename(filename) { fCurBlock = this->createBlock(); fEventsInCurBlock = 0; } SkChromeTracingTracer::~SkChromeTracingTracer() { this->flush(); } SkChromeTracingTracer::BlockPtr SkChromeTracingTracer::createBlock() { return BlockPtr(new TraceEvent[kEventsPerBlock]); } SkChromeTracingTracer::TraceEvent* SkChromeTracingTracer::appendEvent( const TraceEvent& traceEvent) { SkAutoMutexAcquire lock(fMutex); if (fEventsInCurBlock >= kEventsPerBlock) { fBlocks.push_back(std::move(fCurBlock)); fCurBlock = this->createBlock(); fEventsInCurBlock = 0; } fCurBlock[fEventsInCurBlock] = traceEvent; return &fCurBlock[fEventsInCurBlock++]; } SkEventTracer::Handle SkChromeTracingTracer::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) { // TODO: Respect flags (or assert). INSTANT events encode scope in flags, should be stored // using "s" key in JSON. COPY flag should be supported or rejected. TraceEvent traceEvent; traceEvent.fPhase = phase; traceEvent.fNumArgs = numArgs; traceEvent.fName = name; traceEvent.fCategory = fCategories.getCategoryGroupName(categoryEnabledFlag); traceEvent.fClockBegin = std::chrono::high_resolution_clock::now().time_since_epoch().count(); traceEvent.fClockEnd = 0; traceEvent.fThreadID = SkGetThreadID(); for (int i = 0; i < numArgs; ++i) { traceEvent.fArgNames[i] = argNames[i]; traceEvent.fArgTypes[i] = argTypes[i]; if (TRACE_VALUE_TYPE_COPY_STRING == argTypes[i]) { skia::tracing_internals::TraceValueUnion value; value.as_uint = argValues[i]; value.as_string = SkStrDup(value.as_string); traceEvent.fArgValues[i] = value.as_uint; } else { traceEvent.fArgValues[i] = argValues[i]; } } return reinterpret_cast(this->appendEvent(traceEvent)); } void SkChromeTracingTracer::updateTraceEventDuration(const uint8_t* categoryEnabledFlag, const char* name, SkEventTracer::Handle handle) { // We could probably get away with not locking here, but let's be totally safe. SkAutoMutexAcquire lock(fMutex); TraceEvent* traceEvent = reinterpret_cast(handle); traceEvent->fClockEnd = std::chrono::high_resolution_clock::now().time_since_epoch().count(); } Json::Value SkChromeTracingTracer::traceEventToJson(const TraceEvent& traceEvent) { Json::Value json; char phaseString[2] = { traceEvent.fPhase, 0 }; json["ph"] = phaseString; json["name"] = traceEvent.fName; json["cat"] = traceEvent.fCategory; json["ts"] = static_cast(traceEvent.fClockBegin) * 1E-3; if (0 != traceEvent.fClockEnd) { json["dur"] = static_cast(traceEvent.fClockEnd - traceEvent.fClockBegin) * 1E-3; } json["tid"] = static_cast(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; if (traceEvent.fNumArgs) { Json::Value args; skia::tracing_internals::TraceValueUnion value; for (int i = 0; i < traceEvent.fNumArgs; ++i) { value.as_uint = traceEvent.fArgValues[i]; switch (traceEvent.fArgTypes[i]) { case TRACE_VALUE_TYPE_BOOL: args[traceEvent.fArgNames[i]] = value.as_bool; break; case TRACE_VALUE_TYPE_UINT: args[traceEvent.fArgNames[i]] = static_cast(value.as_uint); break; case TRACE_VALUE_TYPE_INT: args[traceEvent.fArgNames[i]] = static_cast(value.as_uint); break; case TRACE_VALUE_TYPE_DOUBLE: args[traceEvent.fArgNames[i]] = value.as_double; break; case TRACE_VALUE_TYPE_POINTER: args[traceEvent.fArgNames[i]] = value.as_pointer; break; case TRACE_VALUE_TYPE_STRING: case TRACE_VALUE_TYPE_COPY_STRING: args[traceEvent.fArgNames[i]] = value.as_string; break; default: args[traceEvent.fArgNames[i]] = ""; break; } } json["args"] = args; } return json; } void SkChromeTracingTracer::flush() { SkAutoMutexAcquire lock(fMutex); Json::Value root(Json::arrayValue); for (int i = 0; i < fBlocks.count(); ++i) { for (int j = 0; j < kEventsPerBlock; ++j) { root.append(this->traceEventToJson(fBlocks[i][j])); } } for (int i = 0; i < fEventsInCurBlock; ++i) { root.append(this->traceEventToJson(fCurBlock[i])); } 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(); }