xref: /aosp_15_r20/external/perfetto/docs/instrumentation/interceptors.md (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker# Trace packet interceptors (Tracing SDK)
2*6dbdd20aSAndroid Build Coastguard Worker
3*6dbdd20aSAndroid Build Coastguard WorkerA trace packet interceptor is used to redirect trace packets written by a
4*6dbdd20aSAndroid Build Coastguard Workerdata source into a custom backend instead of the normal Perfetto tracing
5*6dbdd20aSAndroid Build Coastguard Workerservice. For example, the console interceptor prints all trace packets to the
6*6dbdd20aSAndroid Build Coastguard Workerconsole as they are generated. Another potential use is exporting trace data
7*6dbdd20aSAndroid Build Coastguard Workerto another tracing service such as Android ATrace or Windows ETW.
8*6dbdd20aSAndroid Build Coastguard Worker
9*6dbdd20aSAndroid Build Coastguard WorkerAn interceptor is defined by subclassing the `perfetto::Interceptor` template:
10*6dbdd20aSAndroid Build Coastguard Worker
11*6dbdd20aSAndroid Build Coastguard Worker```C++
12*6dbdd20aSAndroid Build Coastguard Workerclass MyInterceptor : public perfetto::Interceptor<MyInterceptor> {
13*6dbdd20aSAndroid Build Coastguard Worker public:
14*6dbdd20aSAndroid Build Coastguard Worker  ~MyInterceptor() override = default;
15*6dbdd20aSAndroid Build Coastguard Worker
16*6dbdd20aSAndroid Build Coastguard Worker  // This function is called for each intercepted trace packet. |context|
17*6dbdd20aSAndroid Build Coastguard Worker  // contains information about the trace packet as well as other state
18*6dbdd20aSAndroid Build Coastguard Worker  // tracked by the interceptor (e.g., see ThreadLocalState).
19*6dbdd20aSAndroid Build Coastguard Worker  //
20*6dbdd20aSAndroid Build Coastguard Worker  // Intercepted trace data is provided in the form of serialized protobuf
21*6dbdd20aSAndroid Build Coastguard Worker  // bytes, accessed through the |context.packet_data| field.
22*6dbdd20aSAndroid Build Coastguard Worker  //
23*6dbdd20aSAndroid Build Coastguard Worker  // Warning: this function can be called on any thread at any time. See
24*6dbdd20aSAndroid Build Coastguard Worker  // below for how to safely access shared interceptor data from here.
25*6dbdd20aSAndroid Build Coastguard Worker  static void OnTracePacket(InterceptorContext context) {
26*6dbdd20aSAndroid Build Coastguard Worker    perfetto::protos::pbzero::TracePacket::Decoder packet(
27*6dbdd20aSAndroid Build Coastguard Worker        context.packet_data.data, context.packet_data.size);
28*6dbdd20aSAndroid Build Coastguard Worker    // ... Write |packet| to the desired destination ...
29*6dbdd20aSAndroid Build Coastguard Worker  }
30*6dbdd20aSAndroid Build Coastguard Worker};
31*6dbdd20aSAndroid Build Coastguard Worker```
32*6dbdd20aSAndroid Build Coastguard Worker
33*6dbdd20aSAndroid Build Coastguard WorkerAn interceptor should be registered before any tracing sessions are started.
34*6dbdd20aSAndroid Build Coastguard WorkerNote that the interceptor also needs to be activated through the trace config
35*6dbdd20aSAndroid Build Coastguard Workershown below.
36*6dbdd20aSAndroid Build Coastguard Worker
37*6dbdd20aSAndroid Build Coastguard Worker```C++
38*6dbdd20aSAndroid Build Coastguard Workerperfetto::InterceptorDescriptor desc;
39*6dbdd20aSAndroid Build Coastguard Workerdesc.set_name("my_interceptor");
40*6dbdd20aSAndroid Build Coastguard WorkerMyInterceptor::Register(desc);
41*6dbdd20aSAndroid Build Coastguard Worker```
42*6dbdd20aSAndroid Build Coastguard Worker
43*6dbdd20aSAndroid Build Coastguard WorkerFinally, an interceptor is enabled through the trace config like this:
44*6dbdd20aSAndroid Build Coastguard Worker
45*6dbdd20aSAndroid Build Coastguard Worker```C++
46*6dbdd20aSAndroid Build Coastguard Workerperfetto::TraceConfig cfg;
47*6dbdd20aSAndroid Build Coastguard Workerauto* ds_cfg = cfg.add_data_sources()->mutable_config();
48*6dbdd20aSAndroid Build Coastguard Workerds_cfg->set_name("data_source_to_intercept");   // e.g. "track_event"
49*6dbdd20aSAndroid Build Coastguard Workerds_cfg->mutable_interceptor_config()->set_name("my_interceptor");
50*6dbdd20aSAndroid Build Coastguard Worker```
51*6dbdd20aSAndroid Build Coastguard Worker
52*6dbdd20aSAndroid Build Coastguard WorkerOnce an interceptor is enabled, all data from the affected data sources is
53*6dbdd20aSAndroid Build Coastguard Workersent to the interceptor instead of the main tracing buffer.
54*6dbdd20aSAndroid Build Coastguard Worker
55*6dbdd20aSAndroid Build Coastguard Worker## Interceptor state
56*6dbdd20aSAndroid Build Coastguard Worker
57*6dbdd20aSAndroid Build Coastguard WorkerBesides the serialized trace packet data, the `OnTracePacket` interceptor
58*6dbdd20aSAndroid Build Coastguard Workerfunction can access three other types of state:
59*6dbdd20aSAndroid Build Coastguard Worker
60*6dbdd20aSAndroid Build Coastguard Worker1. **Global state:** this is no different from a normal static function, but
61*6dbdd20aSAndroid Build Coastguard Worker   care must be taken because |OnTracePacket| can be called concurrently on
62*6dbdd20aSAndroid Build Coastguard Worker   any thread at any time.
63*6dbdd20aSAndroid Build Coastguard Worker
64*6dbdd20aSAndroid Build Coastguard Worker2. **Per-data source instance state:** since the interceptor class is
65*6dbdd20aSAndroid Build Coastguard Worker   automatically instantiated for each intercepted data source, its fields
66*6dbdd20aSAndroid Build Coastguard Worker   can be used to store per-instance data such as the trace config. This data
67*6dbdd20aSAndroid Build Coastguard Worker   can be maintained through the OnSetup/OnStart/OnStop callbacks:
68*6dbdd20aSAndroid Build Coastguard Worker
69*6dbdd20aSAndroid Build Coastguard Worker   ```C++
70*6dbdd20aSAndroid Build Coastguard Worker   class MyInterceptor : public perfetto::Interceptor<MyInterceptor> {
71*6dbdd20aSAndroid Build Coastguard Worker    public:
72*6dbdd20aSAndroid Build Coastguard Worker     void OnSetup(const SetupArgs& args) override {
73*6dbdd20aSAndroid Build Coastguard Worker       enable_foo_ = args.config.interceptor_config().enable_foo();
74*6dbdd20aSAndroid Build Coastguard Worker     }
75*6dbdd20aSAndroid Build Coastguard Worker
76*6dbdd20aSAndroid Build Coastguard Worker     bool enable_foo_{};
77*6dbdd20aSAndroid Build Coastguard Worker   };
78*6dbdd20aSAndroid Build Coastguard Worker   ```
79*6dbdd20aSAndroid Build Coastguard Worker
80*6dbdd20aSAndroid Build Coastguard Worker   In the interceptor function this data must be accessed through a scoped
81*6dbdd20aSAndroid Build Coastguard Worker   lock for safety:
82*6dbdd20aSAndroid Build Coastguard Worker
83*6dbdd20aSAndroid Build Coastguard Worker   ```C++
84*6dbdd20aSAndroid Build Coastguard Worker   class MyInterceptor : public perfetto::Interceptor<MyInterceptor> {
85*6dbdd20aSAndroid Build Coastguard Worker     ...
86*6dbdd20aSAndroid Build Coastguard Worker     static void OnTracePacket(InterceptorContext context) {
87*6dbdd20aSAndroid Build Coastguard Worker       auto my_interceptor = context.GetInterceptorLocked();
88*6dbdd20aSAndroid Build Coastguard Worker       if (my_interceptor) {
89*6dbdd20aSAndroid Build Coastguard Worker          // Access fields of MyInterceptor here.
90*6dbdd20aSAndroid Build Coastguard Worker          if (my_interceptor->enable_foo_) { ... }
91*6dbdd20aSAndroid Build Coastguard Worker       }
92*6dbdd20aSAndroid Build Coastguard Worker       ...
93*6dbdd20aSAndroid Build Coastguard Worker     }
94*6dbdd20aSAndroid Build Coastguard Worker   };
95*6dbdd20aSAndroid Build Coastguard Worker   ```
96*6dbdd20aSAndroid Build Coastguard Worker
97*6dbdd20aSAndroid Build Coastguard Worker   Since accessing this data involves holding a lock, it should be done
98*6dbdd20aSAndroid Build Coastguard Worker   sparingly.
99*6dbdd20aSAndroid Build Coastguard Worker
100*6dbdd20aSAndroid Build Coastguard Worker3. **Per-thread/TraceWriter state:** many data sources use interning to avoid
101*6dbdd20aSAndroid Build Coastguard Worker   repeating common data in the trace. Since the interning dictionaries are
102*6dbdd20aSAndroid Build Coastguard Worker   typically kept individually for each TraceWriter sequence (i.e., per
103*6dbdd20aSAndroid Build Coastguard Worker   thread), an interceptor can declare a data structure with lifetime
104*6dbdd20aSAndroid Build Coastguard Worker   matching the TraceWriter:
105*6dbdd20aSAndroid Build Coastguard Worker
106*6dbdd20aSAndroid Build Coastguard Worker   ```C++
107*6dbdd20aSAndroid Build Coastguard Worker   class MyInterceptor : public perfetto::Interceptor<MyInterceptor> {
108*6dbdd20aSAndroid Build Coastguard Worker    public:
109*6dbdd20aSAndroid Build Coastguard Worker     struct ThreadLocalState
110*6dbdd20aSAndroid Build Coastguard Worker         : public perfetto::InterceptorBase::ThreadLocalState {
111*6dbdd20aSAndroid Build Coastguard Worker       ThreadLocalState(ThreadLocalStateArgs&) override = default;
112*6dbdd20aSAndroid Build Coastguard Worker       ~ThreadLocalState() override = default;
113*6dbdd20aSAndroid Build Coastguard Worker
114*6dbdd20aSAndroid Build Coastguard Worker       std::map<size_t, std::string> event_names;
115*6dbdd20aSAndroid Build Coastguard Worker     };
116*6dbdd20aSAndroid Build Coastguard Worker   };
117*6dbdd20aSAndroid Build Coastguard Worker   ```
118*6dbdd20aSAndroid Build Coastguard Worker
119*6dbdd20aSAndroid Build Coastguard Worker   This per-thread state can then be accessed and maintained in
120*6dbdd20aSAndroid Build Coastguard Worker   `OnTracePacket` like this:
121*6dbdd20aSAndroid Build Coastguard Worker
122*6dbdd20aSAndroid Build Coastguard Worker   ```C++
123*6dbdd20aSAndroid Build Coastguard Worker   class MyInterceptor : public perfetto::Interceptor<MyInterceptor> {
124*6dbdd20aSAndroid Build Coastguard Worker     ...
125*6dbdd20aSAndroid Build Coastguard Worker     static void OnTracePacket(InterceptorContext context) {
126*6dbdd20aSAndroid Build Coastguard Worker       // Updating interned data.
127*6dbdd20aSAndroid Build Coastguard Worker       auto& tls = context.GetThreadLocalState();
128*6dbdd20aSAndroid Build Coastguard Worker       if (parsed_packet.sequence_flags() & perfetto::protos::pbzero::
129*6dbdd20aSAndroid Build Coastguard Worker               TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) {
130*6dbdd20aSAndroid Build Coastguard Worker         tls.event_names.clear();
131*6dbdd20aSAndroid Build Coastguard Worker       }
132*6dbdd20aSAndroid Build Coastguard Worker       for (const auto& entry : parsed_packet.interned_data().event_names())
133*6dbdd20aSAndroid Build Coastguard Worker         tls.event_names[entry.iid()] = entry.name();
134*6dbdd20aSAndroid Build Coastguard Worker
135*6dbdd20aSAndroid Build Coastguard Worker       // Looking up interned data.
136*6dbdd20aSAndroid Build Coastguard Worker       if (parsed_packet.has_track_event()) {
137*6dbdd20aSAndroid Build Coastguard Worker         size_t name_iid = parsed_packet.track_event().name_iid();
138*6dbdd20aSAndroid Build Coastguard Worker         const std::string& event_name = tls.event_names[name_iid];
139*6dbdd20aSAndroid Build Coastguard Worker       }
140*6dbdd20aSAndroid Build Coastguard Worker       ...
141*6dbdd20aSAndroid Build Coastguard Worker     }
142*6dbdd20aSAndroid Build Coastguard Worker   };
143*6dbdd20aSAndroid Build Coastguard Worker   ```