Merge "trigger_perfetto: Add trigger_perfetto binary"
diff --git a/Android.bp b/Android.bp
index 965c536..f936d82 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3249,6 +3249,131 @@
   ],
   required: [
     "libperfetto_android_internal",
+    "trigger_perfetto",
+  ],
+}
+
+// GN target: //:trigger_perfetto
+cc_binary {
+  name: "trigger_perfetto",
+  srcs: [
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
+    ":perfetto_protos_perfetto_config_zero_gen",
+    ":perfetto_protos_perfetto_ipc_ipc_gen",
+    ":perfetto_protos_perfetto_trace_android_zero_gen",
+    ":perfetto_protos_perfetto_trace_chrome_zero_gen",
+    ":perfetto_protos_perfetto_trace_filesystem_zero_gen",
+    ":perfetto_protos_perfetto_trace_ftrace_zero_gen",
+    ":perfetto_protos_perfetto_trace_interned_data_zero_gen",
+    ":perfetto_protos_perfetto_trace_minimal_lite_gen",
+    ":perfetto_protos_perfetto_trace_power_zero_gen",
+    ":perfetto_protos_perfetto_trace_profiling_zero_gen",
+    ":perfetto_protos_perfetto_trace_ps_zero_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+    ":perfetto_protos_perfetto_trace_track_event_zero_gen",
+    ":perfetto_protos_perfetto_trace_trusted_lite_gen",
+    ":perfetto_protos_perfetto_trace_zero_gen",
+    ":perfetto_src_ipc_wire_protocol_gen",
+    ":perfetto_src_perfetto_cmd_protos_gen",
+    "src/base/event.cc",
+    "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
+    "src/base/paged_memory.cc",
+    "src/base/pipe.cc",
+    "src/base/string_splitter.cc",
+    "src/base/string_utils.cc",
+    "src/base/string_view.cc",
+    "src/base/temp_file.cc",
+    "src/base/thread_checker.cc",
+    "src/base/thread_task_runner.cc",
+    "src/base/time.cc",
+    "src/base/unix_socket.cc",
+    "src/base/unix_task_runner.cc",
+    "src/base/virtual_destructors.cc",
+    "src/base/watchdog_posix.cc",
+    "src/ipc/buffered_frame_deserializer.cc",
+    "src/ipc/client_impl.cc",
+    "src/ipc/deferred.cc",
+    "src/ipc/host_impl.cc",
+    "src/ipc/service_proxy.cc",
+    "src/ipc/virtual_destructors.cc",
+    "src/perfetto_cmd/trigger_perfetto.cc",
+    "src/perfetto_cmd/trigger_perfetto_main.cc",
+    "src/perfetto_cmd/trigger_producer.cc",
+    "src/protozero/message.cc",
+    "src/protozero/message_handle.cc",
+    "src/protozero/proto_decoder.cc",
+    "src/protozero/scattered_heap_buffer.cc",
+    "src/protozero/scattered_stream_null_delegate.cc",
+    "src/protozero/scattered_stream_writer.cc",
+    "src/tracing/core/android_log_config.cc",
+    "src/tracing/core/android_power_config.cc",
+    "src/tracing/core/chrome_config.cc",
+    "src/tracing/core/commit_data_request.cc",
+    "src/tracing/core/data_source_config.cc",
+    "src/tracing/core/data_source_descriptor.cc",
+    "src/tracing/core/ftrace_config.cc",
+    "src/tracing/core/heapprofd_config.cc",
+    "src/tracing/core/id_allocator.cc",
+    "src/tracing/core/inode_file_config.cc",
+    "src/tracing/core/null_trace_writer.cc",
+    "src/tracing/core/observable_events.cc",
+    "src/tracing/core/packet_stream_validator.cc",
+    "src/tracing/core/process_stats_config.cc",
+    "src/tracing/core/shared_memory_abi.cc",
+    "src/tracing/core/shared_memory_arbiter_impl.cc",
+    "src/tracing/core/sliced_protobuf_input_stream.cc",
+    "src/tracing/core/startup_trace_writer.cc",
+    "src/tracing/core/startup_trace_writer_registry.cc",
+    "src/tracing/core/sys_stats_config.cc",
+    "src/tracing/core/test_config.cc",
+    "src/tracing/core/trace_buffer.cc",
+    "src/tracing/core/trace_config.cc",
+    "src/tracing/core/trace_packet.cc",
+    "src/tracing/core/trace_stats.cc",
+    "src/tracing/core/trace_writer_impl.cc",
+    "src/tracing/core/tracing_service_impl.cc",
+    "src/tracing/core/virtual_destructors.cc",
+  ],
+  shared_libs: [
+    "liblog",
+    "libprotobuf-cpp-lite",
+  ],
+  static_libs: [
+    "libgtest_prod",
+    "perfetto_src_tracing_ipc",
+  ],
+  generated_headers: [
+    "perfetto_protos_perfetto_common_lite_gen_headers",
+    "perfetto_protos_perfetto_common_zero_gen_headers",
+    "perfetto_protos_perfetto_config_lite_gen_headers",
+    "perfetto_protos_perfetto_config_zero_gen_headers",
+    "perfetto_protos_perfetto_ipc_ipc_gen_headers",
+    "perfetto_protos_perfetto_trace_android_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_power_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_profiling_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_track_event_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_zero_gen_headers",
+    "perfetto_src_ipc_wire_protocol_gen_headers",
+    "perfetto_src_perfetto_cmd_protos_gen_headers",
+  ],
+  defaults: [
+    "perfetto_defaults",
+  ],
+  cflags: [
+    "-DGOOGLE_PROTOBUF_NO_RTTI",
+    "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+    "-DPERFETTO_BUILD_WITH_ANDROID",
   ],
 }
 
