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);