Merge "Support writing untyped annotations into typed contexts."
diff --git a/Android.bp b/Android.bp
index de0d6e9..70e2297 100644
--- a/Android.bp
+++ b/Android.bp
@@ -83,6 +83,7 @@
     ":perfetto_src_profiling_common_profiler_guardrails",
     ":perfetto_src_profiling_common_unwind_support",
     ":perfetto_src_profiling_memory_daemon",
+    ":perfetto_src_profiling_memory_heapprofd_main",
     ":perfetto_src_profiling_memory_ring_buffer",
     ":perfetto_src_profiling_memory_scoped_spinlock",
     ":perfetto_src_profiling_memory_wire_protocol",
@@ -1760,6 +1761,7 @@
     ":perfetto_src_trace_processor_tables_tables",
     ":perfetto_src_trace_processor_types_types",
     ":perfetto_src_trace_processor_util_descriptors",
+    ":perfetto_src_trace_processor_util_gzip",
     ":perfetto_src_trace_processor_util_interned_message_view",
     ":perfetto_src_trace_processor_util_proto_to_args_parser",
     ":perfetto_src_trace_processor_util_protozero_to_text",
@@ -7287,6 +7289,14 @@
   ],
 }
 
+// GN: //src/profiling/memory:heapprofd_main
+filegroup {
+  name: "perfetto_src_profiling_memory_heapprofd_main",
+  srcs: [
+    "src/profiling/memory/heapprofd.cc",
+  ],
+}
+
 // GN: //src/profiling/memory:malloc_interceptor_bionic_hooks
 filegroup {
   name: "perfetto_src_profiling_memory_malloc_interceptor_bionic_hooks",
@@ -8152,7 +8162,6 @@
     "src/trace_processor/forwarding_trace_parser.cc",
     "src/trace_processor/importers/default_modules.cc",
     "src/trace_processor/importers/ftrace/ftrace_module.cc",
-    "src/trace_processor/importers/gzip/gzip_utils.cc",
     "src/trace_processor/importers/json/json_utils.cc",
     "src/trace_processor/importers/ninja/ninja_log_parser.cc",
     "src/trace_processor/importers/proto/async_track_set_tracker.cc",
@@ -8261,6 +8270,14 @@
   ],
 }
 
+// GN: //src/trace_processor/util:gzip
+filegroup {
+  name: "perfetto_src_trace_processor_util_gzip",
+  srcs: [
+    "src/trace_processor/util/gzip_utils.cc",
+  ],
+}
+
 // GN: //src/trace_processor/util:interned_message_view
 filegroup {
   name: "perfetto_src_trace_processor_util_interned_message_view",
@@ -9299,6 +9316,7 @@
     ":perfetto_src_trace_processor_types_unittests",
     ":perfetto_src_trace_processor_unittests",
     ":perfetto_src_trace_processor_util_descriptors",
+    ":perfetto_src_trace_processor_util_gzip",
     ":perfetto_src_trace_processor_util_interned_message_view",
     ":perfetto_src_trace_processor_util_proto_to_args_parser",
     ":perfetto_src_trace_processor_util_protozero_to_text",
@@ -9589,6 +9607,7 @@
     ":perfetto_src_trace_processor_tables_tables",
     ":perfetto_src_trace_processor_types_types",
     ":perfetto_src_trace_processor_util_descriptors",
+    ":perfetto_src_trace_processor_util_gzip",
     ":perfetto_src_trace_processor_util_interned_message_view",
     ":perfetto_src_trace_processor_util_proto_to_args_parser",
     ":perfetto_src_trace_processor_util_protozero_to_text",
@@ -9740,6 +9759,7 @@
     ":perfetto_src_trace_processor_tables_tables",
     ":perfetto_src_trace_processor_types_types",
     ":perfetto_src_trace_processor_util_descriptors",
+    ":perfetto_src_trace_processor_util_gzip",
     ":perfetto_src_trace_processor_util_interned_message_view",
     ":perfetto_src_trace_processor_util_proto_to_args_parser",
     ":perfetto_src_trace_processor_util_protozero_to_text",
diff --git a/BUILD b/BUILD
index 45b742f..23d5d19 100644
--- a/BUILD
+++ b/BUILD
@@ -1148,6 +1148,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/util:gzip
+filegroup(
+    name = "src_trace_processor_util_gzip",
+    srcs = [
+        "src/trace_processor/util/gzip_utils.cc",
+        "src/trace_processor/util/gzip_utils.h",
+    ],
+)
+
 # GN target: //src/trace_processor/util:interned_message_view
 filegroup(
     name = "src_trace_processor_util_interned_message_view",
@@ -1337,8 +1346,6 @@
         "src/trace_processor/importers/ftrace/ftrace_module.h",
         "src/trace_processor/importers/fuchsia/fuchsia_record.h",
         "src/trace_processor/importers/fuchsia/fuchsia_trace_utils.h",
-        "src/trace_processor/importers/gzip/gzip_utils.cc",
-        "src/trace_processor/importers/gzip/gzip_utils.h",
         "src/trace_processor/importers/json/json_utils.cc",
         "src/trace_processor/importers/json/json_utils.h",
         "src/trace_processor/importers/ninja/ninja_log_parser.cc",
@@ -3572,6 +3579,7 @@
         ":src_trace_processor_tables_tables",
         ":src_trace_processor_types_types",
         ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
@@ -3673,6 +3681,7 @@
         ":src_trace_processor_tables_tables",
         ":src_trace_processor_types_types",
         ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
@@ -3854,6 +3863,7 @@
         ":src_trace_processor_tables_tables",
         ":src_trace_processor_types_types",
         ":src_trace_processor_util_descriptors",
+        ":src_trace_processor_util_gzip",
         ":src_trace_processor_util_interned_message_view",
         ":src_trace_processor_util_proto_to_args_parser",
         ":src_trace_processor_util_protozero_to_text",
diff --git a/BUILD.gn b/BUILD.gn
index 6f6cc7d..0fab609 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -124,6 +124,7 @@
 # compile-time checks for the CI.
 if (perfetto_build_standalone) {
   all_targets += [
+    "src/tracebox",
     "test/configs",
 
     # For syntax-checking the proto.
diff --git a/gn/BUILD.gn b/gn/BUILD.gn
index 1f799ad..7fc0cdd 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -79,6 +79,8 @@
     "PERFETTO_TP_JSON=$enable_perfetto_trace_processor_json",
     "PERFETTO_LOCAL_SYMBOLIZER=$perfetto_local_symbolizer",
     "PERFETTO_ZLIB=$enable_perfetto_zlib",
+    "PERFETTO_TRACED_PERF=$enable_perfetto_traced_perf",
+    "PERFETTO_HEAPPROFD=$enable_perfetto_heapprofd",
   ]
 
   rel_out_path = rebase_path(gen_header_path, "$root_build_dir")
diff --git a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
index f0179c6..d68f557 100644
--- a/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/android_tree/perfetto_build_flags.h
@@ -38,6 +38,8 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (0)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TRACED_PERF() (1)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_HEAPPROFD() (1)
 
 // clang-format on
 #endif  // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
index 6745b3f..dad653c 100644
--- a/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
+++ b/include/perfetto/base/build_configs/bazel/perfetto_build_flags.h
@@ -38,6 +38,8 @@
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TP_JSON() (1)
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_LOCAL_SYMBOLIZER() (PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_MAC() ||PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_WIN())
 #define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_ZLIB() (1)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_TRACED_PERF() (0)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_HEAPPROFD() (0)
 
 // clang-format on
 #endif  // GEN_BUILD_CONFIG_PERFETTO_BUILD_FLAGS_H_
diff --git a/include/perfetto/ext/base/subprocess.h b/include/perfetto/ext/base/subprocess.h
index 77b2dfd..80adbdc 100644
--- a/include/perfetto/ext/base/subprocess.h
+++ b/include/perfetto/ext/base/subprocess.h
@@ -30,6 +30,7 @@
 #include "perfetto/base/platform_handle.h"
 #include "perfetto/base/proc_utils.h"
 #include "perfetto/ext/base/event_fd.h"
+#include "perfetto/ext/base/optional.h"
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/scoped_file.h"
 
@@ -133,6 +134,13 @@
     // just before the exec() call, but after having closed all fds % stdin/o/e.
     // This is for synchronization barriers in tests.
     std::function<void()> posix_entrypoint_for_testing;
+
+    // When set, will will move the process to the given process group. If set
+    // and zero, it will create a new process group. Effectively this calls
+    // setpgid(0 /*self_pid*/, posix_proc_group_id).
+    // This can be used to avoid that subprocesses receive CTRL-C from the
+    // terminal, while still living in the same session.
+    base::Optional<pid_t> posix_proc_group_id{};
 #endif
 
     // If non-empty, replaces the environment passed to exec().
diff --git a/include/perfetto/ext/base/utils.h b/include/perfetto/ext/base/utils.h
index af39b37..729225f 100644
--- a/include/perfetto/ext/base/utils.h
+++ b/include/perfetto/ext/base/utils.h
@@ -124,6 +124,13 @@
 // Child: redirects stdio onto /dev/null and chdirs into .
 void Daemonize();
 
+// Returns the path of the current executable, e.g. /foo/bar/exe.
+std::string GetCurExecutablePath();
+
+// Returns the directory where the current executable lives in, e.g. /foo/bar.
+// This is independent of cwd().
+std::string GetCurExecutableDir();
+
 }  // namespace base
 }  // namespace perfetto
 
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index 20382a1..c26d949 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -326,7 +326,8 @@
       ProducerSMBScrapingMode smb_scraping_mode =
           ProducerSMBScrapingMode::kDefault,
       size_t shared_memory_page_size_hint_bytes = 0,
-      std::unique_ptr<SharedMemory> shm = nullptr) = 0;
+      std::unique_ptr<SharedMemory> shm = nullptr,
+      const std::string& sdk_version = {}) = 0;
 
   // Connects a Consumer instance and obtains a ConsumerEndpoint, which is
   // essentially a 1:1 channel between one Consumer and the Service.
diff --git a/protos/perfetto/common/tracing_service_state.proto b/protos/perfetto/common/tracing_service_state.proto
index 119cea8..1ef4c56 100644
--- a/protos/perfetto/common/tracing_service_state.proto
+++ b/protos/perfetto/common/tracing_service_state.proto
@@ -34,6 +34,12 @@
 
     // Unix uid of the remote process.
     optional int32 uid = 3;
+
+    // The version of the client library used by the producer.
+    // This is a human readable string with and its format varies depending on
+    // the build system and the repo (standalone vs AOSP).
+    // This is intended for human debugging only.
+    optional string sdk_version = 4;
   }
 
   // Describes a data source registered by a producer. Data sources are listed
@@ -57,4 +63,10 @@
 
   // Number of tracing sessions in the started state. Always <= num_sessions.
   optional int32 num_sessions_started = 4;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 5;
 }
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 4c6e882..c8b55e3 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -197,6 +197,12 @@
 
     // Unix uid of the remote process.
     optional int32 uid = 3;
+
+    // The version of the client library used by the producer.
+    // This is a human readable string with and its format varies depending on
+    // the build system and the repo (standalone vs AOSP).
+    // This is intended for human debugging only.
+    optional string sdk_version = 4;
   }
 
   // Describes a data source registered by a producer. Data sources are listed
@@ -220,6 +226,12 @@
 
   // Number of tracing sessions in the started state. Always <= num_sessions.
   optional int32 num_sessions_started = 4;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 5;
 }
 
 // End of protos/perfetto/common/tracing_service_state.proto
diff --git a/protos/perfetto/ipc/producer_port.proto b/protos/perfetto/ipc/producer_port.proto
index ac1b015..9be72ea 100644
--- a/protos/perfetto/ipc/producer_port.proto
+++ b/protos/perfetto/ipc/producer_port.proto
@@ -153,6 +153,16 @@
   // |using_shmem_provided_by_producer| in InitializeConnectionResponse.
   optional bool producer_provided_shmem = 6;
 
