/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef INCLUDE_PERFETTO_TRACING_INTERCEPTOR_H_ #define INCLUDE_PERFETTO_TRACING_INTERCEPTOR_H_ // An interceptor is used to redirect trace packets written by a data source // into a custom backend instead of the normal Perfetto tracing service. For // example, the console interceptor prints all trace packets to the console as // they are generated. Another potential use is exporting trace data to another // tracing service such as Android ATrace or Windows ETW. // // An interceptor is defined by subclassing the perfetto::Interceptor template: // // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { // public: // ~MyInterceptor() override = default; // // // This function is called for each intercepted trace packet. |context| // // contains information about the trace packet as well as other state // // tracked by the interceptor (e.g., see ThreadLocalState). // // // // Intercepted trace data is provided in the form of serialized protobuf // // bytes, accessed through the |context.packet_data| field. // // // // Warning: this function can be called on any thread at any time. See // // below for how to safely access shared interceptor data from here. // static void OnTracePacket(InterceptorContext context) { // perfetto::protos::pbzero::TracePacket::Decoder packet( // context.packet_data.data, context.packet_data.size); // // ... Write |packet| to the desired destination ... // } // }; // // An interceptor should be registered before any tracing sessions are started. // Note that the interceptor also needs to be activated through the trace config // as shown below. // // perfetto::InterceptorDescriptor desc; // desc.set_name("my_interceptor"); // MyInterceptor::Register(desc); // // Finally, an interceptor is enabled through the trace config like this: // // perfetto::TraceConfig cfg; // auto* ds_cfg = cfg.add_data_sources()->mutable_config(); // ds_cfg->set_name("data_source_to_intercept"); // e.g. "track_event" // ds_cfg->mutable_interceptor_config()->set_name("my_interceptor"); // // Once an interceptor is enabled, all data from the affected data sources is // sent to the interceptor instead of the main tracing buffer. // // Interceptor state // ================= // // Besides the serialized trace packet data, the |OnTracePacket| interceptor // function can access three other types of state: // // 1. Global state: this is no different from a normal static function, but care // must be taken because |OnTracePacket| can be called concurrently on any // thread at any time. // // 2. Per-data source instance state: since the interceptor class is // automatically instantiated for each intercepted data source, its fields // can be used to store per-instance data such as the trace config. This data // can be maintained through the OnSetup/OnStart/OnStop callbacks: // // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { // public: // void OnSetup(const SetupArgs& args) override { // enable_foo_ = args.config.interceptor_config().enable_foo(); // } // // bool enable_foo_{}; // }; // // In the interceptor function this data must be accessed through a scoped // lock for safety: // // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { // ... // static void OnTracePacket(InterceptorContext context) { // auto my_interceptor = context.GetInterceptorLocked(); // if (my_interceptor) { // // Access fields of MyInterceptor here. // if (my_interceptor->enable_foo_) { ... } // } // ... // } // }; // // Since accessing this data involves holding a lock, it should be done // sparingly. // // 3. Per-thread/TraceWriter state: many data sources use interning to avoid // repeating common data in the trace. Since the interning dictionaries are // typically kept individually for each TraceWriter sequence (i.e., per // thread), an interceptor can declare a data structure with lifetime // matching the TraceWriter: // // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { // public: // struct ThreadLocalState // : public perfetto::InterceptorBase::ThreadLocalState { // ThreadLocalState(ThreadLocalStateArgs&) override = default; // ~ThreadLocalState() override = default; // // std::map<size_t, std::string> event_names; // }; // }; // // This per-thread state can then be accessed and maintained in // |OnTracePacket| like this: // // class MyInterceptor : public perfetto::Interceptor<MyInterceptor> { // ... // static void OnTracePacket(InterceptorContext context) { // // Updating interned data. // auto& tls = context.GetThreadLocalState(); // if (parsed_packet.sequence_flags() & perfetto::protos::pbzero:: // TracePacket::SEQ_INCREMENTAL_STATE_CLEARED) { // tls.event_names.clear(); // } // for (const auto& entry : parsed_packet.interned_data().event_names()) // tls.event_names[entry.iid()] = entry.name(); // // // Looking up interned data. // if (parsed_packet.has_track_event()) { // size_t name_iid = parsed_packet.track_event().name_iid(); // const std::string& event_name = tls.event_names[name_iid]; // } // ... // } // }; // #include <functional> #include "perfetto/protozero/field.h" #include "perfetto/tracing/core/forward_decls.h" #include "perfetto/tracing/internal/basic_types.h" #include "perfetto/tracing/internal/data_source_internal.h" #include "perfetto/tracing/locked_handle.h" namespace { class MockTracingMuxer; } namespace perfetto { namespace protos { namespace gen { class DataSourceConfig; class InterceptorDescriptor; } // namespace gen } // namespace protos InterceptorDescriptor; namespace internal { class InterceptorTraceWriter; class InterceptorTraceWriterTest; class TracingMuxer; class TracingMuxerFake; class TracingMuxerImpl; } // namespace internal // A virtual base class for interceptors. Users should derive from the templated // subclass below instead of this one. class PERFETTO_EXPORT_COMPONENT InterceptorBase { … }; // Templated interceptor instantiation. See above for usage. template <class InterceptorType> class PERFETTO_EXPORT_COMPONENT Interceptor : public InterceptorBase { … }; } // namespace perfetto #endif // INCLUDE_PERFETTO_TRACING_INTERCEPTOR_H_