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