docs: Add documentation for interception API

Bug: 170628040
Change-Id: I8a98d6e4f35464b7992c6744ffe9e6b5aaf89a2a
diff --git a/CHANGELOG b/CHANGELOG
index 326e1e8..c6781fd 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 Unreleased:
   Tracing service and probes:
-    *
+    * Added trace packet interceptor API for rerouting trace data into
+      non-Perfetto systems.
   Trace Processor:
     *
   UI:
diff --git a/docs/instrumentation/interceptors.md b/docs/instrumentation/interceptors.md
new file mode 100644
index 0000000..f61c469
--- /dev/null
+++ b/docs/instrumentation/interceptors.md
@@ -0,0 +1,143 @@
+# Trace packet interceptors (Tracing SDK)
+
+A trace packet 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:
+
+```C++
+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
+shown below.
+
+```C++
+perfetto::InterceptorDescriptor desc;
+desc.set_name("my_interceptor");
+MyInterceptor::Register(desc);
+```
+
+Finally, an interceptor is enabled through the trace config like this:
+
+```C++
+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:
+
+   ```C++
+   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:
+
+   ```C++
+   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:
+
+   ```C++
+   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:
+
+   ```C++
+   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];
+       }
+       ...
+     }
+   };
+   ```
\ No newline at end of file
diff --git a/docs/toc.md b/docs/toc.md
index b3b87bc..ac1500b 100644
--- a/docs/toc.md
+++ b/docs/toc.md
@@ -28,6 +28,7 @@
 * [App Instrumentation](#)
   * [Tracing SDK](instrumentation/tracing-sdk.md)
   * [Track events](instrumentation/track-events.md)
+  * [Interceptors](instrumentation/interceptors.md)
 
 * [Trace analysis](#)
   * [Trace Processor (SQL)](analysis/trace-processor.md)