| /* |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <functional> |
| #include <initializer_list> |
| #include <thread> |
| |
| #include "perfetto/base/build_config.h" |
| #include "perfetto/base/logging.h" |
| #include "perfetto/ext/base/pipe.h" |
| #include "perfetto/ext/base/string_utils.h" |
| #include "perfetto/ext/base/utils.h" |
| #include "perfetto/ext/traced/traced.h" |
| #include "perfetto/protozero/scattered_heap_buffer.h" |
| #include "src/base/test/test_task_runner.h" |
| #include "src/base/test/utils.h" |
| #include "src/perfetto_cmd/bugreport_path.h" |
| #include "test/gtest_and_gmock.h" |
| #include "test/test_helper.h" |
| |
| #include "protos/perfetto/config/test_config.gen.h" |
| #include "protos/perfetto/config/trace_config.gen.h" |
| #include "protos/perfetto/trace/test_event.gen.h" |
| #include "protos/perfetto/trace/trace.gen.h" |
| #include "protos/perfetto/trace/trace_packet.gen.h" |
| #include "protos/perfetto/trace/trace_packet.pbzero.h" |
| #include "protos/perfetto/trace/trigger.gen.h" |
| |
| namespace perfetto { |
| |
| namespace { |
| |
| using ::testing::ContainsRegex; |
| using ::testing::Each; |
| using ::testing::ElementsAreArray; |
| using ::testing::Eq; |
| using ::testing::HasSubstr; |
| using ::testing::Property; |
| using ::testing::SizeIs; |
| |
| std::string RandomTraceFileName() { |
| #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
| constexpr char kSysTmpPath[] = "/data/misc/perfetto-traces"; |
| #else |
| constexpr char kSysTmpPath[] = "/tmp"; |
| #endif |
| static int suffix = 0; |
| |
| std::string path; |
| path.assign(kSysTmpPath); |
| path.append("/trace-"); |
| path.append(std::to_string(base::GetBootTimeNs().count())); |
| path.append("-"); |
| path.append(std::to_string(suffix++)); |
| return path; |
| } |
| |
| // For the SaveForBugreport* tests. |
| TraceConfig CreateTraceConfigForBugreportTest() { |
| TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(4096); |
| trace_config.set_duration_ms(60000); // Will never hit this. |
| trace_config.set_bugreport_score(10); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(3); |
| ds_config->mutable_for_testing()->set_message_size(10); |
| return trace_config; |
| } |
| |
| class ScopedFileRemove { |
| public: |
| explicit ScopedFileRemove(const std::string& path) : path_(path) {} |
| ~ScopedFileRemove() { remove(path_.c_str()); } |
| std::string path_; |
| }; |
| |
| class PerfettoCmdlineTest : public ::testing::Test { |
| public: |
| void SetUp() override { |
| #if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \ |
| defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER) |
| // 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 ). |
| PERFETTO_LOG("Skipping cmdline integration tests on sanitizers"); |
| GTEST_SKIP(); |
| #endif |
| } |
| |
| void TearDown() override {} |
| |
| void StartServiceIfRequiredNoNewExecsAfterThis() { |
| exec_allowed_ = false; |
| test_helper_.StartServiceIfRequired(); |
| } |
| |
| FakeProducer* ConnectFakeProducer() { |
| return test_helper_.ConnectFakeProducer(); |
| } |
| |
| std::function<void()> WrapTask(const std::function<void()>& function) { |
| return test_helper_.WrapTask(function); |
| } |
| |
| void WaitForProducerSetup() { test_helper_.WaitForProducerSetup(); } |
| |
| void WaitForProducerEnabled() { test_helper_.WaitForProducerEnabled(); } |
| |
| // Creates a process that represents the perfetto binary that will |
| // start when Run() is called. |args| will be passed as part of |
| // the command line and |std_in| will be piped into std::cin. |
| Exec ExecPerfetto(std::initializer_list<std::string> args, |
| std::string std_in = "") { |
| // You can not fork after you've started the service due to risk of |
| // deadlocks. |
| PERFETTO_CHECK(exec_allowed_); |
| return Exec("perfetto", std::move(args), std::move(std_in)); |
| } |
| |
| // Creates a process that represents the trigger_perfetto binary that will |
| // start when Run() is called. |args| will be passed as part of |
| // the command line and |std_in| will be piped into std::cin. |
| Exec ExecTrigger(std::initializer_list<std::string> args, |
| std::string std_in = "") { |
| // You can not fork after you've started the service due to risk of |
| // deadlocks. |
| PERFETTO_CHECK(exec_allowed_); |
| return Exec("trigger_perfetto", std::move(args), std::move(std_in)); |
| } |
| |
| // This is in common to the 3 TEST_F SaveForBugreport* fixtures, which differ |
| // only in the config, passed here as input. |
| void RunBugreportTest(protos::gen::TraceConfig trace_config, |
| bool check_original_trace = true, |
| bool use_explicit_clone = false) { |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| }, |
| trace_config.SerializeAsString()); |
| |
| Exec perfetto_br_proc = |
| use_explicit_clone |
| ? ExecPerfetto({"--out", GetBugreportTracePath(), "--clone", "-1"}) |
| : ExecPerfetto({"--save-for-bugreport"}); |
| |
| // Start the service and connect a simple fake producer. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| |
| auto* fake_producer = ConnectFakeProducer(); |
| ASSERT_TRUE(fake_producer); |
| |
| std::thread background_trace([&perfetto_proc]() { |
| std::string stderr_str; |
| ASSERT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str; |
| }); |
| |
| // Wait for the producer to start, and then write out packets. |
| WaitForProducerEnabled(); |
| auto on_data_written = task_runner_.CreateCheckpoint("data_written"); |
| fake_producer->ProduceEventBatch(WrapTask(on_data_written)); |
| task_runner_.RunUntilCheckpoint("data_written"); |
| |
| ASSERT_EQ(0, perfetto_br_proc.Run(&stderr_)) << "stderr: " << stderr_; |
| perfetto_proc.SendSigterm(); |
| background_trace.join(); |
| |
| auto check_trace_contents = [](std::string trace_path) { |
| // Read the trace written in the fixed location |
| // (/data/misc/perfetto-traces/ on Android, /tmp/ on Linux/Mac) and make |
| // sure it has the right contents. |
| std::string trace_str; |
| base::ReadFile(trace_path, &trace_str); |
| ASSERT_FALSE(trace_str.empty()); |
| protos::gen::Trace trace; |
| ASSERT_TRUE(trace.ParseFromString(trace_str)); |
| int test_packets = 0; |
| for (const auto& p : trace.packet()) |
| test_packets += p.has_for_testing() ? 1 : 0; |
| ASSERT_EQ(test_packets, 3) << trace_path; |
| }; |
| |
| // Verify that both the original trace and the cloned bugreport contain |
| // the expected contents. |
| check_trace_contents(GetBugreportTracePath()); |
| if (check_original_trace) |
| check_trace_contents(path); |
| } |
| |
| // Tests are allowed to freely use these variables. |
| std::string stderr_; |
| base::TestTaskRunner task_runner_; |
| |
| private: |
| bool exec_allowed_ = true; |
| TestHelper test_helper_{&task_runner_}; |
| }; |
| |
| } // namespace |
| |
| #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) |
| #define AndroidOnly(x) x |
| #else |
| #define AndroidOnly(x) DISABLED_##x |
| #endif |
| |
| TEST_F(PerfettoCmdlineTest, InvalidCases) { |
| std::string cfg("duration_ms: 100"); |
| |
| auto invalid_arg = ExecPerfetto({"--invalid-arg"}); |
| auto empty_config = ExecPerfetto({"-c", "-", "-o", "-"}, ""); |
| |
| // Cannot make assertions on --dropbox because on standalone builds it fails |
| // prematurely due to lack of dropbox. |
| auto missing_dropbox = |
| ExecPerfetto({"-c", "-", "--txt", "-o", "-", "--dropbox=foo"}, cfg); |
| auto either_out_or_dropbox = ExecPerfetto({"-c", "-", "--txt"}, cfg); |
| |
| // Disallow mixing simple and file config. |
| auto simple_and_file_1 = |
| ExecPerfetto({"-o", "-", "-c", "-", "-t", "2s"}, cfg); |
| auto simple_and_file_2 = |
| ExecPerfetto({"-o", "-", "-c", "-", "-b", "2m"}, cfg); |
| auto simple_and_file_3 = |
| ExecPerfetto({"-o", "-", "-c", "-", "-s", "2m"}, cfg); |
| |
| // Invalid --attach / --detach cases. |
| auto invalid_stop = |
| ExecPerfetto({"-c", "-", "--txt", "-o", "-", "--stop"}, cfg); |
| auto attach_and_config_1 = |
| ExecPerfetto({"-c", "-", "--txt", "-o", "-", "--attach=foo"}, cfg); |
| auto attach_and_config_2 = |
| ExecPerfetto({"-t", "2s", "-o", "-", "--attach=foo"}, cfg); |
| auto attach_needs_argument = ExecPerfetto({"--attach"}, cfg); |
| auto detach_needs_argument = |
| ExecPerfetto({"-t", "2s", "-o", "-", "--detach"}, cfg); |
| auto detach_without_out_or_dropbox = |
| ExecPerfetto({"-t", "2s", "--detach=foo"}, cfg); |
| |
| // Cannot trace and use --query. |
| auto trace_and_query_1 = ExecPerfetto({"-t", "2s", "--query"}, cfg); |
| auto trace_and_query_2 = ExecPerfetto({"-c", "-", "--query"}, cfg); |
| |
| // Ensure all Exec:: calls have been saved to prevent deadlocks. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| |
| EXPECT_EQ(1, invalid_arg.Run(&stderr_)); |
| |
| EXPECT_EQ(1, empty_config.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("TraceConfig is empty")); |
| |
| // Cannot make assertions on --upload because on standalone builds it fails |
| // prematurely due to lack of dropbox. |
| EXPECT_EQ(1, missing_dropbox.Run(&stderr_)); |
| |
| EXPECT_EQ(1, either_out_or_dropbox.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Either --out or --upload")); |
| |
| // Disallow mixing simple and file config. |
| EXPECT_EQ(1, simple_and_file_1.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Cannot specify both -c")); |
| |
| EXPECT_EQ(1, simple_and_file_2.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Cannot specify both -c")); |
| |
| EXPECT_EQ(1, simple_and_file_3.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Cannot specify both -c")); |
| |
| // Invalid --attach / --detach cases. |
| EXPECT_EQ(1, invalid_stop.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("--stop is supported only in combination")); |
| |
| EXPECT_EQ(1, attach_and_config_1.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Cannot specify a trace config")); |
| |
| EXPECT_EQ(1, attach_and_config_2.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Cannot specify a trace config")); |
| |
| EXPECT_EQ(1, attach_needs_argument.Run(&stderr_)); |
| EXPECT_THAT(stderr_, ContainsRegex("option.*--attach.*requires an argument")); |
| |
| EXPECT_EQ(1, detach_needs_argument.Run(&stderr_)); |
| EXPECT_THAT(stderr_, ContainsRegex("option.*--detach.*requires an argument")); |
| |
| EXPECT_EQ(1, detach_without_out_or_dropbox.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("--out or --upload is required")); |
| |
| // Cannot trace and use --query. |
| EXPECT_EQ(1, trace_and_query_1.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Cannot specify a trace config")); |
| |
| EXPECT_EQ(1, trace_and_query_2.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Cannot specify a trace config")); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, Version) { |
| auto perfetto = ExecPerfetto({"--version"}); |
| EXPECT_EQ(0, perfetto.Run(&stderr_)) << stderr_; |
| } |
| |
| TEST_F(PerfettoCmdlineTest, TxtConfig) { |
| std::string cfg("duration_ms: 100"); |
| auto perfetto = ExecPerfetto({"-c", "-", "--txt", "-o", "-"}, cfg); |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| EXPECT_EQ(0, perfetto.Run(&stderr_)) << stderr_; |
| } |
| |
| TEST_F(PerfettoCmdlineTest, SimpleConfig) { |
| auto perfetto = ExecPerfetto({"-o", "-", "-c", "-", "-t", "100ms"}); |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| EXPECT_EQ(0, perfetto.Run(&stderr_)) << stderr_; |
| } |
| |
| TEST_F(PerfettoCmdlineTest, DetachAndAttach) { |
| auto attach_to_not_existing = ExecPerfetto({"--attach=not_existent"}); |
| |
| std::string cfg("duration_ms: 10000; write_into_file: true"); |
| auto detach_valid_stop = |
| ExecPerfetto({"-o", "-", "-c", "-", "--txt", "--detach=valid_stop"}, cfg); |
| auto stop_valid_stop = ExecPerfetto({"--attach=valid_stop", "--stop"}); |
| |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| |
| EXPECT_NE(0, attach_to_not_existing.Run(&stderr_)); |
| EXPECT_THAT(stderr_, HasSubstr("Session re-attach failed")); |
| |
| EXPECT_EQ(0, detach_valid_stop.Run(&stderr_)) << stderr_; |
| EXPECT_EQ(0, stop_valid_stop.Run(&stderr_)); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, StartTracingTrigger) { |
| // See |message_count| and |message_size| in the TraceConfig above. |
| constexpr size_t kMessageCount = 11; |
| constexpr size_t kMessageSize = 32; |
| protos::gen::TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(1024); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(kMessageCount); |
| ds_config->mutable_for_testing()->set_message_size(kMessageSize); |
| auto* trigger_cfg = trace_config.mutable_trigger_config(); |
| trigger_cfg->set_trigger_mode( |
| protos::gen::TraceConfig::TriggerConfig::START_TRACING); |
| trigger_cfg->set_trigger_timeout_ms(15000); |
| auto* trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name"); |
| // |stop_delay_ms| must be long enough that we can write the packets in |
| // before the trace finishes. This has to be long enough for the slowest |
| // emulator. But as short as possible to prevent the test running a long |
| // time. |
| trigger->set_stop_delay_ms(500); |
| |
| // 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 |
| // printf (and thus hold locks). |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| }, |
| trace_config.SerializeAsString()); |
| |
| auto trigger_proc = ExecTrigger({"trigger_name"}); |
| |
| // Start the service and connect a simple fake producer. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| |
| auto* fake_producer = ConnectFakeProducer(); |
| EXPECT_TRUE(fake_producer); |
| |
| // Start a background thread that will deliver the config now that we've |
| // started the service. See |perfetto_proc| above for the args passed. |
| std::thread background_trace([&perfetto_proc]() { |
| std::string stderr_str; |
| EXPECT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str; |
| }); |
| |
| WaitForProducerSetup(); |
| EXPECT_EQ(0, trigger_proc.Run(&stderr_)); |
| |
| // Wait for the producer to start, and then write out 11 packets. |
| WaitForProducerEnabled(); |
| auto on_data_written = task_runner_.CreateCheckpoint("data_written"); |
| fake_producer->ProduceEventBatch(WrapTask(on_data_written)); |
| task_runner_.RunUntilCheckpoint("data_written"); |
| background_trace.join(); |
| |
| std::string trace_str; |
| base::ReadFile(path, &trace_str); |
| protos::gen::Trace trace; |
| ASSERT_TRUE(trace.ParseFromString(trace_str)); |
| size_t for_testing_packets = 0; |
| size_t trigger_packets = 0; |
| size_t trace_config_packets = 0; |
| for (const auto& packet : trace.packet()) { |
| if (packet.has_trace_config()) { |
| // Ensure the trace config properly includes the trigger mode we set. |
| auto kStartTrig = protos::gen::TraceConfig::TriggerConfig::START_TRACING; |
| EXPECT_EQ(kStartTrig, |
| packet.trace_config().trigger_config().trigger_mode()); |
| ++trace_config_packets; |
| } else if (packet.has_trigger()) { |
| // validate that the triggers are properly added to the trace. |
| EXPECT_EQ("trigger_name", packet.trigger().trigger_name()); |
| ++trigger_packets; |
| } else if (packet.has_for_testing()) { |
| // Make sure that the data size is correctly set based on what we |
| // requested. |
| EXPECT_EQ(kMessageSize, packet.for_testing().str().size()); |
| ++for_testing_packets; |
| } |
| } |
| EXPECT_EQ(trace_config_packets, 1u); |
| EXPECT_EQ(trigger_packets, 1u); |
| EXPECT_EQ(for_testing_packets, kMessageCount); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, StopTracingTrigger) { |
| // See |message_count| and |message_size| in the TraceConfig above. |
| constexpr size_t kMessageCount = 11; |
| constexpr size_t kMessageSize = 32; |
| protos::gen::TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(1024); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(kMessageCount); |
| ds_config->mutable_for_testing()->set_message_size(kMessageSize); |
| auto* trigger_cfg = trace_config.mutable_trigger_config(); |
| trigger_cfg->set_trigger_mode( |
| protos::gen::TraceConfig::TriggerConfig::STOP_TRACING); |
| trigger_cfg->set_trigger_timeout_ms(15000); |
| auto* trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name"); |
| // |stop_delay_ms| must be long enough that we can write the packets in |
| // before the trace finishes. This has to be long enough for the slowest |
| // emulator. But as short as possible to prevent the test running a long |
| // time. |
| trigger->set_stop_delay_ms(500); |
| trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name_3"); |
| trigger->set_stop_delay_ms(60000); |
| |
| // 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 |
| // printf (and thus hold locks). |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| }, |
| trace_config.SerializeAsString()); |
| |
| auto trigger_proc = |
| ExecTrigger({"trigger_name_2", "trigger_name", "trigger_name_3"}); |
| |
| // Start the service and connect a simple fake producer. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| auto* fake_producer = ConnectFakeProducer(); |
| EXPECT_TRUE(fake_producer); |
| |
| // Start a background thread that will deliver the config now that we've |
| // started the service. See |perfetto_proc| above for the args passed. |
| std::thread background_trace([&perfetto_proc]() { |
| std::string stderr_str; |
| EXPECT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str; |
| }); |
| |
| WaitForProducerEnabled(); |
| // Wait for the producer to start, and then write out 11 packets, before the |
| // trace actually starts (the trigger is seen). |
| auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); |
| fake_producer->ProduceEventBatch(WrapTask(on_data_written)); |
| task_runner_.RunUntilCheckpoint("data_written_1"); |
| |
| EXPECT_EQ(0, trigger_proc.Run(&stderr_)) << "stderr: " << stderr_; |
| |
| background_trace.join(); |
| |
| std::string trace_str; |
| base::ReadFile(path, &trace_str); |
| protos::gen::Trace trace; |
| ASSERT_TRUE(trace.ParseFromString(trace_str)); |
| bool seen_first_trigger = false; |
| size_t for_testing_packets = 0; |
| size_t trigger_packets = 0; |
| size_t trace_config_packets = 0; |
| for (const auto& packet : trace.packet()) { |
| if (packet.has_trace_config()) { |
| // Ensure the trace config properly includes the trigger mode we set. |
| auto kStopTrig = protos::gen::TraceConfig::TriggerConfig::STOP_TRACING; |
| EXPECT_EQ(kStopTrig, |
| packet.trace_config().trigger_config().trigger_mode()); |
| ++trace_config_packets; |
| } else if (packet.has_trigger()) { |
| // validate that the triggers are properly added to the trace. |
| if (!seen_first_trigger) { |
| EXPECT_EQ("trigger_name", packet.trigger().trigger_name()); |
| seen_first_trigger = true; |
| } else { |
| EXPECT_EQ("trigger_name_3", packet.trigger().trigger_name()); |
| } |
| ++trigger_packets; |
| } else if (packet.has_for_testing()) { |
| // Make sure that the data size is correctly set based on what we |
| // requested. |
| EXPECT_EQ(kMessageSize, packet.for_testing().str().size()); |
| ++for_testing_packets; |
| } |
| } |
| EXPECT_EQ(trace_config_packets, 1u); |
| EXPECT_EQ(trigger_packets, 2u); |
| EXPECT_EQ(for_testing_packets, kMessageCount); |
| } |
| |
| // Dropbox on the commandline client only works on android builds. So disable |
| // this test on all other builds. |
| TEST_F(PerfettoCmdlineTest, AndroidOnly(NoDataNoFileWithoutTrigger)) { |
| // See |message_count| and |message_size| in the TraceConfig above. |
| constexpr size_t kMessageCount = 11; |
| constexpr size_t kMessageSize = 32; |
| protos::gen::TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(1024); |
| trace_config.set_allow_user_build_tracing(true); |
| auto* incident_config = trace_config.mutable_incident_report_config(); |
| incident_config->set_destination_package("foo.bar.baz"); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(kMessageCount); |
| ds_config->mutable_for_testing()->set_message_size(kMessageSize); |
| auto* trigger_cfg = trace_config.mutable_trigger_config(); |
| trigger_cfg->set_trigger_mode( |
| protos::gen::TraceConfig::TriggerConfig::STOP_TRACING); |
| trigger_cfg->set_trigger_timeout_ms(1000); |
| auto* trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name"); |
| // |stop_delay_ms| must be long enough that we can write the packets in |
| // before the trace finishes. This has to be long enough for the slowest |
| // emulator. But as short as possible to prevent the test running a long |
| // time. |
| trigger->set_stop_delay_ms(500); |
| trigger = trigger_cfg->add_triggers(); |
| |
| // 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 |
| // printf (and thus hold locks). |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "--dropbox", |
| "TAG", |
| "--no-guardrails", |
| "-c", |
| "-", |
| }, |
| trace_config.SerializeAsString()); |
| |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| auto* fake_producer = ConnectFakeProducer(); |
| EXPECT_TRUE(fake_producer); |
| |
| std::string stderr_str; |
| std::thread background_trace([&perfetto_proc, &stderr_str]() { |
| EXPECT_EQ(0, perfetto_proc.Run(&stderr_str)); |
| }); |
| background_trace.join(); |
| |
| EXPECT_THAT(stderr_str, |
| ::testing::HasSubstr("Skipping write to incident. Empty trace.")); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, StopTracingTriggerFromConfig) { |
| // See |message_count| and |message_size| in the TraceConfig above. |
| constexpr size_t kMessageCount = 11; |
| constexpr size_t kMessageSize = 32; |
| protos::gen::TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(1024); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(kMessageCount); |
| ds_config->mutable_for_testing()->set_message_size(kMessageSize); |
| auto* trigger_cfg = trace_config.mutable_trigger_config(); |
| trigger_cfg->set_trigger_mode( |
| protos::gen::TraceConfig::TriggerConfig::STOP_TRACING); |
| trigger_cfg->set_trigger_timeout_ms(15000); |
| auto* trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name"); |
| // |stop_delay_ms| must be long enough that we can write the packets in |
| // before the trace finishes. This has to be long enough for the slowest |
| // emulator. But as short as possible to prevent the test running a long |
| // time. |
| trigger->set_stop_delay_ms(500); |
| trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name_3"); |
| trigger->set_stop_delay_ms(60000); |
| |
| // 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 |
| // printf (and thus hold locks). |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| }, |
| trace_config.SerializeAsString()); |
| |
| std::string triggers = R"( |
| activate_triggers: "trigger_name_2" |
| activate_triggers: "trigger_name" |
| activate_triggers: "trigger_name_3" |
| )"; |
| auto perfetto_proc_2 = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| "--txt", |
| }, |
| triggers); |
| |
| // Start the service and connect a simple fake producer. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| auto* fake_producer = ConnectFakeProducer(); |
| EXPECT_TRUE(fake_producer); |
| |
| std::thread background_trace([&perfetto_proc]() { |
| std::string stderr_str; |
| EXPECT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str; |
| }); |
| |
| WaitForProducerEnabled(); |
| // Wait for the producer to start, and then write out 11 packets, before the |
| // trace actually starts (the trigger is seen). |
| auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); |
| fake_producer->ProduceEventBatch(WrapTask(on_data_written)); |
| task_runner_.RunUntilCheckpoint("data_written_1"); |
| |
| EXPECT_EQ(0, perfetto_proc_2.Run(&stderr_)) << "stderr: " << stderr_; |
| |
| background_trace.join(); |
| |
| std::string trace_str; |
| base::ReadFile(path, &trace_str); |
| protos::gen::Trace trace; |
| ASSERT_TRUE(trace.ParseFromString(trace_str)); |
| EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size()); |
| bool seen_first_trigger = false; |
| for (const auto& packet : trace.packet()) { |
| if (packet.has_trace_config()) { |
| // Ensure the trace config properly includes the trigger mode we set. |
| auto kStopTrig = protos::gen::TraceConfig::TriggerConfig::STOP_TRACING; |
| EXPECT_EQ(kStopTrig, |
| packet.trace_config().trigger_config().trigger_mode()); |
| } else if (packet.has_trigger()) { |
| // validate that the triggers are properly added to the trace. |
| if (!seen_first_trigger) { |
| EXPECT_EQ("trigger_name", packet.trigger().trigger_name()); |
| seen_first_trigger = true; |
| } else { |
| EXPECT_EQ("trigger_name_3", packet.trigger().trigger_name()); |
| } |
| } else if (packet.has_for_testing()) { |
| // Make sure that the data size is correctly set based on what we |
| // requested. |
| EXPECT_EQ(kMessageSize, packet.for_testing().str().size()); |
| } |
| } |
| } |
| |
| TEST_F(PerfettoCmdlineTest, TriggerFromConfigStopsFileOpening) { |
| // See |message_count| and |message_size| in the TraceConfig above. |
| constexpr size_t kMessageCount = 11; |
| constexpr size_t kMessageSize = 32; |
| protos::gen::TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(1024); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(kMessageCount); |
| ds_config->mutable_for_testing()->set_message_size(kMessageSize); |
| auto* trigger_cfg = trace_config.mutable_trigger_config(); |
| trigger_cfg->set_trigger_mode( |
| protos::gen::TraceConfig::TriggerConfig::STOP_TRACING); |
| trigger_cfg->set_trigger_timeout_ms(15000); |
| auto* trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name"); |
| // |stop_delay_ms| must be long enough that we can write the packets in |
| // before the trace finishes. This has to be long enough for the slowest |
| // emulator. But as short as possible to prevent the test running a long |
| // time. |
| trigger->set_stop_delay_ms(500); |
| trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name_3"); |
| trigger->set_stop_delay_ms(60000); |
| |
| // 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 |
| // printf (and thus hold locks). |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| std::string triggers = R"( |
| activate_triggers: "trigger_name_2" |
| activate_triggers: "trigger_name" |
| activate_triggers: "trigger_name_3" |
| )"; |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| "--txt", |
| }, |
| triggers); |
| |
| // Start the service and connect a simple fake producer. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| auto* fake_producer = ConnectFakeProducer(); |
| EXPECT_TRUE(fake_producer); |
| |
| std::string trace_str; |
| EXPECT_FALSE(base::ReadFile(path, &trace_str)); |
| |
| EXPECT_EQ(0, perfetto_proc.Run(&stderr_)) << "stderr: " << stderr_; |
| |
| EXPECT_FALSE(base::ReadFile(path, &trace_str)); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, Query) { |
| auto query = ExecPerfetto({"--query"}); |
| auto query_raw = ExecPerfetto({"--query-raw"}); |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| EXPECT_EQ(0, query.Run(&stderr_)) << stderr_; |
| EXPECT_EQ(0, query_raw.Run(&stderr_)) << stderr_; |
| } |
| |
| TEST_F(PerfettoCmdlineTest, AndroidOnly(CmdTriggerWithUploadFlag)) { |
| // See |message_count| and |message_size| in the TraceConfig above. |
| constexpr size_t kMessageCount = 2; |
| constexpr size_t kMessageSize = 2; |
| protos::gen::TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(1024); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(kMessageCount); |
| ds_config->mutable_for_testing()->set_message_size(kMessageSize); |
| auto* trigger_cfg = trace_config.mutable_trigger_config(); |
| trigger_cfg->set_trigger_mode( |
| protos::gen::TraceConfig::TriggerConfig::STOP_TRACING); |
| trigger_cfg->set_trigger_timeout_ms(15000); |
| auto* trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name"); |
| // |stop_delay_ms| must be long enough that we can write the packets in |
| // before the trace finishes. This has to be long enough for the slowest |
| // emulator. But as short as possible to prevent the test running a long |
| // time. |
| trigger->set_stop_delay_ms(500); |
| |
| // 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 |
| // printf (and thus hold locks). |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| }, |
| trace_config.SerializeAsString()); |
| |
| std::string triggers = R"( |
| activate_triggers: "trigger_name" |
| )"; |
| auto perfetto_proc_2 = ExecPerfetto( |
| { |
| "--upload", |
| "-c", |
| "-", |
| "--txt", |
| }, |
| triggers); |
| |
| // Start the service and connect a simple fake producer. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| auto* fake_producer = ConnectFakeProducer(); |
| EXPECT_TRUE(fake_producer); |
| |
| std::thread background_trace([&perfetto_proc]() { |
| std::string stderr_str; |
| EXPECT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str; |
| }); |
| |
| WaitForProducerEnabled(); |
| // Wait for the producer to start, and then write out 11 packets, before the |
| // trace actually starts (the trigger is seen). |
| auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); |
| fake_producer->ProduceEventBatch(WrapTask(on_data_written)); |
| task_runner_.RunUntilCheckpoint("data_written_1"); |
| |
| EXPECT_EQ(0, perfetto_proc_2.Run(&stderr_)) << "stderr: " << stderr_; |
| |
| background_trace.join(); |
| |
| std::string trace_str; |
| base::ReadFile(path, &trace_str); |
| protos::gen::Trace trace; |
| ASSERT_TRUE(trace.ParseFromString(trace_str)); |
| EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size()); |
| EXPECT_THAT(trace.packet(), |
| Contains(Property(&protos::gen::TracePacket::trigger, |
| Property(&protos::gen::Trigger::trigger_name, |
| Eq("trigger_name"))))); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, TriggerCloneSnapshot) { |
| constexpr size_t kMessageCount = 2; |
| constexpr size_t kMessageSize = 2; |
| protos::gen::TraceConfig trace_config; |
| trace_config.add_buffers()->set_size_kb(1024); |
| auto* ds_config = trace_config.add_data_sources()->mutable_config(); |
| ds_config->set_name("android.perfetto.FakeProducer"); |
| ds_config->mutable_for_testing()->set_message_count(kMessageCount); |
| ds_config->mutable_for_testing()->set_message_size(kMessageSize); |
| auto* trigger_cfg = trace_config.mutable_trigger_config(); |
| trigger_cfg->set_trigger_mode( |
| protos::gen::TraceConfig::TriggerConfig::CLONE_SNAPSHOT); |
| trigger_cfg->set_trigger_timeout_ms(600000); |
| auto* trigger = trigger_cfg->add_triggers(); |
| trigger->set_name("trigger_name"); |
| // |stop_delay_ms| must be long enough that we can write the packets in |
| // before the trace finishes. This has to be long enough for the slowest |
| // emulator. But as short as possible to prevent the test running a long |
| // time. |
| trigger->set_stop_delay_ms(500); |
| |
| // 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 |
| // printf (and thus hold locks). |
| const std::string path = RandomTraceFileName(); |
| ScopedFileRemove remove_on_test_exit(path); |
| auto perfetto_proc = ExecPerfetto( |
| { |
| "-o", |
| path, |
| "-c", |
| "-", |
| }, |
| trace_config.SerializeAsString()); |
| |
| std::string triggers = R"( |
| activate_triggers: "trigger_name" |
| )"; |
| auto trigger_proc = ExecPerfetto( |
| { |
| "-c", |
| "-", |
| "--txt", |
| }, |
| triggers); |
| |
| // Start the service and connect a simple fake producer. |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| auto* fake_producer = ConnectFakeProducer(); |
| EXPECT_TRUE(fake_producer); |
| |
| std::thread background_trace([&perfetto_proc]() { |
| std::string stderr_str; |
| EXPECT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str; |
| }); |
| |
| WaitForProducerEnabled(); |
| // Wait for the producer to start, and then write out 11 packets, before the |
| // trace actually starts (the trigger is seen). |
| auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); |
| fake_producer->ProduceEventBatch(WrapTask(on_data_written)); |
| task_runner_.RunUntilCheckpoint("data_written_1"); |
| |
| EXPECT_EQ(0, trigger_proc.Run(&stderr_)) << "stderr: " << stderr_; |
| |
| // Now we need to wait that the `perfetto_proc` creates the snapshot trace |
| // file in the trace/path.0 file (appending .0). Once that is done we can |
| // kill the perfetto cmd (otherwise it will keep running for the whole |
| // trigger_timeout_ms, unlike the case of STOP_TRACING. |
| std::string snapshot_path = path + ".0"; |
| for (int i = 0; i < 100 && !base::FileExists(snapshot_path); i++) { |
| std::this_thread::sleep_for(std::chrono::milliseconds(100)); |
| } |
| ASSERT_TRUE(base::FileExists(snapshot_path)); |
| |
| perfetto_proc.SendSigterm(); |
| background_trace.join(); |
| |
| std::string trace_str; |
| base::ReadFile(snapshot_path, &trace_str); |
| protos::gen::Trace trace; |
| ASSERT_TRUE(trace.ParseFromString(trace_str)); |
| EXPECT_LT(static_cast<int>(kMessageCount), trace.packet_size()); |
| EXPECT_THAT(trace.packet(), |
| Contains(Property(&protos::gen::TracePacket::trigger, |
| Property(&protos::gen::Trigger::trigger_name, |
| Eq("trigger_name"))))); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, SaveForBugreport) { |
| TraceConfig trace_config = CreateTraceConfigForBugreportTest(); |
| RunBugreportTest(std::move(trace_config)); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, SaveForBugreport_WriteIntoFile) { |
| TraceConfig trace_config = CreateTraceConfigForBugreportTest(); |
| trace_config.set_file_write_period_ms(60000); // Will never hit this. |
| trace_config.set_write_into_file(true); |
| RunBugreportTest(std::move(trace_config)); |
| } |
| |
| TEST_F(PerfettoCmdlineTest, Clone) { |
| TraceConfig trace_config = CreateTraceConfigForBugreportTest(); |
| RunBugreportTest(std::move(trace_config), /*check_original_trace=*/true, |
| /*use_explicit_clone=*/true); |
| } |
| |
| // Regression test for b/279753347 . |
| TEST_F(PerfettoCmdlineTest, UnavailableBugreportLeavesNoEmptyFiles) { |
| ScopedFileRemove remove_on_test_exit(GetBugreportTracePath()); |
| Exec perfetto_br_proc = ExecPerfetto({"--save-for-bugreport"}); |
| StartServiceIfRequiredNoNewExecsAfterThis(); |
| perfetto_br_proc.Run(&stderr_); |
| ASSERT_FALSE(base::FileExists(GetBugreportTracePath())); |
| } |
| |
| // Tests that SaveTraceForBugreport() works also if the trace has triggers |
| // defined and those triggers have not been hit. This is a regression test for |
| // b/188008375 . |
| #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) |
| // Disabled due to b/191940560 |
| #define MAYBE_SaveForBugreport_Triggers DISABLED_SaveForBugreport_Triggers |
| #else |
| #define MAYBE_SaveForBugreport_Triggers SaveForBugreport_Triggers |
| #endif |
| TEST_F(PerfettoCmdlineTest, MAYBE_SaveForBugreport_Triggers) { |
| TraceConfig trace_config = CreateTraceConfigForBugreportTest(); |
| trace_config.set_duration_ms(0); // set_trigger_timeout_ms is used instead. |
| auto* trigger_config = trace_config.mutable_trigger_config(); |
| trigger_config->set_trigger_timeout_ms(8.64e+7); |
| trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::STOP_TRACING); |
| auto* trigger = trigger_config->add_triggers(); |
| trigger->set_name("trigger_name"); |
| trigger->set_stop_delay_ms(1); |
| RunBugreportTest(std::move(trace_config), /*check_original_trace=*/false); |
| } |
| |
| } // namespace perfetto |