Merge "Add tools/busy_threads for spawning N busy-wait threads for debugging"
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/include/perfetto/trace_processor/trace_processor.h b/include/perfetto/trace_processor/trace_processor.h
index b511f46..e67580c 100644
--- a/include/perfetto/trace_processor/trace_processor.h
+++ b/include/perfetto/trace_processor/trace_processor.h
@@ -22,7 +22,6 @@
 #include <vector>
 
 #include "perfetto/base/optional.h"
-#include "perfetto/base/string_view.h"
 #include "perfetto/trace_processor/basic_types.h"
 
 namespace perfetto {
@@ -107,7 +106,7 @@
 
   // Executes a SQLite query on the loaded portion of the trace. The returned
   // iterator can be used to load rows from the result.
-  virtual Iterator ExecuteQuery(base::StringView sql) = 0;
+  virtual Iterator ExecuteQuery(const std::string& sql) = 0;
 
   // Computes the given metrics on the loded portion of the trace. If
   // successful, the output argument |metrics_proto| will be filled with the
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/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index b60f291..dc489f9 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -235,7 +235,10 @@
     "../protozero",
   ]
   if (perfetto_build_standalone) {
-    sources += [ "json_trace_utils_unittest.cc" ]
+    sources += [
+      "json_trace_tokenizer_unittest.cc",
+      "json_trace_utils_unittest.cc",
+    ]
     deps += [ "../../gn:jsoncpp_deps" ]
   }
 }
diff --git a/src/trace_processor/json_trace_tokenizer.cc b/src/trace_processor/json_trace_tokenizer.cc
index ade28dc..f6e3de6 100644
--- a/src/trace_processor/json_trace_tokenizer.cc
+++ b/src/trace_processor/json_trace_tokenizer.cc
@@ -20,16 +20,13 @@
 #include <json/value.h>
 
 #include "src/trace_processor/json_trace_utils.h"
+#include "src/trace_processor/stats.h"
 #include "src/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/trace_sorter.h"
 
 namespace perfetto {
 namespace trace_processor {
 
-namespace {
-
-enum ReadDictRes { kFoundDict, kNeedsMoreData, kEndOfTrace, kFatalError };
-
 // Parses at most one JSON dictionary and returns a pointer to the end of it,
 // or nullptr if no dict could be detected.
 // This is to avoid decoding the full trace in memory and reduce heap traffic.
@@ -42,9 +39,23 @@
   int braces = 0;
   int square_brackets = 0;
   const char* dict_begin = nullptr;
+  bool in_string = false;
+  bool is_escaping = false;
   for (const char* s = start; s < end; s++) {
     if (isspace(*s) || *s == ',')
       continue;
+    if (*s == '"' && !is_escaping) {
+      in_string = !in_string;
+      continue;
+    }
+    if (in_string) {
+      // If we're in a string and we see a backslash and the last character was
+      // not a backslash the next character is escaped:
+      is_escaping = *s == '\\' && !is_escaping;
+      // If we're currently parsing a string we should ignore otherwise special
+      // characters:
+      continue;
+    }
     if (*s == '{') {
       if (braces == 0)
         dict_begin = s;
@@ -79,14 +90,10 @@
       }
       square_brackets--;
     }
-
-    // TODO(primiano): skip braces in quoted strings, e.g.: {"foo": "ba{z" }
   }
   return kNeedsMoreData;
 }
 
-}  // namespace
-
 JsonTraceTokenizer::JsonTraceTokenizer(TraceProcessorContext* ctx)
     : context_(ctx) {}
 JsonTraceTokenizer::~JsonTraceTokenizer() = default;
@@ -125,7 +132,10 @@
 
     base::Optional<int64_t> opt_ts =
         json_trace_utils::CoerceToNs((*value)["ts"]);
-    PERFETTO_CHECK(opt_ts.has_value());
+    if (!opt_ts.has_value()) {
+      context_->storage->IncrementStats(stats::json_tokenizer_failure);
+      continue;
+    }
     int64_t ts = opt_ts.value();
 
     trace_sorter->PushJsonValue(ts, std::move(value));
