diff options
Diffstat (limited to 'test/end_to_end_integrationtest.cc')
-rw-r--r-- | test/end_to_end_integrationtest.cc | 359 |
1 files changed, 178 insertions, 181 deletions
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc index 52947df04..7d001d612 100644 --- a/test/end_to_end_integrationtest.cc +++ b/test/end_to_end_integrationtest.cc @@ -26,12 +26,19 @@ #include "perfetto/base/logging.h" #include "perfetto/ext/base/file_utils.h" #include "perfetto/ext/base/pipe.h" +#include "perfetto/ext/base/scoped_file.h" +#include "perfetto/ext/base/string_utils.h" +#include "perfetto/ext/base/subprocess.h" #include "perfetto/ext/base/temp_file.h" +#include "perfetto/ext/ipc/basic_types.h" #include "perfetto/ext/traced/traced.h" +#include "perfetto/ext/tracing/core/commit_data_request.h" #include "perfetto/ext/tracing/core/trace_packet.h" #include "perfetto/ext/tracing/ipc/default_socket.h" #include "perfetto/protozero/scattered_heap_buffer.h" +#include "perfetto/tracing/core/tracing_service_state.h" #include "src/base/test/test_task_runner.h" +#include "src/base/test/utils.h" #include "src/traced/probes/ftrace/ftrace_controller.h" #include "src/traced/probes/ftrace/ftrace_procfs.h" #include "test/gtest_and_gmock.h" @@ -55,8 +62,11 @@ namespace perfetto { namespace { using ::testing::ContainsRegex; +using ::testing::ElementsAreArray; using ::testing::HasSubstr; +constexpr size_t kBuiltinPackets = 8; + std::string RandomTraceFileName() { #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) constexpr char kSysTmpPath[] = "/data/misc/perfetto-traces"; @@ -82,181 +92,90 @@ std::string RandomTraceFileName() { // any necessary threads) in the parent process is complete. class Exec { public: - // Starts the forked process that was created. If not null then |stderr_out - // will contain the std::cerr output of the process. + // Starts the forked process that was created. If not null then |stderr_out| + // will contain the stderr of the process. int Run(std::string* stderr_out = nullptr) { // We can't be the child process. - PERFETTO_CHECK(pid_ != 0); - - // Send some random bytes so the child process knows the service is up and - // it can connect and execute. - PERFETTO_CHECK(PERFETTO_EINTR(write(*start_pipe_.wr, "42", 2)) == - static_cast<ssize_t>(2)); - start_pipe_.wr.reset(); - - // Setup a large enough buffer and read all of stderr (until the process - // closes the err_pipe on process exit). - std::string stderr_str = std::string(1024 * 1024, '\0'); - ssize_t rsize = 0; - size_t stderr_pos = 0; - while (stderr_pos < stderr_str.size()) { - rsize = PERFETTO_EINTR(read(*err_pipe_.rd, &stderr_str[stderr_pos], - stderr_str.size() - stderr_pos - 1)); - if (rsize <= 0) - break; - stderr_pos += static_cast<size_t>(rsize); - } - stderr_str.resize(stderr_pos); + PERFETTO_CHECK(getpid() != subprocess_.pid()); + // Will cause the entrypoint to continue. + PERFETTO_CHECK(write(*sync_pipe_.wr, "1", 1) == 1); + sync_pipe_.wr.reset(); + subprocess_.Wait(); - // Either output the stderr_out to the provided variable or for the record - // it into the info logs. if (stderr_out) { - *stderr_out = stderr_str; - } else { - PERFETTO_LOG("Child proc %d exited with stderr: \"%s\"", pid_, - stderr_str.c_str()); - } - - int status = 1; - PERFETTO_CHECK(PERFETTO_EINTR(waitpid(pid_, &status, 0)) == pid_); - int exit_code; - if (WIFEXITED(status)) { - exit_code = WEXITSTATUS(status); - } else if (WIFSIGNALED(status)) { - exit_code = -(WTERMSIG(status)); - PERFETTO_CHECK(exit_code < 0); + *stderr_out = std::move(subprocess_.output()); } else { - PERFETTO_FATAL("Unexpected exit status: %d", status); + PERFETTO_LOG("Child proc %d exited with stderr: \"%s\"", + subprocess_.pid(), subprocess_.output().c_str()); } - return exit_code; + return subprocess_.returncode(); } private: - Exec(pid_t pid, base::Pipe err, base::Pipe start) - : pid_(pid), err_pipe_(std::move(err)), start_pipe_(std::move(start)) {} + Exec(const std::string& argv0, + std::initializer_list<std::string> args, + std::string input = "") { + subprocess_.args.stderr_mode = base::Subprocess::kBuffer; + subprocess_.args.stdout_mode = base::Subprocess::kDevNull; + subprocess_.args.input = input; - static Exec Create(const std::string& argv0, - std::initializer_list<std::string> args, - std::string input = "") { - if (argv0 != "perfetto" && argv0 != "trigger_perfetto") { - PERFETTO_FATAL( - "Received argv0: \"%s\" which isn't supported. Supported binaries " - "are \"perfetto\" or \"trigger_perfetto\".", - argv0.c_str()); - } +#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS) + constexpr bool kUseSystemBinaries = false; +#else + constexpr bool kUseSystemBinaries = true; +#endif - // |in_pipe| == std::cin, |err_pipe| == std::cerr for the process we're - // about to fork. |start_pipe| is used to block the process so we can hold - // it until we're ready (the service has started up). - base::Pipe in_pipe = base::Pipe::Create(); - base::Pipe err_pipe = base::Pipe::Create(); - base::Pipe start_pipe = base::Pipe::Create(); - - pid_t pid = fork(); - PERFETTO_CHECK(pid >= 0); - if (pid == 0) { - // Child process, we need to block the child process until we've been - // signaled on the |start_pipe|. - std::string junk = std::string(4, '\0'); - start_pipe.wr.reset(); - ssize_t rsize = 0; - rsize = PERFETTO_EINTR(read(*start_pipe.rd, &junk[0], junk.size() - 1)); - PERFETTO_CHECK(rsize >= 0); - start_pipe.rd.reset(); - - // We've been signalled to start so execute in a sub function. - _exit(RunChild(argv0, std::move(args), std::move(in_pipe), - std::move(err_pipe))); + std::vector<std::string>& cmd = subprocess_.args.exec_cmd; + if (kUseSystemBinaries) { + cmd.push_back("/system/bin/" + argv0); + cmd.insert(cmd.end(), args.begin(), args.end()); } else { - // Parent, we don't need to write to the childs std::cerr nor do we need - // to read the start_pipe. - err_pipe.wr.reset(); - start_pipe.rd.reset(); - - // This is generally an unsafe pattern because the child process might - // be blocked on stdout and stall the stdin reads. It's pragmatically - // okay for our test cases because stdin is not expected to exceed the - // pipe buffer. - // - // We need to write this now up front (rather than in Run(), because in - // some tests we create multiple Exec classes, and if we don't close the - // input pipe up front then future Exec's will have a reference and the - // pipe won't close properly. - PERFETTO_CHECK(input.size() <= base::kPageSize); - PERFETTO_CHECK( - PERFETTO_EINTR(write(*in_pipe.wr, input.data(), input.size())) == - static_cast<ssize_t>(input.size())); - in_pipe.wr.reset(); - // Close the input pipe only after the write so we don't get an EPIPE - // signal in the cases when the child process earlies out without - // reading stdin. - in_pipe.rd.reset(); - - return Exec(pid, std::move(err_pipe), std::move(start_pipe)); + subprocess_.args.env.push_back( + std::string("PERFETTO_PRODUCER_SOCK_NAME=") + + TestHelper::GetProducerSocketName()); + subprocess_.args.env.push_back( + std::string("PERFETTO_CONSUMER_SOCK_NAME=") + + TestHelper::GetConsumerSocketName()); + cmd.push_back(base::GetCurExecutableDir() + "/" + argv0); + cmd.insert(cmd.end(), args.begin(), args.end()); } - } - // Wrapper to contain all the work the child process needs to do. - static int RunChild(const std::string& argv0, - std::initializer_list<std::string> args, - base::Pipe in_pipe, - base::Pipe err_pipe) { - // This sets up the char** argv buffer we're going to provide to the main - // function for |argv0| binary. - std::vector<char> argv_buffer; - std::vector<size_t> argv_offsets; - std::vector<char*> argv; - argv_offsets.push_back(0); - - argv_buffer.insert(argv_buffer.end(), argv0.begin(), argv0.end()); - argv_buffer.push_back('\0'); - - for (const std::string& arg : args) { - argv_offsets.push_back(argv_buffer.size()); - argv_buffer.insert(argv_buffer.end(), arg.begin(), arg.end()); - argv_buffer.push_back('\0'); + if (access(cmd[0].c_str(), F_OK)) { + PERFETTO_FATAL( + "Cannot find %s. Make sure that the target has been built and, on " + "Android, pushed to the device.", + cmd[0].c_str()); } - for (size_t off : argv_offsets) - argv.push_back(&argv_buffer[off]); - argv.push_back(nullptr); - - // We aren't reading std::cerr nor writing to std::cin. - err_pipe.rd.reset(); - in_pipe.wr.reset(); - - // This makes it so the binaries below will correctly write their std::cin - // and std::cerr to the right pipes. - int devnull = open("/dev/null", O_RDWR); - PERFETTO_CHECK(devnull >= 0); - PERFETTO_CHECK(dup2(*in_pipe.rd, STDIN_FILENO) != -1); - PERFETTO_CHECK(dup2(devnull, STDOUT_FILENO) != -1); - PERFETTO_CHECK(dup2(*err_pipe.wr, STDERR_FILENO) != -1); -#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS) - setenv("PERFETTO_CONSUMER_SOCK_NAME", TestHelper::GetConsumerSocketName(), - 1); - setenv("PERFETTO_PRODUCER_SOCK_NAME", TestHelper::GetProducerSocketName(), - 1); - if (argv0 == "perfetto") { - return PerfettoCmdMain(static_cast<int>(argv.size() - 1), argv.data()); - } else if (argv0 == "trigger_perfetto") { - return TriggerPerfettoMain(static_cast<int>(argv.size() - 1), - argv.data()); - } else { - PERFETTO_FATAL("Unknown binary: %s", argv0.c_str()); - return 4; - } -#else - execv((std::string("/system/bin/") + argv0).c_str(), &argv[0]); - return 3; -#endif + // This pipe blocks the execution of the child process until the main test + // process calls Run(). There are two conflicting problems here: + // 1) We can't fork() subprocesses too late, because the test spawns threads + // for hosting the service. fork+threads = bad (see aosp/1089744). + // 2) We can't run the subprocess too early, because we need to wait that + // the service threads are ready before trying to connect from the child + // process. + sync_pipe_ = base::Pipe::Create(); + int sync_pipe_rd = *sync_pipe_.rd; + subprocess_.args.preserve_fds.push_back(sync_pipe_rd); + + // This lambda will be called on the forked child process after having + // setup pipe redirection and closed all FDs, right before the exec(). + // The Subprocesss harness will take care of closing also |sync_pipe_.wr|. + subprocess_.args.entrypoint_for_testing = [sync_pipe_rd] { + // Don't add any logging here, all file descriptors are closed and trying + // to log will likely cause undefined behaviors. + char ignored = 0; + PERFETTO_CHECK(PERFETTO_EINTR(read(sync_pipe_rd, &ignored, 1)) > 0); + PERFETTO_CHECK(close(sync_pipe_rd) == 0 || errno == EINTR); + }; + + subprocess_.Start(); + sync_pipe_.rd.reset(); } friend class PerfettoCmdlineTest; - - pid_t pid_; - base::Pipe err_pipe_; - base::Pipe start_pipe_; + base::Subprocess subprocess_; + base::Pipe sync_pipe_; }; class PerfettoTest : public ::testing::Test { @@ -268,14 +187,6 @@ class PerfettoTest : public ::testing::Test { while (!ftrace_procfs_ && kTracingPaths[index]) { ftrace_procfs_ = FtraceProcfs::Create(kTracingPaths[index++]); } - if (!ftrace_procfs_) - return; - ftrace_procfs_->SetTracingOn(false); - } - - void TearDown() override { - if (ftrace_procfs_) - ftrace_procfs_->SetTracingOn(false); } std::unique_ptr<FtraceProcfs> ftrace_procfs_; @@ -312,7 +223,7 @@ class PerfettoCmdlineTest : public ::testing::Test { // You can not fork after you've started the service due to risk of // deadlocks. PERFETTO_CHECK(exec_allowed_); - return Exec::Create("perfetto", std::move(args), std::move(std_in)); + return Exec("perfetto", std::move(args), std::move(std_in)); } // Creates a process that represents the trigger_perfetto binary that will @@ -323,7 +234,7 @@ class PerfettoCmdlineTest : public ::testing::Test { // You can not fork after you've started the service due to risk of // deadlocks. PERFETTO_CHECK(exec_allowed_); - return Exec::Create("trigger_perfetto", std::move(args), std::move(std_in)); + return Exec("trigger_perfetto", std::move(args), std::move(std_in)); } // Tests are allowed to freely use these variables. @@ -346,13 +257,13 @@ class PerfettoCmdlineTest : public ::testing::Test { #define TEST_PRODUCER_SOCK_NAME ::perfetto::GetProducerSocket() #endif -// TODO(b/73453011): reenable on more platforms (including standalone Android). #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) #define TreeHuggerOnly(x) x #else #define TreeHuggerOnly(x) DISABLED_##x #endif +// TODO(b/73453011): reenable on more platforms (including standalone Android). TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceProducer)) { base::TestTaskRunner task_runner; @@ -398,6 +309,7 @@ TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceProducer)) { } } +// TODO(b/73453011): reenable on more platforms (including standalone Android). TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceFlush)) { base::TestTaskRunner task_runner; @@ -414,7 +326,7 @@ TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceFlush)) { const uint32_t kTestTimeoutMs = 30000; TraceConfig trace_config; - trace_config.add_buffers()->set_size_kb(16); + trace_config.add_buffers()->set_size_kb(32); trace_config.set_duration_ms(kTestTimeoutMs); auto* ds_config = trace_config.add_data_sources()->mutable_config(); @@ -456,6 +368,7 @@ TEST_F(PerfettoTest, TreeHuggerOnly(TestFtraceFlush)) { ASSERT_EQ(marker_found, 1); } +// TODO(b/73453011): reenable on more platforms (including standalone Android). TEST_F(PerfettoTest, TreeHuggerOnly(TestBatteryTracing)) { base::TestTaskRunner task_runner; @@ -582,7 +495,7 @@ TEST_F(PerfettoTest, VeryLargePackets) { helper.WaitForTracingDisabled(); helper.ReadData(); - helper.WaitForReadData(); + helper.WaitForReadData(/* read_count */ 0, /* timeout_ms */ 10000); const auto& packets = helper.trace(); ASSERT_EQ(packets.size(), kNumPackets); @@ -685,6 +598,100 @@ TEST_F(PerfettoTest, ReattachFailsAfterTimeout) { EXPECT_FALSE(helper.AttachConsumer("key")); } +TEST_F(PerfettoTest, TestProducerProvidedSMB) { + base::TestTaskRunner task_runner; + + TestHelper helper(&task_runner); + helper.CreateProducerProvidedSmb(); + + protos::gen::TestConfig test_config; + test_config.set_seed(42); + test_config.set_message_count(1); + test_config.set_message_size(1024); + test_config.set_send_batch_on_register(true); + + // Write a first batch before connection. + helper.ProduceStartupEventBatch(test_config); + + helper.StartServiceIfRequired(); + helper.ConnectFakeProducer(); + helper.ConnectConsumer(); + helper.WaitForConsumerConnect(); + + TraceConfig trace_config; + trace_config.add_buffers()->set_size_kb(1024); + trace_config.set_duration_ms(200); + + auto* ds_config = trace_config.add_data_sources()->mutable_config(); + ds_config->set_name("android.perfetto.FakeProducer"); + ds_config->set_target_buffer(0); + *ds_config->mutable_for_testing() = test_config; + + // The data source is configured to emit another batch when it is started via + // send_batch_on_register in the TestConfig. + helper.StartTracing(trace_config); + helper.WaitForTracingDisabled(); + + EXPECT_TRUE(helper.IsShmemProvidedByProducer()); + + helper.ReadData(); + helper.WaitForReadData(); + + const auto& packets = helper.trace(); + // We should have produced two batches, one before the producer connected and + // another one when the data source was started. + ASSERT_EQ(packets.size(), 2u); + ASSERT_TRUE(packets[0].has_for_testing()); + ASSERT_TRUE(packets[1].has_for_testing()); +} + +// Regression test for b/153142114. +TEST_F(PerfettoTest, QueryServiceStateLargeResponse) { + base::TestTaskRunner task_runner; + + TestHelper helper(&task_runner); + helper.StartServiceIfRequired(); + helper.ConnectConsumer(); + helper.WaitForConsumerConnect(); + + FakeProducer* producer = helper.ConnectFakeProducer(); + + // Register 5 data sources with very large descriptors. Each descriptor will + // max out the IPC message size, so that the service has no other choice + // than chunking them. + std::map<std::string, std::string> ds_expected; + for (int i = 0; i < 5; i++) { + DataSourceDescriptor dsd; + std::string name = "big_ds_" + std::to_string(i); + dsd.set_name(name); + std::string descriptor(ipc::kIPCBufferSize - 64, (' ' + i) % 64); + dsd.set_track_event_descriptor_raw(descriptor); + ds_expected[name] = std::move(descriptor); + producer->RegisterDataSource(dsd); + } + + // Linearize the producer with the service. We need to make sure that all the + // RegisterDataSource() calls above have been seen by the service before + // continuing. + helper.SyncAndWaitProducer(); + + // Now invoke QueryServiceState() and wait for the reply. The service will + // send 6 (1 + 5) IPCs which will be merged together in + // producer_ipc_client_impl.cc. + auto svc_state = helper.QueryServiceStateAndWait(); + + ASSERT_GE(svc_state.producers().size(), 1u); + + std::map<std::string, std::string> ds_found; + for (const auto& ds : svc_state.data_sources()) { + if (!base::StartsWith(ds.ds_descriptor().name(), "big_ds_")) + continue; + ds_found[ds.ds_descriptor().name()] = + ds.ds_descriptor().track_event_descriptor_raw(); + } + EXPECT_THAT(ds_found, ElementsAreArray(ds_expected)); +} + // Disable cmdline tests on sanitizets because they use fork() and that messes // up leak / races detections, which has been fixed only recently (see // https://github.com/google/sanitizers/issues/836 ). @@ -836,11 +843,6 @@ TEST_F(PerfettoCmdlineTest, NoSanitizers(StartTracingTrigger)) { // time. trigger->set_stop_delay_ms(500); - // We have 6 normal preamble packets (start clock, trace config, clock, - // system info, sync marker, stats) and then since this is a trace with a - // trigger config we have an additional ReceivedTriggers packet. - constexpr size_t kPreamblePackets = 7; - // We have to construct all the processes we want to fork before we start the // service with |StartServiceIfRequired()|. this is because it is unsafe // (could deadlock) to fork after we've spawned some threads which might @@ -884,7 +886,7 @@ TEST_F(PerfettoCmdlineTest, NoSanitizers(StartTracingTrigger)) { base::ReadFile(path, &trace_str); protos::gen::Trace trace; ASSERT_TRUE(trace.ParseFromString(trace_str)); - EXPECT_EQ(static_cast<int>(kPreamblePackets + kMessageCount), + EXPECT_EQ(static_cast<int>(kBuiltinPackets + kMessageCount), trace.packet_size()); for (const auto& packet : trace.packet()) { if (packet.has_trace_config()) { @@ -928,11 +930,6 @@ TEST_F(PerfettoCmdlineTest, NoSanitizers(StopTracingTrigger)) { trigger->set_name("trigger_name_3"); trigger->set_stop_delay_ms(60000); - // We have 6 normal preamble packets (start clock, trace config, clock, - // system info, sync marker, stats) and then since this is a trace with a - // trigger config we have an additional ReceivedTriggers packet. - constexpr size_t kPreamblePackets = 8; - // We have to construct all the processes we want to fork before we start the // service with |StartServiceIfRequired()|. this is because it is unsafe // (could deadlock) to fork after we've spawned some threads which might @@ -977,7 +974,7 @@ TEST_F(PerfettoCmdlineTest, NoSanitizers(StopTracingTrigger)) { base::ReadFile(path, &trace_str); protos::gen::Trace trace; ASSERT_TRUE(trace.ParseFromString(trace_str)); - EXPECT_EQ(static_cast<int>(kPreamblePackets + kMessageCount), + EXPECT_EQ(static_cast<int>(kBuiltinPackets + 1 + kMessageCount), trace.packet_size()); bool seen_first_trigger = false; for (const auto& packet : trace.packet()) { |