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 InterceptorTraceWriterTest; 175 class TracingMuxer; 176 class TracingMuxerFake; 177 class TracingMuxerImpl; 178 } // namespace internal 179 180 // A virtual base class for interceptors. Users should derive from the templated 181 // subclass below instead of this one. 182 class PERFETTO_EXPORT_COMPONENT InterceptorBase { 183 public: 184 virtual ~InterceptorBase(); 185 186 // A virtual base class for thread-local state needed by the interceptor. 187 // To define your own state, subclass this with the same name in the 188 // interceptor class. A reference to the state can then be looked up through 189 // context.GetThreadLocalState() in the trace packet interceptor function. 190 class PERFETTO_EXPORT_COMPONENT ThreadLocalState { 191 public: 192 virtual ~ThreadLocalState(); 193 }; 194 195 struct SetupArgs { 196 const DataSourceConfig& config; 197 }; 198 struct StartArgs {}; 199 struct StopArgs {}; 200 201 // Called when an intercepted data source is set up. Both the interceptor's 202 // and the data source's configuration is available in 203 // |SetupArgs|. Called on an internal Perfetto service thread, but not 204 // concurrently. OnSetup(const SetupArgs &)205 virtual void OnSetup(const SetupArgs&) {} 206 207 // Called when an intercepted data source starts. Called on an internal 208 // Perfetto service thread, but not concurrently. OnStart(const StartArgs &)209 virtual void OnStart(const StartArgs&) {} 210 211 // Called when an intercepted data source stops. Called on an internal 212 // Perfetto service thread, but not concurrently. OnStop(const StopArgs &)213 virtual void OnStop(const StopArgs&) {} 214 215 private: 216 friend class internal::InterceptorTraceWriter; 217 friend class internal::InterceptorTraceWriterTest; 218 friend class internal::TracingMuxer; 219 friend class internal::TracingMuxerFake; 220 friend class internal::TracingMuxerImpl; 221 friend MockTracingMuxer; 222 template <class T> 223 friend class Interceptor; 224 225 // Data passed from DataSource::Trace() into the interceptor. 226 struct TracePacketCallbackArgs { 227 internal::DataSourceStaticState* static_state; 228 uint32_t instance_index; 229 protozero::ConstBytes packet_data; 230 ThreadLocalState* tls; 231 }; 232 233 // These callback functions are defined as stateless to avoid accidentally 234 // introducing cross-thread data races. 235 using TLSFactory = std::unique_ptr<ThreadLocalState> (*)( 236 internal::DataSourceStaticState*, 237 uint32_t data_source_instance_index); 238 using TracePacketCallback = void (*)(TracePacketCallbackArgs); 239 240 static void RegisterImpl( 241 const InterceptorDescriptor& descriptor, 242 std::function<std::unique_ptr<InterceptorBase>()> factory, 243 InterceptorBase::TLSFactory tls_factory, 244 InterceptorBase::TracePacketCallback on_trace_packet); 245 }; 246 247 // Templated interceptor instantiation. See above for usage. 248 template <class InterceptorType> 249 class PERFETTO_EXPORT_COMPONENT Interceptor : public InterceptorBase { 250 public: 251 // A context object provided to the ThreadLocalState constructor. Provides 252 // access to the per-instance interceptor object. 253 class ThreadLocalStateArgs { 254 public: 255 ~ThreadLocalStateArgs() = default; 256 257 ThreadLocalStateArgs(const ThreadLocalStateArgs&) = delete; 258 ThreadLocalStateArgs& operator=(const ThreadLocalStateArgs&) = delete; 259 260 ThreadLocalStateArgs(ThreadLocalStateArgs&&) noexcept = default; 261 ThreadLocalStateArgs& operator=(ThreadLocalStateArgs&&) noexcept = default; 262 263 // Return a locked reference to the interceptor session. The session object 264 // will remain valid as long as the returned handle is in scope. GetInterceptorLocked()265 LockedHandle<InterceptorType> GetInterceptorLocked() { 266 auto* internal_state = static_state_->TryGet(data_source_instance_index_); 267 if (!internal_state) 268 return LockedHandle<InterceptorType>(); 269 std::unique_lock<std::recursive_mutex> lock(internal_state->lock); 270 return LockedHandle<InterceptorType>( 271 std::move(lock), 272 static_cast<InterceptorType*>(internal_state->interceptor.get())); 273 } 274 275 private: 276 friend class Interceptor<InterceptorType>; 277 friend class InterceptorContext; 278 friend class TracingMuxerImpl; 279 ThreadLocalStateArgs(internal::DataSourceStaticState * static_state,uint32_t data_source_instance_index)280 ThreadLocalStateArgs(internal::DataSourceStaticState* static_state, 281 uint32_t data_source_instance_index) 282 : static_state_(static_state), 283 data_source_instance_index_(data_source_instance_index) {} 284 285 internal::DataSourceStaticState* const static_state_; 286 const uint32_t data_source_instance_index_; 287 }; 288 289 // A context object provided to each call into |OnTracePacket|. Contains the 290 // intercepted serialized trace packet data. 291 class InterceptorContext { 292 public: 293 InterceptorContext(InterceptorContext&&) noexcept = default; 294 ~InterceptorContext() = default; 295 296 // Return a locked reference to the interceptor session. The session object 297 // will remain valid as long as the returned handle is in scope. GetInterceptorLocked()298 LockedHandle<InterceptorType> GetInterceptorLocked() { 299 return tls_args_.GetInterceptorLocked(); 300 } 301 302 // Return the thread-local state for this interceptor. See 303 // InterceptorBase::ThreadLocalState. GetThreadLocalState()304 typename InterceptorType::ThreadLocalState& GetThreadLocalState() { 305 return static_cast<typename InterceptorType::ThreadLocalState&>(*tls_); 306 } 307 308 // A buffer containing the serialized TracePacket protocol buffer message. 309 // This memory is only valid during the call to OnTracePacket. 310 protozero::ConstBytes packet_data; 311 312 private: 313 friend class Interceptor<InterceptorType>; InterceptorContext(TracePacketCallbackArgs args)314 InterceptorContext(TracePacketCallbackArgs args) 315 : packet_data(args.packet_data), 316 tls_args_(args.static_state, args.instance_index), 317 tls_(args.tls) {} 318 InterceptorContext(const InterceptorContext&) = delete; 319 InterceptorContext& operator=(const InterceptorContext&) = delete; 320 321 ThreadLocalStateArgs tls_args_; 322 InterceptorBase::ThreadLocalState* const tls_; 323 }; 324 325 // Register the interceptor for use in tracing sessions. 326 // The optional |constructor_args| will be passed to the interceptor when it 327 // is constructed. 328 template <class... Args> Register(const InterceptorDescriptor & descriptor,const Args &...constructor_args)329 static void Register(const InterceptorDescriptor& descriptor, 330 const Args&... constructor_args) { 331 auto factory = [constructor_args...]() { 332 return std::unique_ptr<InterceptorBase>( 333 new InterceptorType(constructor_args...)); 334 }; 335 auto tls_factory = [](internal::DataSourceStaticState* static_state, 336 uint32_t data_source_instance_index) { 337 // Don't bother allocating TLS state unless the interceptor is actually 338 // using it. 339 if (std::is_same<typename InterceptorType::ThreadLocalState, 340 InterceptorBase::ThreadLocalState>::value) { 341 return std::unique_ptr<InterceptorBase::ThreadLocalState>(nullptr); 342 } 343 ThreadLocalStateArgs args(static_state, data_source_instance_index); 344 return std::unique_ptr<InterceptorBase::ThreadLocalState>( 345 new typename InterceptorType::ThreadLocalState(args)); 346 }; 347 auto on_trace_packet = [](TracePacketCallbackArgs args) { 348 InterceptorType::OnTracePacket(InterceptorContext(std::move(args))); 349 }; 350 RegisterImpl(descriptor, std::move(factory), std::move(tls_factory), 351 std::move(on_trace_packet)); 352 } 353 }; 354 355 } // namespace perfetto 356 357 #endif // INCLUDE_PERFETTO_TRACING_INTERCEPTOR_H_ 358