diff --git a/src/trace_processor/json_trace_tokenizer.h b/src/trace_processor/json_trace_tokenizer.h
index 91d120c..c2c4ab5 100644
--- a/src/trace_processor/json_trace_tokenizer.h
+++ b/src/trace_processor/json_trace_tokenizer.h
@@ -32,6 +32,15 @@
 
 class TraceProcessorContext;
 
+// Visible for testing.
+enum ReadDictRes { kFoundDict, kNeedsMoreData, kEndOfTrace, kFatalError };
+
+// Visible for testing.
+ReadDictRes ReadOneJsonDict(const char* start,
+                            const char* end,
+                            Json::Value* value,
+                            const char** next);
+
 // Reads a JSON trace in chunks and extracts top level json objects.
 class JsonTraceTokenizer : public ChunkedTraceReader {
  public:
diff --git a/src/trace_processor/json_trace_tokenizer_unittest.cc b/src/trace_processor/json_trace_tokenizer_unittest.cc
new file mode 100644
index 0000000..9bfb87e
--- /dev/null
+++ b/src/trace_processor/json_trace_tokenizer_unittest.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/trace_processor/json_trace_tokenizer.h"
+
+#include <json/value.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+TEST(JsonTraceTokenizerTest, Success) {
+  const char* start = R"({ "foo": "bar" })";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  Json::Value value;
+  ReadDictRes result = ReadOneJsonDict(start, end, &value, &next);
+
+  ASSERT_EQ(result, kFoundDict);
+  ASSERT_EQ(next, end);
+  ASSERT_EQ(value["foo"].asString(), "bar");
+}
+
+TEST(JsonTraceTokenizerTest, QuotedBraces) {
+  const char* start = R"({ "foo": "}\"bar{\\" })";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  Json::Value value;
+  ReadDictRes result = ReadOneJsonDict(start, end, &value, &next);
+
+  ASSERT_EQ(result, kFoundDict);
+  ASSERT_EQ(next, end);
+  ASSERT_EQ(value["foo"].asString(), "}\"bar{\\");
+}
+
+TEST(JsonTraceTokenizerTest, TwoDicts) {
+  const char* start = R"({"foo": 1}, {"bar": 2})";
+  const char* middle = start + strlen(R"({"foo": 1})");
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  Json::Value value;
+
+  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kFoundDict);
+  ASSERT_EQ(next, middle);
+  ASSERT_EQ(value["foo"].asInt(), 1);
+
+  ASSERT_EQ(ReadOneJsonDict(next, end, &value, &next), kFoundDict);
+  ASSERT_EQ(next, end);
+  ASSERT_EQ(value["bar"].asInt(), 2);
+}
+
+TEST(JsonTraceTokenizerTest, NeedMoreData) {
+  const char* start = R"({"foo": 1)";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  Json::Value value;
+
+  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kNeedsMoreData);
+  ASSERT_EQ(next, nullptr);
+}
+
+TEST(JsonTraceTokenizerTest, FatalError) {
+  const char* start = R"({helloworld})";
+  const char* end = start + strlen(start);
+  const char* next = nullptr;
+  Json::Value value;
+
+  ASSERT_EQ(ReadOneJsonDict(start, end, &value, &next), kFatalError);
+  ASSERT_EQ(next, nullptr);
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/stats.h b/src/trace_processor/stats.h
index 2b59783..f75a3ad 100644
--- a/src/trace_processor/stats.h
+++ b/src/trace_processor/stats.h
@@ -88,7 +88,8 @@
   F(traced_tracing_sessions,                    kSingle,  kInfo,  kTrace),    \
   F(vmstat_unknown_keys,                        kSingle,  kError, kAnalysis), \
   F(clock_sync_failure,                         kSingle,  kError, kAnalysis), \
-  F(process_tracker_errors,                     kSingle,  kError, kAnalysis)
+  F(process_tracker_errors,                     kSingle,  kError, kAnalysis), \
+  F(json_tokenizer_failure,                     kSingle,  kError, kTrace)
 // clang-format on
 
 enum Type {
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 36cf6b6..7dab3fb 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -363,9 +363,9 @@
 }
 
 TraceProcessor::Iterator TraceProcessorImpl::ExecuteQuery(
-    base::StringView sql) {
+    const std::string& sql) {
   sqlite3_stmt* raw_stmt;
-  int err = sqlite3_prepare_v2(*db_, sql.data(), static_cast<int>(sql.size()),
+  int err = sqlite3_prepare_v2(*db_, sql.c_str(), static_cast<int>(sql.size()),
                                &raw_stmt, nullptr);
   base::Optional<std::string> error;
   uint32_t col_count = 0;
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 4fcb919..db6ab22 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -62,7 +62,7 @@
       const protos::RawQueryArgs&,
       std::function<void(const protos::RawQueryResult&)>) override;
 
-  Iterator ExecuteQuery(base::StringView sql) override;
+  Iterator ExecuteQuery(const std::string& sql) override;
 
   int ComputeMetric(const std::vector<std::string>& metric_names,
                     std::vector<uint8_t>* metrics) override;
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index ef78526..1b3d2b6 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -203,7 +203,7 @@
 
   std::string attach_sql =
       "ATTACH DATABASE '" + output_name + "' AS perfetto_export";
-  auto attach_it = g_tp->ExecuteQuery(base::StringView(attach_sql));
+  auto attach_it = g_tp->ExecuteQuery(attach_sql);
   bool attach_has_more = attach_it.Next();
   PERFETTO_DCHECK(!attach_has_more);
   if (base::Optional<std::string> opt_error = attach_it.GetLastError()) {
@@ -220,7 +220,7 @@
     std::string export_sql = "CREATE TABLE perfetto_export." + table_name +
                              " AS SELECT * FROM " + table_name;
 
-    auto export_it = g_tp->ExecuteQuery(base::StringView(export_sql));
+    auto export_it = g_tp->ExecuteQuery(export_sql);
     bool export_has_more = export_it.Next();
     PERFETTO_DCHECK(!export_has_more);
     if (base::Optional<std::string> opt_error = export_it.GetLastError()) {
@@ -349,16 +349,14 @@
 }
 
 void PrintQueryResultAsCsv(TraceProcessor::Iterator* it, FILE* output) {
-  for (uint32_t rows = 0; it->Next(); rows++) {
-    if (rows == 0) {
-      for (uint32_t c = 0; c < it->ColumnCount(); c++) {
-        if (c > 0)
-          fprintf(output, ",");
-        fprintf(output, "\"%s\"", it->GetColumName(c).c_str());
-      }
-      fprintf(output, "\n");
-    }
+  for (uint32_t c = 0; c < it->ColumnCount(); c++) {
+    if (c > 0)
+      fprintf(output, ",");
+    fprintf(output, "\"%s\"", it->GetColumName(c).c_str());
+  }
+  fprintf(output, "\n");
 
+  for (uint32_t rows = 0; it->Next(); rows++) {
     for (uint32_t c = 0; c < it->ColumnCount(); c++) {
       if (c > 0)
         fprintf(output, ",");
@@ -423,13 +421,19 @@
 
     PERFETTO_ILOG("Executing query: %s", sql_query.c_str());
 
-    auto it = g_tp->ExecuteQuery(base::StringView(sql_query));
+    auto it = g_tp->ExecuteQuery(sql_query);
     if (base::Optional<std::string> opt_error = it.GetLastError()) {
       PERFETTO_ELOG("SQLite error: %s", opt_error->c_str());
       is_query_error = true;
       break;
     }
-    if (has_output && it.ColumnCount() > 0) {
+    if (it.ColumnCount() == 0) {
+      bool it_has_more = it.Next();
+      PERFETTO_DCHECK(!it_has_more);
+      continue;
+    }
+
+    if (has_output) {
       PERFETTO_ELOG(
           "More than one query generated result rows. This is "
           "unsupported.");
@@ -437,7 +441,7 @@
       break;
     }
     PrintQueryResultAsCsv(&it, output);
-    has_output = it.ColumnCount() > 0;
+    has_output = true;
   }
   return !is_query_error;
 }
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);
diff --git a/test/trace_processor/memory_counters_args_string_filter_null.out b/test/trace_processor/memory_counters_args_string_filter_null.out
index e69de29..683ea38 100644
--- a/test/trace_processor/memory_counters_args_string_filter_null.out
+++ b/test/trace_processor/memory_counters_args_string_filter_null.out
@@ -0,0 +1 @@
+"string_value"
diff --git a/test/trace_processor/sfgate_smoke.out b/test/trace_processor/sfgate_smoke.out
index e69de29..72fd6a8 100644
--- a/test/trace_processor/sfgate_smoke.out
+++ b/test/trace_processor/sfgate_smoke.out
@@ -0,0 +1 @@
+"ts","cpu","dur","ts_end","utid","end_state","priority","row_id"
diff --git a/tools/heap_profile b/tools/heap_profile
index 3ef51f2..0e29f86 100755
--- a/tools/heap_profile
+++ b/tools/heap_profile
@@ -37,6 +37,13 @@
 TRACE_TO_TEXT_BASE_URL = (
     'https://storage.googleapis.com/perfetto/')
 
+NULL = open(os.devnull)
+NOOUT = {
+  'stdout': NULL,
+  'stderr': NULL,
+}
+
+
 def check_hash(file_name, sha_value):
   with open(file_name, 'rb') as fd:
     # TODO(fmayer): Chunking.
@@ -64,8 +71,6 @@
   return local_file
 
 
-NULL = open('/dev/null', 'r')
-
 CFG_IDENT = '      '
 CFG='''buffers {{
   size_kb: 32768
@@ -203,10 +208,14 @@
 
   old_handler = signal.signal(signal.SIGINT, sigint_handler)
   print("Profiling active. Press Ctrl+C to terminate.")
+  print("You may disconnect your device.")
   exists = True
-  while exists and not IS_INTERRUPTED:
+  device_connected = True
+  while not device_connected or (exists and not IS_INTERRUPTED):
     exists = subprocess.call(
-        ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)]) == 0
+        ['adb', 'shell', '[ -d /proc/{} ]'.format(perfetto_pid)],
+        **NOOUT) == 0
+    device_connected = subprocess.call(['adb', 'shell', 'true'], **NOOUT) == 0
     time.sleep(1)
   signal.signal(signal.SIGINT, old_handler)
   if IS_INTERRUPTED:
@@ -223,8 +232,7 @@
                          '/data/misc/perfetto-traces/profile-{}'.format(user),
                          '/tmp/profile'], stdout=NULL)
   trace_to_text_output = subprocess.check_output(
-      [trace_to_text_binary, 'profile', '/tmp/profile'],
-      stderr=NULL)
+      [trace_to_text_binary, 'profile', '/tmp/profile'])
   profile_path = None
   for word in trace_to_text_output.split():
     if 'heap_profile-' in word:
diff --git a/tools/trace_to_text/trace_to_systrace.cc b/tools/trace_to_text/trace_to_systrace.cc
index 276e47d..5e07c05 100644
--- a/tools/trace_to_text/trace_to_systrace.cc
+++ b/tools/trace_to_text/trace_to_systrace.cc
@@ -130,7 +130,7 @@
         output_(output) {}
 
   template <typename Callback>
-  bool RunQuery(base::StringView sql, Callback callback) {
+  bool RunQuery(const std::string& sql, Callback callback) {
     char buffer[2048];
     auto iterator = tp_->ExecuteQuery(sql);
     for (uint32_t rows = 0; iterator.Next(); rows++) {