+  // ---------------------------------------------------
+  // All fields below have been introduced in Android S.
+  // ---------------------------------------------------
+
+  // The version of the client library used by the producer.
+  // This is a human readable string with and its format varies depending on
+  // the build system that is used to build the code and the repo (standalone
+  // vs AOSP). This is intended for human debugging only.
+  optional string sdk_version = 8;
+
   // On Windows, when producer_provided_shmem = true, the client creates a named
   // SHM region and passes the name (an unguessable token) back to the service.
   // Introduced in v13.
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 043b682..4ee4df6 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -197,6 +197,12 @@
 
     // Unix uid of the remote process.
     optional int32 uid = 3;
+
+    // The version of the client library used by the producer.
+    // This is a human readable string with and its format varies depending on
+    // the build system and the repo (standalone vs AOSP).
+    // This is intended for human debugging only.
+    optional string sdk_version = 4;
   }
 
   // Describes a data source registered by a producer. Data sources are listed
@@ -220,6 +226,12 @@
 
   // Number of tracing sessions in the started state. Always <= num_sessions.
   optional int32 num_sessions_started = 4;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 5;
 }
 
 // End of protos/perfetto/common/tracing_service_state.proto
@@ -8326,6 +8338,12 @@
 
   // Ticks per second - sysconf(_SC_CLK_TCK).
   optional int64 hz = 3;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 4;
 }
 
 // End of protos/perfetto/trace/system_info.proto
diff --git a/protos/perfetto/trace/system_info.proto b/protos/perfetto/trace/system_info.proto
index 90a7a57..ddce4ca 100644
--- a/protos/perfetto/trace/system_info.proto
+++ b/protos/perfetto/trace/system_info.proto
@@ -31,4 +31,10 @@
 
   // Ticks per second - sysconf(_SC_CLK_TCK).
   optional int64 hz = 3;
+
+  // The version of traced (the same returned by `traced --version`).
+  // This is a human readable string with and its format varies depending on
+  // the build system and the repo (standalone vs AOSP).
+  // This is intended for human debugging only.
+  optional string tracing_service_version = 4;
 }
diff --git a/protos/third_party/chromium/chrome_track_event.proto b/protos/third_party/chromium/chrome_track_event.proto
index 813b174..54e11ce 100644
--- a/protos/third_party/chromium/chrome_track_event.proto
+++ b/protos/third_party/chromium/chrome_track_event.proto
@@ -28,6 +28,9 @@
 
 message ChromeTaskAnnotator {
   optional uint32 ipc_hash = 1;
+  // The delay in microseconds that was specified, if any, when this task was
+  // posted. This is only valid for delayed tasks.
+  optional uint64 task_delay_us = 2;
 }
 
 message ChromeBrowserContext {
@@ -132,9 +135,16 @@
   optional string mark = 4;
 }
 
+// These IDs are generated at compile time and differ for each chrome version.
+// IDs are stable on for a given chrome version but are changing when resources
+// are added or removed to chrome.
+message ResourceBundle {
+  optional uint32 resource_id = 1;
+}
+
 message ChromeTrackEvent {
   // Extension range for Chrome: 1000-1999
-  // Next ID: 1012
+  // Next ID: 1017
   extend TrackEvent {
     optional ChromeAppState chrome_app_state = 1000;
 
@@ -162,5 +172,9 @@
     optional FrameTreeNodeInfo frame_tree_node_info = 1010;
 
     optional ChromeHashedPerformanceMark chrome_hashed_performance_mark = 1011;
+
+    // reserved 1012 to 1015.
+
+    optional ResourceBundle resource_bundle = 1016;
   }
 }
diff --git a/src/base/subprocess_posix.cc b/src/base/subprocess_posix.cc
index 85df339..5e09544 100644
--- a/src/base/subprocess_posix.cc
+++ b/src/base/subprocess_posix.cc
@@ -82,6 +82,12 @@
     _exit(128);
   };
 
+  if (args->create_args->posix_proc_group_id.has_value()) {
+    if (setpgid(0 /*self*/, args->create_args->posix_proc_group_id.value())) {
+      die("setpgid() failed");
+    }
+  }
+
   auto set_fd_close_on_exec = [&die](int fd, bool close_on_exec) {
     int flags = fcntl(fd, F_GETFD, 0);
     if (flags < 0)
@@ -150,12 +156,15 @@
     }
   }
 
-  // Clears O_CLOEXEC from stdin/out/err. These are the only FDs that we want
-  // to be preserved after the exec().
+  // Clears O_CLOEXEC from stdin/out/err and the |preserve_fds| list. These are
+  // the only FDs that we want to be preserved after the exec().
   set_fd_close_on_exec(STDIN_FILENO, false);
   set_fd_close_on_exec(STDOUT_FILENO, false);
   set_fd_close_on_exec(STDERR_FILENO, false);
 
+  for (auto fd : preserve_fds)
+    set_fd_close_on_exec(fd, false);
+
   // If the caller specified a std::function entrypoint, run that first.
   if (args->create_args->posix_entrypoint_for_testing)
     args->create_args->posix_entrypoint_for_testing();
