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
181 switch (arg1Type) {
182 case TRACE_VALUE_TYPE_BOOL: {
183 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, SkToBool(arg1Val),
184 [&](perfetto::EventContext ctx) {
185 ctx.event()->set_name(eventName); });
186 break;
187 }
188 case TRACE_VALUE_TYPE_UINT: {
189 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val,
190 [&](perfetto::EventContext ctx) {
191 ctx.event()->set_name(eventName); });
192 break;
193 }
194 case TRACE_VALUE_TYPE_INT: {
195 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, static_cast<int64_t>(arg1Val),
196 [&](perfetto::EventContext ctx) {
197 ctx.event()->set_name(eventName); });
198 break;
199 }
200 case TRACE_VALUE_TYPE_DOUBLE: {
201 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, sk_bit_cast<double>(arg1Val),
202 [&](perfetto::EventContext ctx) {
203 ctx.event()->set_name(eventName); });
204 break;
205 }
206 case TRACE_VALUE_TYPE_POINTER: {
207 TRACE_EVENT_BEGIN(category, nullptr,
208 arg1Name, skia_private::TraceValueAsPointer(arg1Val),
209 [&](perfetto::EventContext ctx) {
210 ctx.event()->set_name(eventName); });
211 break;
212 }
213 case TRACE_VALUE_TYPE_COPY_STRING: [[fallthrough]]; // Perfetto always copies string data
214 case TRACE_VALUE_TYPE_STRING: {
215 TRACE_EVENT_BEGIN(category, nullptr,
216 arg1Name, skia_private::TraceValueAsString(arg1Val),
217 [&](perfetto::EventContext ctx) {
218 ctx.event()->set_name(eventName); });
219 break;
220 }
221 default: {
222 SkUNREACHABLE;
223 }
224 }
225 }
226
227 namespace {
228 /* Define a template to help handle all the possible TRACE_EVENT_BEGIN macro call
229 * combinations with 2 arguments of all the types supported by SetTraceValue.
230 */
231 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)232 void begin_event_with_second_arg(const char * categoryName, const char* eventName,
233 const char* arg1Name, T arg1Val, const char* arg2Name,
234 const uint8_t& arg2Type, const uint64_t& arg2Val) {
235 perfetto::DynamicCategory category{categoryName};
236
237 switch (arg2Type) {
238 case TRACE_VALUE_TYPE_BOOL: {
239 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, SkToBool(arg2Val),
240 [&](perfetto::EventContext ctx) {
241 ctx.event()->set_name(eventName); });
242 break;
243 }
244 case TRACE_VALUE_TYPE_UINT: {
245 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, arg2Val,
246 [&](perfetto::EventContext ctx) {
247 ctx.event()->set_name(eventName); });
248 break;
249 }
250 case TRACE_VALUE_TYPE_INT: {
251 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val,
252 arg2Name, static_cast<int64_t>(arg2Val),
253 [&](perfetto::EventContext ctx) {
254 ctx.event()->set_name(eventName); });
255 break;
256 }
257 case TRACE_VALUE_TYPE_DOUBLE: {
258 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val,
259 arg2Name, sk_bit_cast<double>(arg2Val),
260 [&](perfetto::EventContext ctx) {
261 ctx.event()->set_name(eventName); });
262 break;
263 }
264 case TRACE_VALUE_TYPE_POINTER: {
265 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val,
266 arg2Name, skia_private::TraceValueAsPointer(arg2Val),
267 [&](perfetto::EventContext ctx) {
268 ctx.event()->set_name(eventName); });
269 break;
270 }
271 case TRACE_VALUE_TYPE_COPY_STRING: [[fallthrough]];
272 case TRACE_VALUE_TYPE_STRING: {
273 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val,
274 arg2Name, skia_private::TraceValueAsString(arg2Val),
275 [&](perfetto::EventContext ctx) {
276 ctx.event()->set_name(eventName); });
277 break;
278 }
279 default: {
280 SkUNREACHABLE;
281 break;
282 }
283 }
284 }
285 } // anonymous namespace
286
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)287 void SkPerfettoTrace::triggerTraceEvent(const uint8_t* categoryEnabledFlag,
288 const char* eventName, const char* arg1Name,
289 const uint8_t& arg1Type, const uint64_t& arg1Val,
290 const char* arg2Name, const uint8_t& arg2Type,
291 const uint64_t& arg2Val) {
292
293 const char * category{ this->getCategoryGroupName(categoryEnabledFlag) };
294
295 switch (arg1Type) {
296 case TRACE_VALUE_TYPE_BOOL: {
297 begin_event_with_second_arg(category, eventName, arg1Name, SkToBool(arg1Val),
298 arg2Name, arg2Type, arg2Val);
299 break;
300 }
301 case TRACE_VALUE_TYPE_UINT: {
302 begin_event_with_second_arg(category, eventName, arg1Name, arg1Val,
303 arg2Name, arg2Type, arg2Val);
304 break;
305 }
306 case TRACE_VALUE_TYPE_INT: {
307 begin_event_with_second_arg(category, eventName,
308 arg1Name, static_cast<int64_t>(arg1Val),
309 arg2Name, arg2Type, arg2Val);
310 break;
311 }
312 case TRACE_VALUE_TYPE_DOUBLE: {
313 begin_event_with_second_arg(category, eventName, arg1Name, sk_bit_cast<double>(arg1Val),
314 arg2Name, arg2Type, arg2Val);
315 break;
316 }
317 case TRACE_VALUE_TYPE_POINTER: {
318 begin_event_with_second_arg(category, eventName,
319 arg1Name, skia_private::TraceValueAsPointer(arg1Val),
320 arg2Name, arg2Type, arg2Val);
321 break;
322 }
323 case TRACE_VALUE_TYPE_COPY_STRING: [[fallthrough]];
324 case TRACE_VALUE_TYPE_STRING: {
325 begin_event_with_second_arg(category, eventName,
326 arg1Name, skia_private::TraceValueAsString(arg1Val),
327 arg2Name, arg2Type, arg2Val);
328 break;
329 }
330 default: {
331 SkUNREACHABLE;
332 }
333 }
334 }
335
newTracingSection(const char * name)336 void SkPerfettoTrace::newTracingSection(const char* name) {
337 if (perfetto::Tracing::IsInitialized()) {
338 this->closeTracingSession();
339 }
340 this->openNewTracingSession(name);
341 }
342