diff --git a/BUILD.gn b/BUILD.gn
index 0e1d257..084bf9d 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -54,6 +54,7 @@
       ":perfetto_integrationtests",
       ":traced",
       ":traced_probes",
+      ":trigger_perfetto",
       "protos/perfetto/config:merged_config",  # For syntax-checking the proto.
       "protos/perfetto/trace:merged_trace",  # For syntax-checking the proto.
       "src/ipc/protoc_plugin:ipc_plugin($host_toolchain)",
@@ -213,6 +214,22 @@
     ]
   }
 
+  # Tool to finalize long running traces.
+  # This connects to traced as a producer and sends the triggers passed on the
+  # commandline. This is a subset of what the perfetto binary can do but we
+  # need a separate binary for programs that cannot (for good reason) use the
+  # additional functionality (for example starting traces via consumer socket)
+  # due to selinux rules.
+  executable("trigger_perfetto") {
+    deps = [
+      "gn:default_deps",
+      "src/perfetto_cmd:trigger_perfetto_cmd",
+    ]
+    sources = [
+      "src/perfetto_cmd/trigger_perfetto_main.cc",
+    ]
+  }
+
   if (perfetto_build_with_android) {
     executable("trace_to_text") {
       testonly = true
diff --git a/include/perfetto/traced/traced.h b/include/perfetto/traced/traced.h
index df70e93..dd58d63 100644
--- a/include/perfetto/traced/traced.h
+++ b/include/perfetto/traced/traced.h
@@ -24,6 +24,7 @@
 int ServiceMain(int argc, char** argv);
 int ProbesMain(int argc, char** argv);
 int PerfettoCmdMain(int argc, char** argv);
+int TriggerPerfettoMain(int argc, char** argv);
 
 }  // namespace perfetto
 
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index f7c7b3a..2a5408b 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -21,6 +21,7 @@
     "../../include/perfetto/traced",
   ]
   deps = [
+    ":trigger_producer",
     "../../buildtools:protobuf_lite",
     "../../gn:default_deps",
     "../../protos/perfetto/config:lite",
@@ -38,8 +39,6 @@
     "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" ]
@@ -51,6 +50,36 @@
   }
 }
 
