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 ```