#include "perfetto/tracing/tracing.h" #include #include #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: void SetUp() override { #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) GTEST_SKIP() << "Unix sockets not supported on windows"; #endif } // 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() override { 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; std::optional 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 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 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 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 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