chromium/third_party/perfetto/include/perfetto/tracing/interceptor.h

/*
 * 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_