1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef INCLUDE_PERFETTO_TRACING_INTERCEPTOR_H_ 18 #define INCLUDE_PERFETTO_TRACING_INTERCEPTOR_H_ 19 20 // An interceptor is used to redirect trace packets written by a data source 21 // into a custom backend instead of the normal Perfetto tracing service. For 22 // example, the console interceptor prints all trace packets to the console as 23 // they are generated. Another potential use is exporting trace data to another 24 // tracing service such as Android ATrace or Windows ETW. 25 // 26 // An interceptor is defined by subclassing the perfetto::Interceptor template: 27 // 28 // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { 29 // public: 30 // ~MyInterceptor() override = default; 31 // 32 // // This function is called for each intercepted trace packet. |context| 33 // // contains information about the trace packet as well as other state 34 // // tracked by the interceptor (e.g., see ThreadLocalState). 35 // // 36 // // Intercepted trace data is provided in the form of serialized protobuf 37 // // bytes, accessed through the |context.packet_data| field. 38 // // 39 // // Warning: this function can be called on any thread at any time. See 40 // // below for how to safely access shared interceptor data from here. 41 // static void OnTracePacket(InterceptorContext context) { 42 // perfetto::protos::pbzero::TracePacket::Decoder packet( 43 // context.packet_data.data, context.packet_data.size); 44 // // ... Write |packet| to the desired destination ... 45 // } 46 // }; 47 // 48 // An interceptor should be registered before any tracing sessions are started. 49 // Note that the interceptor also needs to be activated through the trace config 50 // as shown below. 51 // 52 // perfetto::InterceptorDescriptor desc; 53 // desc.set_name("my_interceptor"); 54 // MyInterceptor::Register(desc); 55 // 56 // Finally, an interceptor is enabled through the trace config like this: 57 // 58 // perfetto::TraceConfig cfg; 59 // auto* ds_cfg = cfg.add_data_sources()->mutable_config(); 60 // ds_cfg->set_name("data_source_to_intercept"); // e.g. "track_event" 61 // ds_cfg->mutable_interceptor_config()->set_name("my_interceptor"); 62 // 63 // Once an interceptor is enabled, all data from the affected data sources is 64 // sent to the interceptor instead of the main tracing buffer. 65 // 66 // Interceptor state 67 // ================= 68 // 69 // Besides the serialized trace packet data, the |OnTracePacket| interceptor 70 // function can access three other types of state: 71 // 72 // 1. Global state: this is no different from a normal static function, but care 73 // must be taken because |OnTracePacket| can be called concurrently on any 74 // thread at any time. 75 // 76 // 2. Per-data source instance state: since the interceptor class is 77 // automatically instantiated for each intercepted data source, its fields 78 // can be used to store per-instance data such as the trace config. This data 79 // can be maintained through the OnSetup/OnStart/OnStop callbacks: 80 // 81 // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { 82 // public: 83 // void OnSetup(const SetupArgs& args) override { 84 // enable_foo_ = args.config.interceptor_config().enable_foo(); 85 // } 86 // 87 // bool enable_foo_{}; 88 // }; 89 // 90 // In the interceptor function this data must be accessed through a scoped 91 // lock for safety: 92 // 93 // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { 94 // ... 95 // static void OnTracePacket(InterceptorContext context) { 96 // auto my_interceptor = context.GetInterceptorLocked(); 97 // if (my_interceptor) { 98 // // Access fields of MyInterceptor here. 99 // if (my_interceptor->enable_foo_) { ... } 100 // } 101 // ... 102 // } 103 // }; 104 // 105 // Since accessing this data involves holding a lock, it should be done 106 // sparingly. 107 // 108 // 3. Per-thread/TraceWriter state: many data sources use interning to avoid 109 // repeating common data in the trace. Since the interning dictionaries are 110 // typically kept individually for each TraceWriter sequence (i.e., per 111 // thread), an interceptor can declare a data structure with lifetime 112 // matching the TraceWriter: 113 // 114 // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { 115 // public: 116 // struct ThreadLocalState 117 // : public perfetto::InterceptorBase::ThreadLocalState { 118 // ThreadLocalState(ThreadLocalStateArgs&) override = default; 119 // ~ThreadLocalState() override = default; 120 // 121 // std::map<size_t, std::string> event_names; 122 // }; 123 // }; 124 // 125 // This per-thread state can then be accessed and maintained in 126 // |OnTracePacket| like this: 127 // 128 // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { 129 // ... 130 // static void OnTracePacket(InterceptorContext context) { 131 // // Updating interned data. 132 // auto& tls = context.GetThreadLocalState(); 133 // if (parsed_packet.sequence_flags() & perfetto::protos::pbzero:: 134 // TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) { 135 // tls.event_names.clear(); 136 // } 137 // for (const auto& entry : parsed_packet.interned_data().event_names()) 138 // tls.event_names[entry.iid()] = entry.name(); 139 // 140 // // Looking up interned data. 141 // if (parsed_packet.has_track_event()) { 142 // size_t name_iid = parsed_packet.track_event().name_iid(); 143 // const std::string& event_name = tls.event_names[name_iid]; 144 // } 145 // ... 146 // } 147 // }; 148 // 149 150 #include <functional> 151 152 #include "perfetto/protozero/field.h" 153 #include "perfetto/tracing/core/forward_decls.h" 154 #include "perfetto/tracing/internal/basic_types.h" 155 #include "perfetto/tracing/internal/data_source_internal.h" 156 #include "perfetto/tracing/locked_handle.h" 157 158 namespace { 159 class MockTracingMuxer; 160 } 161 162 namespace perfetto { 163 namespace protos { 164 namespace gen { 165 class DataSourceConfig; 166 class InterceptorDescriptor; 167 } // namespace gen 168 } // namespace protos 169 170 using protos::gen::InterceptorDescriptor; 171 172 namespace internal { 173 class InterceptorTraceWriter; 174 class TracingMuxer; 175 class TracingMuxerFake; 176 class TracingMuxerImpl; 177 } // namespace internal 178 179 // A virtual base class for interceptors. Users should derive from the templated 180 // subclass below instead of this one. 181 class PERFETTO_EXPORT InterceptorBase { 182 public: 183 virtual ~InterceptorBase(); 184 185 // A virtual base class for thread-local state needed by the interceptor. 186 // To define your own state, subclass this with the same name in the 187 // interceptor class. A reference to the state can then be looked up through 188 // context.GetThreadLocalState() in the trace packet interceptor function. 189 class ThreadLocalState { 190 public: 191 virtual ~ThreadLocalState(); 192 }; 193 194 struct SetupArgs { 195 const DataSourceConfig& config; 196 }; 197 struct StartArgs {}; 198 struct StopArgs {}; 199 200 // Called when an intercepted data source is set up. Both the interceptor's 201 // and the data source's configuration is available in 202 // |SetupArgs|. Called on an internal Perfetto service thread, but not 203 // concurrently. OnSetup(const SetupArgs &)204 virtual void OnSetup(const SetupArgs&) {} 205 206 // Called when an intercepted data source starts. Called on an internal 207 // Perfetto service thread, but not concurrently. OnStart(const StartArgs &)208 virtual void OnStart(const StartArgs&) {} 209 210 // Called when an intercepted data source stops. Called on an internal 211 // Perfetto service thread, but not concurrently. OnStop(const StopArgs &)212 virtual void OnStop(const StopArgs&) {} 213 214 private: 215 friend class internal::InterceptorTraceWriter; 216 friend class internal::TracingMuxer; 217 friend class internal::TracingMuxerFake; 218 friend class internal::TracingMuxerImpl; 219 friend MockTracingMuxer; 220 template <class T> 221 friend class Interceptor; 222 223 // Data passed from DataSource::Trace() into the interceptor. 224 struct TracePacketCallbackArgs { 225 internal::DataSourceStaticState* static_state; 226 uint32_t instance_index; 227 protozero::ConstBytes packet_data; 228 ThreadLocalState* tls; 229 }; 230 231 // These callback functions are defined as stateless to avoid accidentally 232 // introducing cross-thread data races. 233 using TLSFactory = std::unique_ptr<ThreadLocalState> (*)( 234 internal::DataSourceStaticState*, 235 uint32_t data_source_instance_index); 236 using TracePacketCallback = void (*)(TracePacketCallbackArgs); 237 238 static void RegisterImpl( 239 const InterceptorDescriptor& descriptor, 240 std::function<std::unique_ptr<InterceptorBase>()> factory, 241 InterceptorBase::TLSFactory tls_factory, 242 InterceptorBase::TracePacketCallback on_trace_packet); 243 }; 244 245 // Templated interceptor instantiation. See above for usage. 246 template <class InterceptorType> 247 class PERFETTO_EXPORT Interceptor : public InterceptorBase { 248 public: 249 // A context object provided to the ThreadLocalState constructor. Provides 250 // access to the per-instance interceptor object. 251 class ThreadLocalStateArgs { 252 public: 253 ~ThreadLocalStateArgs() = default; 254 255 // Return a locked reference to the interceptor session. The session object 256 // will remain valid as long as the returned handle is in scope. GetInterceptorLocked()257 LockedHandle<InterceptorType> GetInterceptorLocked() { 258 auto* internal_state = static_state_->TryGet(data_source_instance_index_); 259 if (!internal_state) 260 return LockedHandle<InterceptorType>(); 261 return LockedHandle<InterceptorType>( 262 &internal_state->lock, 263 static_cast<InterceptorType*>(internal_state->interceptor.get())); 264 } 265 266 private: 267 friend class Interceptor<InterceptorType>; 268 friend class InterceptorContext; 269 friend class TracingMuxerImpl; 270 ThreadLocalStateArgs(internal::DataSourceStaticState * static_state,uint32_t data_source_instance_index)271 ThreadLocalStateArgs(internal::DataSourceStaticState* static_state, 272 uint32_t data_source_instance_index) 273 : static_state_(static_state), 274 data_source_instance_index_(data_source_instance_index) {} 275 276 internal::DataSourceStaticState* const static_state_; 277 const uint32_t data_source_instance_index_; 278 }; 279 280 // A context object provided to each call into |OnTracePacket|. Contains the 281 // intercepted serialized trace packet data. 282 class InterceptorContext { 283 public: 284 InterceptorContext(InterceptorContext&&) noexcept = default; 285 ~InterceptorContext() = default; 286 287 // Return a locked reference to the interceptor session. The session object 288 // will remain valid as long as the returned handle is in scope. GetInterceptorLocked()289 LockedHandle<InterceptorType> GetInterceptorLocked() { 290 return tls_args_.GetInterceptorLocked(); 291 } 292 293 // Return the thread-local state for this interceptor. See 294 // InterceptorBase::ThreadLocalState. GetThreadLocalState()295 typename InterceptorType::ThreadLocalState& GetThreadLocalState() { 296 return static_cast<typename InterceptorType::ThreadLocalState&>(*tls_); 297 } 298 299 // A buffer containing the serialized TracePacket protocol buffer message. 300 // This memory is only valid during the call to OnTracePacket. 301 protozero::ConstBytes packet_data; 302 303 private: 304 friend class Interceptor<InterceptorType>; InterceptorContext(TracePacketCallbackArgs args)305 InterceptorContext(TracePacketCallbackArgs args) 306 : packet_data(args.packet_data), 307 tls_args_(args.static_state, args.instance_index), 308 tls_(args.tls) {} 309 InterceptorContext(const InterceptorContext&) = delete; 310 InterceptorContext& operator=(const InterceptorContext&) = delete; 311 312 ThreadLocalStateArgs tls_args_; 313 InterceptorBase::ThreadLocalState* const tls_; 314 }; 315 316 // Register the interceptor for use in tracing sessions. 317 // The optional |constructor_args| will be passed to the interceptor when it 318 // is constructed. 319 template <class... Args> Register(const InterceptorDescriptor & descriptor,const Args &...constructor_args)320 static void Register(const InterceptorDescriptor& descriptor, 321 const Args&... constructor_args) { 322 auto factory = [constructor_args...]() { 323 return std::unique_ptr<InterceptorBase>( 324 new InterceptorType(constructor_args...)); 325 }; 326 auto tls_factory = [](internal::DataSourceStaticState* static_state, 327 uint32_t data_source_instance_index) { 328 // Don't bother allocating TLS state unless the interceptor is actually 329 // using it. 330 if (std::is_same<typename InterceptorType::ThreadLocalState, 331 InterceptorBase::ThreadLocalState>::value) { 332 return std::unique_ptr<InterceptorBase::ThreadLocalState>(nullptr); 333 } 334 ThreadLocalStateArgs args(static_state, data_source_instance_index); 335 return std::unique_ptr<InterceptorBase::ThreadLocalState>( 336 new typename InterceptorType::ThreadLocalState(args)); 337 }; 338 auto on_trace_packet = [](TracePacketCallbackArgs args) { 339 InterceptorType::OnTracePacket(InterceptorContext(std::move(args))); 340 }; 341 RegisterImpl(descriptor, std::move(factory), std::move(tls_factory), 342 std::move(on_trace_packet)); 343 } 344 }; 345 346 } // namespace perfetto 347 348 #endif // INCLUDE_PERFETTO_TRACING_INTERCEPTOR_H_ 349