diff --git a/src/base/test/utils.cc b/src/base/test/utils.cc
index 0a56611..ff00f64 100644
--- a/src/base/test/utils.cc
+++ b/src/base/test/utils.cc
@@ -23,55 +23,11 @@
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/ext/base/file_utils.h"
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-#include <limits.h>
-#include <unistd.h>
-#endif
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <Windows.h>
-#include <io.h>
-#endif
-
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-#include <mach-o/dyld.h>
-#endif
+#include "perfetto/ext/base/utils.h"
 
 namespace perfetto {
 namespace base {
 
-std::string GetCurExecutableDir() {
-  std::string self_path;
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
-  char buf[PATH_MAX];
-  ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf));
-  PERFETTO_CHECK(size != -1);
-  // readlink does not null terminate.
-  self_path = std::string(buf, static_cast<size_t>(size));
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-  uint32_t size = 0;
-  PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size));
-  self_path.resize(size);
-  PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0);
-#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-  char buf[MAX_PATH];
-  auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
-  self_path = std::string(buf, len);
-  self_path = self_path.substr(0, self_path.find_last_of("\\"));
-#else
-  PERFETTO_FATAL(
-      "GetCurExecutableDir() not implemented on the current platform");
-#endif
-  // Cut binary name.
-  return self_path.substr(0, self_path.find_last_of("/"));
-}
-
 std::string GetTestDataPath(const std::string& path) {
   std::string self_path = GetCurExecutableDir();
   std::string full_path = self_path + "/../../" + path;
diff --git a/src/base/test/utils.h b/src/base/test/utils.h
index e53b786..032194b 100644
--- a/src/base/test/utils.h
+++ b/src/base/test/utils.h
@@ -50,7 +50,6 @@
 namespace perfetto {
 namespace base {
 
-std::string GetCurExecutableDir();
 std::string GetTestDataPath(const std::string& path);
 
 // Returns a xxd-style hex dump (hex + ascii chars) of the input data.
diff --git a/src/base/utils.cc b/src/base/utils.cc
index d5bd5a0..041e010 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -14,22 +14,32 @@
  * limitations under the License.
  */
 
-#include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/utils.h"
 
+#include <string>
+
 #include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/ext/base/file_utils.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+#include <limits.h>
 #include <unistd.h>  // For getpagesize() and geteuid() & fork()
 #endif
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+#include <mach-o/dyld.h>
 #include <mach/vm_page_size.h>
 #endif
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <Windows.h>
+#include <io.h>
+#endif
+
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
 #include <dlfcn.h>
 #include <malloc.h>
@@ -39,7 +49,7 @@
 #else
 // Only available in in-tree builds and on newer SDKs.
 #define PERFETTO_M_PURGE -101
-#endif
+#endif  // M_PURGE
 
 namespace {
 extern "C" {
@@ -110,35 +120,71 @@
 }
 
 void Daemonize() {
-   #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
-       PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
-       PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
-      pid_t pid;
-      switch (pid = fork()) {
-        case -1:
-          PERFETTO_FATAL("fork");
-        case 0: {
-          PERFETTO_CHECK(setsid() != -1);
-          base::ignore_result(chdir("/"));
-          base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
-          PERFETTO_CHECK(null);
-          PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
-          PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
-          PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
-          // Do not accidentally close stdin/stdout/stderr.
-          if (*null <= 2)
-            null.release();
-          break;
-        }
-        default:
-          printf("%d\n", pid);
-          exit(0);
-      }
-  #else
-    // Avoid -Wunreachable warnings.
-    if (reinterpret_cast<intptr_t>(&Daemonize) != 16)
-      PERFETTO_FATAL("--background is only supported on Linux/Android/Mac");
-  #endif  // OS_WIN
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  pid_t pid;
+  switch (pid = fork()) {
+    case -1:
+      PERFETTO_FATAL("fork");
+    case 0: {
+      PERFETTO_CHECK(setsid() != -1);
+      base::ignore_result(chdir("/"));
+      base::ScopedFile null = base::OpenFile("/dev/null", O_RDONLY);
+      PERFETTO_CHECK(null);
+      PERFETTO_CHECK(dup2(*null, STDIN_FILENO) != -1);
+      PERFETTO_CHECK(dup2(*null, STDOUT_FILENO) != -1);
+      PERFETTO_CHECK(dup2(*null, STDERR_FILENO) != -1);
+      // Do not accidentally close stdin/stdout/stderr.
+      if (*null <= 2)
+        null.release();
+      break;
+    }
+    default:
+      printf("%d\n", pid);
+      exit(0);
+  }
+#else
+  // Avoid -Wunreachable warnings.
+  if (reinterpret_cast<intptr_t>(&Daemonize) != 16)
+    PERFETTO_FATAL("--background is only supported on Linux/Android/Mac");
+#endif  // OS_WIN
+}
+
+std::string GetCurExecutablePath() {
+  std::string self_path;
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||   \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_FUCHSIA)
+  char buf[PATH_MAX];
+  ssize_t size = readlink("/proc/self/exe", buf, sizeof(buf));
+  PERFETTO_CHECK(size != -1);
+  // readlink does not null terminate.
+  self_path = std::string(buf, static_cast<size_t>(size));
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  uint32_t size = 0;
+  PERFETTO_CHECK(_NSGetExecutablePath(nullptr, &size));
+  self_path.resize(size);
+  PERFETTO_CHECK(_NSGetExecutablePath(&self_path[0], &size) == 0);
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  char buf[MAX_PATH];
+  auto len = ::GetModuleFileNameA(nullptr /*current*/, buf, sizeof(buf));
+  self_path = std::string(buf, len);
+#else
+  PERFETTO_FATAL(
+      "GetCurExecutableDir() not implemented on the current platform");
+#endif
+  return self_path;
+}
+
+std::string GetCurExecutableDir() {
+  auto path = GetCurExecutablePath();
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // Paths in Windows can have both kinds of slashes (mingw vs msvc).
+  path = path.substr(0, path.find_last_of("\\"));
+#endif
+  path = path.substr(0, path.find_last_of("/"));
+  return path;
 }
 
 }  // namespace base
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index c8f6abc..c6fe500 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -70,7 +70,7 @@
 namespace perfetto {
 namespace {
 
-perfetto::PerfettoCmd* g_consumer_cmd;
+perfetto::PerfettoCmd* g_perfetto_cmd;
 
 uint32_t kOnTraceDataTimeoutMs = 3000;
 
@@ -166,55 +166,69 @@
 
 const char* kStateDir = "/data/misc/perfetto-traces";
 
+PerfettoCmd::PerfettoCmd() {
+  PERFETTO_DCHECK(!g_perfetto_cmd);
+  g_perfetto_cmd = this;
+}
+
+PerfettoCmd::~PerfettoCmd() {
+  PERFETTO_DCHECK(g_perfetto_cmd == this);
+  g_perfetto_cmd = nullptr;
+}
+
 int PerfettoCmd::PrintUsage(const char* argv0) {
-  PERFETTO_ELOG(R"(
+  fprintf(stderr, R"(
 Usage: %s
-  --background     -d      : Exits immediately and continues tracing in
-                             background
+  --background     -d      : Exits immediately and continues in the background.
+                             Prints the PID of the bg process. The printed PID
+                             can used to gracefully terminate the tracing
+                             session by ussuing a `kill -TERM $PRINTED_PID`.
   --config         -c      : /path/to/trace/config/file or - for stdin
   --out            -o      : /path/to/out/trace/file or - for stdout
-  --upload                 : Upload field trace (Android only)
-  --dropbox        TAG     : DEPRECATED: Use --upload instead
-                             TAG should always be set to 'perfetto'
-  --no-guardrails          : Ignore guardrails triggered when using --upload
-                             (for testing).
   --txt                    : Parse config as pbtxt. Not for production use.
                              Not a stable API.
-  --reset-guardrails       : Resets the state of the guardails and exits
-                             (for testing).
   --query                  : Queries the service state and prints it as
                              human-readable text.
   --query-raw              : Like --query, but prints raw proto-encoded bytes
                              of tracing_service_state.proto.
-  --save-for-bugreport     : If a trace with bugreport_score > 0 is running, it
-                             saves it into a file. Outputs the path when done.
   --help           -h
 
-
-light configuration flags: (only when NOT using -c/--config)
+Light configuration flags: (only when NOT using -c/--config)
   --time           -t      : Trace duration N[s,m,h] (default: 10s)
   --buffer         -b      : Ring buffer size N[mb,gb] (default: 32mb)
-  --size           -s      : Max file size N[mb,gb] (default: in-memory ring-buffer only)
+  --size           -s      : Max file size N[mb,gb]
+                            (default: in-memory ring-buffer only)
   --app            -a      : Android (atrace) app name
-  ATRACE_CAT               : Record ATRACE_CAT (e.g. wm)
   FTRACE_GROUP/FTRACE_NAME : Record ftrace event (e.g. sched/sched_switch)
+  ATRACE_CAT               : Record ATRACE_CAT (e.g. wm) (Android only)
 
-statsd-specific flags:
+Statsd-specific and other Android-only flags:
   --alert-id           : ID of the alert that triggered this trace.
   --config-id          : ID of the triggering config.
   --config-uid         : UID of app which registered the config.
   --subscription-id    : ID of the subscription that triggered this trace.
+  --upload             : Upload trace.
+  --dropbox        TAG : DEPRECATED: Use --upload instead
+                         TAG should always be set to 'perfetto'.
+  --save-for-bugreport : If a trace with bugreport_score > 0 is running, it
+                         saves it into a file. Outputs the path when done.
+  --no-guardrails      : Ignore guardrails triggered when using --upload
+                         (testing only).
+  --reset-guardrails   : Resets the state of the guardails and exits
+                         (testing only).
 
-Detach mode. DISCOURAGED, read https://perfetto.dev/docs/concepts/detached-mode :
+Detach mode. DISCOURAGED, read https://perfetto.dev/docs/concepts/detached-mode
   --detach=key          : Detach from the tracing session with the given key.
-  --attach=key [--stop] : Re-attach to the session (optionally stop tracing once reattached).
-  --is_detached=key     : Check if the session can be re-attached (0:Yes, 2:No, 1:Error).
+  --attach=key [--stop] : Re-attach to the session (optionally stop tracing
+                          once reattached).
+  --is_detached=key     : Check if the session can be re-attached.
+                          Exit code:  0:Yes, 2:No, 1:Error.
 )", /* this comment fixes syntax highlighting in some editors */
-                argv0);
+          argv0);
   return 1;
 }
 
-int PerfettoCmd::Main(int argc, char** argv) {
+int PerfettoCmd::ParseCmdlineAndMaybeDaemonize(int argc, char** argv) {
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   umask(0000);  // make sure that file creation is not affected by umask.
 #endif
@@ -267,16 +281,18 @@
 
   std::string config_file_name;
   std::string trace_config_raw;
-  bool background = false;
-  bool ignore_guardrails = false;
   bool parse_as_pbtxt = false;
-  bool upload_flag = false;
   TraceConfig::StatsdMetadata statsd_metadata;
-  RateLimiter limiter;
+  limiter_.reset(new RateLimiter());
 
   ConfigOptions config_options;
   bool has_config_options = false;
 
+  if (argc <= 1) {
+    PrintUsage(argv[0]);
+    return 1;
+  }
+
   for (;;) {
     int option =
         getopt_long(argc, argv, "hc:o:dt:b:s:a:", long_options, nullptr);
@@ -314,7 +330,7 @@
     }
 
     if (option == 'd') {
-      background = true;
+      background_ = true;
       continue;
     }
     if (option == 't') {
@@ -343,7 +359,7 @@
 
     if (option == OPT_UPLOAD) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-      upload_flag = true;
+      upload_flag_ = true;
       continue;
 #else
       PERFETTO_ELOG("--upload is only supported on Android");
@@ -354,7 +370,7 @@
     if (option == OPT_DROPBOX) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
       PERFETTO_CHECK(optarg);
-      upload_flag = true;
+      upload_flag_ = true;
       continue;
 #else
       PERFETTO_ELOG("--dropbox is only supported on Android");
@@ -368,12 +384,12 @@
     }
 
     if (option == OPT_IGNORE_GUARDRAILS) {
-      ignore_guardrails = true;
+      ignore_guardrails_ = true;
       continue;
     }
 
     if (option == OPT_RESET_GUARDRAILS) {
-      PERFETTO_CHECK(limiter.ClearState());
+      PERFETTO_CHECK(limiter_->ClearState());
       PERFETTO_ILOG("Guardrail state cleared");
       return 0;
     }
@@ -451,7 +467,7 @@
     config_options.categories.push_back(argv[i]);
   }
 
-  if (query_service_ && (is_detach() || is_attach() || background)) {
+  if (query_service_ && (is_detach() || is_attach() || background_)) {
     PERFETTO_ELOG("--query cannot be combined with any other argument");
     return 1;
   }
@@ -461,7 +477,7 @@
     return 1;
   }
 
-  if (is_detach() && background) {
+  if (is_detach() && background_) {
     PERFETTO_ELOG("--detach and --background are mutually exclusive");
     return 1;
   }
@@ -485,7 +501,6 @@
   // For this we are just acting on already existing sessions.
   trace_config_.reset(new TraceConfig());
 
-  std::vector<std::string> triggers_to_activate;
   bool parsed = false;
   const bool will_trace = !is_attach() && !query_service_ && !bugreport_;
   if (!will_trace) {
@@ -540,7 +555,7 @@
   }
 
   if (!trace_config_->incident_report_config().destination_package().empty() &&
-      !upload_flag) {
+      !upload_flag_) {
     PERFETTO_ELOG(
         "Unexpected IncidentReportConfig without --dropbox / --upload.");
     return 1;
@@ -549,7 +564,7 @@
   if (trace_config_->activate_triggers().empty() &&
       trace_config_->incident_report_config().destination_package().empty() &&
       !trace_config_->incident_report_config().skip_incidentd() &&
-      upload_flag) {
+      upload_flag_) {
     PERFETTO_ELOG(
         "Missing IncidentReportConfig.destination_package with --dropbox / "
         "--upload.");
@@ -559,7 +574,7 @@
   // Only save to incidentd if both --upload is set and |skip_incidentd| is
   // absent or false.
   save_to_incidentd_ =
-      upload_flag && !trace_config_->incident_report_config().skip_incidentd();
+      upload_flag_ && !trace_config_->incident_report_config().skip_incidentd();
 
   // Respect the wishes of the config with respect to statsd logging or fall
   // back on the presence of the --upload flag if not set.
@@ -571,7 +586,7 @@
       statsd_logging_ = false;
       break;
     case TraceConfig::STATSD_LOGGING_UNSPECIFIED:
-      statsd_logging_ = upload_flag;
+      statsd_logging_ = upload_flag_;
       break;
   }
   trace_config_->set_statsd_logging(statsd_logging_
@@ -581,7 +596,7 @@
   // Set up the output file. Either --out or --upload are expected, with the
   // only exception of --attach. In this case the output file is passed when
   // detaching.
-  if (!trace_out_path_.empty() && upload_flag) {
+  if (!trace_out_path_.empty() && upload_flag_) {
     PERFETTO_ELOG(
         "Can't log to a file (--out) and incidentd (--upload) at the same "
         "time");
@@ -589,7 +604,7 @@
   }
 
   if (!trace_config_->output_path().empty()) {
-    if (!trace_out_path_.empty() || upload_flag) {
+    if (!trace_out_path_.empty() || upload_flag_) {
       PERFETTO_ELOG(
           "Can't pass --out or --upload if output_path is set in the "
           "trace config");
@@ -611,7 +626,7 @@
   // and activate triggers.
   if (!trace_config_->activate_triggers().empty()) {
     for (const auto& trigger : trace_config_->activate_triggers()) {
-      triggers_to_activate.push_back(trigger);
+      triggers_to_activate_.push_back(trigger);
     }
     trace_config_.reset(new TraceConfig());
   }
@@ -619,15 +634,15 @@
   bool open_out_file = true;
   if (!will_trace) {
     open_out_file = false;
-    if (!trace_out_path_.empty() || upload_flag) {
+    if (!trace_out_path_.empty() || upload_flag_) {
       PERFETTO_ELOG("Can't pass an --out file (or --upload) with this option");
       return 1;
     }
-  } else if (!triggers_to_activate.empty() ||
+  } else if (!triggers_to_activate_.empty() ||
              (trace_config_->write_into_file() &&
               !trace_config_->output_path().empty())) {
     open_out_file = false;
-  } else if (trace_out_path_.empty() && !upload_flag) {
+  } else if (trace_out_path_.empty() && !upload_flag_) {
     PERFETTO_ELOG("Either --out or --upload is required");
     return 1;
   } else if (is_detach() && !trace_config_->write_into_file()) {
@@ -654,43 +669,6 @@
       packet_writer_ = CreateFilePacketWriter(trace_out_stream_.get());
   }
 
-  if (background) {
-    base::Daemonize();
-  }
-
-  // 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()) {
-    LogUploadEvent(PerfettoStatsdAtom::kTriggerBegin);
-    LogTriggerEvents(PerfettoTriggerAtom::kCmdTrigger, triggers_to_activate);
-
-    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();
-    if (finished_with_success) {
-      LogUploadEvent(PerfettoStatsdAtom::kTriggerSuccess);
-    } else {
-      LogUploadEvent(PerfettoStatsdAtom::kTriggerFailure);
-      LogTriggerEvents(PerfettoTriggerAtom::kCmdTriggerFail,
-                       triggers_to_activate);
-    }
-    return finished_with_success ? 0 : 1;
-  }
-
-  if (query_service_ || bugreport_) {
-    consumer_endpoint_ =
-        ConsumerIPCClient::Connect(GetConsumerSocket(), this, &task_runner_);
-    task_runner_.Run();
-    return 1;  // We can legitimately get here if the service disconnects.
-  }
-
   if (trace_config_->compression_type() ==
       TraceConfig::COMPRESSION_TYPE_DEFLATE) {
     if (packet_writer_) {
@@ -704,11 +682,59 @@
     }
   }
 
+  if (save_to_incidentd_ && !ignore_guardrails_ &&
+      (trace_config_->duration_ms() == 0 &&
+       trace_config_->trigger_config().trigger_timeout_ms() == 0)) {
+    PERFETTO_ELOG("Can't trace indefinitely when tracing to Incidentd.");
+    return 1;
+  }
+
+  if (background_) {
+    base::Daemonize();
+  }
+
+  return 0;  // Continues in ConnectToServiceAndRun() below.
+}
+
+int PerfettoCmd::ConnectToServiceAndRun() {
+  // 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()) {
+    LogUploadEvent(PerfettoStatsdAtom::kTriggerBegin);
+    LogTriggerEvents(PerfettoTriggerAtom::kCmdTrigger, triggers_to_activate_);
+
+    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();
+    if (finished_with_success) {
+      LogUploadEvent(PerfettoStatsdAtom::kTriggerSuccess);
+    } else {
+      LogUploadEvent(PerfettoStatsdAtom::kTriggerFailure);
+      LogTriggerEvents(PerfettoTriggerAtom::kCmdTriggerFail,
+                       triggers_to_activate_);
+    }
+    return finished_with_success ? 0 : 1;
+  }  // if (triggers_to_activate_)
+
+  if (query_service_ || bugreport_) {
+    consumer_endpoint_ =
+        ConsumerIPCClient::Connect(GetConsumerSocket(), this, &task_runner_);
+    task_runner_.Run();
+    return 1;  // We can legitimately get here if the service disconnects.
+  }            // if (query_service || bugreport_)
+
   RateLimiter::Args args{};
   args.is_user_build = IsUserBuild();
   args.is_uploading = save_to_incidentd_;
   args.current_time = base::GetWallTimeS();
-  args.ignore_guardrails = ignore_guardrails;
+  args.ignore_guardrails = ignore_guardrails_;
   args.allow_user_build_tracing = trace_config_->allow_user_build_tracing();
   args.unique_session_name = trace_config_->unique_session_name();
   args.max_upload_bytes_override =
@@ -717,13 +743,6 @@
   if (!args.unique_session_name.empty())
     base::MaybeSetThreadName("p-" + args.unique_session_name);
 
-  if (args.is_uploading && !args.ignore_guardrails &&
-      (trace_config_->duration_ms() == 0 &&
-       trace_config_->trigger_config().trigger_timeout_ms() == 0)) {
-    PERFETTO_ELOG("Can't trace indefinitely when tracing to Dropbox.");
-    return 1;
-  }
-
   expected_duration_ms_ = trace_config_->duration_ms();
   if (!expected_duration_ms_) {
     uint32_t timeout_ms = trace_config_->trigger_config().trigger_timeout_ms();
@@ -740,7 +759,7 @@
     LogUploadEvent(PerfettoStatsdAtom::kBackgroundTraceBegin);
   }
 
-  auto err_atom = ConvertRateLimiterResponseToAtom(limiter.ShouldTrace(args));
+  auto err_atom = ConvertRateLimiterResponseToAtom(limiter_->ShouldTrace(args));
   if (err_atom) {
     // TODO(lalitm): remove this once we're ready on server side.
     LogUploadEvent(PerfettoStatsdAtom::kHitGuardrails);
@@ -749,8 +768,8 @@
   }
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  if (!background && !is_detach() && !upload_flag &&
-      triggers_to_activate.empty() && !isatty(STDIN_FILENO) &&
+  if (!background_ && !is_detach() && !upload_flag_ &&
+      triggers_to_activate_.empty() && !isatty(STDIN_FILENO) &&
       !isatty(STDERR_FILENO)) {
     fprintf(stderr,
             "Warning: No PTY. CTRL+C won't gracefully stop the trace. If you "
@@ -765,8 +784,9 @@
   SetupCtrlCSignalHandler();
   task_runner_.Run();
 
-  return limiter.OnTraceDone(args, update_guardrail_state_, bytes_written_) ? 0
-                                                                            : 1;
+  return limiter_->OnTraceDone(args, update_guardrail_state_, bytes_written_)
+             ? 0
+             : 1;
 }
 
 void PerfettoCmd::OnConnect() {
@@ -962,7 +982,7 @@
 }
 
 void PerfettoCmd::SetupCtrlCSignalHandler() {
-  base::InstallCtrCHandler([] { g_consumer_cmd->SignalCtrlC(); });
+  base::InstallCtrCHandler([] { g_perfetto_cmd->SignalCtrlC(); });
   task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [this] {
     PERFETTO_LOG("SIGINT/SIGTERM received: disabling tracing.");
     ctrl_c_evt_.Clear();
@@ -1037,6 +1057,7 @@
     printf("  id: %d\n", producer.id());
     printf("  name: \"%s\" \n", producer.name().c_str());
     printf("  uid: %d \n", producer.uid());
+    printf("  sdk_version: \"%s\" \n", producer.sdk_version().c_str());
     printf("}\n");
   }
 
@@ -1048,6 +1069,8 @@
     printf("  }\n");
     printf("}\n");
   }
+  printf("tracing_service_version: \"%s\"\n",
+         svc_state.tracing_service_version().c_str());
   printf("num_sessions: %d\n", svc_state.num_sessions());
   printf("num_sessions_started: %d\n", svc_state.num_sessions_started());
 }
@@ -1071,8 +1094,11 @@
 }
 
 int PERFETTO_EXPORT_ENTRYPOINT PerfettoCmdMain(int argc, char** argv) {
-  g_consumer_cmd = new perfetto::PerfettoCmd();
-  return g_consumer_cmd->Main(argc, argv);
+  perfetto::PerfettoCmd cmd;
+  int res = cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
+  if (res)
+    return res;
+  return cmd.ConnectToServiceAndRun();
 }
 
 }  // namespace perfetto
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 215c3be..4087f49 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -36,6 +36,7 @@
 namespace perfetto {
 
 class PacketWriter;
+class RateLimiter;
 
 // Directory for local state and temporary files. This is automatically
 // created by the system by setting setprop persist.traced.enable=1.
@@ -43,7 +44,14 @@
 
 class PerfettoCmd : public Consumer {
  public:
-  int Main(int argc, char** argv);
+  PerfettoCmd();
+  ~PerfettoCmd() override;
+
+  // The main() is split in two stages: cmdline parsing and actual interaction
+  // with traced. This is to allow tools like tracebox to avoid spawning the
+  // service for no reason if the cmdline parsing fails.
+  int ParseCmdlineAndMaybeDaemonize(int argc, char** argv);
+  int ConnectToServiceAndRun();
 
   // perfetto::Consumer implementation.
   void OnConnect() override;
@@ -86,13 +94,13 @@
 
   base::UnixTaskRunner task_runner_;
 
+  std::unique_ptr<RateLimiter> limiter_;
   std::unique_ptr<perfetto::TracingService::ConsumerEndpoint>
       consumer_endpoint_;
   std::unique_ptr<TraceConfig> trace_config_;
-
   std::unique_ptr<PacketWriter> packet_writer_;
   base::ScopedFstream trace_out_stream_;
-
+  std::vector<std::string> triggers_to_activate_;
   std::string trace_out_path_;
   base::EventFd ctrl_c_evt_;
   bool save_to_incidentd_ = false;
@@ -106,6 +114,9 @@
   bool query_service_ = false;
   bool query_service_output_raw_ = false;
   bool bugreport_ = false;
+  bool background_ = false;
+  bool ignore_guardrails_ = false;
+  bool upload_flag_ = false;
   std::string uuid_;
 
   // How long we expect to trace for or 0 if the trace is indefinite.
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index f0e350f..804f70e 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -21,6 +21,14 @@
 # The Android heap profiling daemon.
 executable("heapprofd") {
   deps = [
+    ":heapprofd_main",
+    "../../../gn:default_deps",
+  ]
+  sources = [ "main.cc" ]
+}
+
+source_set("heapprofd_main") {
+  deps = [
     "../../../gn:default_deps",
     "../../../protos/perfetto/trace:zero",
     "../../../src/base",
@@ -29,7 +37,10 @@
     "../../../src/profiling/memory:wire_protocol",
     "../../../src/tracing/ipc/producer",
   ]
-  sources = [ "main.cc" ]
+  sources = [
+    "heapprofd.cc",
+    "heapprofd.h",
+  ]
 }
 
 # This library gets loaded into (and executes in) arbitrary android processes.
diff --git a/src/profiling/memory/heapprofd.cc b/src/profiling/memory/heapprofd.cc
new file mode 100644
index 0000000..d3a8cae
--- /dev/null
+++ b/src/profiling/memory/heapprofd.cc
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 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/profiling/memory/heapprofd.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <array>
+#include <memory>
+#include <vector>
+
+#include <signal.h>
+
+#include "perfetto/ext/base/event_fd.h"
+#include "perfetto/ext/base/getopt.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/unix_socket.h"
+#include "perfetto/ext/base/watchdog.h"
+#include "perfetto/ext/tracing/ipc/default_socket.h"
+#include "src/profiling/memory/heapprofd_producer.h"
+#include "src/profiling/memory/java_hprof_producer.h"
+#include "src/profiling/memory/wire_protocol.h"
+
+#include "perfetto/ext/base/unix_task_runner.h"
+
+// TODO(rsavitski): the task runner watchdog spawns a thread (normally for
+// tracking cpu/mem usage) that we don't strictly need.
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+int StartCentralHeapprofd();
+
+int GetListeningSocket() {
+  const char* sock_fd = getenv(kHeapprofdSocketEnvVar);
+  if (sock_fd == nullptr)
+    PERFETTO_FATAL("Did not inherit socket from init.");
+  char* end;
+  int raw_fd = static_cast<int>(strtol(sock_fd, &end, 10));
+  if (*end != '\0')
+    PERFETTO_FATAL("Invalid %s. Expected decimal integer.",
+                   kHeapprofdSocketEnvVar);
+  return raw_fd;
+}
+
+base::EventFd* g_dump_evt = nullptr;
+
+int StartCentralHeapprofd() {
+  // We set this up before launching any threads, so we do not have to use a
+  // std::atomic for g_dump_evt.
+  g_dump_evt = new base::EventFd();
+
+  base::UnixTaskRunner task_runner;
+  base::Watchdog::GetInstance()->Start();  // crash on exceedingly long tasks
+  HeapprofdProducer producer(HeapprofdMode::kCentral, &task_runner,
+                             /* exit_when_done= */ false);
+
+  int listening_raw_socket = GetListeningSocket();
+  auto listening_socket = base::UnixSocket::Listen(
+      base::ScopedFile(listening_raw_socket), &producer.socket_delegate(),
+      &task_runner, base::SockFamily::kUnix, base::SockType::kStream);
+
+  struct sigaction action = {};
+  action.sa_handler = [](int) { g_dump_evt->Notify(); };
+  // Allow to trigger a full dump by sending SIGUSR1 to heapprofd.
+  // This will allow manually deciding when to dump on userdebug.
+  PERFETTO_CHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
+  task_runner.AddFileDescriptorWatch(g_dump_evt->fd(), [&producer] {
+    g_dump_evt->Clear();
+    producer.DumpAll();
+  });
+  producer.ConnectWithRetries(GetProducerSocket());
+  // TODO(fmayer): Create one producer that manages both heapprofd and Java
+  // producers, so we do not have two connections to traced.
+  JavaHprofProducer java_producer(&task_runner);
+  java_producer.ConnectWithRetries(GetProducerSocket());
+  task_runner.Run();
+  return 0;
+}
+
+}  // namespace
+
+int HeapprofdMain(int argc, char** argv) {
+  bool cleanup_crash = false;
+
+  enum { kCleanupCrash = 256, kTargetPid, kTargetCmd, kInheritFd };
+  static option long_options[] = {
+      {"cleanup-after-crash", no_argument, nullptr, kCleanupCrash},
+      {nullptr, 0, nullptr, 0}};
+  int c;
+  while ((c = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
+    switch (c) {
+      case kCleanupCrash:
+        cleanup_crash = true;
+        break;
+    }
+  }
+
+  if (cleanup_crash) {
+    PERFETTO_LOG(
+        "Recovering from crash: unsetting heapprofd system properties. "
+        "Expect SELinux denials for unrelated properties.");
+    SystemProperties::ResetHeapprofdProperties();
+    PERFETTO_LOG(
+        "Finished unsetting heapprofd system properties. "
+        "SELinux denials about properties are unexpected after "
+        "this point.");
+    return 0;
+  }
+
+  // start as a central daemon.
+  return StartCentralHeapprofd();
+}
+
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/profiling/memory/heapprofd.h b/src/profiling/memory/heapprofd.h
new file mode 100644
index 0000000..e405585
--- /dev/null
+++ b/src/profiling/memory/heapprofd.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2021 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_PROFILING_MEMORY_HEAPPROFD_H_
+#define SRC_PROFILING_MEMORY_HEAPPROFD_H_
+
+namespace perfetto {
+namespace profiling {
+
+int HeapprofdMain(int argc, char** argv);
+
+}  // namespace profiling
+}  // namespace perfetto
+
+#endif  // SRC_PROFILING_MEMORY_HEAPPROFD_H_
diff --git a/src/profiling/memory/main.cc b/src/profiling/memory/main.cc
index f0bb47b..078ca86 100644
--- a/src/profiling/memory/main.cc
+++ b/src/profiling/memory/main.cc
@@ -14,117 +14,7 @@
  * limitations under the License.
  */
 
-#include <stdlib.h>
-#include <unistd.h>
-#include <array>
-#include <memory>
-#include <vector>
-
-#include <signal.h>
-
-#include "perfetto/ext/base/event_fd.h"
-#include "perfetto/ext/base/getopt.h"
-#include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/unix_socket.h"
-#include "perfetto/ext/base/watchdog.h"
-#include "perfetto/ext/tracing/ipc/default_socket.h"
-#include "src/profiling/memory/heapprofd_producer.h"
-#include "src/profiling/memory/java_hprof_producer.h"
-#include "src/profiling/memory/wire_protocol.h"
-
-#include "perfetto/ext/base/unix_task_runner.h"
-
-// TODO(rsavitski): the task runner watchdog spawns a thread (normally for
-// tracking cpu/mem usage) that we don't strictly need.
-
-namespace perfetto {
-namespace profiling {
-namespace {
-
-int StartCentralHeapprofd();
-
-int GetListeningSocket() {
-  const char* sock_fd = getenv(kHeapprofdSocketEnvVar);
-  if (sock_fd == nullptr)
-    PERFETTO_FATAL("Did not inherit socket from init.");
-  char* end;
-  int raw_fd = static_cast<int>(strtol(sock_fd, &end, 10));
-  if (*end != '\0')
-    PERFETTO_FATAL("Invalid %s. Expected decimal integer.",
-                   kHeapprofdSocketEnvVar);
-  return raw_fd;
-}
-
-base::EventFd* g_dump_evt = nullptr;
-
-int HeapprofdMain(int argc, char** argv) {
-  bool cleanup_crash = false;
-
-  enum { kCleanupCrash = 256, kTargetPid, kTargetCmd, kInheritFd };
-  static option long_options[] = {
-      {"cleanup-after-crash", no_argument, nullptr, kCleanupCrash},
-      {nullptr, 0, nullptr, 0}};
-  int c;
-  while ((c = getopt_long(argc, argv, "", long_options, nullptr)) != -1) {
-    switch (c) {
-      case kCleanupCrash:
-        cleanup_crash = true;
-        break;
-    }
-  }
-
-  if (cleanup_crash) {
-    PERFETTO_LOG(
-        "Recovering from crash: unsetting heapprofd system properties. "
-        "Expect SELinux denials for unrelated properties.");
-    SystemProperties::ResetHeapprofdProperties();
-    PERFETTO_LOG(
-        "Finished unsetting heapprofd system properties. "
-        "SELinux denials about properties are unexpected after "
-        "this point.");
-    return 0;
-  }
-
-  // start as a central daemon.
-  return StartCentralHeapprofd();
-}
-
-int StartCentralHeapprofd() {
-  // We set this up before launching any threads, so we do not have to use a
-  // std::atomic for g_dump_evt.
-  g_dump_evt = new base::EventFd();
-
-  base::UnixTaskRunner task_runner;
-  base::Watchdog::GetInstance()->Start();  // crash on exceedingly long tasks
-  HeapprofdProducer producer(HeapprofdMode::kCentral, &task_runner,
-                             /* exit_when_done= */ false);
-
-  int listening_raw_socket = GetListeningSocket();
-  auto listening_socket = base::UnixSocket::Listen(
-      base::ScopedFile(listening_raw_socket), &producer.socket_delegate(),
-      &task_runner, base::SockFamily::kUnix, base::SockType::kStream);
-
-  struct sigaction action = {};
-  action.sa_handler = [](int) { g_dump_evt->Notify(); };
-  // Allow to trigger a full dump by sending SIGUSR1 to heapprofd.
-  // This will allow manually deciding when to dump on userdebug.
-  PERFETTO_CHECK(sigaction(SIGUSR1, &action, nullptr) == 0);
-  task_runner.AddFileDescriptorWatch(g_dump_evt->fd(), [&producer] {
-    g_dump_evt->Clear();
-    producer.DumpAll();
-  });
-  producer.ConnectWithRetries(GetProducerSocket());
-  // TODO(fmayer): Create one producer that manages both heapprofd and Java
-  // producers, so we do not have two connections to traced.
-  JavaHprofProducer java_producer(&task_runner);
-  java_producer.ConnectWithRetries(GetProducerSocket());
-  task_runner.Run();
-  return 0;
-}
-
-}  // namespace
-}  // namespace profiling
-}  // namespace perfetto
+#include "src/profiling/memory/heapprofd.h"
 
 int main(int argc, char** argv) {
   return perfetto::profiling::HeapprofdMain(argc, argv);
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index f07f3fb..f2358ac 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -76,8 +76,6 @@
     "importers/ftrace/ftrace_module.h",
     "importers/fuchsia/fuchsia_record.h",
     "importers/fuchsia/fuchsia_trace_utils.h",
-    "importers/gzip/gzip_utils.cc",
-    "importers/gzip/gzip_utils.h",
     "importers/json/json_utils.cc",
     "importers/json/json_utils.h",
     "importers/ninja/ninja_log_parser.cc",
@@ -154,6 +152,7 @@
     "types",
     "util",
     "util:descriptors",
+    "util:gzip",
     "util:interned_message_view",
     "util:proto_to_args_parser",
   ]
@@ -180,11 +179,6 @@
   if (enable_perfetto_trace_processor_json) {
     deps += [ "../../gn:jsoncpp" ]
   }
-
-  # gzip_utils optionally depends on zlib.
-  if (enable_perfetto_zlib) {
-    deps += [ "../../gn:zlib" ]
-  }
 }
 
 source_set("storage_full") {
@@ -272,6 +266,7 @@
     "tables",
     "types",
     "util",
+    "util:gzip",
   ]
   if (enable_perfetto_trace_processor_json) {
     deps += [ "../../gn:jsoncpp" ]
@@ -344,6 +339,7 @@
       "tables",
       "types",
       "util",
+      "util:gzip",
       "util:protozero_to_text",
     ]
     public_deps = [
diff --git a/src/trace_processor/importers/gzip/gzip_trace_parser.cc b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
index bc42db3..cf8416e 100644
--- a/src/trace_processor/importers/gzip/gzip_trace_parser.cc
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.cc
@@ -22,6 +22,7 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
 #include "src/trace_processor/forwarding_trace_parser.h"
+#include "src/trace_processor/util/gzip_utils.h"
 #include "src/trace_processor/util/status_macros.h"
 
 namespace perfetto {
@@ -29,7 +30,7 @@
 
 namespace {
 
-using ResultCode = GzipDecompressor::ResultCode;
+using ResultCode = util::GzipDecompressor::ResultCode;
 
 }  // namespace
 
diff --git a/src/trace_processor/importers/gzip/gzip_trace_parser.h b/src/trace_processor/importers/gzip/gzip_trace_parser.h
index 82b4cd9..051284a 100644
--- a/src/trace_processor/importers/gzip/gzip_trace_parser.h
+++ b/src/trace_processor/importers/gzip/gzip_trace_parser.h
@@ -18,7 +18,7 @@
 #define SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_TRACE_PARSER_H_
 
 #include "src/trace_processor/importers/common/chunked_trace_reader.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
+#include "src/trace_processor/util/gzip_utils.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -41,7 +41,7 @@
 
  private:
   TraceProcessorContext* const context_;
-  GzipDecompressor decompressor_;
+  util::GzipDecompressor decompressor_;
   std::unique_ptr<ChunkedTraceReader> inner_;
 
   std::unique_ptr<uint8_t[]> buffer_;
diff --git a/src/trace_processor/importers/proto/proto_trace_reader.cc b/src/trace_processor/importers/proto/proto_trace_reader.cc
index ee03db3..be5f1ff 100644
--- a/src/trace_processor/importers/proto/proto_trace_reader.cc
+++ b/src/trace_processor/importers/proto/proto_trace_reader.cc
@@ -30,7 +30,6 @@
 #include "src/trace_processor/importers/common/event_tracker.h"
 #include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/importers/ftrace/ftrace_module.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
 #include "src/trace_processor/importers/proto/metadata_tracker.h"
 #include "src/trace_processor/importers/proto/packet_sequence_state.h"
 #include "src/trace_processor/importers/proto/proto_incremental_state.h"
@@ -38,6 +37,7 @@
 #include "src/trace_processor/storage/trace_storage.h"
 #include "src/trace_processor/trace_sorter.h"
 #include "src/trace_processor/util/descriptors.h"
+#include "src/trace_processor/util/gzip_utils.h"
 
 #include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/perfetto/config/trace_config.pbzero.h"
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
index fdc7bcd..9bf23df 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.cc
@@ -25,7 +25,7 @@
 
 util::Status ProtoTraceTokenizer::Decompress(TraceBlobView input,
                                              TraceBlobView* output) {
-  PERFETTO_DCHECK(gzip::IsGzipSupported());
+  PERFETTO_DCHECK(util::IsGzipSupported());
 
   uint8_t out[4096];
 
@@ -36,7 +36,7 @@
   decompressor_.Reset();
   decompressor_.SetInput(input.data(), input.length());
 
-  using ResultCode = GzipDecompressor::ResultCode;
+  using ResultCode = util::GzipDecompressor::ResultCode;
   for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
     auto res = decompressor_.Decompress(out, base::ArraySize(out));
     ret = res.ret;
diff --git a/src/trace_processor/importers/proto/proto_trace_tokenizer.h b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
index 484c674..350cb34 100644
--- a/src/trace_processor/importers/proto/proto_trace_tokenizer.h
+++ b/src/trace_processor/importers/proto/proto_trace_tokenizer.h
@@ -21,7 +21,7 @@
 
 #include "perfetto/protozero/proto_utils.h"
 #include "perfetto/trace_processor/status.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
+#include "src/trace_processor/util/gzip_utils.h"
 #include "src/trace_processor/util/status_macros.h"
 #include "src/trace_processor/util/trace_blob_view.h"
 
@@ -143,7 +143,7 @@
     protos::pbzero::TracePacket::Decoder decoder(packet.data(),
                                                  packet.length());
     if (decoder.has_compressed_packets()) {
-      if (!gzip::IsGzipSupported()) {
+      if (!util::IsGzipSupported()) {
         return util::Status(
             "Cannot decode compressed packets. Zlib not enabled");
       }
@@ -185,7 +185,7 @@
   std::vector<uint8_t> partial_buf_;
 
   // Allows support for compressed trace packets.
-  GzipDecompressor decompressor_;
+  util::GzipDecompressor decompressor_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/read_trace.cc b/src/trace_processor/read_trace.cc
index 8f5b6f0..4a9d8fd 100644
--- a/src/trace_processor/read_trace.cc
+++ b/src/trace_processor/read_trace.cc
@@ -25,8 +25,8 @@
 
 #include "src/trace_processor/forwarding_trace_parser.h"
 #include "src/trace_processor/importers/gzip/gzip_trace_parser.h"
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
 #include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
+#include "src/trace_processor/util/gzip_utils.h"
 #include "src/trace_processor/util/status_macros.h"
 
 #include "protos/perfetto/trace/trace.pbzero.h"
@@ -210,7 +210,7 @@
   PERFETTO_CHECK(type == TraceType::kProtoTraceType);
 
   protos::pbzero::Trace::Decoder decoder(data, size);
-  GzipDecompressor decompressor;
+  util::GzipDecompressor decompressor;
   if (size > 0 && !decoder.packet()) {
     return util::ErrStatus("Trace does not contain valid packets");
   }
@@ -226,7 +226,7 @@
     decompressor.Reset();
     decompressor.SetInput(bytes.data, bytes.size);
 
-    using ResultCode = GzipDecompressor::ResultCode;
+    using ResultCode = util::GzipDecompressor::ResultCode;
     uint8_t out[4096];
     for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
       auto res = decompressor.Decompress(out, base::ArraySize(out));
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 5418f55..d03072d 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -693,7 +693,7 @@
 
   context_.systrace_trace_parser.reset(new SystraceTraceParser(&context_));
 
-  if (gzip::IsGzipSupported())
+  if (util::IsGzipSupported())
     context_.gzip_trace_parser.reset(new GzipTraceParser(&context_));
 
   if (json::IsJsonSupported()) {
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index 31925e8..055cc81 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -29,6 +29,22 @@
   ]
 }
 
+source_set("gzip") {
+  sources = [
+    "gzip_utils.cc",
+    "gzip_utils.h",
+  ]
+  deps = [
+    "../../../gn:default_deps",
+    "../../../include/perfetto/base",
+  ]
+
+  # gzip_utils optionally depends on zlib.
+  if (enable_perfetto_zlib) {
+    deps += [ "../../../gn:zlib" ]
+  }
+}
+
 source_set("protozero_to_text") {
   sources = [
     "protozero_to_text.cc",
diff --git a/src/trace_processor/importers/gzip/gzip_utils.cc b/src/trace_processor/util/gzip_utils.cc
similarity index 96%
rename from src/trace_processor/importers/gzip/gzip_utils.cc
rename to src/trace_processor/util/gzip_utils.cc
index 8a30c7f..79cf1c0 100644
--- a/src/trace_processor/importers/gzip/gzip_utils.cc
+++ b/src/trace_processor/util/gzip_utils.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "src/trace_processor/importers/gzip/gzip_utils.h"
+#include "src/trace_processor/util/gzip_utils.h"
 
 // For bazel build.
 #include "perfetto/base/build_config.h"
@@ -28,7 +28,7 @@
 
 namespace perfetto {
 namespace trace_processor {
-namespace gzip {
+namespace util {
 
 bool IsGzipSupported() {
 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
@@ -38,8 +38,6 @@
 #endif
 }
 
-}  // namespace gzip
-
 #if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
 GzipDecompressor::GzipDecompressor() : z_stream_(new z_stream()) {
   z_stream_->zalloc = Z_NULL;
@@ -108,5 +106,6 @@
 #endif
 }
 
+}  // namespace util
 }  // namespace trace_processor
 }  // namespace perfetto
diff --git a/src/trace_processor/importers/gzip/gzip_utils.h b/src/trace_processor/util/gzip_utils.h
similarity index 88%
rename from src/trace_processor/importers/gzip/gzip_utils.h
rename to src/trace_processor/util/gzip_utils.h
index 624363f..189cfbd 100644
--- a/src/trace_processor/importers/gzip/gzip_utils.h
+++ b/src/trace_processor/util/gzip_utils.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
-#define SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
+#ifndef SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_
+#define SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_
 
 #include <memory>
 
@@ -23,15 +23,12 @@
 
 namespace perfetto {
 namespace trace_processor {
-
-namespace gzip {
+namespace util {
 
 // Returns whether gzip related functioanlity is supported with the current
 // build flags.
 bool IsGzipSupported();
 
-}  // namespace gzip
-
 class GzipDecompressor {
  public:
   enum class ResultCode {
@@ -66,7 +63,8 @@
   std::unique_ptr<z_stream_s> z_stream_;
 };
 
+}  // namespace util
 }  // namespace trace_processor
 }  // namespace perfetto
 
-#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_GZIP_GZIP_UTILS_H_
+#endif  // SRC_TRACE_PROCESSOR_UTIL_GZIP_UTILS_H_
diff --git a/src/tracebox/BUILD.gn b/src/tracebox/BUILD.gn
new file mode 100644
index 0000000..647ad5e
--- /dev/null
+++ b/src/tracebox/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright (C) 2021 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.
+
+import("../../gn/perfetto.gni")
+
+assert(perfetto_build_standalone)
+
+executable("tracebox") {
+  deps = [
+    "../../gn:default_deps",
+    "../base",
+    "../perfetto_cmd",
+    "../perfetto_cmd:trigger_perfetto_cmd",
+    "../traced/probes",
+    "../traced/service",
+  ]
+  sources = [ "tracebox.cc" ]
+}
diff --git a/src/tracebox/tracebox.cc b/src/tracebox/tracebox.cc
new file mode 100644
index 0000000..f99aaa8
--- /dev/null
+++ b/src/tracebox/tracebox.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2021 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 "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/pipe.h"
+#include "perfetto/ext/base/subprocess.h"
+#include "perfetto/ext/base/utils.h"
+#include "perfetto/ext/traced/traced.h"
+#include "src/perfetto_cmd/perfetto_cmd.h"
+
+#include <stdio.h>
+
+#include <tuple>
+
+namespace perfetto {
+namespace {
+
+struct Applet {
+  using MainFunction = int (*)(int /*argc*/, char** /*argv*/);
+  const char* name;
+  MainFunction entrypoint;
+};
+
+const Applet g_applets[]{
+    {"traced", ServiceMain},
+    {"traced_probes", ProbesMain},
+    {"perfetto", PerfettoCmdMain},
+    {"trigger_perfetto", TriggerPerfettoMain},
+};
+
+void PrintUsage() {
+  printf(R"(Welcome to Perfetto tracing!
+
+Tracebox is a bundle containing all the tracing services and the perfetto
+cmdline client in one binary. It can be used either to spawn manually the
+various subprocess or in "autostart" mode, which will take care of starting
+and tearing down the services for you.
+
+Usage in autostart mode:
+  tracebox -t 10s -o trace_file.perfetto-trace sched/sched_switch
+  See tracebox --help for more options.
+
+Usage in manual mode:
+  tracebox applet_name [args ...]  (e.g. ./tracebox traced --help)
+  Applets:)");
+
+  for (const Applet& applet : g_applets)
+    printf(" %s", applet.name);
+
+  printf(R"(
+
+See also:
+  * https://perfetto.dev/docs/
+  * The config editor in the record page of https://ui.perfetto.dev/
+)");
+}
+
+int TraceboxMain(int argc, char** argv) {
+  // Manual mode: if either the 1st argument (argv[1]) or the exe name (argv[0])
+  // match the name of an applet, directly invoke that without further
+  // modifications.
+
+  // Extract the file name from argv[0].
+  char* slash = strrchr(argv[0], '/');
+  char* argv0 = slash ? slash + 1 : argv[0];
+
+  for (const Applet& applet : g_applets) {
+    if (!strcmp(argv0, applet.name))
+      return applet.entrypoint(argc, argv);
+    if (argc > 1 && !strcmp(argv[1], applet.name))
+      return applet.entrypoint(argc - 1, &argv[1]);
+  }
+
+  // If no matching applet is found, switch to the autostart mode. In this mode
+  // we make tracebox behave like the cmdline client (without needing to prefix
+  // it with "perfetto"), but will also start traced and traced_probes.
+  // As part of this we also use a different namespace for the producer/consumer
+  // sockets, to avoid clashing with the system daemon.
+
+  if (argc <= 1) {
+    PrintUsage();
+    return 1;
+  }
+
+  auto pid_str = std::to_string(static_cast<uint64_t>(base::GetProcessId()));
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+  // Use an unlinked abstract domain socket on Linux/Android.
+  std::string consumer_socket = "@traced-c-" + pid_str;
+  std::string producer_socket = "@traced-p-" + pid_str;
+#elif PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
+  std::string consumer_socket = "/tmp/traced-c-" + pid_str;
+  std::string producer_socket = "/tmp/traced-p-" + pid_str;
+#else
+  PERFETTO_FATAL("The autostart mode is not supported on this platform");
+#endif
+
+  // If the caller has set the PERFETTO_*_SOCK_NAME, respect those.
+  const char* env;
+  if ((env = getenv("PERFETTO_CONSUMER_SOCK_NAME")))
+    consumer_socket = env;
+  if ((env = getenv("PERFETTO_PRODUCER_SOCK_NAME")))
+    consumer_socket = env;
+
+  base::SetEnv("PERFETTO_CONSUMER_SOCK_NAME", consumer_socket);
+  base::SetEnv("PERFETTO_PRODUCER_SOCK_NAME", producer_socket);
+
+  PerfettoCmd perfetto_cmd;
+
+  // If the cmdline parsing fails, stop here, no need to spawn services.
+  // It will daemonize if --background. In that case the subprocesses will be
+  // spawned by the damonized cmdline client, which is what we want so killing
+  // the backgrounded cmdline client will also kill the other services, as they
+  // will live in the same background session.
+  int res = perfetto_cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
+  if (res)
+    return res;
+
+  std::string self_path = base::GetCurExecutablePath();
+  base::Subprocess traced({self_path, "traced"});
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  // |traced_sync_pipe| is used to synchronize with traced socket creation.
+  // traced will write "1" and close the FD when the IPC socket is listening
+  // (or traced crashed).
+  base::Pipe traced_sync_pipe = base::Pipe::Create();
+  int wr_fd = *traced_sync_pipe.wr;
+  base::SetEnv("TRACED_NOTIFY_FD", std::to_string(wr_fd));
+  traced.args.preserve_fds.emplace_back(wr_fd);
+  // Create a new process group so CTRL-C is delivered only to the cmdline
+  // process (the tracebox one) and not to traced. traced will still exit once
+  // the main process exits, but this allows graceful stopping of the trace
+  // without abruptedly killing traced{,probes} when hitting CTRL+C.
+  traced.args.posix_proc_group_id = 0;  // 0 = start a new process group.
+#endif
+  traced.Start();
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  traced_sync_pipe.wr.reset();
+
+  std::string traced_notify_msg;
+  base::ReadPlatformHandle(*traced_sync_pipe.rd, &traced_notify_msg);
+  if (traced_notify_msg != "1")
+    PERFETTO_FATAL("The tracing service failed unexpectedly. Check the logs");
+#endif
+
+  base::Subprocess traced_probes(
+      {self_path, "traced_probes", "--reset-ftrace"});
+  // Put traced_probes in the same process group as traced. Same reason (CTRL+C)
+  // but it's not worth creating a new group.
+  traced_probes.args.posix_proc_group_id = traced.pid();
+  traced_probes.Start();
+
+  perfetto_cmd.ConnectToServiceAndRun();
+  return 0;
+}
+
+}  // namespace
+}  // namespace perfetto
+
+int main(int argc, char** argv) {
+  return perfetto::TraceboxMain(argc, argv);
+}
diff --git a/src/traced/probes/ftrace/ftrace_controller.cc b/src/traced/probes/ftrace/ftrace_controller.cc
index 53ac1e5..0193e49 100644
--- a/src/traced/probes/ftrace/ftrace_controller.cc
+++ b/src/traced/probes/ftrace/ftrace_controller.cc
@@ -33,6 +33,7 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/metatrace.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/tracing/core/trace_writer.h"
 #include "src/kallsyms/kernel_symbol_map.h"
 #include "src/kallsyms/lazy_kernel_symbolizer.h"
@@ -84,15 +85,17 @@
   return drain_period_ms;
 }
 
-void WriteToFile(const char* path, const char* str) {
+bool WriteToFile(const char* path, const char* str) {
   auto fd = base::OpenFile(path, O_WRONLY);
   if (!fd)
-    return;
-  base::ignore_result(base::WriteAll(*fd, str, strlen(str)));
+    return false;
+  const size_t str_len = strlen(str);
+  return base::WriteAll(*fd, str, str_len) == static_cast<ssize_t>(str_len);
 }
 
-void ClearFile(const char* path) {
+bool ClearFile(const char* path) {
   auto fd = base::OpenFile(path, O_WRONLY | O_TRUNC);
+  return !!fd;
 }
 
 }  // namespace
@@ -100,18 +103,21 @@
 // Method of last resort to reset ftrace state.
 // We don't know what state the rest of the system and process is so as far
 // as possible avoid allocations.
-void HardResetFtraceState() {
-  PERFETTO_LOG("Hard resetting ftrace state.");
-
-  WriteToFile("/sys/kernel/debug/tracing/tracing_on", "0");
-  WriteToFile("/sys/kernel/debug/tracing/buffer_size_kb", "4");
-  WriteToFile("/sys/kernel/debug/tracing/events/enable", "0");
-  ClearFile("/sys/kernel/debug/tracing/trace");
-
-  WriteToFile("/sys/kernel/tracing/tracing_on", "0");
-  WriteToFile("/sys/kernel/tracing/buffer_size_kb", "4");
-  WriteToFile("/sys/kernel/tracing/events/enable", "0");
-  ClearFile("/sys/kernel/tracing/trace");
+bool HardResetFtraceState() {
+  for (const char* const* item = FtraceProcfs::kTracingPaths; *item; ++item) {
+    std::string prefix(*item);
+    PERFETTO_CHECK(base::EndsWith(prefix, "/"));
+    bool res = true;
+    res &= WriteToFile((prefix + "tracing_on").c_str(), "0");
+    res &= WriteToFile((prefix + "buffer_size_kb").c_str(), "4");
+    // We deliberately don't check for this as on some older versions of Android
+    // events/enable was not writable by the shell user.
+    WriteToFile((prefix + "events/enable").c_str(), "0");
+    res &= ClearFile((prefix + "trace").c_str());
+    if (res)
+      return true;
+  }
+  return false;
 }
 
 // static
diff --git a/src/traced/probes/ftrace/ftrace_controller.h b/src/traced/probes/ftrace/ftrace_controller.h
index 3e150a3..71250c8 100644
--- a/src/traced/probes/ftrace/ftrace_controller.h
+++ b/src/traced/probes/ftrace/ftrace_controller.h
@@ -45,7 +45,7 @@
 struct FtraceStats;
 
 // Method of last resort to reset ftrace state.
-void HardResetFtraceState();
+bool HardResetFtraceState();
 
 // Utility class for controlling ftrace.
 class FtraceController {
diff --git a/src/traced/probes/probes.cc b/src/traced/probes/probes.cc
index b02b6d5..3ec4da1 100644
--- a/src/traced/probes/probes.cc
+++ b/src/traced/probes/probes.cc
@@ -38,13 +38,16 @@
     OPT_CLEANUP_AFTER_CRASH = 1000,
     OPT_VERSION,
     OPT_BACKGROUND,
+    OPT_RESET_FTRACE,
   };
 
   bool background = false;
+  bool reset_ftrace = false;
 
   static const option long_options[] = {
       {"background", no_argument, nullptr, OPT_BACKGROUND},
       {"cleanup-after-crash", no_argument, nullptr, OPT_CLEANUP_AFTER_CRASH},
+      {"reset-ftrace", no_argument, nullptr, OPT_RESET_FTRACE},
       {"version", no_argument, nullptr, OPT_VERSION},
       {nullptr, 0, nullptr, 0}};
 
@@ -57,17 +60,33 @@
         background = true;
         break;
       case OPT_CLEANUP_AFTER_CRASH:
+        // Used by perfetto.rc in Android.
+        PERFETTO_LOG("Hard resetting ftrace state.");
         HardResetFtraceState();
         return 0;
+      case OPT_RESET_FTRACE:
+        // This is like --cleanup-after-crash but doesn't quit.
+        reset_ftrace = true;
+        break;
       case OPT_VERSION:
         printf("%s\n", base::GetVersionString());
         return 0;
       default:
-        PERFETTO_ELOG("Usage: %s [--background|--cleanup-after-crash|--version]", argv[0]);
+        fprintf(
+            stderr,
+            "Usage: %s [--background] [--reset-ftrace] [--cleanup-after-crash] "
+            "[--version]\n",
+            argv[0]);
         return 1;
     }
   }
 
+  if (reset_ftrace && !HardResetFtraceState()) {
+    PERFETTO_ELOG(
+        "Failed to reset ftrace. Either run this as root or run "
+        "`sudo chown -R $USER /sys/kernel/tracing`");
+  }
+
   if (background) {
     base::Daemonize();
   }
diff --git a/src/traced/service/service.cc b/src/traced/service/service.cc
index 6d9ba8b..f0a6c4a 100644
--- a/src/traced/service/service.cc
+++ b/src/traced/service/service.cc
@@ -17,6 +17,7 @@
 #include <stdio.h>
 #include <algorithm>
 
+#include "perfetto/ext/base/file_utils.h"
 #include "perfetto/ext/base/getopt.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/unix_task_runner.h"
@@ -75,7 +76,7 @@
 #endif  // defined(PERFETTO_SET_SOCKET_PERMISSIONS)
 
 void PrintUsage(const char* prog_name) {
-  PERFETTO_ELOG(R"(
+  fprintf(stderr, R"(
 Usage: %s [option] ...
 Options and arguments
     --background : Exits immediately and continues running in the background
@@ -87,12 +88,14 @@
         <prod_mode> is the mode bits (e.g. 0660) for chmod the produce socket,
         <cons_group> is the group name for chgrp the consumer socket, and
         <cons_mode> is the mode bits (e.g. 0660) for chmod the consumer socket.
-Example: %s --set-socket-permissions traced-producer:0660:traced-consumer:0660
+
+Example:
+    %s --set-socket-permissions traced-producer:0660:traced-consumer:0660
     starts the service and sets the group ownership of the producer and consumer
     sockets to "traced-producer" and "traced-consumer", respectively. Both
-    producer and consumer sockets are chmod with 0660  (rw-rw----) mode bits.
+    producer and consumer sockets are chmod with 0660 (rw-rw----) mode bits.
 )",
-                prog_name, prog_name);
+          prog_name, prog_name);
 }
 }  // namespace
 
@@ -203,6 +206,16 @@
                         base::kWatchdogDefaultCpuWindow);
   watchdog->Start();
 
+  // If the TRACED_NOTIFY_FD env var is set, write 1 and close the FD. This is
+  // so tools can synchronize with the point where the IPC socket has been
+  // opened, without having to poll. This is used for //src/tracebox.
+  const char* env_notif = getenv("TRACED_NOTIFY_FD");
+  if (env_notif) {
+    int notif_fd = atoi(env_notif);
+    PERFETTO_CHECK(base::WriteAll(notif_fd, "1", 1) == 1);
+    PERFETTO_CHECK(base::CloseFile(notif_fd) == 0);
+  }
+
   PERFETTO_ILOG("Started traced, listening on %s %s", GetProducerSocket(),
                 GetConsumerSocket());
   task_runner.Run();
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 7084dec..63839b0 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -57,6 +57,7 @@
 #include "perfetto/ext/base/metatrace.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/utils.h"
+#include "perfetto/ext/base/version.h"
 #include "perfetto/ext/base/watchdog.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/consumer.h"
@@ -332,7 +333,8 @@
                                     bool in_process,
                                     ProducerSMBScrapingMode smb_scraping_mode,
                                     size_t shared_memory_page_size_hint_bytes,
-                                    std::unique_ptr<SharedMemory> shm) {
+                                    std::unique_ptr<SharedMemory> shm,
+                                    const std::string& sdk_version) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
 
   if (lockdown_mode_ && uid != base::GetCurrentUserId()) {
@@ -361,8 +363,8 @@
   }
 
   std::unique_ptr<ProducerEndpointImpl> endpoint(new ProducerEndpointImpl(
-      id, uid, this, task_runner_, producer, producer_name, in_process,
-      smb_scraping_enabled));
+      id, uid, this, task_runner_, producer, producer_name, sdk_version,
+      in_process, smb_scraping_enabled));
   auto it_and_inserted = producers_.emplace(id, endpoint.get());
   PERFETTO_DCHECK(it_and_inserted.second);
   endpoint->shmem_size_hint_bytes_ = shared_memory_size_hint_bytes;
@@ -3039,7 +3041,7 @@
   tracing_session->did_emit_system_info = true;
   protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
   auto* info = packet->set_system_info();
-  base::ignore_result(info);  // For PERFETTO_OS_WIN.
+  info->set_tracing_service_version(base::GetVersionString());
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
     !PERFETTO_BUILDFLAG(PERFETTO_OS_NACL)
   struct utsname uname_info;
@@ -3479,6 +3481,7 @@
   TracingServiceState svc_state;
 
   const auto& sessions = service_->tracing_sessions_;
+  svc_state.set_tracing_service_version(base::GetVersionString());
   svc_state.set_num_sessions(static_cast<int>(sessions.size()));
 
   int num_started = 0;
@@ -3490,6 +3493,7 @@
     auto* producer = svc_state.add_producers();
     producer->set_id(static_cast<int>(kv.first));
     producer->set_name(kv.second->name_);
+    producer->set_sdk_version(kv.second->sdk_version_);
     producer->set_uid(static_cast<int32_t>(producer->uid()));
   }
 
@@ -3547,6 +3551,7 @@
     base::TaskRunner* task_runner,
     Producer* producer,
     const std::string& producer_name,
+    const std::string& sdk_version,
     bool in_process,
     bool smb_scraping_enabled)
     : id_(id),
@@ -3555,6 +3560,7 @@
       task_runner_(task_runner),
       producer_(producer),
       name_(producer_name),
+      sdk_version_(sdk_version),
       in_process_(in_process),
       smb_scraping_enabled_(smb_scraping_enabled),
       weak_ptr_factory_(this) {}
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 27b172c..401aaa1 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -87,6 +87,7 @@
                          base::TaskRunner*,
                          Producer*,
                          const std::string& producer_name,
+                         const std::string& sdk_version,
                          bool in_process,
                          bool smb_scraping_enabled);
     ~ProducerEndpointImpl() override;
@@ -154,6 +155,7 @@
     size_t shmem_page_size_hint_bytes_ = 0;
     bool is_shmem_provided_by_producer_ = false;
     const std::string name_;
+    std::string sdk_version_;
     bool in_process_;
     bool smb_scraping_enabled_;
 
@@ -291,7 +293,8 @@
       ProducerSMBScrapingMode smb_scraping_mode =
           ProducerSMBScrapingMode::kDefault,
       size_t shared_memory_page_size_hint_bytes = 0,
-      std::unique_ptr<SharedMemory> shm = nullptr) override;
+      std::unique_ptr<SharedMemory> shm = nullptr,
+      const std::string& sdk_version = {}) override;
 
   std::unique_ptr<TracingService::ConsumerEndpoint> ConnectConsumer(
       Consumer*,
diff --git a/src/tracing/ipc/producer/producer_ipc_client_impl.cc b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
index 694a3a9..97b622c 100644
--- a/src/tracing/ipc/producer/producer_ipc_client_impl.cc
+++ b/src/tracing/ipc/producer/producer_ipc_client_impl.cc
@@ -21,6 +21,7 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/task_runner.h"
+#include "perfetto/ext/base/version.h"
 #include "perfetto/ext/ipc/client.h"
 #include "perfetto/ext/tracing/core/commit_data_request.h"
 #include "perfetto/ext/tracing/core/producer.h"
@@ -179,6 +180,7 @@
   req.set_build_flags(
       protos::gen::InitializeConnectionRequest::BUILD_FLAGS_DCHECKS_OFF);
 #endif
+  req.set_sdk_version(base::GetVersionString());
   producer_port_.InitializeConnection(req, std::move(on_init), shm_fd);
 
   // Create the back channel to receive commands from the Service.
diff --git a/src/tracing/ipc/service/producer_ipc_service.cc b/src/tracing/ipc/service/producer_ipc_service.cc
index dc74374..71abd8c 100644
--- a/src/tracing/ipc/service/producer_ipc_service.cc
+++ b/src/tracing/ipc/service/producer_ipc_service.cc
@@ -131,7 +131,8 @@
       producer.get(), client_info.uid(), req.producer_name(),
       req.shared_memory_size_hint_bytes(),
       /*in_process=*/false, smb_scraping_mode,
-      req.shared_memory_page_size_hint_bytes(), std::move(shmem));
+      req.shared_memory_page_size_hint_bytes(), std::move(shmem),
+      req.sdk_version());
 
   // Could happen if the service has too many producers connected.
   if (!producer->service_endpoint) {
diff --git a/test/end_to_end_integrationtest.cc b/test/end_to_end_integrationtest.cc
index 64a7c80..af54f08 100644
--- a/test/end_to_end_integrationtest.cc
+++ b/test/end_to_end_integrationtest.cc
@@ -30,6 +30,7 @@
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/subprocess.h"
 #include "perfetto/ext/base/temp_file.h"
+#include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/ipc/basic_types.h"
 #include "perfetto/ext/traced/traced.h"
 #include "perfetto/ext/tracing/core/commit_data_request.h"
diff --git a/tools/gen_bazel b/tools/gen_bazel
index f4e3650..a8b7309 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -43,6 +43,8 @@
     'enable_perfetto_watchdog=true',
     'monolithic_binaries=true',
     'target_os="linux"',
+    'enable_perfetto_heapprofd=false',
+    'enable_perfetto_traced_perf=false',
 ])
 
 # Default targets to translate to the blueprint file.
diff --git a/tools/record_android_trace b/tools/record_android_trace
index 7874638..5de8a4e 100755
--- a/tools/record_android_trace
+++ b/tools/record_android_trace
@@ -1,14 +1,29 @@
 #!/usr/bin/env python3
+# Copyright (C) 2021 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.
 
 import atexit
 import argparse
 import datetime
+import hashlib
 import http.server
 import os
 import shutil
 import socketserver
 import subprocess
 import sys
+import tempfile
 import time
 import webbrowser
 
@@ -18,6 +33,24 @@
 # PATH. It's fine if it doesn't exist so this script can be copied elsewhere.
 HERMETIC_ADB_PATH = ROOT_DIR + '/buildtools/android_sdk/platform-tools/adb'
 
+# For downloading and sideloading tracebox on older devices, or when the
+# --sideload argument is passed.
+TRACEBOX_BASE_URL = 'https://storage.googleapis.com/perfetto/'
+TRACEBOX_SHA1S = {
+    'android-arm': '6e9dfee326468fc6858c6d95e00be110efd187a3',  # v15.0.248
+    'android-arm64': 'ca2d4a02511f73dac32a2ae49964f3e5cd59d252',  # v15.0.248
+    'android-x86': '3fdbc9246412e460d0a373140c114cff858b9b7c',  # v15.0.248
+    'android-x64': 'be85f6f4a2d014d425246b85941c137c28d158cc',  # v15.0.248
+}
+
+# Translates the Android ro.product.cpu.abi into the GN's target_cpu.
+ABI_TO_ARCH = {
+    'armeabi-v7a': 'arm',
+    'arm64-v8a': 'arm64',
+    'x86': 'x86',
+    'x86_64': 'x64',
+}
+
 devnull = open(os.devnull, 'rb')
 adb_path = None
 procs = []
@@ -54,7 +87,7 @@
   default_out_dir = os.path.expanduser(default_out_dir_str)
 
   examples = '\n'.join([
-      ANSI.BOLD + 'Examples' + ANSI.END, '  -t 10s -b 32mb sched gfx wm',
+      ANSI.BOLD + 'Examples' + ANSI.END, '  -t 10s -b 32mb sched gfx wm -a*',
       '  -t 5s sched/sched_switch raw_syscalls/sys_enter raw_syscalls/sys_exit',
       '  -c /path/to/full-textual-trace.config', '',
       ANSI.BOLD + 'Long traces' + ANSI.END,
@@ -71,6 +104,16 @@
   help = 'Don\'t open in the browser'
   parser.add_argument('-n', '--no-open', action='store_true', help=help)
 
+  help = 'Force the use of the sideloaded binaries rather than system daemons'
+  parser.add_argument('--sideload', action='store_true', help=help)
+
+  help = ('Sideload the the given binary rather than downloading it. ' +
+          'Implies --sideload')
+  parser.add_argument('--sideload-path', default=None, help=help)
+
+  help = 'Don\'t run `adb root` run as user (only when sideloading)'
+  parser.add_argument('-u', '--user', action='store_true', help=help)
+
   grp = parser.add_argument_group(
       'Short options: (only when not using -c/--config)')
 
@@ -80,11 +123,12 @@
   help = 'Ring buffer size N[mb,gb] (default: 32mb)'
   grp.add_argument('-b', '--buffer', default='32mb', help=help)
 
-  help = 'Android (atrace) app names (can be specified multiple times)'
+  help = ('Android (atrace) app names. Can be specified multiple times.\n-a*' +
+          'for all apps (without space between a and * or bash will expand it)')
   grp.add_argument(
       '-a',
       '--app',
-      metavar='Atrace apps',
+      metavar='com.myapp',
       action='append',
       default=[],
       help=help)
@@ -107,10 +151,7 @@
   help = 'Can be generated with https://ui.perfetto.dev/#!/record'
   grp.add_argument('-c', '--config', default=None, help=help)
   args = parser.parse_args()
-
-  tstamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M')
-  fname = '%s.pftrace' % tstamp
-  device_file = '/data/misc/perfetto-traces/' + fname
+  args.sideload = args.sideload or args.sideload_path is not None
 
   find_adb()
 
@@ -137,13 +178,51 @@
          'Did you mean to pass -c / --config ?'), ANSI.RED)
     sys.exit(1)
 
-  cmd = ['perfetto', '--background', '--txt', '-o', device_file]
+  perfetto_cmd = 'perfetto'
+  device_dir = '/data/misc/perfetto-traces/'
+
+  # Check the version of android. If too old (< Q) sideload tracebox. Also use
+  # use /data/local/tmp as /data/misc/perfetto-traces was introduced only later.
+  probe_cmd = 'getprop ro.build.version.sdk; getprop ro.product.cpu.abi; whoami'
+  probe = adb('shell', probe_cmd, stdout=subprocess.PIPE)
+  lines = probe.communicate()[0].decode().strip().split('\n')
+  lines = [x.strip() for x in lines]  # To strip \r(s) on Windows.
+  if probe.returncode != 0:
+    prt('ADB connection failed', ANSI.RED)
+    sys.exit(1)
+  api_level = int(lines[0])
+  abi = lines[1]
+  arch = ABI_TO_ARCH.get(abi)
+  if arch is None:
+    prt('Unsupported ABI: ' + abi)
+    sys.exit(1)
+  shell_user = lines[2]
+  if api_level < 29 or args.sideload:  # 29: Android Q.
+    tracebox_bin = args.sideload_path or download_tracebox_if_needed(arch)
+    perfetto_cmd = '/data/local/tmp/tracebox'
+    exit_code = adb('push', '--sync', tracebox_bin, perfetto_cmd).wait()
+    exit_code |= adb('shell', 'chmod 755 ' + perfetto_cmd).wait()
+    if exit_code != 0:
+      prt('ADB push failed', ANSI.RED)
+      sys.exit(1)
+    device_dir = '/data/local/tmp/'
+    if shell_user != 'root' and not args.user:
+      # Run as root if possible as that will give access to more tracing
+      # capabilities. Non-root still works, but some ftrace events might not be
+      # available.
+      adb('root').wait()
+
+  tstamp = datetime.datetime.now().strftime('%Y-%m-%d_%H-%M')
+  fname = '%s-%s.pftrace' % (tstamp, os.urandom(3).hex())
+  device_file = device_dir + fname
+
+  cmd = [perfetto_cmd, '--background', '--txt', '-o', device_file]
   if args.config is not None:
     cmd += ['-c', '-']
   else:
     cmd += ['-t', args.time, '-b', args.buffer]
     for app in args.app:
-      cmd += ['--app', app]
+      cmd += ['--app', '\'' + app + '\'']
     cmd += args.events
 
   # Perfetto will error out with a proper message if both a config file and
@@ -235,7 +314,7 @@
   socketserver.TCPServer.allow_reuse_address = True
   with socketserver.TCPServer(('127.0.0.1', PORT), HttpHandler) as httpd:
     webbrowser.open_new_tab(
-            'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' %
+        'https://ui.perfetto.dev/#!/?url=http://127.0.0.1:%d/%s' %
         (PORT, fname))
     while httpd.__dict__.get('last_request') != '/' + fname:
       httpd.handle_request()
@@ -258,5 +337,32 @@
     p.kill()
 
 
+def check_hash(file_name, sha_value):
+  with open(file_name, 'rb') as fd:
+    file_hash = hashlib.sha1(fd.read()).hexdigest()
+    return file_hash == sha_value
+
+
+def download_tracebox_if_needed(arch):
+  sha_value = TRACEBOX_SHA1S.get('android-' + arch)
+  if sha_value is None:
+    prt('Unsupported architecture ' + arch)
+    sys.exit(1)
+  file_name = 'tracebox-android-' + arch + '-' + sha_value
+  local_file = os.path.join(tempfile.gettempdir(), file_name)
+  if os.path.exists(local_file):
+    if not check_hash(local_file, sha_value):
+      os.remove(local_file)
+    else:
+      return local_file
+  url = TRACEBOX_BASE_URL + file_name
+  prt('Downloading %s' % url)
+  subprocess.check_call(['curl', '-L', '-#', '-o', local_file, url])
+  if not check_hash(local_file, sha_value):
+    os.remove(local_file)
+    raise ValueError('Invalid SHA1 for %s' % local_file)
+  return local_file
+
+
 if __name__ == '__main__':
   sys.exit(main())
diff --git a/tools/update_tracebox b/tools/update_tracebox
new file mode 100755
index 0000000..0836b25
--- /dev/null
+++ b/tools/update_tracebox
@@ -0,0 +1,76 @@
+#!/bin/bash
+# Copyright (C) 2021 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.
+
+set -e
+
+# cd into the repo root so all commands are independent of the cwd.
+cd -P ${BASH_SOURCE[0]%/*}/..
+
+GN_ARGS="$GN_ARGS"  # Allow to expand GN_ARGS from env for ccache.
+
+set -u
+
+DIR=out/tmp_tracebox
+mkdir -p $DIR
+
+function cleanup {
+  rm -rf "$DIR"
+  echo "Deleted temp working directory $DIR"
+}
+
+#trap cleanup EXIT
+
+function is_mac {
+  ! test -d /proc
+  return $?
+}
+
+VERSION="$(tools/write_version_header.py --stdout)"
+
+# Allow overriding the build targets via the cmdline (for testing).
+if [ "$#" -gt 0 ]; then
+  COMBOS="$@"
+else
+  COMBOS="android-arm android-arm64 android-x86 android-x64"
+fi
+
+for COMBO in $COMBOS; do
+  IFS=- read PLATFORM ARCH <<< "$COMBO"
+  echo "Building for $COMBO"
+  rm -rf $DIR
+  mkdir -p $DIR
+  GN_ARGS="$GN_ARGS is_debug=false monolithic_binaries = true"
+  GN_ARGS="$GN_ARGS target_os = \"$PLATFORM\" target_cpu = \"$ARCH\""
+  set -x
+  tools/gn gen $DIR --args="$GN_ARGS"
+  tools/ninja -C $DIR tracebox
+  set +x
+  BINARY=$DIR/stripped/tracebox
+if which shasum; then
+  NEW_SHA=$(shasum $BINARY | cut -f1 -d' ') # Mac OS
+else
+  NEW_SHA=$(sha1sum $BINARY | cut -f1 -d' ') # Linux
+fi
+  FULL_NAME=tracebox-$COMBO-$NEW_SHA
+
+  set -x
+  gsutil cp -n -a public-read $BINARY gs://perfetto/$FULL_NAME
+  sed -e \
+      "s/'$COMBO': '.*/'$COMBO': '$NEW_SHA',  # $VERSION/" \
+      -i .tmp tools/record_android_trace
+  set +x
+  rm -f tools/record_android_trace.tmp
+  echo ""
+done