Flutter Engine
The Flutter Engine
SkPerfettoTrace.cpp
Go to the documentation of this file.
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
9
10#include <fcntl.h>
11#include <fstream>
15
17
18static 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.");
22static 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.");
28static 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.");
31static 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
38 fOutputPath = FLAGS_perfettoOutputDir[0];
39 fOutputFileExtension = FLAGS_perfettoOutputFileExtension[0];
40 this->openNewTracingSession(FLAGS_perfettoOutputFileName[0]);
41}
42
44 this->closeTracingSession();
45}
46
47void 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 }
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
100void SkPerfettoTrace::closeTracingSession() {
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
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
152void 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
161 return fCategories.getCategoryGroupEnabled(name);
162}
163
164const char* SkPerfettoTrace::getCategoryGroupName(const uint8_t* categoryEnabledFlag) {
165 return fCategories.getCategoryGroupName(categoryEnabledFlag);
166}
167
168void 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
176void 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) {
183 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, SkToBool(arg1Val),
184 [&](perfetto::EventContext ctx) {
185 ctx.event()->set_name(eventName); });
186 break;
187 }
189 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val,
190 [&](perfetto::EventContext ctx) {
191 ctx.event()->set_name(eventName); });
192 break;
193 }
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 }
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 }
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
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: {
223 }
224 }
225}
226
227namespace {
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 */
231template <typename T>
232void 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) {
239 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, SkToBool(arg2Val),
240 [&](perfetto::EventContext ctx) {
241 ctx.event()->set_name(eventName); });
242 break;
243 }
245 TRACE_EVENT_BEGIN(category, nullptr, arg1Name, arg1Val, arg2Name, arg2Val,
246 [&](perfetto::EventContext ctx) {
247 ctx.event()->set_name(eventName); });
248 break;
249 }
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 }
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 }
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]];
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: {
281 break;
282 }
283 }
284}
285} // anonymous namespace
286
287void 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) {
297 begin_event_with_second_arg(category, eventName, arg1Name, SkToBool(arg1Val),
298 arg2Name, arg2Type, arg2Val);
299 break;
300 }
302 begin_event_with_second_arg(category, eventName, arg1Name, arg1Val,
303 arg2Name, arg2Type, arg2Val);
304 break;
305 }
307 begin_event_with_second_arg(category, eventName,
308 arg1Name, static_cast<int64_t>(arg1Val),
309 arg2Name, arg2Type, arg2Val);
310 break;
311 }
313 begin_event_with_second_arg(category, eventName, arg1Name, sk_bit_cast<double>(arg1Val),
314 arg2Name, arg2Type, arg2Val);
315 break;
316 }
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]];
325 begin_event_with_second_arg(category, eventName,
326 arg1Name, skia_private::TraceValueAsString(arg1Val),
327 arg2Name, arg2Type, arg2Val);
328 break;
329 }
330 default: {
332 }
333 }
334}
335
337 if (perfetto::Tracing::IsInitialized()) {
338 this->closeTracingSession();
339 }
340 this->openNewTracingSession(name);
341}
#define SkUNREACHABLE
Definition: SkAssert.h:135
PERFETTO_TRACK_EVENT_STATIC_STORAGE()
static DEFINE_bool(longPerfettoTrace, false, "Perfetto within Skia is optimized for tracing performance of 'smaller' traces" "(~10 seconds or less). Set this flag to true to optimize for longer tracing" "sessions.\n" "Will only have an effect if perfetto tracing is enabled. See --trace.")
static DEFINE_string(perfettoOutputDir, "./", "Output directory for perfetto trace file(s).\n" "Note: not the name of the file itself.\n" "Will only have an effect if perfetto tracing is enabled. See --trace.")
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
#define TRACE_VALUE_TYPE_STRING
#define TRACE_VALUE_TYPE_BOOL
#define TRACE_VALUE_TYPE_POINTER
#define TRACE_VALUE_TYPE_COPY_STRING
#define TRACE_VALUE_TYPE_INT
#define TRACE_VALUE_TYPE_UINT
#define TRACE_VALUE_TYPE_DOUBLE
#define TRACE_EVENT_PHASE_END
#define TRACE_EVENT_PHASE_INSTANT
#define TRACE_EVENT_PHASE_COMPLETE
uint64_t Handle
Definition: SkEventTracer.h:26
const char * getCategoryGroupName(const uint8_t *categoryEnabledFlag)
uint8_t * getCategoryGroupEnabled(const char *name)
SkEventTracer::Handle 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) override
~SkPerfettoTrace() override
void updateTraceEventDuration(const uint8_t *categoryEnabledFlag, const char *name, SkEventTracer::Handle handle) override
const uint8_t * getCategoryGroupEnabled(const char *name) override
void newTracingSection(const char *name) override
const char * getCategoryGroupName(const uint8_t *categoryEnabledFlag) override
FlutterSemanticsFlag flags
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
void Initialize(zx::channel directory_request, std::optional< zx::eventpair > view_ref)
Initializes Dart bindings for the Fuchsia application model.
Definition: fuchsia.cc:103
void Flush(SkSurface *surface)
Definition: GpuTools.h:25
static const void * TraceValueAsPointer(uint64_t value)
Definition: SkTraceEvent.h:300
static const char * TraceValueAsString(uint64_t value)
Definition: SkTraceEvent.h:296
#define T
Definition: precompiler.cc:65