1 /*
2 * Copyright 2022 Google LLC
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 "tools/trace/SkPerfettoTrace.h"
9
10 #include <fcntl.h>
11 #include <fstream>
12 #include "src/core/SkTraceEvent.h"
13 #include "src/core/SkTraceEventCommon.h"
14 #include "tools/flags/CommandLineFlags.h"
15
16 PERFETTO_TRACK_EVENT_STATIC_STORAGE();
17
18 static DEFINE_string(perfettoOutputDir, "./",
19 "Output directory for perfetto trace file(s).\n"
20 "Note: not the name of the file itself.\n"
21 "Will only have an effect if perfetto tracing is enabled. See --trace.");
22 static DEFINE_string(perfettoOutputFileName, "trace",
23 "Output file name (excluding path and file extension) for the perfetto trace"
24 "file.\nNote: When splitting trace files by benchmark (see "
25 "--splitPerfettoTracesByBenchmark), file name will be determined by the "
26 "benchmark name.\n"
27 "Will only have an effect if perfetto tracing is enabled. See --trace.");
28 static DEFINE_string(perfettoOutputFileExtension, ".perfetto-trace",
29 "Output file extension for perfetto trace file(s).\n"
30 "Will only have an effect if perfetto tracing is enabled. See --trace.");
31 static DEFINE_bool(longPerfettoTrace, false,
32 "Perfetto within Skia is optimized for tracing performance of 'smaller' traces"
33 "(~10 seconds or less). Set this flag to true to optimize for longer tracing"
34 "sessions.\n"
35 "Will only have an effect if perfetto tracing is enabled. See --trace.");
36
SkPerfettoTrace()37 SkPerfettoTrace::SkPerfettoTrace() {
38 fOutputPath = FLAGS_perfettoOutputDir[0];
39 fOutputFileExtension = FLAGS_perfettoOutputFileExtension[0];
40 this->openNewTracingSession(FLAGS_perfettoOutputFileName[0]);
41 }
42
~SkPerfettoTrace()43 SkPerfettoTrace::~SkPerfettoTrace() {
44 this->closeTracingSession();
45 }
46
openNewTracingSession(const std::string & baseFileName)47 void SkPerfettoTrace::openNewTracingSession(const std::string& baseFileName) {
48 perfetto::TracingInitArgs args;
49 /* Store the current tracing session's output file path as a member attribute so it can
50 * be referenced when closing a tracing session (needed for short traces where writing to
51 * the output file occurs at the end of all tracing). */
52 fCurrentSessionFullOutputPath = fOutputPath + baseFileName + fOutputFileExtension;
53
54 /* Enable using only the in-process backend (recording only within the app itself). This is as
55 * opposed to additionally including perfetto::kSystemBackend, which uses a Perfetto daemon. */
56 args.backends |= perfetto::kInProcessBackend;
57
58 if (FLAGS_longPerfettoTrace) {
59 /* Set the shared memory buffer size higher than the default of 256 KB to
60 reduce trace writer packet loss occurrences associated with larger traces. */
61 args.shmem_size_hint_kb = 2000;
62 }
63 perfetto::Tracing::Initialize(args);
64 perfetto::TrackEvent::Register();
65
66 // Set up event tracing configuration.
67 perfetto::protos::gen::TrackEventConfig track_event_cfg;
68 perfetto::TraceConfig cfg;
69
70 /* Set the central memory buffer size - will record up to this amount of data. */
71 cfg.add_buffers()->set_size_kb(32000);
72
73 if (FLAGS_longPerfettoTrace) {
74 /* Enable continuous file writing/"streaming mode" to output trace data throughout the
75 * program instead of one large dump at the end. */
76 cfg.set_write_into_file(true);
77 /* If set to a value other than the default, set how often trace data gets written to the
78 * output file. */
79 cfg.set_file_write_period_ms(5000);
80 /* Force periodic commitment of shared memory buffer pages to the central buffer.
81 * Helps prevent out-of-order event slices with long traces. */
82 cfg.set_flush_period_ms(10000);
83 }
84
85 auto* ds_cfg = cfg.add_data_sources()->mutable_config();
86 ds_cfg->set_name("track_event");
87 ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
88
89 // Begin a tracing session.
90 tracingSession = perfetto::Tracing::NewTrace();
91 if (FLAGS_longPerfettoTrace) {
92 fd = open(fCurrentSessionFullOutputPath.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0600);
93 tracingSession->Setup(cfg, fd);
94 } else {
95 tracingSession->Setup(cfg);
96 }
97 tracingSession->StartBlocking();
98 }
99
closeTracingSession()100 void SkPerfettoTrace::closeTracingSession() {
101 perfetto::TrackEvent::Flush();
102 tracingSession->StopBlocking();
103 if (!FLAGS_longPerfettoTrace) {
104 std::vector<char> trace_data(tracingSession->ReadTraceBlocking());
105 std::ofstream output;
106 output.open(fCurrentSessionFullOutputPath, std::ios::out | std::ios::binary);
107 output.write(&trace_data[0], trace_data.size());
108 output.close();
109 } else {
110 close(fd);
111 }
112 }
113
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)114 SkEventTracer::Handle SkPerfettoTrace::addTraceEvent(char phase,
115 const uint8_t* categoryEnabledFlag,
116 const char* name,
117 uint64_t id,
118 int numArgs,
119 const char** argNames,
120 const uint8_t* argTypes,
121 const uint64_t* argValues,
122 uint8_t flags) {
123 perfetto::DynamicCategory category{ this->getCategoryGroupName(categoryEnabledFlag) };
124 if (TRACE_EVENT_PHASE_COMPLETE == phase ||
125 TRACE_EVENT_PHASE_INSTANT == phase) {
126 switch (numArgs) {
127 case 0: {
128 this->triggerTraceEvent(categoryEnabledFlag, name);
129 break;
130 }
131 case 1: {
132 this->triggerTraceEvent(categoryEnabledFlag, name, argNames[0], argTypes[0],
133 argValues[0]);
134 break;
135 }
136 case 2: {
137 this->triggerTraceEvent(categoryEnabledFlag, name, argNames[0], argTypes[0],
138 argValues[0], argNames[1], argTypes[1], argValues[1]);
139 break;
140 }
141 }
142 } else if (TRACE_EVENT_PHASE_END == phase) {
143 TRACE_EVENT_END(category);
144 }
145
146 if (TRACE_EVENT_PHASE_INSTANT == phase) {
147 TRACE_EVENT_END(category);
148 }
149 return 0;
150 }
151
updateTraceEventDuration(const uint8_t * categoryEnabledFlag,const char * name,SkEventTracer::Handle handle)152 void SkPerfettoTrace::updateTraceEventDuration(const uint8_t* categoryEnabledFlag,
153 const char* name,
154 SkEventTracer::Handle handle) {
155 // This is only ever called from a scoped trace event, so we will just end the event.
156 perfetto::DynamicCategory category{ this->getCategoryGroupName(categoryEnabledFlag) };
157 TRACE_EVENT_END(category);
158 }
159
getCategoryGroupEnabled(const char * name)160 const uint8_t* SkPerfettoTrace::getCategoryGroupEnabled(const char* name) {
161 return fCategories.getCategoryGroupEnabled(name);
162 }
163
getCategoryGroupName(const uint8_t * categoryEnabledFlag)164 const char* SkPerfettoTrace::getCategoryGroupName(const uint8_t* categoryEnabledFlag) {
165 return fCategories.getCategoryGroupName(categoryEnabledFlag);
166 }
167
triggerTraceEvent(const uint8_t * categoryEnabledFlag,const char * eventName)168 void SkPerfettoTrace::triggerTraceEvent(const uint8_t* categoryEnabledFlag,
169 const char* eventName) {
170 perfetto::DynamicCategory category{ this->getCategoryGroupName(categoryEnabledFlag) };
171 TRACE_EVENT_BEGIN(category, nullptr, [&](perfetto::EventContext ctx) {
172 ctx.event()->set_name(eventName);
173 });
174 }
175
triggerTraceEvent(const uint8_t * categoryEnabledFlag,const char * eventName,const char * arg1Name,const uint8_t & arg1Type,const uint64_t & arg1Val)176 void SkPerfettoTrace::triggerTraceEvent(const uint8_t* categoryEnabledFlag, const char* eventName,
177 const char* arg1Name, const uint8_t& arg1Type,
178 const uint64_t& arg1Val) {
179 perfetto::DynamicCategory category{ this->getCategoryGroupName(categoryEnabledFlag) };
180 skia_private::TraceValueUnion value;
181 value.as_uint = arg1Val;
182
183 switch (arg1Type) {
184 case TRACE_VALUE_TYPE_BOOL: {
185 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, value.as_bool,
186 [&](perfetto::EventContext ctx) {
187 ctx.event()->set_name(eventName); });
188 break;
189 }
190 case TRACE_VALUE_TYPE_UINT: {
191 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, value.as_uint,
192 [&](perfetto::EventContext ctx) {
193 ctx.event()->set_name(eventName); });
194 break;
195 }
196 case TRACE_VALUE_TYPE_INT: {
197 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, value.as_int,
198 [&](perfetto::EventContext ctx) {
199 ctx.event()->set_name(eventName); });
200 break;
201 }
202 case TRACE_VALUE_TYPE_DOUBLE: {
203 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, value.as_double,
204 [&](perfetto::EventContext ctx) {
205 ctx.event()->set_name(eventName); });
206 break;
207 }
208 case TRACE_VALUE_TYPE_POINTER: {
209 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, value.as_pointer,
210 [&](perfetto::EventContext ctx) {
211 ctx.event()->set_name(eventName); });
212 break;
213 }
214 case TRACE_VALUE_TYPE_STRING: {
215 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, value.as_string,
216 [&](perfetto::EventContext ctx) {
217 ctx.event()->set_name(eventName); });
218 break;
219 }
220 case TRACE_VALUE_TYPE_COPY_STRING: {
221 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, value.as_string,
222 [&](perfetto::EventContext ctx) {
223 ctx.event()->set_name(eventName); });
224 break;
225 }
226 default: {
227 SkUNREACHABLE;
228 }
229 }
230 }
231
232 namespace {
233 /* Define a template to help handle all the possible TRACE_EVENT_BEGIN macro call
234 * combinations with 2 arguments of various types (defined in TraceValueUnion).
235 */
236 template <typename T>
begin_event_with_second_arg(const char * categoryName,const char * eventName,const char * arg1Name,T arg1Val,const char * arg2Name,const uint8_t & arg2Type,const uint64_t & arg2Val)237 void begin_event_with_second_arg(const char * categoryName, const char* eventName,
238 const char* arg1Name, T arg1Val, const char* arg2Name,
239 const uint8_t& arg2Type, const uint64_t& arg2Val) {
240 perfetto::DynamicCategory category{categoryName};
241 skia_private::TraceValueUnion value;
242 value.as_uint = arg2Val;
243
244 switch (arg2Type) {
245 case TRACE_VALUE_TYPE_BOOL: {
246 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, value.as_bool,
247 [&](perfetto::EventContext ctx) {
248 ctx.event()->set_name(eventName); });
249 break;
250 }
251 case TRACE_VALUE_TYPE_UINT: {
252 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, value.as_uint,
253 [&](perfetto::EventContext ctx) {
254 ctx.event()->set_name(eventName); });
255 break;
256 }
257 case TRACE_VALUE_TYPE_INT: {
258 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg1Name, value.as_int,
259 [&](perfetto::EventContext ctx) {
260 ctx.event()->set_name(eventName); });
261 break;
262 }
263 case TRACE_VALUE_TYPE_DOUBLE: {
264 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, value.as_double,
265 [&](perfetto::EventContext ctx) {
266 ctx.event()->set_name(eventName); });
267 break;
268 }
269 case TRACE_VALUE_TYPE_POINTER: {
270 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, value.as_pointer,
271 [&](perfetto::EventContext ctx) {
272 ctx.event()->set_name(eventName); });
273 break;
274 }
275 case TRACE_VALUE_TYPE_STRING: {
276 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, value.as_string,
277 [&](perfetto::EventContext ctx) {
278 ctx.event()->set_name(eventName); });
279 break;
280 }
281 case TRACE_VALUE_TYPE_COPY_STRING: {
282 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, value.as_string,
283 [&](perfetto::EventContext ctx) {
284 ctx.event()->set_name(eventName); });
285 break;
286 }
287 default: {
288 SkUNREACHABLE;
289 break;
290 }
291 }
292 }
293 } // anonymous namespace
294
triggerTraceEvent(const uint8_t * categoryEnabledFlag,const char * eventName,const char * arg1Name,const uint8_t & arg1Type,const uint64_t & arg1Val,const char * arg2Name,const uint8_t & arg2Type,const uint64_t & arg2Val)295 void SkPerfettoTrace::triggerTraceEvent(const uint8_t* categoryEnabledFlag,
296 const char* eventName, const char* arg1Name,
297 const uint8_t& arg1Type, const uint64_t& arg1Val,
298 const char* arg2Name, const uint8_t& arg2Type,
299 const uint64_t& arg2Val) {
300
301 const char * category{ this->getCategoryGroupName(categoryEnabledFlag) };
302 skia_private::TraceValueUnion value;
303 value.as_uint = arg1Val;
304
305 switch (arg1Type) {
306 case TRACE_VALUE_TYPE_BOOL: {
307 begin_event_with_second_arg(category, eventName, arg1Name, value.as_bool, arg2Name,
308 arg2Type, arg2Val);
309 break;
310 }
311 case TRACE_VALUE_TYPE_UINT: {
312 begin_event_with_second_arg(category, eventName, arg1Name, value.as_uint, arg2Name,
313 arg2Type, arg2Val);
314 break;
315 }
316 case TRACE_VALUE_TYPE_INT: {
317 begin_event_with_second_arg(category, eventName, arg1Name, value.as_int, arg2Name,
318 arg2Type, arg2Val);
319 break;
320 }
321 case TRACE_VALUE_TYPE_DOUBLE: {
322 begin_event_with_second_arg(category, eventName, arg1Name, value.as_double, arg2Name,
323 arg2Type, arg2Val);
324 break;
325 }
326 case TRACE_VALUE_TYPE_POINTER: {
327 begin_event_with_second_arg(category, eventName, arg1Name, value.as_pointer, arg2Name,
328 arg2Type, arg2Val);
329 break;
330 }
331 case TRACE_VALUE_TYPE_STRING: {
332 begin_event_with_second_arg(category, eventName, arg1Name, value.as_string, arg2Name,
333 arg2Type, arg2Val);
334 break;
335 }
336 case TRACE_VALUE_TYPE_COPY_STRING: {
337 begin_event_with_second_arg(category, eventName, arg1Name, value.as_string, arg2Name,
338 arg2Type, arg2Val);
339 break;
340 }
341 default: {
342 SkUNREACHABLE;
343 }
344 }
345 }
346
newTracingSection(const char * name)347 void SkPerfettoTrace::newTracingSection(const char* name) {
348 if (perfetto::Tracing::IsInitialized()) {
349 this->closeTracingSession();
350 }
351 this->openNewTracingSession(name);
352 }
353