blob: 5711d73c337077163a010ef689b560528f042164 [file] [log] [blame]
#include "perfetto/tracing/tracing.h"
#include <stdio.h>
#include <optional>
#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;
std::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