xref: /aosp_15_r20/external/perfetto/docs/instrumentation/tracing-sdk.md (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1# Tracing SDK
2
3The Perfetto Tracing SDK is a C++17 library that allows userspace applications
4to emit trace events and add more app-specific context to a Perfetto trace.
5
6When using the Tracing SDK there are two main aspects to consider:
7
81. Whether you are interested only in tracing events coming from your own app or
9   want to collect full-stack traces that overlay app trace events with system
10   trace events like scheduler traces, syscalls or any other Perfetto data
11   source.
12
132. For app-specific tracing, whether you need to trace simple types of timeline
14   events (e.g., slices, counters) or need to define complex data sources with a
15   custom strongly-typed schema (e.g., for dumping the state of a subsystem of
16   your app into the trace).
17
18For Android-only instrumentation, the advice is to keep using the existing
19[android.os.Trace (SDK)][atrace-sdk] / [ATrace\_\* (NDK)][atrace-ndk] if they
20are sufficient for your use cases. Atrace-based instrumentation is fully
21supported in Perfetto. See the [Data Sources -> Android System -> Atrace
22Instrumentation][atrace-ds] for details.
23
24## Getting started
25
26TIP: The code from these examples is also available
27[in the repository](/examples/sdk/README.md).
28
29To start using the Client API, first check out the latest SDK release:
30
31```bash
32git clone https://android.googlesource.com/platform/external/perfetto -b v48.1
33```
34
35The SDK consists of two files, `sdk/perfetto.h` and `sdk/perfetto.cc`. These are
36an amalgamation of the Client API designed to easy to integrate to existing
37build systems. The sources are self-contained and require only a C++17 compliant
38standard library.
39
40For example, to add the SDK to a CMake project, edit your CMakeLists.txt:
41
42```cmake
43cmake_minimum_required(VERSION 3.13)
44project(PerfettoExample)
45find_package(Threads)
46
47# Define a static library for Perfetto.
48include_directories(perfetto/sdk)
49add_library(perfetto STATIC perfetto/sdk/perfetto.cc)
50
51# Link the library to your main executable.
52add_executable(example example.cc)
53target_link_libraries(example perfetto ${CMAKE_THREAD_LIBS_INIT})
54
55if (WIN32)
56  # The perfetto library contains many symbols, so it needs the big object
57  # format.
58  target_compile_options(perfetto PRIVATE "/bigobj")
59  # Disable legacy features in windows.h.
60  add_definitions(-DWIN32_LEAN_AND_MEAN -DNOMINMAX)
61  # On Windows we should link to WinSock2.
62  target_link_libraries(example ws2_32)
63endif (WIN32)
64
65# Enable standards-compliant mode when using the Visual Studio compiler.
66if (MSVC)
67  target_compile_options(example PRIVATE "/permissive-")
68endif (MSVC)
69```
70
71Next, initialize Perfetto in your program:
72
73```C++
74#include <perfetto.h>
75
76int main(int argc, char** argv) {
77  perfetto::TracingInitArgs args;
78
79  // The backends determine where trace events are recorded. You may select one
80  // or more of:
81
82  // 1) The in-process backend only records within the app itself.
83  args.backends |= perfetto::kInProcessBackend;
84
85  // 2) The system backend writes events into a system Perfetto daemon,
86  //    allowing merging app and system events (e.g., ftrace) on the same
87  //    timeline. Requires the Perfetto `traced` daemon to be running (e.g.,
88  //    on Android Pie and newer).
89  args.backends |= perfetto::kSystemBackend;
90
91  perfetto::Tracing::Initialize(args);
92}
93```
94
95You are now ready to instrument your app with trace events.
96
97## Custom data sources vs Track events
98
99The SDK offers two abstraction layers to inject tracing data, built on top of
100each other, which trade off code complexity vs expressive power:
101[track events](#track-events) and [custom data sources](#custom-data-sources).
102
103### Track events
104
105Track events are the suggested option when dealing with app-specific tracing as
106they take care of a number of subtleties (e.g., thread safety, flushing, string
107interning). Track events are time bounded events (e.g., slices, counter) based
108on simple `TRACE_EVENT` annotation tags in the codebase, like this:
109
110```c++
111#include <perfetto.h>
112
113PERFETTO_DEFINE_CATEGORIES(
114    perfetto::Category("rendering")
115        .SetDescription("Events from the graphics subsystem"),
116    perfetto::Category("network")
117        .SetDescription("Network upload and download statistics"));
118
119PERFETTO_TRACK_EVENT_STATIC_STORAGE();
120...
121
122int main(int argc, char** argv) {
123  ...
124  perfetto::Tracing::Initialize(args);
125  perfetto::TrackEvent::Register();
126}
127
128...
129
130void LayerTreeHost::DoUpdateLayers() {
131  TRACE_EVENT("rendering", "LayerTreeHost::DoUpdateLayers");
132  ...
133  for (PictureLayer& pl : layers) {
134    TRACE_EVENT("rendering", "PictureLayer::Update");
135    pl.Update();
136  }
137}
138```
139
140Which are rendered in the UI as follows:
141
142![Track event example](/docs/images/track-events.png)
143
144Track events are the best default option and serve most tracing use cases with
145very little complexity.
146
147To include your new track events in the trace, ensure that the `track_event`
148data source is included in the trace config, with a list of enabled and disabled
149categories.
150
151```protobuf
152data_sources {
153  config {
154    name: "track_event"
155    track_event_config {
156        enabled_categories: "rendering"
157        disabled_categories: "*"
158    }
159  }
160}
161```
162
163See the [Track events page](track-events.md) for full instructions.
164
165### Custom data sources
166
167For most uses, track events are the most straightforward way of instrumenting
168apps for tracing. However, in some rare circumstances they are not flexible
169enough, e.g., when the data doesn't fit the notion of a track or is high volume
170enough that it needs a strongly typed schema to minimize the size of each event.
171In this case, you can implement a _custom data source_ for Perfetto.
172
173Unlike track events, when working with custom data sources, you will also need
174corresponding changes in [trace processor](/docs/analysis/trace-processor.md) to
175enable importing your data format.
176
177A custom data source is a subclass of `perfetto::DataSource`. Perfetto will
178automatically create one instance of the class for each tracing session it is
179active in (usually just one).
180
181```C++
182class CustomDataSource : public perfetto::DataSource<CustomDataSource> {
183 public:
184  void OnSetup(const SetupArgs&) override {
185    // Use this callback to apply any custom configuration to your data source
186    // based on the TraceConfig in SetupArgs.
187  }
188
189  void OnStart(const StartArgs&) override {
190    // This notification can be used to initialize the GPU driver, enable
191    // counters, etc. StartArgs will contains the DataSourceDescriptor,
192    // which can be extended.
193  }
194
195  void OnStop(const StopArgs&) override {
196    // Undo any initialization done in OnStart.
197  }
198
199  // Data sources can also have per-instance state.
200  int my_custom_state = 0;
201};
202
203PERFETTO_DECLARE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource);
204```
205
206The data source's static data should be defined in one source file like this:
207
208```C++
209PERFETTO_DEFINE_DATA_SOURCE_STATIC_MEMBERS(CustomDataSource);
210```
211
212Custom data sources need to be registered with Perfetto:
213
214```C++
215int main(int argc, char** argv) {
216  ...
217  perfetto::Tracing::Initialize(args);
218  // Add the following:
219  perfetto::DataSourceDescriptor dsd;
220  dsd.set_name("com.example.custom_data_source");
221  CustomDataSource::Register(dsd);
222}
223```
224
225As with all data sources, the custom data source needs to be specified in the
226trace config to enable tracing:
227
228```C++
229perfetto::TraceConfig cfg;
230auto* ds_cfg = cfg.add_data_sources()->mutable_config();
231ds_cfg->set_name("com.example.custom_data_source");
232```
233
234Finally, call the `Trace()` method to record an event with your custom data
235source. The lambda function passed to that method will only be called if tracing
236is enabled. It is always called synchronously and possibly multiple times if
237multiple concurrent tracing sessions are active.
238
239```C++
240CustomDataSource::Trace([](CustomDataSource::TraceContext ctx) {
241  auto packet = ctx.NewTracePacket();
242  packet->set_timestamp(perfetto::TrackEvent::GetTraceTimeNs());
243  packet->set_for_testing()->set_str("Hello world!");
244});
245```
246
247If necessary the `Trace()` method can access the custom data source state
248(`my_custom_state` in the example above). Doing so, will take a mutex to ensure
249data source isn't destroyed (e.g., because of stopping tracing) while the
250`Trace()` method is called on another thread. For example:
251
252```C++
253CustomDataSource::Trace([](CustomDataSource::TraceContext ctx) {
254  auto safe_handle = trace_args.GetDataSourceLocked();  // Holds a RAII lock.
255  DoSomethingWith(safe_handle->my_custom_state);
256});
257```
258
259## In-process vs System mode
260
261The two modes are not mutually exclusive. An app can be configured to work in
262both modes and respond both to in-process tracing requests and system tracing
263requests. Both modes generate the same trace file format.
264
265### In-process mode
266
267In this mode both the perfetto service and the app-defined data sources are
268hosted fully in-process, in the same process of the profiled app. No connection
269to the system `traced` daemon will be attempted.
270
271In-process mode can be enabled by setting
272`TracingInitArgs.backends = perfetto::kInProcessBackend` when initializing the
273SDK, see examples below.
274
275This mode is used to generate traces that contain only events emitted by the
276app, but not other types of events (e.g. scheduler traces).
277
278The main advantage is that by running fully in-process, it doesn't require any
279special OS privileges and the profiled process can control the lifecycle of
280tracing sessions.
281
282This mode is supported on Android, Linux, MacOS and Windows.
283
284### System mode
285
286In this mode the app-defined data sources will connect to the external `traced`
287service using the [IPC over UNIX socket][ipc].
288
289System mode can be enabled by setting
290`TracingInitArgs.backends = perfetto::kSystemBackend` when initializing the SDK,
291see examples below.
292
293The main advantage of this mode is that it is possible to create fused traces
294where app events are overlaid on the same timeline of OS events. This enables
295full-stack performance investigations, looking all the way through syscalls and
296kernel scheduling events.
297
298The main limitation of this mode is that it requires the external `traced`
299daemon to be up and running and reachable through the UNIX socket connection.
300
301This is suggested for local debugging or lab testing scenarios where the user
302(or the test harness) can control the OS deployment (e.g., sideload binaries on
303Android).
304
305When using system mode, the tracing session must be controlled from the outside,
306using the `perfetto` command-line client (See
307[reference](/docs/reference/perfetto-cli)). This is because when collecting
308system traces, tracing data producers are not allowed to read back the trace
309data as it might disclose information about other processes and allow
310side-channel attacks.
311
312- On Android 9 (Pie) and beyond, traced is shipped as part of the platform.
313- On older versions of Android, traced can be built from sources using the the
314  [standalone NDK-based workflow](/docs/contributing/build-instructions.md) and
315  sideloaded via adb shell.
316- On Linux and MacOS and Windows `traced` must be built and run separately. See
317  the [Linux quickstart](/docs/quickstart/linux-tracing.md) for instructions.
318- On Windows the tracing protocol works over TCP/IP (
319  [127.0.0.1:32278](https://cs.android.com/android/platform/superproject/main/+/main:external/perfetto/src/tracing/ipc/default_socket.cc;l=75;drc=4f88a2fdfd3801c109d5e927b8206f9756288b12)
320  ) + named shmem.
321
322## {#recording} Recording traces through the API
323
324_Tracing through the API is currently only supported with the in-process mode.
325When using system mode, use the `perfetto` cmdline client (see quickstart
326guides)._
327
328First initialize a [TraceConfig](/docs/reference/trace-config-proto.autogen)
329message which specifies what type of data to record.
330
331If your app includes [track events](track-events.md) (i.e, `TRACE_EVENT`), you
332typically want to choose the categories which are enabled for tracing.
333
334By default, all non-debug categories are enabled, but you can enable a specific
335one like this:
336
337```C++
338perfetto::protos::gen::TrackEventConfig track_event_cfg;
339track_event_cfg.add_disabled_categories("*");
340track_event_cfg.add_enabled_categories("rendering");
341```
342
343Next, build the main trace config together with the track event part:
344
345```C++
346perfetto::TraceConfig cfg;
347cfg.add_buffers()->set_size_kb(1024);  // Record up to 1 MiB.
348auto* ds_cfg = cfg.add_data_sources()->mutable_config();
349ds_cfg->set_name("track_event");
350ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
351```
352
353If your app includes a custom data source, you can also enable it here:
354
355```C++
356ds_cfg = cfg.add_data_sources()->mutable_config();
357ds_cfg->set_name("my_data_source");
358```
359
360After building the trace config, you can begin tracing:
361
362```C++
363std::unique_ptr<perfetto::TracingSession> tracing_session(
364    perfetto::Tracing::NewTrace());
365tracing_session->Setup(cfg);
366tracing_session->StartBlocking();
367```
368
369TIP: API methods with `Blocking` in their name will suspend the calling thread
370until the respective operation is complete. There are also asynchronous variants
371that don't have this limitation.
372
373Now that tracing is active, instruct your app to perform the operation you want
374to record. After that, stop tracing and collect the protobuf-formatted trace
375data:
376
377```C++
378tracing_session->StopBlocking();
379std::vector<char> trace_data(tracing_session->ReadTraceBlocking());
380
381// Write the trace into a file.
382std::ofstream output;
383output.open("example.perfetto-trace", std::ios::out | std::ios::binary);
384output.write(&trace_data[0], trace_data.size());
385output.close();
386```
387
388To save memory with longer traces, you can also tell Perfetto to write directly
389into a file by passing a file descriptor into Setup(), remembering to close the
390file after tracing is done:
391
392```C++
393int fd = open("example.perfetto-trace", O_RDWR | O_CREAT | O_TRUNC, 0600);
394tracing_session->Setup(cfg, fd);
395tracing_session->StartBlocking();
396// ...
397tracing_session->StopBlocking();
398close(fd);
399```
400
401The resulting trace file can be directly opened in the
402[Perfetto UI](https://ui.perfetto.dev) or the
403[Trace Processor](/docs/analysis/trace-processor.md).
404
405[ipc]: /docs/design-docs/api-and-abi.md#socket-protocol
406[atrace-ds]: /docs/data-sources/atrace.md
407[atrace-ndk]: https://developer.android.com/ndk/reference/group/tracing
408[atrace-sdk]: https://developer.android.com/reference/android/os/Trace
409