Add support for disallowing concurrent sessions

A custom data source class can have
`static constexpr bool kSupportsMultipleInstances = false;`
member to indicate its support for multiple instances. It is true by
default.

When this flag is false, we cannot have multiple instances of that data
sources. When a data source is already active and if we attempt
to start another instance of that data source (via another tracing
session), it will fail.

We have also added a flag named `support_concurrent_sessions` in
TracingInitArgs. It is true by default. When its is set to false,
it overrides `DataSource::kSupportsMultipleInstances` for all
the data sources.

Bug: b/245118150
Change-Id: I012a8bea0f29e50beb3ce7802d8873e5a0161e58
diff --git a/include/perfetto/tracing/data_source.h b/include/perfetto/tracing/data_source.h
index b98a033..fafb5df 100644
--- a/include/perfetto/tracing/data_source.h
+++ b/include/perfetto/tracing/data_source.h
@@ -177,6 +177,12 @@
   constexpr static BufferExhaustedPolicy kBufferExhaustedPolicy =
       BufferExhaustedPolicy::kDrop;
 
+  // When this flag is false, we cannot have multiple instances of this data
+  // source. When a data source is already active and if we attempt
+  // to start another instance of that data source (via another tracing
+  // session), it will fail to start the second instance of data source.
+  static constexpr bool kSupportsMultipleInstances = true;
+
   // Argument passed to the lambda function passed to Trace() (below).
   class TraceContext {
    public:
@@ -467,8 +473,9 @@
           new DataSourceType(constructor_args...));
     };
     auto* tracing_impl = internal::TracingMuxer::Get();
-    return tracing_impl->RegisterDataSource(descriptor, factory,
-                                            &static_state_);
+    return tracing_impl->RegisterDataSource(
+        descriptor, factory, DataSourceType::kSupportsMultipleInstances,
+        &static_state_);
   }
 
   // Updates the data source descriptor.
diff --git a/include/perfetto/tracing/internal/tracing_muxer.h b/include/perfetto/tracing/internal/tracing_muxer.h
index e067652..433880e 100644
--- a/include/perfetto/tracing/internal/tracing_muxer.h
+++ b/include/perfetto/tracing/internal/tracing_muxer.h
@@ -62,6 +62,7 @@
   using DataSourceFactory = std::function<std::unique_ptr<DataSourceBase>()>;
   virtual bool RegisterDataSource(const DataSourceDescriptor&,
                                   DataSourceFactory,
+                                  bool supports_multiple_instances,
                                   DataSourceStaticState*) = 0;
 
   // Updates the DataSourceDescriptor for the DataSource.
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 179869b..ea31a2b 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -116,6 +116,13 @@
   // callback instead of being logged directly.
   LogMessageCallback log_message_callback = nullptr;
 
+  // When this flag is set to false, it overrides
+  // `DataSource::kSupportsMultipleInstances` for all the data sources.
+  // As a result when a tracing session is already running and if we attempt to
+  // start another session, it will fail to start the data source which were
+  // already active.
+  bool supports_multiple_data_source_instances = true;
+
  protected:
   friend class Tracing;
   friend class internal::TracingMuxerImpl;
diff --git a/src/tracing/internal/tracing_muxer_fake.cc b/src/tracing/internal/tracing_muxer_fake.cc
index a6700f3..1f8df96 100644
--- a/src/tracing/internal/tracing_muxer_fake.cc
+++ b/src/tracing/internal/tracing_muxer_fake.cc
@@ -55,6 +55,7 @@
 
 bool TracingMuxerFake::RegisterDataSource(const DataSourceDescriptor&,
                                           DataSourceFactory,
+                                          bool,
                                           DataSourceStaticState*) {
   FailUninitialized();
 }
diff --git a/src/tracing/internal/tracing_muxer_fake.h b/src/tracing/internal/tracing_muxer_fake.h
index 78fc769..876e427 100644
--- a/src/tracing/internal/tracing_muxer_fake.h
+++ b/src/tracing/internal/tracing_muxer_fake.h
@@ -54,6 +54,7 @@
   // TracingMuxer implementation.
   bool RegisterDataSource(const DataSourceDescriptor&,
                           DataSourceFactory,
+                          bool supports_multiple_instances,
                           DataSourceStaticState*) override;
   void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
                                   const DataSourceStaticState*) override;
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 83e4f08..66a14c1 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -821,6 +821,8 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);  // Rebind the thread checker.
 
   policy_ = args.tracing_policy;
