tracing: Send triggers on connection
The newly introduced ActivateTriggers only sends triggers to the
currently connected backends.
Since perfetto::Tracing::Initialize() is not blocking, it can happen
that a backend takes a while to connect.
If a registered backend is not connected when ActivateTriggers is
called, the system will remember that and send the trigger when the
backend connects.
An expiration must be provided to avoid sending triggers if the tracing
service takes too much to connect.
Bug: 259929612
Change-Id: I183ef3ae04e960797adc835e137f854d0ebd409d
diff --git a/Android.bp b/Android.bp
index 09c13f0..640af7c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2027,6 +2027,7 @@
":perfetto_src_tracing_core_service",
":perfetto_src_tracing_core_test_support",
":perfetto_src_tracing_in_process_backend",
+ ":perfetto_src_tracing_integrationtests",
":perfetto_src_tracing_ipc_common",
":perfetto_src_tracing_ipc_consumer_consumer",
":perfetto_src_tracing_ipc_default_socket",
@@ -10507,6 +10508,14 @@
],
}
+// GN: //src/tracing:integrationtests
+filegroup {
+ name: "perfetto_src_tracing_integrationtests",
+ srcs: [
+ "src/tracing/internal/tracing_muxer_impl_integrationtest.cc",
+ ],
+}
+
// GN: //src/tracing/ipc:common
filegroup {
name: "perfetto_src_tracing_ipc_common",
diff --git a/gn/perfetto_integrationtests.gni b/gn/perfetto_integrationtests.gni
index 2001d34..02c3b11 100644
--- a/gn/perfetto_integrationtests.gni
+++ b/gn/perfetto_integrationtests.gni
@@ -21,7 +21,10 @@
if (enable_perfetto_ipc && enable_perfetto_system_consumer) {
perfetto_integrationtests_targets +=
- [ "src/tracing/test:tracing_integration_test" ]
+ [
+ "src/tracing/test:tracing_integration_test",
+ "src/tracing:integrationtests",
+ ]
}
if (enable_perfetto_traced_probes) {
diff --git a/include/perfetto/tracing/internal/tracing_muxer.h b/include/perfetto/tracing/internal/tracing_muxer.h
index c10ce47..c8bc0e6 100644
--- a/include/perfetto/tracing/internal/tracing_muxer.h
+++ b/include/perfetto/tracing/internal/tracing_muxer.h
@@ -96,7 +96,12 @@
// Informs the tracing services to activate any of these triggers if any
// tracing session was waiting for them.
- virtual void ActivateTriggers(const std::vector<std::string>&) = 0;
+ //
+ // Sends the trigger signal to all the initialized backends that are currently
+ // connected and that connect in the next `ttl_ms` milliseconds (but returns
+ // immediately anyway).
+ virtual void ActivateTriggers(const std::vector<std::string>&,
+ uint32_t ttl_ms) = 0;
protected:
explicit TracingMuxer(Platform* platform) : platform_(platform) {}
diff --git a/include/perfetto/tracing/tracing.h b/include/perfetto/tracing/tracing.h
index 1eedfcc..0d54d5d 100644
--- a/include/perfetto/tracing/tracing.h
+++ b/include/perfetto/tracing/tracing.h
@@ -266,7 +266,12 @@
// Informs the tracing services to activate any of these triggers if any
// tracing session was waiting for them.
- static void ActivateTriggers(const std::vector<std::string>& triggers);
+ //
+ // Sends the trigger signal to all the initialized backends that are currently
+ // connected and that connect in the next `ttl_ms` milliseconds (but
+ // returns immediately anyway).
+ static void ActivateTriggers(const std::vector<std::string>& triggers,
+ uint32_t ttl_ms);
private:
static void InitializeInternal(const TracingInitArgs&);
diff --git a/src/base/test/tmp_dir_tree.cc b/src/base/test/tmp_dir_tree.cc
index 9ed785f..8b70ec0 100644
--- a/src/base/test/tmp_dir_tree.cc
+++ b/src/base/test/tmp_dir_tree.cc
@@ -44,12 +44,16 @@
void TmpDirTree::AddFile(const std::string& relative_path,
const std::string& content) {
- files_to_remove_.push(relative_path);
+ TrackFile(relative_path);
base::ScopedFile fd(base::OpenFile(AbsolutePath(relative_path),
O_WRONLY | O_CREAT | O_TRUNC, 0600));
PERFETTO_CHECK(base::WriteAll(fd.get(), content.c_str(), content.size()) ==
static_cast<ssize_t>(content.size()));
}
+void TmpDirTree::TrackFile(const std::string& relative_path) {
+ files_to_remove_.push(relative_path);
+}
+
} // namespace base
} // namespace perfetto
diff --git a/src/base/test/tmp_dir_tree.h b/src/base/test/tmp_dir_tree.h
index 6bb730c..9b282f8 100644
--- a/src/base/test/tmp_dir_tree.h
+++ b/src/base/test/tmp_dir_tree.h
@@ -47,6 +47,11 @@
// succeeds.
void AddFile(const std::string& relative_path, const std::string& content);
+ // Tells this object to remove `relative_path` on destruction. This is used
+ // for files created by the caller that still need to be removed on cleanup by
+ // this object.
+ void TrackFile(const std::string& relative_path);
+
private:
TmpDirTree(const TmpDirTree&) = delete;
TmpDirTree& operator=(const TmpDirTree&) = delete;
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index b0ce40a..b4a28a0 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -135,6 +135,21 @@
}
}
+# Separate target because the embedder might not want this.
+source_set("integrationtests") {
+ testonly = true
+ deps = [
+ "../../gn:default_deps",
+ "../../gn:gtest_and_gmock",
+ "../../include/perfetto/ext/tracing/ipc",
+ "../../include/perfetto/tracing",
+ "../../protos/perfetto/trace:cpp",
+ "../base",
+ "../base:test_support",
+ ]
+ sources = [ "internal/tracing_muxer_impl_integrationtest.cc" ]
+}
+
perfetto_unittest_source_set("unittests") {
testonly = true
deps = [
diff --git a/src/tracing/internal/tracing_muxer_fake.cc b/src/tracing/internal/tracing_muxer_fake.cc
index e94467e..59c3d5b 100644
--- a/src/tracing/internal/tracing_muxer_fake.cc
+++ b/src/tracing/internal/tracing_muxer_fake.cc
@@ -86,7 +86,8 @@
FailUninitialized();
}
-void TracingMuxerFake::ActivateTriggers(const std::vector<std::string>&) {
+void TracingMuxerFake::ActivateTriggers(const std::vector<std::string>&,
+ uint32_t) {
FailUninitialized();
}
diff --git a/src/tracing/internal/tracing_muxer_fake.h b/src/tracing/internal/tracing_muxer_fake.h
index 36c825e..f541d82 100644
--- a/src/tracing/internal/tracing_muxer_fake.h
+++ b/src/tracing/internal/tracing_muxer_fake.h
@@ -68,7 +68,7 @@
InterceptorFactory,
InterceptorBase::TLSFactory,
InterceptorBase::TracePacketCallback) override;
- void ActivateTriggers(const std::vector<std::string>& triggers) override;
+ void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
private:
static TracingMuxerFake instance;
diff --git a/src/tracing/internal/tracing_muxer_impl.cc b/src/tracing/internal/tracing_muxer_impl.cc
index 4fd5f2b..6e8f0ee 100644
--- a/src/tracing/internal/tracing_muxer_impl.cc
+++ b/src/tracing/internal/tracing_muxer_impl.cc
@@ -220,6 +220,7 @@
}
connected_ = true;
muxer_->UpdateDataSourcesOnAllBackends();
+ SendOnConnectTriggers();
}
void TracingMuxerImpl::ProducerImpl::OnDisconnect() {
@@ -326,6 +327,22 @@
return dead_services_.empty();
}
+void TracingMuxerImpl::ProducerImpl::SendOnConnectTriggers() {
+ PERFETTO_DCHECK_THREAD(thread_checker_);
+ base::TimeMillis now = base::GetWallTimeMs();
+ std::vector<std::string> triggers;
+ while (!on_connect_triggers_.empty()) {
+ // Skip if we passed TTL.
+ if (on_connect_triggers_.front().second > now) {
+ triggers.push_back(std::move(on_connect_triggers_.front().first));
+ }
+ on_connect_triggers_.pop_front();
+ }
+ if (!triggers.empty()) {
+ service_->ActivateTriggers(triggers);
+ }
+}
+
// ----- End of TracingMuxerImpl::ProducerImpl methods.
// ----- Begin of TracingMuxerImpl::ConsumerImpl
@@ -983,11 +1000,20 @@
});
}
-void TracingMuxerImpl::ActivateTriggers(const std::vector<std::string>& triggers) {
- task_runner_->PostTask([this, triggers] {
+void TracingMuxerImpl::ActivateTriggers(
+ const std::vector<std::string>& triggers,
+ uint32_t ttl_ms) {
+ base::TimeMillis expire_time =
+ base::GetWallTimeMs() + base::TimeMillis(ttl_ms);
+ task_runner_->PostTask([this, triggers, expire_time] {
for (RegisteredBackend& backend : backends_) {
if (backend.producer->connected_) {
backend.producer->service_->ActivateTriggers(triggers);
+ } else {
+ for (const std::string& trigger : triggers) {
+ backend.producer->on_connect_triggers_.emplace_back(trigger,
+ expire_time);
+ }
}
}
});
diff --git a/src/tracing/internal/tracing_muxer_impl.h b/src/tracing/internal/tracing_muxer_impl.h
index 9dcf935..ce1db48 100644
--- a/src/tracing/internal/tracing_muxer_impl.h
+++ b/src/tracing/internal/tracing_muxer_impl.h
@@ -28,6 +28,7 @@
#include <memory>
#include <vector>
+#include "perfetto/base/time.h"
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/thread_checker.h"
#include "perfetto/ext/tracing/core/basic_types.h"
@@ -130,7 +131,7 @@
InterceptorBase::TLSFactory,
InterceptorBase::TracePacketCallback) override;
- void ActivateTriggers(const std::vector<std::string>& triggers) override;
+ void ActivateTriggers(const std::vector<std::string>&, uint32_t) override;
std::unique_ptr<TracingSession> CreateTracingSession(BackendType);
std::unique_ptr<StartupTracingSession> CreateStartupTracingSession(
@@ -220,6 +221,7 @@
void ClearIncrementalState(const DataSourceInstanceID*, size_t) override;
bool SweepDeadServices();
+ void SendOnConnectTriggers();
PERFETTO_THREAD_CHECKER(thread_checker_)
TracingMuxerImpl* muxer_;
@@ -245,6 +247,10 @@
// that no longer have any writers (see SweepDeadServices).
std::list<std::shared_ptr<ProducerEndpoint>> dead_services_;
+ // Triggers that should be sent when the service connects (trigger_name,
+ // expiration).
+ std::list<std::pair<std::string, base::TimeMillis>> on_connect_triggers_;
+
// The currently active service endpoint is maintained as an atomic shared
// pointer so it won't get deleted from underneath threads that are creating
// trace writers. At any given time one endpoint can be shared (and thus
diff --git a/src/tracing/internal/tracing_muxer_impl_integrationtest.cc b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
new file mode 100644
index 0000000..e1cfbfd
--- /dev/null
+++ b/src/tracing/internal/tracing_muxer_impl_integrationtest.cc
@@ -0,0 +1,172 @@
+#include "perfetto/tracing/tracing.h"
+
+#include <stdio.h>
+
+#include "perfetto/ext/base/optional.h"
+#include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/ext/base/waitable_event.h"
+#include "perfetto/ext/tracing/ipc/service_ipc_host.h"
+#include "perfetto/tracing/backend_type.h"
+#include "protos/perfetto/config/trace_config.gen.h"
+#include "protos/perfetto/trace/trace.gen.h"
+#include "protos/perfetto/trace/trace_packet.gen.h"
+#include "protos/perfetto/trace/trigger.gen.h"
+#include "src/base/test/test_task_runner.h"
+#include "src/base/test/tmp_dir_tree.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace internal {
+namespace {
+
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Property;
+
+class TracingMuxerImplIntegrationTest : public testing::Test {
+ protected:
+ // Sets the environment variable `name` to `value`. Restores it to the
+ // previous value when the test finishes.
+ void SetEnvVar(const char* name, const char* value) {
+ prev_state_.emplace();
+ EnvVar& var = prev_state_.top();
+ var.name = name;
+ const char* prev_value = getenv(name);
+ if (prev_value) {
+ var.value.emplace(prev_value);
+ }
+ base::SetEnv(name, value);
+ }
+
+ ~TracingMuxerImplIntegrationTest() {
+ perfetto::Tracing::ResetForTesting();
+ while (!prev_state_.empty()) {
+ const EnvVar& var = prev_state_.top();
+ if (var.value) {
+ base::SetEnv(var.name, *var.value);
+ } else {
+ base::UnsetEnv(var.name);
+ }
+ prev_state_.pop();
+ }
+ }
+
+ struct EnvVar {
+ const char* name;
+ base::Optional<std::string> value;
+ };
+ // Stores previous values of environment variables overridden by tests. We
+ // need to to this because some android integration tests need to talk to the
+ // real system tracing service and need the PERFETTO_PRODUCER_SOCK_NAME and
+ // PERFETTO_CONSUMER_SOCK_NAME to be set to their original value.
+ std::stack<EnvVar> prev_state_;
+};
+
+class TracingServiceThread {
+ public:
+ TracingServiceThread(const std::string& producer_socket,
+ const std::string& consumer_socket)
+ : runner_(base::ThreadTaskRunner::CreateAndStart("perfetto.svc")),
+ producer_socket_(producer_socket),
+ consumer_socket_(consumer_socket) {
+ runner_.PostTaskAndWaitForTesting([this]() {
+ svc_ = ServiceIPCHost::CreateInstance(&runner_);
+ bool res =
+ svc_->Start(producer_socket_.c_str(), consumer_socket_.c_str());
+ if (!res) {
+ PERFETTO_FATAL("Failed to start service listening on %s and %s",
+ producer_socket_.c_str(), consumer_socket_.c_str());
+ }
+ });
+ }
+
+ ~TracingServiceThread() {
+ runner_.PostTaskAndWaitForTesting([this]() { svc_.reset(); });
+ }
+
+ const std::string& producer_socket() const { return producer_socket_; }
+ const std::string& consumer_socket() const { return consumer_socket_; }
+
+ private:
+ base::ThreadTaskRunner runner_;
+
+ std::string producer_socket_;
+ std::string consumer_socket_;
+ std::unique_ptr<ServiceIPCHost> svc_;
+};
+
+TEST_F(TracingMuxerImplIntegrationTest, ActivateTriggers) {
+ base::TmpDirTree tmpdir_;
+
+ base::TestTaskRunner task_runner;
+
+ ASSERT_FALSE(perfetto::Tracing::IsInitialized());
+
+ tmpdir_.TrackFile("producer2.sock");
+ tmpdir_.TrackFile("consumer.sock");
+ TracingServiceThread tracing_service(tmpdir_.AbsolutePath("producer2.sock"),
+ tmpdir_.AbsolutePath("consumer.sock"));
+ // Instead of being a unix socket, producer.sock is a regular empty file.
+ tmpdir_.AddFile("producer.sock", "");
+
+ // Wrong producer socket: the producer won't connect yet, but the consumer
+ // will.
+ SetEnvVar("PERFETTO_PRODUCER_SOCK_NAME",
+ tmpdir_.AbsolutePath("producer.sock").c_str());
+ SetEnvVar("PERFETTO_CONSUMER_SOCK_NAME",
+ tmpdir_.AbsolutePath("consumer.sock").c_str());
+
+ TracingInitArgs args;
+ args.backends = perfetto::kSystemBackend;
+ perfetto::Tracing::Initialize(args);
+
+ // TracingMuxerImpl::ActivateTriggers will be called without the producer side
+ // of the service being connected. It should store the trigger for 10000ms.
+ perfetto::Tracing::ActivateTriggers({"trigger2", "trigger1"}, 10000);
+
+ perfetto::TraceConfig cfg;
+ cfg.add_buffers()->set_size_kb(1024);
+ perfetto::TraceConfig::TriggerConfig* tr_cfg = cfg.mutable_trigger_config();
+ tr_cfg->set_trigger_mode(perfetto::TraceConfig::TriggerConfig::STOP_TRACING);
+ tr_cfg->set_trigger_timeout_ms(10000);
+ perfetto::TraceConfig::TriggerConfig::Trigger* trigger =
+ tr_cfg->add_triggers();
+ trigger->set_name("trigger1");
+
+ std::unique_ptr<TracingSession> session =
+ perfetto::Tracing::NewTrace(perfetto::kSystemBackend);
+ base::WaitableEvent on_stop;
+ session->SetOnStopCallback([&on_stop] { on_stop.Notify(); });
+ session->Setup(cfg);
+
+ session->StartBlocking();
+
+ // Swap producer.sock and producer2.sock. Now the client should connect to the
+ // tracing service as a producer.
+ ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer2.sock").c_str(),
+ tmpdir_.AbsolutePath("producer3.sock").c_str()),
+ 0);
+ ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer.sock").c_str(),
+ tmpdir_.AbsolutePath("producer2.sock").c_str()),
+ 0);
+ ASSERT_EQ(rename(tmpdir_.AbsolutePath("producer3.sock").c_str(),
+ tmpdir_.AbsolutePath("producer.sock").c_str()),
+ 0);
+
+ on_stop.Wait();
+
+ std::vector<char> bytes = session->ReadTraceBlocking();
+ perfetto::protos::gen::Trace parsed_trace;
+ ASSERT_TRUE(parsed_trace.ParseFromArray(bytes.data(), bytes.size()));
+ EXPECT_THAT(
+ parsed_trace,
+ Property(&perfetto::protos::gen::Trace::packet,
+ Contains(Property(
+ &perfetto::protos::gen::TracePacket::trigger,
+ Property(&perfetto::protos::gen::Trigger::trigger_name,
+ "trigger1")))));
+}
+
+} // namespace
+} // namespace internal
+} // namespace perfetto
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index 0c456f8..fdba858 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -325,7 +325,7 @@
perfetto::InterceptorBase::TLSFactory,
perfetto::InterceptorBase::TracePacketCallback) override {}
- void ActivateTriggers(const std::vector<std::string>&) override {}
+ void ActivateTriggers(const std::vector<std::string>&, uint32_t) override {}
std::vector<DataSource> data_sources;
@@ -5451,7 +5451,7 @@
auto* tracing_session = NewTrace(cfg);
tracing_session->get()->StartBlocking();
- perfetto::Tracing::ActivateTriggers({"trigger2", "trigger1"});
+ perfetto::Tracing::ActivateTriggers({"trigger2", "trigger1"}, 10000);
tracing_session->on_stop.Wait();
diff --git a/src/tracing/tracing.cc b/src/tracing/tracing.cc
index c3ee261..c5112b4 100644
--- a/src/tracing/tracing.cc
+++ b/src/tracing/tracing.cc
@@ -110,8 +110,9 @@
}
// static
-void Tracing::ActivateTriggers(const std::vector<std::string>& triggers) {
- internal::TracingMuxer::Get()->ActivateTriggers(triggers);
+void Tracing::ActivateTriggers(const std::vector<std::string>& triggers,
+ uint32_t ttl_ms) {
+ internal::TracingMuxer::Get()->ActivateTriggers(triggers, ttl_ms);
}
TracingSession::~TracingSession() = default;