tracing: Add init option to avoid linking system consumer

Today when using perfetto::Tracing::Initialize() with the system
backend, both the producer and the consumer side of the ipcs are linked
in, even though most perfetto users will not likely use the consumer
side.

The consumer side implementation is responsible for a large portion of
the statically linked binary size (~100Kbytes out of ~700kBytes, thanks
rzuklie@google.com for finding that out!).

This commit adds an option to perfetto::Tracing::Initialize to avoid
initializing the consumer side of the system backend.

Change-Id: I17a72f4864c6c300a6a734f93b349e6d020f1967
(cherry picked from commit 04073f60907237b14fa68f5fc422a291944b7b15)
Merged-In: I17a72f4864c6c300a6a734f93b349e6d020f1967
diff --git a/CHANGELOG b/CHANGELOG
index 99e6c4b..4b35e38 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -8,8 +8,8 @@
   UI:
     *
   SDK:
-    *
-
+    * Added TracingInitArgs::enable_system_consumer configuration option, that
+      allows the linker to discard the consumer IPC, if not required.
 
 v25.0 - 2022-04-01:
   Tracing service and probes:
diff --git a/include/perfetto/tracing/internal/system_tracing_backend.h b/include/perfetto/tracing/internal/system_tracing_backend.h
index 69a1cad..7617109 100644
--- a/include/perfetto/tracing/internal/system_tracing_backend.h
+++ b/include/perfetto/tracing/internal/system_tracing_backend.h
@@ -35,6 +35,8 @@
 // together with system traces, useful to correlate on the timeline system
 // events (e.g. scheduling slices from the kernel) with in-app events.
 namespace internal {
+
+// Full backend (with producer and consumer)
 class PERFETTO_EXPORT SystemTracingBackend : public TracingBackend {
  public:
   static TracingBackend* GetInstance();
@@ -49,6 +51,21 @@
   SystemTracingBackend();
 };
 
+// Producer only backend.
+class PERFETTO_EXPORT SystemTracingProducerOnlyBackend : public TracingBackend {
+ public:
+  static TracingBackend* GetInstance();
+
+  // TracingBackend implementation.
+  std::unique_ptr<ProducerEndpoint> ConnectProducer(
+      const ConnectProducerArgs&) override;
+  std::unique_ptr<ConsumerEndpoint> ConnectConsumer(
+      const ConnectConsumerArgs&) override;
+
+ private:
+  SystemTracingProducerOnlyBackend();
+};
+
 }  // namespace internal
 }  // namespace perfetto
 
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 9aae9e8..01d4851 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -115,6 +115,13 @@
   // callback instead of being logged directly.
   LogMessageCallback log_message_callback = nullptr;
 
+  // This flag can be set to false in order to avoid enabling the system
+  // consumer in Tracing::Initialize(), so that the linker can remove the unused
+  // consumer IPC implementation to reduce binary size. When this option is
+  // false, calling Tracing::NewTrace() on the system backend will fail. This
+  // setting only has an effect if kSystemBackend is specified in |backends|.
+  bool enable_system_consumer = true;
+
  protected:
   friend class Tracing;
   friend class internal::TracingMuxerImpl;
@@ -124,11 +131,13 @@
   bool operator==(const TracingInitArgs& other) const {
     return std::tie(backends, custom_backend, platform, shmem_size_hint_kb,
                     shmem_page_size_hint_kb, in_process_backend_factory_,
-                    system_backend_factory_, dcheck_is_on_) ==
+                    system_backend_factory_, dcheck_is_on_,
+                    enable_system_consumer) ==
            std::tie(other.backends, other.custom_backend, other.platform,
                     other.shmem_size_hint_kb, other.shmem_page_size_hint_kb,
                     other.in_process_backend_factory_,
-                    other.system_backend_factory_, other.dcheck_is_on_);
+                    other.system_backend_factory_, other.dcheck_is_on_,
+                    other.enable_system_consumer);
   }
 
   using BackendFactoryFunction = TracingBackend* (*)();
@@ -161,8 +170,13 @@
           &internal::InProcessTracingBackend::GetInstance;
     }
     if (args.backends & kSystemBackend) {
-      args_copy.system_backend_factory_ =
-          &internal::SystemTracingBackend::GetInstance;
+      if (args.enable_system_consumer) {
+        args_copy.system_backend_factory_ =
+            &internal::SystemTracingBackend::GetInstance;
+      } else {
+        args_copy.system_backend_factory_ =
+            &internal::SystemTracingProducerOnlyBackend::GetInstance;
+      }
     }
     InitializeInternal(args_copy);
   }
@@ -172,8 +186,6 @@
 
   // Start a new tracing session using the given tracing backend. Use
   // |kUnspecifiedBackend| to select an available backend automatically.
-  // For the moment this can be used only when initializing tracing in
-  // kInProcess mode. For the system mode use the 'bin/perfetto' cmdline client.
   static std::unique_ptr<TracingSession> NewTrace(
       BackendType = kUnspecifiedBackend);
 