+  supports_multiple_data_source_instances_ =
+      args.supports_multiple_data_source_instances;
 
   auto add_backend = [this, &args](TracingBackend* backend, BackendType type) {
     if (!backend) {
@@ -877,6 +879,7 @@
 bool TracingMuxerImpl::RegisterDataSource(
     const DataSourceDescriptor& descriptor,
     DataSourceFactory factory,
+    bool supports_multiple_instances,
     DataSourceStaticState* static_state) {
   // Ignore repeated registrations.
   if (static_state->index != kMaxDataSources)
@@ -903,11 +906,14 @@
   hash.Update(base::GetWallTimeNs().count());
   static_state->id = hash.digest() ? hash.digest() : 1;
 
-  task_runner_->PostTask([this, descriptor, factory, static_state] {
+  task_runner_->PostTask([this, descriptor, factory, static_state,
+                          supports_multiple_instances] {
     data_sources_.emplace_back();
     RegisteredDataSource& rds = data_sources_.back();
     rds.descriptor = descriptor;
     rds.factory = factory;
+    rds.supports_multiple_instances =
+        supports_multiple_data_source_instances_ && supports_multiple_instances;
     rds.static_state = static_state;
 
     UpdateDataSourceOnAllBackends(rds, /*is_changed=*/false);
@@ -1078,6 +1084,17 @@
   PERFETTO_DCHECK_THREAD(thread_checker_);
   DataSourceStaticState& static_state = *rds.static_state;
 
+  if (!rds.supports_multiple_instances) {
+    for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
+      if (static_state.TryGet(i)) {
+        PERFETTO_ELOG(
+            "Failed to setup data source because some another instance of this "
+            "data source is already active");
+        return FindDataSourceRes();
+      }
+    }
+  }
+
   for (uint32_t i = 0; i < kMaxDataSourceInstances; i++) {
     // Find a free slot.
     if (static_state.TryGet(i))
@@ -1232,6 +1249,7 @@
   if (ds.internal_state->interceptor)
     ds.internal_state->interceptor->OnStart({});
   ds.internal_state->trace_lambda_enabled = true;
+  PERFETTO_DCHECK(ds.internal_state->data_source != nullptr);
   ds.internal_state->data_source->OnStart(start_args);
 }
 
diff --git a/src/tracing/internal/tracing_muxer_impl.h b/src/tracing/internal/tracing_muxer_impl.h
index bf04fd3..cec7836 100644
--- a/src/tracing/internal/tracing_muxer_impl.h
+++ b/src/tracing/internal/tracing_muxer_impl.h
@@ -103,6 +103,7 @@
   struct RegisteredDataSource {
     DataSourceDescriptor descriptor;
     DataSourceFactory factory{};
+    bool supports_multiple_instances = false;
     DataSourceStaticState* static_state = nullptr;
   };
 
@@ -113,6 +114,7 @@
   // TracingMuxer implementation.
   bool RegisterDataSource(const DataSourceDescriptor&,
                           DataSourceFactory,
+                          bool supports_multiple_instances,
                           DataSourceStaticState*) override;
   void UpdateDataSourceDescriptor(const DataSourceDescriptor&,
                                   const DataSourceStaticState*) override;
@@ -476,6 +478,9 @@
   std::vector<RegisteredInterceptor> interceptors_;
   TracingPolicy* policy_ = nullptr;
 
+  // Learn more at TracingInitArgs::supports_multiple_data_source_instances
+  bool supports_multiple_data_source_instances_ = true;
+
   std::atomic<TracingSessionGlobalID> next_tracing_session_id_{};
   std::atomic<uint32_t> next_data_source_index_{};
   uint32_t muxer_id_for_testing_{};
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 372efd0..173ffa1 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -291,6 +291,7 @@
   bool RegisterDataSource(
       const perfetto::DataSourceDescriptor& dsd,
       DataSourceFactory,
+      bool,
       perfetto::internal::DataSourceStaticState* static_state) override {
     data_sources.emplace_back(DataSource{dsd, static_state});
     return true;
@@ -5735,6 +5736,100 @@
   EXPECT_THAT(slices, ElementsAre("B:test.MainEvent"));
 }
 
+class ConcurrentSessionTest : public ::testing::Test {
+ public:
+  void SetUp() override {
+    if (!perfetto::test::StartSystemService()) {
+      GTEST_SKIP();
+    }
+    ASSERT_FALSE(perfetto::Tracing::IsInitialized());
+  }
+
+  void InitPerfetto(bool supports_multiple_data_source_instances = true) {
+    TracingInitArgs args;
+    args.backends = perfetto::kInProcessBackend | perfetto::kSystemBackend;
+    args.supports_multiple_data_source_instances =
+        supports_multiple_data_source_instances;
+    g_test_tracing_policy->should_allow_consumer_connection = true;
+    args.tracing_policy = g_test_tracing_policy;
+    perfetto::Tracing::Initialize(args);
+    perfetto::TrackEvent::Register();
+    perfetto::test::SyncProducers();
+    perfetto::test::DisableReconnectLimit();
+  }
+
+  void TearDown() override { perfetto::Tracing::ResetForTesting(); }
+
+  static std::unique_ptr<perfetto::TracingSession> StartTracing(
+      perfetto::BackendType backend_type) {
+    perfetto::TraceConfig cfg;
+    cfg.add_buffers()->set_size_kb(1024);
+    auto* ds_cfg = cfg.add_data_sources()->mutable_config();
+    ds_cfg->set_name("track_event");
+    auto tracing_session = perfetto::Tracing::NewTrace(backend_type);
+    tracing_session->Setup(cfg);
+    tracing_session->StartBlocking();
+    return tracing_session;
+  }
+  std::vector<std::string> StopTracing(
+      std::unique_ptr<perfetto::TracingSession> tracing_session,
+      bool expect_incremental_state_cleared = true) {
+    perfetto::TrackEvent::Flush();
+    tracing_session->StopBlocking();
+    std::vector<char> trace_data(tracing_session->ReadTraceBlocking());
+    return ReadSlicesFromTrace(trace_data, expect_incremental_state_cleared);
+  }
+};
+
+// Verify that concurrent sessions works well by default.
+// (i.e. when `disallow_concurrent_sessions` param is not set)
+TEST_F(ConcurrentSessionTest, ConcurrentBackends) {
+  InitPerfetto();
+  auto tracing_session1 = StartTracing(perfetto::kSystemBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame1");
+
+  auto tracing_session2 = StartTracing(perfetto::kInProcessBackend);
+  // Should be recorded by both sessions.
+  TRACE_EVENT_BEGIN("test", "DrawGame2");
+
+  auto slices1 = StopTracing(std::move(tracing_session1));
+  EXPECT_THAT(slices1, ElementsAre("B:test.DrawGame1", "B:test.DrawGame2"));
+
+  auto slices2 = StopTracing(std::move(tracing_session2));
+  EXPECT_THAT(slices2, ElementsAre("B:test.DrawGame2"));
+
+  auto tracing_session3 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame3");
+
+  auto slices3 = StopTracing(std::move(tracing_session3));
+  EXPECT_THAT(slices3, ElementsAre("B:test.DrawGame3"));
+}
+
+// When `supports_multiple_data_source_instances = false`, second session
+// should not be started.
+TEST_F(ConcurrentSessionTest, DisallowMultipleSessionBasic) {
+  InitPerfetto(/* supports_multiple_data_source_instances = */ false);
+  auto tracing_session1 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame1");
+
+  auto tracing_session2 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame2");
+
+  auto slices1 = StopTracing(std::move(tracing_session1));
+  EXPECT_THAT(slices1, ElementsAre("B:test.DrawGame1", "B:test.DrawGame2"));
+
+  auto slices2 = StopTracing(std::move(tracing_session2),
+                             false /* expect_incremental_state_cleared */);
+  // Because `tracing_session2` was not really started.
+  EXPECT_THAT(slices2, ElementsAre());
+
+  auto tracing_session3 = StartTracing(perfetto::kInProcessBackend);
+  TRACE_EVENT_BEGIN("test", "DrawGame3");
+
+  auto slices3 = StopTracing(std::move(tracing_session3));
+  EXPECT_THAT(slices3, ElementsAre("B:test.DrawGame3"));
+}
+
 struct BackendTypeAsString {
   std::string operator()(
       const ::testing::TestParamInfo<perfetto::BackendType>& info) const {