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