diff --git a/src/tracing/internal/system_tracing_backend.cc b/src/tracing/internal/system_tracing_backend.cc
index 5aa3493..5562421 100644
--- a/src/tracing/internal/system_tracing_backend.cc
+++ b/src/tracing/internal/system_tracing_backend.cc
@@ -25,17 +25,10 @@
 
 namespace perfetto {
 namespace internal {
+namespace {
 
-// static
-TracingBackend* SystemTracingBackend::GetInstance() {
-  static auto* instance = new SystemTracingBackend();
-  return instance;
-}
-
-SystemTracingBackend::SystemTracingBackend() {}
-
-std::unique_ptr<ProducerEndpoint> SystemTracingBackend::ConnectProducer(
-    const ConnectProducerArgs& args) {
+std::unique_ptr<ProducerEndpoint> CreateProducerEndpoint(
+    const TracingBackend::ConnectProducerArgs& args) {
   PERFETTO_DCHECK(args.task_runner->RunsTasksOnCurrentThread());
 
   auto endpoint = ProducerIPCClient::Connect(
@@ -47,6 +40,21 @@
   return endpoint;
 }
 
+}  // namespace
+
+// static
+TracingBackend* SystemTracingBackend::GetInstance() {
+  static auto* instance = new SystemTracingBackend();
+  return instance;
+}
+
+SystemTracingBackend::SystemTracingBackend() {}
+
+std::unique_ptr<ProducerEndpoint> SystemTracingBackend::ConnectProducer(
+    const ConnectProducerArgs& args) {
+  return CreateProducerEndpoint(args);
+}
+
 std::unique_ptr<ConsumerEndpoint> SystemTracingBackend::ConnectConsumer(
     const ConnectConsumerArgs& args) {
   auto endpoint = ConsumerIPCClient::Connect(GetConsumerSocket(), args.consumer,
@@ -55,5 +63,29 @@
   return endpoint;
 }
 
+// static
+TracingBackend* SystemTracingProducerOnlyBackend::GetInstance() {
+  static auto* instance = new SystemTracingProducerOnlyBackend();
+  return instance;
+}
+
+SystemTracingProducerOnlyBackend::SystemTracingProducerOnlyBackend() {}
+
+std::unique_ptr<ProducerEndpoint>
+SystemTracingProducerOnlyBackend::ConnectProducer(
+    const ConnectProducerArgs& args) {
+  return CreateProducerEndpoint(args);
+}
+
+std::unique_ptr<ConsumerEndpoint>
+SystemTracingProducerOnlyBackend::ConnectConsumer(
+    const ConnectConsumerArgs& args) {
+  base::ignore_result(args);
+  PERFETTO_FATAL(
+      "System backend consumer support disabled. "
+      "TracingInitArgs::enable_system_consumer was false");
+  return nullptr;
+}
+
 }  // namespace internal
 }  // namespace perfetto
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index dd34ca4..876b90b 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -761,6 +761,7 @@
     rb.backend = backend;
     rb.id = backend_id;
     rb.type = type;
+    rb.consumer_enabled = type != kSystemBackend || args.enable_system_consumer;
     rb.producer.reset(new ProducerImpl(this, backend_id,
                                        args.shmem_batch_commits_duration_ms));
     rb.producer_conn_args.producer = rb.producer.get();
@@ -1645,6 +1646,10 @@
         continue;
       }
 
+      if (!backend.consumer_enabled) {
+        continue;
+      }
+
       TracingBackendId backend_id = backend.id;
 
       // Create the consumer now, even if we have to ask the embedder below, so
diff --git a/src/tracing/internal/tracing_muxer_impl.h b/src/tracing/internal/tracing_muxer_impl.h
index 1841cd4..c545a0b 100644
--- a/src/tracing/internal/tracing_muxer_impl.h
+++ b/src/tracing/internal/tracing_muxer_impl.h
@@ -380,6 +380,8 @@
     // The calling code can request more than one concurrently active tracing
     // session for the same backend. We need to create one consumer per session.
     std::vector<std::unique_ptr<ConsumerImpl>> consumers;
+
+    bool consumer_enabled = true;
   };
 
   void UpdateDataSourceOnAllBackends(RegisteredDataSource& rds,
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 0604d09..98963fc 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -4952,6 +4952,44 @@
   EXPECT_EQ(it, trace.packet().end());
 }
 
+TEST(PerfettoApiInitTest, DisableSystemConsumer) {
+  g_test_tracing_policy->should_allow_consumer_connection = true;
+
+  if (!perfetto::test::StartSystemService()) {
+    GTEST_SKIP();
+  }
+
+  EXPECT_FALSE(perfetto::Tracing::IsInitialized());
+  TracingInitArgs args;
+  args.backends = perfetto::kSystemBackend;
+  args.tracing_policy = g_test_tracing_policy;
+  args.enable_system_consumer = false;
+  perfetto::Tracing::Initialize(args);
+
+  // If this wasn't the first test to run in this process, any producers
+  // connected to the old system service will have been disconnected by the
+  // service restarting above. Wait for all producers to connect again before
+  // proceeding with the test.
+  perfetto::test::SyncProducers();
+
+  perfetto::test::DisableReconnectLimit();
+
+  std::unique_ptr<perfetto::TracingSession> ts =
+      perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+
+  // Creating the consumer should cause an asynchronous disconnect error.
+  WaitableTestEvent got_error;
+  ts->SetOnErrorCallback([&](perfetto::TracingError error) {
+    EXPECT_EQ(perfetto::TracingError::kDisconnected, error.code);
+    EXPECT_FALSE(error.message.empty());
+    got_error.Notify();
+  });
+  got_error.Wait();
+  ts.reset();
+
+  perfetto::Tracing::ResetForTesting();
+}
+
 struct BackendTypeAsString {
   std::string operator()(
       const ::testing::TestParamInfo<perfetto::BackendType>& info) const {