• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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