+source_set("trigger_perfetto_cmd") {
+  public_deps = [
+    ":protos",
+    "../../include/perfetto/traced",
+  ]
+  deps = [
+    ":trigger_producer",
+    "../../gn:default_deps",
+    "../base",
+    "../tracing:ipc",
+  ]
+  sources = [
+    "trigger_perfetto.cc",
+  ]
+}
+
+source_set("trigger_producer") {
+  sources = [
+    "trigger_producer.cc",
+    "trigger_producer.h",
+  ]
+  deps = [
+    "../../buildtools:protobuf_lite",
+    "../../gn:default_deps",
+    "../../protos/perfetto/config:lite",
+    "../base",
+    "../tracing:ipc",
+  ]
+}
+
 proto_library("protos") {
   generate_python = false
   deps = []
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 7f98826..6648c43 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -139,9 +139,6 @@
   --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
 
 
@@ -176,7 +173,6 @@
     OPT_CONFIG_UID,
     OPT_SUBSCRIPTION_ID,
     OPT_RESET_GUARDRAILS,
-    OPT_TRIGGER,
     OPT_PBTXT_CONFIG,
     OPT_DROPBOX,
     OPT_ATRACE_APP,
@@ -202,7 +198,6 @@
       {"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},
@@ -221,7 +216,6 @@
 
   ConfigOptions config_options;
   bool has_config_options = false;
-  std::vector<std::string> triggers_to_activate;
 
   for (;;) {
     int option =
@@ -311,11 +305,6 @@
       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;
@@ -393,25 +382,16 @@
   // 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 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.
+  // The only cases in which a trace config is not expected is --attach.
+  // For this we are just acting on already existing sessions.
   perfetto::protos::TraceConfig trace_config_proto;
+  std::vector<std::string> triggers_to_activate;
   bool parsed = false;
   if (is_attach()) {
     if ((!trace_config_raw.empty() || has_config_options)) {
       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(
@@ -439,7 +419,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() && triggers_to_activate.empty()) {
+  } else if (!is_attach()) {
     PERFETTO_ELOG("The trace config is invalid, bailing out.");
     return 1;
   }
@@ -454,10 +434,10 @@
     return 1;
   }
 
-  // |activate_triggers| in the trace config is shorthand for --trigger. In this
-  // case we don't intend to send any trace config to the service, rather use
-  // that as a signal to the cmdline client to connect as a producer and
-  // activate triggers.
+  // |activate_triggers| in the trace config is shorthand for trigger_perfetto.
+  // In this case we don't intend to send any trace config to the service,
+  // rather use that as a signal to the cmdline client to connect as a producer
+  // and activate triggers.
   if (!trace_config_->activate_triggers().empty()) {
     for (const auto& trigger : trace_config_->activate_triggers()) {
       triggers_to_activate.push_back(trigger);
diff --git a/src/perfetto_cmd/trigger_perfetto.cc b/src/perfetto_cmd/trigger_perfetto.cc
new file mode 100644
index 0000000..c6e3a14
--- /dev/null
+++ b/src/perfetto_cmd/trigger_perfetto.cc
@@ -0,0 +1,82 @@
+/*
+ * 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 <getopt.h>
+
+#include <string>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/unix_task_runner.h"
+#include "perfetto/traced/traced.h"
+#include "src/perfetto_cmd/trigger_producer.h"
+
+namespace perfetto {
+namespace {
+
+int PrintUsage(const char* argv0) {
+  PERFETTO_ELOG(R"(
+Usage: %s TRIGGER...
+  -h|--help  Show this message
+)",
+                argv0);
+  return 1;
+}
+
+}  // namespace
+
+int __attribute__((visibility("default")))
+TriggerPerfettoMain(int argc, char** argv) {
+  static const struct option long_options[] = {
+      {"help", no_argument, nullptr, 'h'}, {nullptr, 0, nullptr, 0}};
+
+  int option_index = 0;
+
+  std::vector<std::string> triggers_to_activate;
+
+  for (;;) {
+    int option = getopt_long(argc, argv, "h", long_options, &option_index);
+
+    if (option == 'h')
+      return PrintUsage(argv[0]);
+
+    if (option == -1)
+      break;  // EOF.
+  }
+
+  for (int i = optind; i < argc; i++)
+    triggers_to_activate.push_back(std::string(argv[i]));
+
+  if (triggers_to_activate.size() == 0) {
+    PERFETTO_ELOG("At least one trigger must the specified.");
+    return PrintUsage(argv[0]);
+  }
+
+  bool finished_with_success = false;
+  base::UnixTaskRunner task_runner;
+  TriggerProducer producer(
+      &task_runner,
+      [&task_runner, &finished_with_success](bool success) {
+        finished_with_success = success;
+        task_runner.Quit();
+      },
+      &triggers_to_activate);
+  task_runner.Run();
+
+  return finished_with_success ? 0 : 1;
+}
+
+}  // namespace perfetto
diff --git a/src/perfetto_cmd/trigger_perfetto_main.cc b/src/perfetto_cmd/trigger_perfetto_main.cc
new file mode 100644
index 0000000..3ba5581
--- /dev/null
+++ b/src/perfetto_cmd/trigger_perfetto_main.cc
@@ -0,0 +1,22 @@
+/*
+ * 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 <stdio.h>
+#include "perfetto/traced/traced.h"
+
+int main(int argc, char** argv) {
+  return perfetto::TriggerPerfettoMain(argc, argv);
+}
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index c4c2612..11b1ef1 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -328,7 +328,7 @@
 
   if (has_trigger_config && cfg.duration_ms() != 0) {
     PERFETTO_ELOG(
-        "duration_ms was set, this field is ignored for traces with triggers.");
+        "duration_ms was set, this must not be set for traces with triggers.");
     return false;
   }
 
@@ -947,7 +947,7 @@
               iter->stop_delay_ms());
           break;
         case TraceConfig::TriggerConfig::UNSPECIFIED:
-          // There are no triggers in this session move onto the next.
+          PERFETTO_ELOG("Trigger activated but trigger mode unspecified.");
           break;
       }
     }
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 085047e..44caac8 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -43,7 +43,10 @@
     # In CTS mode we use /syste/bin/perfetto for the cmdline tests and the
     # perfetto_cmd is not required. Outside of CTS mode, instead, we need to
     # build the cmdline code as part of the test executable.
-    deps += [ "../src/perfetto_cmd" ]
+    deps += [
+      "../src/perfetto_cmd",
+      "../src/perfetto_cmd:trigger_perfetto_cmd",
+    ]
   }
 }
 
diff --git a/test/configs/BUILD.gn b/test/configs/BUILD.gn
index 965b41c..9fbbea7 100644
--- a/test/configs/BUILD.gn
+++ b/test/configs/BUILD.gn
@@ -26,6 +26,7 @@
     sources = [
       "android_log.cfg",
       "atrace.cfg",
+      "background.cfg",
       "ftrace.cfg",
       "ftrace_largebuffer.cfg",
       "heapprofd.cfg",
diff --git a/test/configs/background.cfg b/test/configs/background.cfg
new file mode 100644
index 0000000..b490c10
--- /dev/null
+++ b/test/configs/background.cfg
@@ -0,0 +1,59 @@
+buffers {
+  size_kb: 100024
+  fill_policy: RING_BUFFER
+}
+
+buffers {
+  size_kb: 100024
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.ftrace"
+    target_buffer: 0
+    ftrace_config {
+      buffer_size_kb: 4096
+      drain_period_ms: 200
+      ftrace_events: "print"
+      ftrace_events: "sched_blocked_reason"
+      ftrace_events: "sched_cpu_hotplug"
+      ftrace_events: "sched_process_exec"
+      ftrace_events: "sched_process_exit"
+      ftrace_events: "sched_process_fork"
+      ftrace_events: "sched_process_free"
+      ftrace_events: "sched_process_hang"
+      ftrace_events: "sched_process_wait"
+      ftrace_events: "sched_switch"
+      ftrace_events: "sched_wakeup_new"
+      ftrace_events: "sched_wakeup"
+      ftrace_events: "sched_waking"
+      ftrace_events: "task_newtask"
+      ftrace_events: "task_rename"
+    }
+  }
+}
+
+data_sources {
+  config {
+    target_buffer: 1
+    name: "android.log"
+    android_log_config {
+      log_ids: LID_DEFAULT
+      log_ids: LID_SYSTEM
+      log_ids: LID_EVENTS
+      log_ids: LID_CRASH
+    }
+  }
+}
+
+trigger_config {
+  triggers: {
+    name: "test-configs-trigger"
+    stop_delay_ms: 2000
+  }
+
+  trigger_mode: STOP_TRACING
+  trigger_timeout_ms: 300000
+}
+
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index 8013b7b..e56b256 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -99,15 +99,26 @@
 
   void TearDown() override {}
 
+  int ExecPerfetto(std::initializer_list<std::string> args,
+                   std::string input = "") {
+    return Exec("perfetto", args, input);
+  }
+
+  int ExecTrigger(std::initializer_list<std::string> args,
+                  std::string input = "") {
+    return Exec("trigger_perfetto", args, input);
+  }
+
   // Fork() + executes the perfetto cmdline client with the given args and
   // returns the exit code.
-  int Exec(std::initializer_list<std::string> args, std::string input = "") {
+  int Exec(const std::string& argv0,
+           std::initializer_list<std::string> args,
+           std::string input = "") {
     std::vector<char> argv_buffer;
     std::vector<size_t> argv_offsets;
     std::vector<char*> argv;
     argv_offsets.push_back(0);
 
-    std::string argv0("perfetto");
     argv_buffer.insert(argv_buffer.end(), argv0.begin(), argv0.end());
     argv_buffer.push_back('\0');
 
@@ -142,9 +153,16 @@
              1);
       setenv("PERFETTO_PRODUCER_SOCK_NAME", TestHelper::GetProducerSocketName(),
              1);
-      _exit(PerfettoCmdMain(static_cast<int>(argv.size() - 1), argv.data()));
+      if (argv0 == "perfetto") {
+        _exit(PerfettoCmdMain(static_cast<int>(argv.size() - 1), argv.data()));
+      } else if (argv0 == "trigger_perfetto") {
+        _exit(TriggerPerfettoMain(static_cast<int>(argv.size() - 1),
+                                  argv.data()));
+      } else {
+        ADD_FAILURE() << "Unknown binary: " << argv0.c_str();
+      }
 #else
-      execv("/system/bin/perfetto", &argv[0]);
+      execv((std::string("/system/bin/") + argv0).c_str(), &argv[0]);
       _exit(3);
 #endif
     }
@@ -554,66 +572,68 @@
 TEST_F(PerfettoCmdlineTest, NoSanitizers(InvalidCases)) {
   std::string cfg("duration_ms: 100");
 
-  EXPECT_EQ(1, Exec({"--invalid-arg"}));
+  EXPECT_EQ(1, ExecPerfetto({"--invalid-arg"}));
 
-  EXPECT_EQ(1, Exec({"-c", "-", "-o", "-"}, ""));
+  EXPECT_EQ(1, ExecPerfetto({"-c", "-", "-o", "-"}, ""));
   EXPECT_THAT(stderr_, HasSubstr("TraceConfig is empty"));
 
   // Cannot make assertions on --dropbox because on standalone builds it fails
   // prematurely due to lack of dropbox.
-  EXPECT_EQ(1, Exec({"-c", "-", "--txt", "-o", "-", "--dropbox=foo"}, cfg));
+  EXPECT_EQ(
+      1, ExecPerfetto({"-c", "-", "--txt", "-o", "-", "--dropbox=foo"}, cfg));
 
-  EXPECT_EQ(1, Exec({"-c", "-", "--txt"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-c", "-", "--txt"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("Either --out or --dropbox"));
 
   // Disallow mixing simple and file config.
-  EXPECT_EQ(1, Exec({"-o", "-", "-c", "-", "-t", "2s"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-o", "-", "-c", "-", "-t", "2s"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("Cannot specify both -c"));
 
-  EXPECT_EQ(1, Exec({"-o", "-", "-c", "-", "-b", "2m"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-o", "-", "-c", "-", "-b", "2m"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("Cannot specify both -c"));
 
-  EXPECT_EQ(1, Exec({"-o", "-", "-c", "-", "-s", "2m"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-o", "-", "-c", "-", "-s", "2m"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("Cannot specify both -c"));
 
   // Invalid --attach / --detach cases.
-  EXPECT_EQ(1, Exec({"-c", "-", "--txt", "-o", "-", "--stop"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-c", "-", "--txt", "-o", "-", "--stop"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("--stop is supported only in combination"));
 
-  EXPECT_EQ(1, Exec({"-c", "-", "--txt", "-o", "-", "--attach=foo"}, cfg));
+  EXPECT_EQ(1,
+            ExecPerfetto({"-c", "-", "--txt", "-o", "-", "--attach=foo"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("trace config with --attach"));
 
-  EXPECT_EQ(1, Exec({"-t", "2s", "-o", "-", "--attach=foo"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-t", "2s", "-o", "-", "--attach=foo"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("trace config with --attach"));
 
-  EXPECT_EQ(1, Exec({"--attach"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"--attach"}, cfg));
   EXPECT_THAT(stderr_, ContainsRegex("option.*--attach.*requires an argument"));
 
-  EXPECT_EQ(1, Exec({"-t", "2s", "-o", "-", "--detach"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-t", "2s", "-o", "-", "--detach"}, cfg));
   EXPECT_THAT(stderr_, ContainsRegex("option.*--detach.*requires an argument"));
 
-  EXPECT_EQ(1, Exec({"-t", "2s", "--detach=foo"}, cfg));
+  EXPECT_EQ(1, ExecPerfetto({"-t", "2s", "--detach=foo"}, cfg));
   EXPECT_THAT(stderr_, HasSubstr("--out or --dropbox is required"));
 }
 
 TEST_F(PerfettoCmdlineTest, NoSanitizers(TxtConfig)) {
   std::string cfg("duration_ms: 100");
-  EXPECT_EQ(0, Exec({"-c", "-", "--txt", "-o", "-"}, cfg)) << stderr_;
+  EXPECT_EQ(0, ExecPerfetto({"-c", "-", "--txt", "-o", "-"}, cfg)) << stderr_;
 }
 
 TEST_F(PerfettoCmdlineTest, NoSanitizers(SimpleConfig)) {
-  EXPECT_EQ(0, Exec({"-o", "-", "-c", "-", "-t", "100ms"}));
+  EXPECT_EQ(0, ExecPerfetto({"-o", "-", "-c", "-", "-t", "100ms"}));
 }
 
 TEST_F(PerfettoCmdlineTest, NoSanitizers(DetachAndAttach)) {
-  EXPECT_NE(0, Exec({"--attach=not_existent"}));
+  EXPECT_NE(0, ExecPerfetto({"--attach=not_existent"}));
   EXPECT_THAT(stderr_, HasSubstr("Session re-attach failed"));
 
   std::string cfg("duration_ms: 10000; write_into_file: true");
-  EXPECT_EQ(0,
-            Exec({"-o", "-", "-c", "-", "--txt", "--detach=valid_stop"}, cfg))
+  EXPECT_EQ(0, ExecPerfetto(
+                   {"-o", "-", "-c", "-", "--txt", "--detach=valid_stop"}, cfg))
       << stderr_;
-  EXPECT_EQ(0, Exec({"--attach=valid_stop", "--stop"}));
+  EXPECT_EQ(0, ExecPerfetto({"--attach=valid_stop", "--stop"}));
 }
 
 TEST_F(PerfettoCmdlineTest, NoSanitizers(StartTracingTrigger)) {
@@ -652,7 +672,7 @@
   EXPECT_TRUE(fake_producer);
   const std::string path = RandomTraceFileName();
   std::thread background_trace([&path, &trace_config, this]() {
-    EXPECT_EQ(0, Exec(
+    EXPECT_EQ(0, ExecPerfetto(
                      {
                          "-o", path, "-c", "-",
                      },
@@ -660,7 +680,7 @@
   });
 
   helper.WaitForProducerSetup();
-  EXPECT_EQ(0, Exec({"--trigger=trigger_name"})) << "stderr: " << stderr_;
+  EXPECT_EQ(0, ExecTrigger({"trigger_name"})) << "stderr: " << stderr_;
 
   // Wait for the producer to start, and then write out 11 packets.
   helper.WaitForProducerEnabled();
@@ -730,7 +750,7 @@
 
   const std::string path = RandomTraceFileName();
   std::thread background_trace([&path, &trace_config, this]() {
-    EXPECT_EQ(0, Exec(
+    EXPECT_EQ(0, ExecPerfetto(
                      {
                          "-o", path, "-c", "-",
                      },
@@ -744,8 +764,8 @@
   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"}))
+  EXPECT_EQ(0,
+            ExecTrigger({"trigger_name_2", "trigger_name", "trigger_name_3"}))
       << "stderr: " << stderr_;
 
   background_trace.join();
@@ -814,7 +834,7 @@
   EXPECT_TRUE(fake_producer);
 
   std::thread background_trace([&trace_config, this]() {
-    EXPECT_EQ(0, Exec(
+    EXPECT_EQ(0, ExecPerfetto(
                      {
                          "--dropbox", "TAG", "--no-guardrails", "-c", "-",
                      },
@@ -864,7 +884,7 @@
 
   const std::string path = RandomTraceFileName();
   std::thread background_trace([&path, &trace_config, this]() {
-    EXPECT_EQ(0, Exec(
+    EXPECT_EQ(0, ExecPerfetto(
                      {
                          "-o", path, "-c", "-",
                      },
@@ -884,7 +904,7 @@
     activate_triggers: "trigger_name_3"
   )";
 
-  EXPECT_EQ(0, Exec(
+  EXPECT_EQ(0, ExecPerfetto(
                    {
                        "-o", path, "-c", "-", "--txt",
                    },
@@ -967,7 +987,7 @@
     activate_triggers: "trigger_name_3"
   )";
 
-  EXPECT_EQ(0, Exec(
+  EXPECT_EQ(0, ExecPerfetto(
                    {
                        "-o", path, "-c", "-", "--txt",
                    },
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 286253b..e9ca6ab 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -47,6 +47,7 @@
     '//:trace_to_text',
     '//:heapprofd_client',
     '//:heapprofd',
+    '//:trigger_perfetto',
 ]
 
 # Defines a custom init_rc argument to be applied to the corresponding output
@@ -112,7 +113,7 @@
         ('static_libs', ['libasync_safe']),
     ],
     'traced_probes': [
-      ('required', ['libperfetto_android_internal']),
+      ('required', ['libperfetto_android_internal', 'trigger_perfetto']),
     ],
     'libperfetto_android_internal': [
       ('static_libs', ['libhealthhalutils']),
diff --git a/tools/tmux b/tools/tmux
index 866627e..4f626b7 100755
--- a/tools/tmux
+++ b/tools/tmux
@@ -128,8 +128,8 @@
 CONFIG_DEVICE_PATH="$CONFIG"
 CMD_OPTS=""
 if [[ "$CONFIG" == *.protobuf ]]; then
-  CONFIG_DEVICE_PATH="$CONFIG.protobuf"
-  CONFIG_PATH=$OUT/$CONFIG.protobuf;
+  CONFIG_DEVICE_PATH="$CONFIG"
+  CONFIG_PATH=$OUT/$CONFIG;
   if [[ ! -f $CONFIG_PATH ]]; then
     echo 'Config "'$CONFIG_PATH'" not known.'
     exit 1