xref: /aosp_15_r20/external/skia/tools/trace/SkPerfettoTrace.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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