Add the ability for the command line client to activate triggers.
Also add end to end tests for this behaviour and ensure it works for
both START_TRACING, STOP_TRACING. In addition make it so that empty traces
(no triggers reached) do not create files.
Bug: 128966650
Change-Id: I8f1c6b12b76a767b394e195829b129ed6a2ee707
diff --git a/Android.bp b/Android.bp
index 110ac0a..5e92386 100644
--- a/Android.bp
+++ b/Android.bp
@@ -430,6 +430,7 @@
"src/perfetto_cmd/pbtxt_to_pb.cc",
"src/perfetto_cmd/perfetto_cmd.cc",
"src/perfetto_cmd/rate_limiter.cc",
+ "src/perfetto_cmd/trigger_producer.cc",
"src/protozero/message.cc",
"src/protozero/message_handle.cc",
"src/protozero/proto_decoder.cc",
@@ -464,9 +465,6 @@
"src/tracing/core/trace_writer_impl.cc",
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/virtual_destructors.cc",
- "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
- "src/tracing/ipc/default_socket.cc",
- "src/tracing/ipc/posix_shared_memory.cc",
],
shared_libs: [
"libandroid",
@@ -478,6 +476,7 @@
],
static_libs: [
"libgtest_prod",
+ "perfetto_src_tracing_ipc",
],
generated_headers: [
"perfetto_protos_perfetto_common_lite_gen_headers",
@@ -2900,6 +2899,7 @@
"src/perfetto_cmd/perfetto_cmd.cc",
"src/perfetto_cmd/rate_limiter.cc",
"src/perfetto_cmd/rate_limiter_unittest.cc",
+ "src/perfetto_cmd/trigger_producer.cc",
"src/profiling/memory/bookkeeping.cc",
"src/profiling/memory/bookkeeping_unittest.cc",
"src/profiling/memory/client.cc",
@@ -3026,9 +3026,6 @@
"src/tracing/core/tracing_service_impl.cc",
"src/tracing/core/tracing_service_impl_unittest.cc",
"src/tracing/core/virtual_destructors.cc",
- "src/tracing/ipc/consumer/consumer_ipc_client_impl.cc",
- "src/tracing/ipc/default_socket.cc",
- "src/tracing/ipc/posix_shared_memory.cc",
"src/tracing/ipc/posix_shared_memory_unittest.cc",
"src/tracing/test/aligned_buffer_test.cc",
"src/tracing/test/fake_packet.cc",
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 995989f..f7c7b3a 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -26,7 +26,7 @@
"../../protos/perfetto/config:lite",
"../base",
"../protozero",
- "../tracing:ipc_consumer",
+ "../tracing:ipc",
]
sources = [
"config.cc",
@@ -38,6 +38,8 @@
"perfetto_config.descriptor.h",
"rate_limiter.cc",
"rate_limiter.h",
+ "trigger_producer.cc",
+ "trigger_producer.h",
]
if (perfetto_build_with_android) {
deps += [ "../base:android_task_runner" ]
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index a461683..6ad30b1 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -43,6 +43,7 @@
#include "perfetto/tracing/core/trace_packet.h"
#include "src/perfetto_cmd/config.h"
#include "src/perfetto_cmd/pbtxt_to_pb.h"
+#include "src/perfetto_cmd/trigger_producer.h"
#include "perfetto/config/trace_config.pb.h"
@@ -131,13 +132,16 @@
int PerfettoCmd::PrintUsage(const char* argv0) {
PERFETTO_ELOG(R"(
Usage: %s
- --background -d : Exits immediately and continues tracing in background
- --config -c : /path/to/trace/config/file or - for stdin
- --out -o : /path/to/out/trace/file or - for stdout
- --dropbox TAG : Upload trace into DropBox using tag TAG
- --no-guardrails : Ignore guardrails triggered when using --dropbox (for testing).
- --txt : Parse config as pbtxt. Not a stable API. Not for production use.
- --reset-guardrails : Resets the state of the guardails and exits (for testing).
+ --background -d : Exits immediately and continues tracing in background
+ --config -c : /path/to/trace/config/file or - for stdin
+ --out -o : /path/to/out/trace/file or - for stdout
+ --dropbox TAG : Upload trace into DropBox using tag TAG
+ --no-guardrails : Ignore guardrails triggered when using --dropbox (for testing).
+ --txt : Parse config as pbtxt. Not a stable API. Not for production use.
+ --reset-guardrails : Resets the state of the guardails and exits (for testing).
+ --trigger NAME : Activate the NAME on to the service. If specified multiple times
+ will activate them all. Cannot be used with --config or
+ configuration flags.
--help -h
@@ -172,6 +176,7 @@
OPT_CONFIG_UID,
OPT_SUBSCRIPTION_ID,
OPT_RESET_GUARDRAILS,
+ OPT_TRIGGER,
OPT_PBTXT_CONFIG,
OPT_DROPBOX,
OPT_ATRACE_APP,
@@ -197,6 +202,7 @@
{"config-uid", required_argument, nullptr, OPT_CONFIG_UID},
{"subscription-id", required_argument, nullptr, OPT_SUBSCRIPTION_ID},
{"reset-guardrails", no_argument, nullptr, OPT_RESET_GUARDRAILS},
+ {"trigger", required_argument, nullptr, OPT_TRIGGER},
{"detach", required_argument, nullptr, OPT_DETACH},
{"attach", required_argument, nullptr, OPT_ATTACH},
{"is_detached", required_argument, nullptr, OPT_IS_DETACHED},
@@ -215,6 +221,7 @@
ConfigOptions config_options;
bool has_config_options = false;
+ std::vector<std::string> triggers_to_activate;
for (;;) {
int option =
@@ -304,6 +311,11 @@
return 0;
}
+ if (option == OPT_TRIGGER) {
+ triggers_to_activate.push_back(std::string(optarg));
+ continue;
+ }
+
if (option == OPT_ALERT_ID) {
statsd_metadata.set_triggering_alert_id(atoll(optarg));
continue;
@@ -381,8 +393,9 @@
// 1) A proto-encoded file/stdin (-c ...).
// 2) A proto-text file/stdin (-c ... --txt).
// 3) A set of option arguments (-t 10s -s 10m).
- // The only case in which a trace config is not expected is --attach. In this
- // case we are just re-attaching to an already started session.
+ // The only cases in which a trace config is not expected is --attach or
+ // --trigger. For both of these we are just acting on already
+ // existing sessions.
perfetto::protos::TraceConfig trace_config_proto;
bool parsed = false;
if (is_attach()) {
@@ -390,6 +403,15 @@
PERFETTO_ELOG("Cannot specify a trace config with --attach");
return 1;
}
+ if (!triggers_to_activate.empty()) {
+ PERFETTO_ELOG("Cannot specify triggers to activate with --attach");
+ return 1;
+ }
+ } else if (!triggers_to_activate.empty()) {
+ if (!trace_config_raw.empty() || has_config_options) {
+ PERFETTO_ELOG("Cannot specify a trace config with --trigger");
+ return 1;
+ }
} else if (has_config_options) {
if (!trace_config_raw.empty()) {
PERFETTO_ELOG(
@@ -403,7 +425,6 @@
PERFETTO_ELOG("The TraceConfig is empty");
return 1;
}
-
PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
if (parse_as_pbtxt) {
parsed = ParseTraceConfigPbtxt(config_file_name, trace_config_raw,
@@ -418,7 +439,7 @@
*trace_config_proto.mutable_statsd_metadata() = std::move(statsd_metadata);
trace_config_->FromProto(trace_config_proto);
trace_config_raw.clear();
- } else if (!is_attach()) {
+ } else if (!is_attach() && triggers_to_activate.empty()) {
PERFETTO_ELOG("The trace config is invalid, bailing out.");
return 1;
}
@@ -440,6 +461,8 @@
PERFETTO_ELOG("Can't pass an --out file (or --dropbox) to --attach");
return 1;
}
+ } else if (!triggers_to_activate.empty()) {
+ open_out_file = false;
} else if (trace_out_path_.empty() && dropbox_tag_.empty()) {
PERFETTO_ELOG("Either --out or --dropbox is required");
return 1;
@@ -478,6 +501,21 @@
}
}
+ // If we are just activating triggers then we don't need to rate limit,
+ // connect as a consumer or run the trace. So bail out after processing all
+ // the options.
+ if (!triggers_to_activate.empty()) {
+ bool finished_with_success = false;
+ TriggerProducer producer(&task_runner_,
+ [this, &finished_with_success](bool success) {
+ finished_with_success = success;
+ task_runner_.Quit();
+ },
+ &triggers_to_activate);
+ task_runner_.Run();
+ return finished_with_success ? 0 : 1;
+ }
+
RateLimiter::Args args{};
args.is_dropbox = !dropbox_tag_.empty();
args.current_time = base::GetWallTimeS();
@@ -589,6 +627,12 @@
}
} else {
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+ if (bytes_written_ == 0) {
+ PERFETTO_ILOG("Skipping upload to dropbox. Empty trace.");
+ did_process_full_trace_ = true;
+ task_runner_.Quit();
+ return;
+ }
android::sp<android::os::DropBoxManager> dropbox =
new android::os::DropBoxManager();
fseek(*trace_out_stream_, 0, SEEK_SET);
diff --git a/src/perfetto_cmd/trigger_producer.cc b/src/perfetto_cmd/trigger_producer.cc
new file mode 100644
index 0000000..45d6c4c
--- /dev/null
+++ b/src/perfetto_cmd/trigger_producer.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2019 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 "src/perfetto_cmd/trigger_producer.h"
+
+#include <memory>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/tracing/core/producer.h"
+#include "perfetto/tracing/ipc/producer_ipc_client.h"
+#include "src/tracing/ipc/default_socket.h"
+
+namespace perfetto {
+
+class DataSourceConfig;
+
+TriggerProducer::TriggerProducer(base::TaskRunner* task_runner,
+ std::function<void(bool)> callback,
+ const std::vector<std::string>* const triggers)
+ : task_runner_(task_runner),
+ callback_(std::move(callback)),
+ triggers_(triggers),
+ producer_endpoint_(ProducerIPCClient::Connect(GetProducerSocket(),
+ this,
+ "perfetto_cmd_producer",
+ task_runner)),
+ weak_factory_(this) {
+ // Give the socket up to 10 seconds to attach and send the triggers before
+ // reporting a failure.
+ auto weak_this = weak_factory_.GetWeakPtr();
+ task_runner_->PostDelayedTask(
+ [weak_this]() {
+ if (!weak_this || weak_this->issued_callback_)
+ return;
+ weak_this->issued_callback_ = true;
+ weak_this->callback_(false);
+ },
+ 10000);
+}
+
+TriggerProducer::~TriggerProducer() {}
+
+void TriggerProducer::OnConnect() {
+ PERFETTO_DLOG("Producer connected, sending triggers.");
+ // Send activation signal.
+ producer_endpoint_->ActivateTriggers(*triggers_);
+ auto weak_this = weak_factory_.GetWeakPtr();
+ task_runner_->PostTask([weak_this]() {
+ if (!weak_this || weak_this->issued_callback_)
+ return;
+ weak_this->issued_callback_ = true;
+ weak_this->callback_(true);
+ });
+}
+
+void TriggerProducer::OnDisconnect() {}
+
+void TriggerProducer::OnTracingSetup() {}
+
+void TriggerProducer::SetupDataSource(DataSourceInstanceID,
+ const DataSourceConfig&) {
+ PERFETTO_DFATAL("Attempted to SetupDataSource() on commandline producer");
+}
+void TriggerProducer::StartDataSource(DataSourceInstanceID,
+ const DataSourceConfig&) {
+ PERFETTO_DFATAL("Attempted to StartDataSource() on commandline producer");
+}
+void TriggerProducer::StopDataSource(DataSourceInstanceID) {
+ PERFETTO_DFATAL("Attempted to StopDataSource() on commandline producer");
+}
+void TriggerProducer::Flush(FlushRequestID,
+ const DataSourceInstanceID*,
+ size_t) {
+ PERFETTO_DFATAL("Attempted to Flush() on commandline producer");
+}
+
+} // namespace perfetto
diff --git a/src/perfetto_cmd/trigger_producer.h b/src/perfetto_cmd/trigger_producer.h
new file mode 100644
index 0000000..dfd189c
--- /dev/null
+++ b/src/perfetto_cmd/trigger_producer.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#ifndef SRC_PERFETTO_CMD_TRIGGER_PRODUCER_H_
+#define SRC_PERFETTO_CMD_TRIGGER_PRODUCER_H_
+
+#include <string>
+#include <vector>
+
+#include "perfetto/base/task_runner.h"
+#include "perfetto/base/weak_ptr.h"
+#include "perfetto/tracing/core/producer.h"
+#include "perfetto/tracing/core/tracing_service.h"
+
+namespace perfetto {
+
+class DataSourceConfig;
+
+// This is a producer that only sends the provided |triggers| to the service. It
+// will never register any data sources.
+class TriggerProducer : public Producer {
+ public:
+ TriggerProducer(base::TaskRunner* task_runner,
+ std::function<void(bool)> callback,
+ const std::vector<std::string>* const triggers);
+ ~TriggerProducer() override;
+
+ // We will call Trigger() on the |producer_endpoint_| and then
+ // immediately call |callback_|.
+ void OnConnect() override;
+ // We have no clean up to do OnDisconnect.
+ void OnDisconnect() override;
+
+ // Unimplemented methods are below this.
+ void OnTracingSetup() override;
+ void SetupDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
+ void StartDataSource(DataSourceInstanceID, const DataSourceConfig&) override;
+ void StopDataSource(DataSourceInstanceID) override;
+ void Flush(FlushRequestID, const DataSourceInstanceID*, size_t) override;
+
+ private:
+ bool issued_callback_ = false;
+ base::TaskRunner* const task_runner_;
+ const std::function<void(bool)> callback_;
+ const std::vector<std::string>* const triggers_;
+ std::unique_ptr<TracingService::ProducerEndpoint> producer_endpoint_;
+ base::WeakPtrFactory<TriggerProducer> weak_factory_;
+};
+
+} // namespace perfetto
+
+#endif // SRC_PERFETTO_CMD_TRIGGER_PRODUCER_H_
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index f697933..412ef9b 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -155,30 +155,6 @@
]
}
- # IPC transport: only consumer side
- # TODO(fmayer): Remove duplication between this and ipc.
- source_set("ipc_consumer") {
- public_deps = [
- "../../include/perfetto/tracing/core",
- "../../include/perfetto/tracing/ipc",
- ]
- sources = [
- "ipc/consumer/consumer_ipc_client_impl.cc",
- "ipc/consumer/consumer_ipc_client_impl.h",
- "ipc/default_socket.cc",
- "ipc/default_socket.h",
- "ipc/posix_shared_memory.cc",
- "ipc/posix_shared_memory.h",
- ]
- deps = [
- ":tracing",
- "../../gn:default_deps",
- "../../protos/perfetto/ipc",
- "../base",
- "../ipc",
- ]
- }
-
# Posix specialization of the tracing library for Linux/Android/Mac. Provides
# an IPC transport over a UNIX domain socket.
static_library("ipc") {
diff --git a/test/cts/producer/jni/fake_producer_jni.cc b/test/cts/producer/jni/fake_producer_jni.cc
index 412a0fb..cbeafe2 100644
--- a/test/cts/producer/jni/fake_producer_jni.cc
+++ b/test/cts/producer/jni/fake_producer_jni.cc
@@ -28,7 +28,7 @@
void ListenAndRespond(const std::string& name) {
base::TestTaskRunner task_runner;
FakeProducer producer(name);
- producer.Connect(GetProducerSocket(), &task_runner, [] {});
+ producer.Connect(GetProducerSocket(), &task_runner, [] {}, [] {});
task_runner.Run();
}
} // namespace
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index e1017d5..8d3b3cc 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -26,8 +26,10 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "perfetto/base/build_config.h"
+#include "perfetto/base/file_utils.h"
#include "perfetto/base/logging.h"
#include "perfetto/base/pipe.h"
+#include "perfetto/base/temp_file.h"
#include "perfetto/traced/traced.h"
#include "perfetto/tracing/core/trace_config.h"
#include "perfetto/tracing/core/trace_packet.h"
@@ -39,6 +41,7 @@
#include "test/task_runner_thread_delegates.h"
#include "test/test_helper.h"
+#include "perfetto/trace/trace.pb.h"
#include "perfetto/trace/trace_packet.pb.h"
#include "perfetto/trace/trace_packet.pbzero.h"
@@ -73,7 +76,15 @@
class PerfettoCmdlineTest : public ::testing::Test {
public:
- void SetUp() override { test_helper_.StartServiceIfRequired(); }
+ void SetUp() override {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+ // On android P perfetto shell only has permission to write traces to this
+ // directory. So we update TMPDIR to this so that our client will have
+ // permissions.
+ setenv("TMPDIR", "/data/misc/perfetto-traces", 1);
+#endif
+ test_helper_.StartServiceIfRequired();
+ }
void TearDown() override {}
@@ -118,8 +129,13 @@
#if PERFETTO_BUILDFLAG(PERFETTO_START_DAEMONS)
setenv("PERFETTO_CONSUMER_SOCK_NAME", TestHelper::GetConsumerSocketName(),
1);
+ setenv("PERFETTO_PRODUCER_SOCK_NAME", TestHelper::GetProducerSocketName(),
+ 1);
_exit(PerfettoCmdMain(static_cast<int>(argv.size() - 1), argv.data()));
#else
+ // We have to choose a location that the perfetto binary will have
+ // permission to write to. This does not include /data/local/tmp so
+ // instead we override TMPDIR to the trace directory.
execv("/system/bin/perfetto", &argv[0]);
_exit(3);
#endif
@@ -592,4 +608,218 @@
EXPECT_EQ(0, Exec({"--attach=valid_stop", "--stop"}));
}
+TEST_F(PerfettoCmdlineTest, NoSanitizers(StartTracingTrigger)) {
+ // See |message_count| and |message_size| in the TraceConfig above.
+ constexpr size_t kMessageCount = 11;
+ constexpr size_t kMessageSize = 32;
+ protos::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::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 5 normal preample packets (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 = 6;
+
+ base::TestTaskRunner task_runner;
+
+ // Enable tracing and detach as soon as it gets started.
+ TestHelper helper(&task_runner);
+ helper.StartServiceIfRequired();
+ auto* fake_producer = helper.ConnectFakeProducer();
+ EXPECT_TRUE(fake_producer);
+ base::TempFile trace_output = base::TempFile::Create();
+ const std::string path = trace_output.path();
+ trace_output.Unlink();
+ std::thread background_trace([&path, &trace_config, this]() {
+ EXPECT_EQ(0, Exec(
+ {
+ "-o", path, "-c", "-",
+ },
+ trace_config.SerializeAsString()));
+ });
+
+ helper.WaitForProducerSetup();
+ EXPECT_EQ(0, Exec({"--trigger=trigger_name"})) << "stderr: " << stderr_;
+
+ // Wait for the producer to start, and then write out 11 packets.
+ helper.WaitForProducerEnabled();
+ auto on_data_written = task_runner.CreateCheckpoint("data_written");
+ fake_producer->ProduceEventBatch(helper.WrapTask(on_data_written));
+ task_runner.RunUntilCheckpoint("data_written");
+ background_trace.join();
+
+ std::string trace_str;
+ base::ReadFile(path, &trace_str);
+ protos::Trace trace;
+ ASSERT_TRUE(trace.ParseFromString(trace_str));
+ EXPECT_EQ(kPreamblePackets + kMessageCount, trace.packet_size());
+ for (const auto& packet : trace.packet()) {
+ if (packet.data_case() == protos::TracePacket::kTraceConfig) {
+ // Ensure the trace config properly includes the trigger mode we set.
+ EXPECT_EQ(protos::TraceConfig::TriggerConfig::START_TRACING,
+ packet.trace_config().trigger_config().trigger_mode());
+ } else if (packet.data_case() == protos::TracePacket::kTrigger) {
+ // validate that the triggers are properly added to the trace.
+ EXPECT_EQ("trigger_name", packet.trigger().trigger_name());
+ } else if (packet.data_case() == protos::TracePacket::kForTesting) {
+ // 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, NoSanitizers(StopTracingTrigger)) {
+ // See |message_count| and |message_size| in the TraceConfig above.
+ constexpr size_t kMessageCount = 11;
+ constexpr size_t kMessageSize = 32;
+ protos::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::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 5 normal preample packets (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;
+
+ base::TestTaskRunner task_runner;
+
+ // Enable tracing and detach as soon as it gets started.
+ TestHelper helper(&task_runner);
+ helper.StartServiceIfRequired();
+ auto* fake_producer = helper.ConnectFakeProducer();
+ EXPECT_TRUE(fake_producer);
+
+ base::TempFile trace_output = base::TempFile::Create();
+ const std::string path = trace_output.path();
+ trace_output.Unlink();
+ std::thread background_trace([&path, &trace_config, this]() {
+ EXPECT_EQ(0, Exec(
+ {
+ "-o", path, "-c", "-",
+ },
+ trace_config.SerializeAsString()));
+ });
+
+ helper.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(helper.WrapTask(on_data_written));
+ task_runner.RunUntilCheckpoint("data_written_1");
+
+ EXPECT_EQ(0, Exec({"--trigger=trigger_name_2", "--trigger=trigger_name",
+ "--trigger=trigger_name_3"}))
+ << "stderr: " << stderr_;
+
+ background_trace.join();
+
+ std::string trace_str;
+ base::ReadFile(path, &trace_str);
+ protos::Trace trace;
+ ASSERT_TRUE(trace.ParseFromString(trace_str));
+ EXPECT_EQ(kPreamblePackets + kMessageCount, trace.packet_size());
+ bool seen_first_trigger = false;
+ for (const auto& packet : trace.packet()) {
+ if (packet.data_case() == protos::TracePacket::kTraceConfig) {
+ // Ensure the trace config properly includes the trigger mode we set.
+ EXPECT_EQ(protos::TraceConfig::TriggerConfig::STOP_TRACING,
+ packet.trace_config().trigger_config().trigger_mode());
+ } else if (packet.data_case() == protos::TracePacket::kTrigger) {
+ // 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.data_case() == protos::TracePacket::kForTesting) {
+ // Make sure that the data size is correctly set based on what we
+ // requested.
+ EXPECT_EQ(kMessageSize, packet.for_testing().str().size());
+ }
+ }
+}
+
+// Dropbox on the commandline client only works on android builds. So disable
+// this test on all other builds.
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+TEST_F(PerfettoCmdlineTest, NoSanitizers(NoDataNoFileWithoutTrigger)) {
+#else
+TEST_F(PerfettoCmdlineTest, DISABLED_NoDataNoFileWithoutTrigger) {
+#endif
+ // See |message_count| and |message_size| in the TraceConfig above.
+ constexpr size_t kMessageCount = 11;
+ constexpr size_t kMessageSize = 32;
+ protos::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::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();
+
+ // Enable tracing and detach as soon as it gets started.
+ base::TestTaskRunner task_runner;
+ TestHelper helper(&task_runner);
+ helper.StartServiceIfRequired();
+ auto* fake_producer = helper.ConnectFakeProducer();
+ EXPECT_TRUE(fake_producer);
+
+ std::thread background_trace([&trace_config, this]() {
+ EXPECT_EQ(0, Exec(
+ {
+ "--dropbox", "TAG", "--no-guardrails", "-c", "-",
+ },
+ trace_config.SerializeAsString()));
+ });
+ background_trace.join();
+
+ EXPECT_THAT(stderr_,
+ ::testing::HasSubstr("Skipping upload to dropbox. Empty trace."));
+}
+
} // namespace perfetto
diff --git a/test/fake_producer.cc b/test/fake_producer.cc
index 20c4049..0ae52a1 100644
--- a/test/fake_producer.cc
+++ b/test/fake_producer.cc
@@ -37,11 +37,13 @@
void FakeProducer::Connect(
const char* socket_name,
base::TaskRunner* task_runner,
+ std::function<void()> on_setup_data_source_instance,
std::function<void()> on_create_data_source_instance) {
PERFETTO_DCHECK_THREAD(thread_checker_);
task_runner_ = task_runner;
endpoint_ = ProducerIPCClient::Connect(
socket_name, this, "android.perfetto.FakeProducer", task_runner);
+ on_setup_data_source_instance_ = std::move(on_setup_data_source_instance);
on_create_data_source_instance_ = std::move(on_create_data_source_instance);
}
@@ -58,7 +60,9 @@
}
void FakeProducer::SetupDataSource(DataSourceInstanceID,
- const DataSourceConfig&) {}
+ const DataSourceConfig&) {
+ task_runner_->PostTask(on_setup_data_source_instance_);
+}
void FakeProducer::StartDataSource(DataSourceInstanceID,
const DataSourceConfig& source_config) {
diff --git a/test/fake_producer.h b/test/fake_producer.h
index baacb9f..5e8d587 100644
--- a/test/fake_producer.h
+++ b/test/fake_producer.h
@@ -37,6 +37,7 @@
void Connect(const char* socket_name,
base::TaskRunner* task_runner,
+ std::function<void()> on_setup_data_source_instance,
std::function<void()> on_create_data_source_instance);
// Produces a batch of events (as configured in the DataSourceConfig) and
@@ -64,6 +65,7 @@
uint32_t message_size_ = 0;
uint32_t message_count_ = 0;
uint32_t max_messages_per_second_ = 0;
+ std::function<void()> on_setup_data_source_instance_;
std::function<void()> on_create_data_source_instance_;
std::unique_ptr<TracingService::ProducerEndpoint> endpoint_;
std::unique_ptr<TraceWriter> trace_writer_;
diff --git a/test/task_runner_thread_delegates.h b/test/task_runner_thread_delegates.h
index 241ecc4..8eecbba 100644
--- a/test/task_runner_thread_delegates.h
+++ b/test/task_runner_thread_delegates.h
@@ -64,14 +64,17 @@
class FakeProducerDelegate : public ThreadDelegate {
public:
FakeProducerDelegate(const std::string& producer_socket,
+ std::function<void()> setup_callback,
std::function<void()> connect_callback)
: producer_socket_(producer_socket),
+ setup_callback_(std::move(setup_callback)),
connect_callback_(std::move(connect_callback)) {}
~FakeProducerDelegate() override = default;
void Initialize(base::TaskRunner* task_runner) override {
producer_.reset(new FakeProducer("android.perfetto.FakeProducer"));
producer_->Connect(producer_socket_.c_str(), task_runner,
+ std::move(setup_callback_),
std::move(connect_callback_));
}
@@ -80,6 +83,7 @@
private:
std::string producer_socket_;
std::unique_ptr<FakeProducer> producer_;
+ std::function<void()> setup_callback_;
std::function<void()> connect_callback_;
};
} // namespace perfetto
diff --git a/test/test_helper.cc b/test/test_helper.cc
index 2d5b7e2..bcacda3 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -88,6 +88,7 @@
FakeProducer* TestHelper::ConnectFakeProducer() {
std::unique_ptr<FakeProducerDelegate> producer_delegate(
new FakeProducerDelegate(TEST_PRODUCER_SOCK_NAME,
+ WrapTask(CreateCheckpoint("producer.setup")),
WrapTask(CreateCheckpoint("producer.enabled"))));
FakeProducerDelegate* producer_delegate_cached = producer_delegate.get();
producer_thread_.Start(std::move(producer_delegate));
@@ -150,6 +151,10 @@
RunUntilCheckpoint("consumer.connected." + std::to_string(cur_consumer_num_));
}
+void TestHelper::WaitForProducerSetup() {
+ RunUntilCheckpoint("producer.setup");
+}
+
void TestHelper::WaitForProducerEnabled() {
RunUntilCheckpoint("producer.enabled");
}
@@ -186,4 +191,9 @@
return TEST_CONSUMER_SOCK_NAME;
}
+// static
+const char* TestHelper::GetProducerSocketName() {
+ return TEST_PRODUCER_SOCK_NAME;
+}
+
} // namespace perfetto
diff --git a/test/test_helper.h b/test/test_helper.h
index 083a529..8b2ae2d 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -33,6 +33,7 @@
class TestHelper : public Consumer {
public:
static const char* GetConsumerSocketName();
+ static const char* GetProducerSocketName();
explicit TestHelper(base::TestTaskRunner* task_runner);
@@ -58,6 +59,7 @@
bool AttachConsumer(const std::string& key);
void WaitForConsumerConnect();
+ void WaitForProducerSetup();
void WaitForProducerEnabled();
void WaitForTracingDisabled(uint32_t timeout_ms = 5000);
void WaitForReadData(uint32_t read_count = 0);