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 {