/* * 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 #include #include #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 "perfetto/tracing/core/tracing_service_state.h" #include "src/base/test/test_task_runner.h" #include "src/base/test/utils.h" #include "src/perfetto_cmd/bugreport_path.h" #include "src/protozero/filtering/filter_bytecode_generator.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::ElementsAre; using ::testing::ElementsAreArray; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::IsEmpty; 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(int score = 1, bool add_filter = false, uint32_t msg_count = 3, uint32_t msg_size = 10) { TraceConfig trace_config; trace_config.add_buffers()->set_size_kb(32768); trace_config.set_duration_ms(60000); // Will never hit this. trace_config.set_bugreport_score(score); if (add_filter) { // Add a trace filter which disallows the trace config echo-back. protozero::FilterBytecodeGenerator filt; filt.AddNestedField(1 /* root trace.packet*/, 1); filt.EndMessage(); // Add a random unrelated field to keep the generator happy. filt.AddSimpleField(protos::pbzero::TracePacket::kTraceUuidFieldNumber); filt.EndMessage(); trace_config.mutable_trace_filter()->set_bytecode_v2(filt.Serialize()); } 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(msg_count); ds_config->mutable_for_testing()->set_message_size(msg_size); return trace_config; } // For the regular tests. TraceConfig CreateTraceConfigForTest(uint32_t test_msg_count = 11, uint32_t test_msg_size = 32) { 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(test_msg_count); ds_config->mutable_for_testing()->set_message_size(test_msg_size); return trace_config; } void ExpectTraceContainsTestMessages(const protos::gen::Trace& trace, uint32_t count) { ssize_t actual_test_packets_count = std::count_if( trace.packet().begin(), trace.packet().end(), [](const protos::gen::TracePacket& tp) { return tp.has_for_testing(); }); EXPECT_EQ(count, static_cast(actual_test_packets_count)); } void ExpectTraceContainsTestMessagesWithSize(const protos::gen::Trace& trace, uint32_t message_size) { for (const auto& packet : trace.packet()) { if (packet.has_for_testing()) { EXPECT_EQ(message_size, packet.for_testing().str().size()); } } } void ExpectTraceContainsConfigWithTriggerMode( const protos::gen::Trace& trace, protos::gen::TraceConfig::TriggerConfig::TriggerMode trigger_mode) { // GTest three level nested Property matcher is hard to read, so we use // 'find_if' with lambda to ensure the trace config properly includes the // trigger mode we set. auto found = std::find_if(trace.packet().begin(), trace.packet().end(), [trigger_mode](const protos::gen::TracePacket& tp) { return tp.has_trace_config() && tp.trace_config().trigger_config().trigger_mode() == trigger_mode; }); EXPECT_NE(found, trace.packet().end()) << "Trace config doesn't include expected trigger mode."; } class ScopedFileRemove { public: explicit ScopedFileRemove(const std::string& path) : path_(path) {} ~ScopedFileRemove() { remove(path_.c_str()); } std::string path_; }; bool ParseNotEmptyTraceFromFile(const std::string& trace_path, protos::gen::Trace& out) { std::string trace_str; if (!base::ReadFile(trace_path, &trace_str)) return false; if (trace_str.empty()) return false; return out.ParseFromString(trace_str); } std::vector GetReceivedTriggerNames( const protos::gen::Trace& trace) { std::vector triggers; for (const protos::gen::TracePacket& packet : trace.packet()) { if (packet.has_trigger()) { triggers.push_back(packet.trigger().trigger_name()); } } return triggers; } class PerfettoCmdlineTest : public ::testing::Test { public: void StartServiceIfRequiredNoNewExecsAfterThis() { exec_allowed_ = false; test_helper_.StartServiceIfRequired(); } TestHelper& test_helper() { return test_helper_; } // 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 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 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 = test_helper().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. test_helper().WaitForProducerEnabled(); auto on_data_written = task_runner_.CreateCheckpoint("data_written"); fake_producer->ProduceEventBatch(test_helper().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(); uint32_t expected_packets = 0; for (auto& ds : trace_config.data_sources()) { if (ds.config().has_for_testing()) expected_packets = ds.config().for_testing().message_count(); } auto check_trace_contents = [expected_packets](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. protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(trace_path, trace)); uint32_t test_packets = 0; for (const auto& p : trace.packet()) test_packets += p.has_for_testing() ? 1 : 0; ASSERT_EQ(test_packets, expected_packets) << 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_; // We use these two constants to set test data payload parameters and assert // it was correctly written to the trace. static constexpr size_t kTestMessageCount = 11; static constexpr size_t kTestMessageSize = 32; 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) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); 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 = test_helper().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; }); test_helper().WaitForProducerSetup(); EXPECT_EQ(0, trigger_proc.Run(&stderr_)); // Wait for the producer to start, and then write out some test packets. test_helper().WaitForProducerEnabled(); auto on_data_written = task_runner_.CreateCheckpoint("data_written"); fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written)); task_runner_.RunUntilCheckpoint("data_written"); background_trace.join(); protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace)); ExpectTraceContainsConfigWithTriggerMode( trace, protos::gen::TraceConfig::TriggerConfig::START_TRACING); EXPECT_THAT(GetReceivedTriggerNames(trace), ElementsAre("trigger_name")); ExpectTraceContainsTestMessages(trace, kTestMessageCount); ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize); } TEST_F(PerfettoCmdlineTest, StopTracingTrigger) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); 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 = test_helper().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; }); test_helper().WaitForProducerEnabled(); // Wait for the producer to start, and then write out some test packets, // before the trace actually starts (the trigger is seen). auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written)); task_runner_.RunUntilCheckpoint("data_written_1"); EXPECT_EQ(0, trigger_proc.Run(&stderr_)) << "stderr: " << stderr_; background_trace.join(); protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace)); ExpectTraceContainsConfigWithTriggerMode( trace, protos::gen::TraceConfig::TriggerConfig::STOP_TRACING); EXPECT_THAT(GetReceivedTriggerNames(trace), ElementsAre("trigger_name", "trigger_name_3")); ExpectTraceContainsTestMessages(trace, kTestMessageCount); ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize); } // Dropbox on the commandline client only works on android builds. So disable // this test on all other builds. TEST_F(PerfettoCmdlineTest, AndroidOnly(NoDataNoFileWithoutTrigger)) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); auto* incident_config = trace_config.mutable_incident_report_config(); incident_config->set_destination_package("foo.bar.baz"); 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 = test_helper().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) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); 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 = test_helper().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; }); test_helper().WaitForProducerEnabled(); // Wait for the producer to start, and then write out some test packets, // before the trace actually starts (the trigger is seen). auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written)); task_runner_.RunUntilCheckpoint("data_written_1"); EXPECT_EQ(0, perfetto_proc_2.Run(&stderr_)) << "stderr: " << stderr_; background_trace.join(); protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace)); EXPECT_LT(static_cast(kTestMessageCount), trace.packet_size()); ExpectTraceContainsConfigWithTriggerMode( trace, protos::gen::TraceConfig::TriggerConfig::STOP_TRACING); EXPECT_THAT(GetReceivedTriggerNames(trace), ElementsAre("trigger_name", "trigger_name_3")); ExpectTraceContainsTestMessages(trace, kTestMessageCount); ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize); } TEST_F(PerfettoCmdlineTest, TriggerFromConfigStopsFileOpening) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); 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 = test_helper().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)) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); 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 = test_helper().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; }); test_helper().WaitForProducerEnabled(); // Wait for the producer to start, and then write out some test packets, // before the trace actually starts (the trigger is seen). auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written)); task_runner_.RunUntilCheckpoint("data_written_1"); EXPECT_EQ(0, perfetto_proc_2.Run(&stderr_)) << "stderr: " << stderr_; background_trace.join(); protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace)); ExpectTraceContainsTestMessages(trace, kTestMessageCount); ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize); EXPECT_LT(static_cast(kTestMessageCount), 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) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); 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 = test_helper().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; }); test_helper().WaitForProducerEnabled(); // Wait for the producer to start, and then write out some test packets, // before the trace actually starts (the trigger is seen). auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); fake_producer->ProduceEventBatch(test_helper().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(); protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(snapshot_path, trace)); ExpectTraceContainsTestMessages(trace, kTestMessageCount); ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize); EXPECT_LT(static_cast(kTestMessageCount), 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, MultipleTriggersCloneSnapshot) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); 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); // Add two triggers, the "trigger_name_2" hits before "trigger_name_1". auto* trigger = trigger_cfg->add_triggers(); trigger->set_name("trigger_name_1"); trigger->set_stop_delay_ms(1500); trigger = trigger_cfg->add_triggers(); trigger->set_name("trigger_name_2"); 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 triggers_proc = ExecTrigger({"trigger_name_1", "trigger_name_2"}); // Start the service and connect a simple fake producer. StartServiceIfRequiredNoNewExecsAfterThis(); auto* fake_producer = test_helper().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; }); test_helper().WaitForProducerEnabled(); // Wait for the producer to start, and then write out some test packets, // before the trace actually starts (the trigger is seen). auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written)); task_runner_.RunUntilCheckpoint("data_written_1"); EXPECT_EQ(0, triggers_proc.Run(&stderr_)) << "stderr: " << stderr_; // Wait for both clone triggers to hit and wait for two snapshot files. 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)); std::string snapshot_path_2 = path + ".1"; for (int i = 0; i < 100 && !base::FileExists(snapshot_path_2); i++) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } ASSERT_TRUE(base::FileExists(snapshot_path_2)); perfetto_proc.SendSigterm(); background_trace.join(); // We now have two traces, the first one was cloned by "trigger_name_2", // the second was cloned by "trigger_name_1". // Asserts for the first trace. protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(snapshot_path, trace)); EXPECT_LT(static_cast(kTestMessageCount), trace.packet_size()); EXPECT_THAT(GetReceivedTriggerNames(trace), ElementsAre("trigger_name_1", "trigger_name_2")); std::vector clone_trigger_packets; protos::gen::TracePacket trigger_packet; for (const auto& packet : trace.packet()) { if (packet.has_clone_snapshot_trigger()) { clone_trigger_packets.push_back(packet); } else if (packet.has_trigger() && packet.trigger().trigger_name() == "trigger_name_2") { trigger_packet = packet; } } ASSERT_EQ(clone_trigger_packets.size(), 1ul); EXPECT_EQ(clone_trigger_packets[0].clone_snapshot_trigger().trigger_name(), "trigger_name_2"); // Assert that all fields of 'clone_snapshot_trigger' equal to the same fields // of a 'trigger'. EXPECT_EQ(clone_trigger_packets[0].timestamp(), trigger_packet.timestamp()); EXPECT_EQ(clone_trigger_packets[0].clone_snapshot_trigger(), trigger_packet.trigger()); // Asserts for the second trace. protos::gen::Trace trace_2; ASSERT_TRUE(ParseNotEmptyTraceFromFile(snapshot_path_2, trace_2)); EXPECT_LT(static_cast(kTestMessageCount), trace_2.packet_size()); // List of received triggers from the main session was cleaned after the first // clone operation happened, the list is empty in the second trace. EXPECT_THAT(GetReceivedTriggerNames(trace_2), IsEmpty()); std::vector clone_trigger_packets_2; for (const auto& packet : trace_2.packet()) { if (packet.has_clone_snapshot_trigger()) { clone_trigger_packets_2.push_back(packet); } } ASSERT_EQ(clone_trigger_packets_2.size(), 1ul); EXPECT_EQ(clone_trigger_packets_2[0].clone_snapshot_trigger().trigger_name(), "trigger_name_1"); // There is no triggers in the second snapshot, but we can compare the // "clone_snapshot_trigger" with the trigger saved into the first snapshot. protos::gen::TracePacket trigger_packet_from_first_snapshot; for (const auto& packet : trace.packet()) { if (packet.has_trigger() && packet.trigger().trigger_name() == "trigger_name_1") { trigger_packet_from_first_snapshot = packet; } } // Assert that all fields of 'clone_snapshot_trigger' equal to the same fields // of a 'trigger'. EXPECT_EQ(clone_trigger_packets_2[0].timestamp(), trigger_packet_from_first_snapshot.timestamp()); EXPECT_EQ(clone_trigger_packets_2[0].clone_snapshot_trigger(), trigger_packet_from_first_snapshot.trigger()); } 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); } TEST_F(PerfettoCmdlineTest, CloneByName) { protos::gen::TraceConfig trace_config = CreateTraceConfigForTest(kTestMessageCount, kTestMessageSize); trace_config.set_unique_session_name("my_unique_session_name"); // 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()); const std::string path_cloned = RandomTraceFileName(); ScopedFileRemove path_cloned_remove(path_cloned); auto perfetto_proc_clone = ExecPerfetto({ "-o", path_cloned, "--clone-by-name", "my_unique_session_name", }); const std::string path_cloned_2 = RandomTraceFileName(); ScopedFileRemove path_cloned_2_remove(path_cloned_2); auto perfetto_proc_clone_2 = ExecPerfetto({ "-o", path_cloned_2, "--clone-by-name", "non_existing_session_name", }); // Start the service and connect a simple fake producer. StartServiceIfRequiredNoNewExecsAfterThis(); auto* fake_producer = test_helper().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; }); test_helper().WaitForProducerEnabled(); auto on_data_written = task_runner_.CreateCheckpoint("data_written_1"); fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written)); task_runner_.RunUntilCheckpoint("data_written_1"); EXPECT_EQ(0, perfetto_proc_clone.Run(&stderr_)) << "stderr: " << stderr_; EXPECT_TRUE(base::FileExists(path_cloned)); // The command still returns 0, but doesn't create a file. EXPECT_EQ(0, perfetto_proc_clone_2.Run(&stderr_)) << "stderr: " << stderr_; EXPECT_FALSE(base::FileExists(path_cloned_2)); protos::gen::Trace cloned_trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(path_cloned, cloned_trace)); ExpectTraceContainsTestMessages(cloned_trace, kTestMessageCount); ExpectTraceContainsTestMessagesWithSize(cloned_trace, kTestMessageSize); perfetto_proc.SendSigterm(); background_trace.join(); protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(path, trace)); ExpectTraceContainsTestMessages(trace, kTestMessageCount); ExpectTraceContainsTestMessagesWithSize(trace, kTestMessageSize); } // Regression test for b/279753347: --save-for-bugreport would create an empty // file if no session with bugreport_score was active. TEST_F(PerfettoCmdlineTest, UnavailableBugreportLeavesNoEmptyFiles) { ScopedFileRemove remove_on_test_exit(GetBugreportTracePath()); Exec perfetto_br_proc = ExecPerfetto({"--save-for-bugreport"}); StartServiceIfRequiredNoNewExecsAfterThis(); perfetto_br_proc.Run(&stderr_); // No file exists. Great. if (!base::FileExists(GetBugreportTracePath())) { return; } // A file exists. There are two possiblilities: // 1. There was a bugreport_score session. // 2. There was no bugreport_score session and we're hitting b/279753347. // // Let's check that we're not hitting b/279753347, by checking that the file // is not empty. EXPECT_NE(base::GetFileSize(GetBugreportTracePath()), 0); } // 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); } TEST_F(PerfettoCmdlineTest, SaveAllForBugreport_NoTraces) { auto save_all_cmd = ExecPerfetto({"--save-all-for-bugreport"}); StartServiceIfRequiredNoNewExecsAfterThis(); EXPECT_EQ(0, save_all_cmd.Run(&stderr_)); EXPECT_THAT(stderr_, HasSubstr("No tracing sessions eligible")); } TEST_F(PerfettoCmdlineTest, SaveAllForBugreport_FourTraces) { struct TraceProc { explicit TraceProc(TraceConfig c) : cfg(std::move(c)) {} TraceConfig cfg; std::optional proc; std::thread thd; }; auto remove_br_files = [] { remove((GetBugreportTraceDir() + "/systrace.pftrace").c_str()); remove((GetBugreportTraceDir() + "/custom_name.pftrace").c_str()); remove((GetBugreportTraceDir() + "/custom_name_1.pftrace").c_str()); remove((GetBugreportTraceDir() + "/systrace_1.pftrace").c_str()); }; remove_br_files(); // Remove both before and after ending the test. auto remove_on_exit = base::OnScopeExit(remove_br_files); auto session_prefix = "bugreport_test_" + std::to_string(base::GetWallTimeNs().count() % 1000000); // Create four tracing sessions with different bugreport scores. // Two of them will have the default "systrace.pftrace" name. std::vector traces; const bool add_filt = true; traces.emplace_back(CreateTraceConfigForBugreportTest(/*score=*/1, add_filt)); traces.back().cfg.set_unique_session_name(session_prefix + "_1"); traces.emplace_back(CreateTraceConfigForBugreportTest(/*score=*/2, add_filt)); traces.back().cfg.set_bugreport_filename("custom_name.pftrace"); traces.back().cfg.set_unique_session_name(session_prefix + "_2"); traces.emplace_back(CreateTraceConfigForBugreportTest(/*score=*/3, add_filt)); traces.back().cfg.set_bugreport_filename("custom_name.pftrace"); traces.back().cfg.set_unique_session_name(session_prefix + "_3"); traces.emplace_back(CreateTraceConfigForBugreportTest(/*score=*/4, add_filt)); traces.back().cfg.set_unique_session_name(session_prefix + "_4"); for (auto& trace : traces) { std::string cfg = trace.cfg.SerializeAsString(); trace.proc = ExecPerfetto({"-o", base::kDevNull, "-c", "-"}, cfg); } Exec perfetto_br_proc = ExecPerfetto({"--save-all-for-bugreport"}); StartServiceIfRequiredNoNewExecsAfterThis(); for (auto& trace : traces) { trace.thd = std::thread([&trace] { std::string stderr_str; ASSERT_EQ(0, trace.proc->Run(&stderr_str)) << stderr_str; PERFETTO_DLOG("perfetto-cmd output:\n%s", stderr_str.c_str()); }); } // Wait that all tracing sessions are started. // Note that in CTS mode, the Android test infra will start other tracing // sessions for performance reasons. We can't just wait to see 4 sessions, // we need to actually check the unique session name. test_helper().ConnectConsumer(); test_helper().WaitForConsumerConnect(); for (;;) { auto state = test_helper().QueryServiceStateAndWait(); const auto& sessions = state.tracing_sessions(); if (std::count_if(sessions.begin(), sessions.end(), [&](const TracingServiceState::TracingSession& s) { return base::StartsWith(s.unique_session_name(), session_prefix); }) >= 4) { break; } base::SleepMicroseconds(100 * 1000); } EXPECT_EQ(0, perfetto_br_proc.Run(&stderr_)) << stderr_; PERFETTO_DLOG("perfetto --save-all-for-bugreport output:\n-----\n%s\n-----\n", stderr_.c_str()); // Stop all the four ongoing traces, which by now got cloned. for (auto& trace : traces) { trace.proc->SendSigterm(); trace.thd.join(); } auto check_trace = [&](std::string fname, int expected_score) { std::string fpath = GetBugreportTraceDir() + "/" + fname; ASSERT_TRUE(base::FileExists(fpath)) << fpath; protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(fpath, trace)) << fpath; EXPECT_THAT( trace.packet(), Contains(Property(&protos::gen::TracePacket::trace_config, Property(&protos::gen::TraceConfig::bugreport_score, Eq(expected_score))))); }; check_trace("systrace.pftrace", /*expected_score=*/4); check_trace("custom_name.pftrace", /*expected_score=*/3); check_trace("custom_name_1.pftrace", /*expected_score=*/2); check_trace("systrace_1.pftrace", /*expected_score=*/1); } TEST_F(PerfettoCmdlineTest, SaveAllForBugreport_LargeTrace) { auto remove_br_files = [] { remove((GetBugreportTraceDir() + "/systrace.pftrace").c_str()); }; remove_br_files(); // Remove both before and after ending the test. auto remove_on_exit = base::OnScopeExit(remove_br_files); const uint32_t kMsgCount = 10000; const uint32_t kMsgSize = 1024; TraceConfig cfg = CreateTraceConfigForBugreportTest( /*score=*/1, /*add_filter=*/false, kMsgCount, kMsgSize); auto session_name = "bugreport_test_" + std::to_string(base::GetWallTimeNs().count() % 1000000); cfg.set_unique_session_name(session_name); std::string cfg_str = cfg.SerializeAsString(); Exec trace_proc = ExecPerfetto({"-o", base::kDevNull, "-c", "-"}, cfg_str); Exec perfetto_br_proc = ExecPerfetto({"--save-all-for-bugreport"}); StartServiceIfRequiredNoNewExecsAfterThis(); auto* fake_producer = test_helper().ConnectFakeProducer(); EXPECT_TRUE(fake_producer); std::thread thd([&trace_proc] { std::string stderr_str; ASSERT_EQ(0, trace_proc.Run(&stderr_str)) << stderr_str; PERFETTO_DLOG("perfetto-cmd output:\n%s", stderr_str.c_str()); }); // Wait that the tracing session is started. test_helper().ConnectConsumer(); test_helper().WaitForConsumerConnect(); for (;;) { auto state = test_helper().QueryServiceStateAndWait(); const auto& sessions = state.tracing_sessions(); if (std::count_if(sessions.begin(), sessions.end(), [&](const TracingServiceState::TracingSession& s) { return s.unique_session_name() == session_name; }) >= 1) { break; } base::SleepMicroseconds(100 * 1000); } test_helper().SyncAndWaitProducer(); auto on_data_written = task_runner_.CreateCheckpoint("data_written"); fake_producer->ProduceEventBatch(test_helper().WrapTask(on_data_written)); task_runner_.RunUntilCheckpoint("data_written"); EXPECT_EQ(0, perfetto_br_proc.Run(&stderr_)) << stderr_; PERFETTO_DLOG("perfetto --save-all-for-bugreport output:\n-----\n%s\n-----\n", stderr_.c_str()); // Stop the ongoing trace, which by now got cloned. trace_proc.SendSigterm(); thd.join(); std::string fpath = GetBugreportTraceDir() + "/systrace.pftrace"; ASSERT_TRUE(base::FileExists(fpath)) << fpath; protos::gen::Trace trace; ASSERT_TRUE(ParseNotEmptyTraceFromFile(fpath, trace)) << fpath; ExpectTraceContainsTestMessages(trace, kMsgCount); ExpectTraceContainsTestMessagesWithSize(trace, kMsgSize); } } // namespace perfetto