• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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