Merge "proto_merger: Fix some typos"
diff --git a/Android.bp b/Android.bp
index ad54f8d..92728f9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1005,6 +1005,7 @@
         ":perfetto_src_base_version",
         ":perfetto_src_ipc_client",
         ":perfetto_src_ipc_common",
+        ":perfetto_src_perfetto_cmd_bugreport_path",
         ":perfetto_src_perfetto_cmd_perfetto_cmd",
         ":perfetto_src_perfetto_cmd_protos_cpp_gen",
         ":perfetto_src_perfetto_cmd_trigger_producer",
@@ -1191,6 +1192,7 @@
         ":perfetto_src_ipc_perfetto_ipc",
         ":perfetto_src_kallsyms_kallsyms",
         ":perfetto_src_kernel_utils_syscall_table",
+        ":perfetto_src_perfetto_cmd_bugreport_path",
         ":perfetto_src_protozero_filtering_bytecode_common",
         ":perfetto_src_protozero_filtering_bytecode_generator",
         ":perfetto_src_protozero_filtering_bytecode_parser",
@@ -1990,6 +1992,7 @@
         ":perfetto_src_ipc_perfetto_ipc",
         ":perfetto_src_kallsyms_kallsyms",
         ":perfetto_src_kernel_utils_syscall_table",
+        ":perfetto_src_perfetto_cmd_bugreport_path",
         ":perfetto_src_profiling_common_callstack_trie",
         ":perfetto_src_profiling_common_interner",
         ":perfetto_src_profiling_common_interning_output",
@@ -8467,6 +8470,11 @@
     ],
 }
 
+// GN: //src/perfetto_cmd:bugreport_path
+filegroup {
+    name: "perfetto_src_perfetto_cmd_bugreport_path",
+}
+
 // GN: //src/perfetto_cmd:gen_cc_config_descriptor
 genrule {
     name: "perfetto_src_perfetto_cmd_gen_cc_config_descriptor",
@@ -8688,6 +8696,7 @@
     name: "perfetto_src_profiling_memory_end_to_end_tests",
     srcs: [
         "src/profiling/memory/heapprofd_end_to_end_test.cc",
+        "src/profiling/memory/heapprofd_producer_integrationtest.cc",
     ],
 }
 
@@ -9625,6 +9634,7 @@
         "src/trace_processor/importers/proto/active_chrome_processes_tracker_unittest.cc",
         "src/trace_processor/importers/proto/heap_graph_tracker_unittest.cc",
         "src/trace_processor/importers/proto/heap_profile_tracker_unittest.cc",
+        "src/trace_processor/importers/proto/network_trace_module_unittest.cc",
         "src/trace_processor/importers/proto/perf_sample_tracker_unittest.cc",
         "src/trace_processor/importers/proto/proto_trace_parser_unittest.cc",
     ],
@@ -11623,6 +11633,7 @@
         ":perfetto_src_kallsyms_kallsyms",
         ":perfetto_src_kallsyms_unittests",
         ":perfetto_src_kernel_utils_syscall_table",
+        ":perfetto_src_perfetto_cmd_bugreport_path",
         ":perfetto_src_perfetto_cmd_perfetto_cmd",
         ":perfetto_src_perfetto_cmd_protos_cpp_gen",
         ":perfetto_src_perfetto_cmd_trigger_producer",
diff --git a/BUILD b/BUILD
index e5d3c9b..cb6b454 100644
--- a/BUILD
+++ b/BUILD
@@ -906,6 +906,14 @@
     ],
 )
 
+# GN target: //src/perfetto_cmd:bugreport_path
+perfetto_filegroup(
+    name = "src_perfetto_cmd_bugreport_path",
+    srcs = [
+        "src/perfetto_cmd/bugreport_path.h",
+    ],
+)
+
 # GN target: //src/perfetto_cmd:gen_cc_config_descriptor
 perfetto_cc_proto_descriptor(
     name = "src_perfetto_cmd_gen_cc_config_descriptor",
@@ -4537,6 +4545,7 @@
         ":include_perfetto_tracing_tracing",
         ":src_android_stats_android_stats",
         ":src_android_stats_perfetto_atoms",
+        ":src_perfetto_cmd_bugreport_path",
         ":src_perfetto_cmd_perfetto_cmd",
         ":src_perfetto_cmd_trigger_producer",
         ":src_tracing_common",
diff --git a/include/perfetto/ext/tracing/core/basic_types.h b/include/perfetto/ext/tracing/core/basic_types.h
index ac75b4d..0950fe8 100644
--- a/include/perfetto/ext/tracing/core/basic_types.h
+++ b/include/perfetto/ext/tracing/core/basic_types.h
@@ -76,6 +76,11 @@
 
 constexpr uint32_t kDefaultFlushTimeoutMs = 5000;
 
+// The special id 0xffff..ffff represents the tracing session with the highest
+// bugreport score. This is used for CloneSession(kBugreportSessionId).
+constexpr TracingSessionID kBugreportSessionId =
+    static_cast<TracingSessionID>(-1);
+
 }  // namespace perfetto
 
 #endif  // INCLUDE_PERFETTO_EXT_TRACING_CORE_BASIC_TYPES_H_
diff --git a/include/perfetto/ext/tracing/core/tracing_service.h b/include/perfetto/ext/tracing/core/tracing_service.h
index 68dc086..82e53f7 100644
--- a/include/perfetto/ext/tracing/core/tracing_service.h
+++ b/include/perfetto/ext/tracing/core/tracing_service.h
@@ -42,9 +42,6 @@
 class SharedMemoryArbiter;
 class TraceWriter;
 
-// Exposed for testing.
-std::string GetBugreportPath();
-
 // TODO: for the moment this assumes that all the calls happen on the same
 // thread/sequence. Not sure this will be the case long term in Chrome.
 
@@ -192,6 +189,8 @@
   // Clones an existing tracing session and attaches to it. The session is
   // cloned in read-only mode and can only be used to read a snapshot of an
   // existing tracing session. Will invoke Consumer::OnSessionCloned().
+  // If TracingSessionID == kBugreportSessionId (0xff...ff) the session with the
+  // highest bugreport score is cloned (if any exists).
   // TODO(primiano): make pure virtual after various 3way patches.
   virtual void CloneSession(TracingSessionID);
 
diff --git a/protos/perfetto/config/chrome/BUILD.gn b/protos/perfetto/config/chrome/BUILD.gn
index 4f44718..4397cfc 100644
--- a/protos/perfetto/config/chrome/BUILD.gn
+++ b/protos/perfetto/config/chrome/BUILD.gn
@@ -15,6 +15,8 @@
 import("../../../../gn/perfetto.gni")
 import("../../../../gn/proto_library.gni")
 
-perfetto_proto_library("@TYPE@") {
-  sources = [ "chrome_config.proto" ]
+perfetto_proto_library("scenario_descriptor") {
+  proto_generators = [ "descriptor" ]
+  generate_descriptor = "scenario_config.descriptor"
+  sources = [ "scenario_config.proto" ]
 }
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index 03f9d8a..2eb9d1a 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -868,7 +868,7 @@
 // Begin of protos/perfetto/config/profiling/heapprofd_config.proto
 
 // Configuration for go/heapprofd.
-// Next id: 27
+// Next id: 28
 message HeapprofdConfig {
   message ContinuousDumpConfig {
     // ms to wait before first dump.
diff --git a/protos/perfetto/config/profiling/heapprofd_config.proto b/protos/perfetto/config/profiling/heapprofd_config.proto
index 2636e06..bb0a700 100644
--- a/protos/perfetto/config/profiling/heapprofd_config.proto
+++ b/protos/perfetto/config/profiling/heapprofd_config.proto
@@ -19,7 +19,7 @@
 package perfetto.protos;
 
 // Configuration for go/heapprofd.
-// Next id: 27
+// Next id: 28
 message HeapprofdConfig {
   message ContinuousDumpConfig {
     // ms to wait before first dump.
diff --git a/protos/perfetto/ipc/consumer_port.proto b/protos/perfetto/ipc/consumer_port.proto
index aa7fc93..d5a4025 100644
--- a/protos/perfetto/ipc/consumer_port.proto
+++ b/protos/perfetto/ipc/consumer_port.proto
@@ -270,7 +270,7 @@
 // or something failed.
 message SaveTraceForBugreportResponse {
   // If true, an eligible the trace was saved into a known location (on Android
-  // /data/misc/perfetto-traces, see GetBugreportPath()).
+  // /data/misc/perfetto-traces, see GetBugreportTracePath()).
   // If false no trace with bugreport_score > 0 was found or an error occurred.
   // see |msg| in that case for details about the failure.
   optional bool success = 1;
@@ -279,6 +279,8 @@
 
 // Arguments for rpc CloneSession.
 message CloneSessionRequest {
+  // The session ID to clone. If session_id == kBugreportSessionId (0xff...ff)
+  // the session with the highest bugreport score is cloned (if any exists).
   optional uint64 session_id = 1;
 }
 
diff --git a/protos/perfetto/trace/perfetto/tracing_service_event.proto b/protos/perfetto/trace/perfetto/tracing_service_event.proto
index 04923dc..0573be0 100644
--- a/protos/perfetto/trace/perfetto/tracing_service_event.proto
+++ b/protos/perfetto/trace/perfetto/tracing_service_event.proto
@@ -60,6 +60,8 @@
     // the contents are routed onto the bugreport file. This event flags the
     // situation explicitly. Traces that contain this marker should be discarded
     // by test infrastructures / pipelines.
+    // Deprecated since Android U, where --save-for-bugreport uses
+    // non-destructive cloning.
     bool seized_for_bugreport = 6;
   }
 }
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index 66055fa..3efbb44 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -868,7 +868,7 @@
 // Begin of protos/perfetto/config/profiling/heapprofd_config.proto
 
 // Configuration for go/heapprofd.
-// Next id: 27
+// Next id: 28
 message HeapprofdConfig {
   message ContinuousDumpConfig {
     // ms to wait before first dump.
@@ -10372,6 +10372,8 @@
     // the contents are routed onto the bugreport file. This event flags the
     // situation explicitly. Traces that contain this marker should be discarded
     // by test infrastructures / pipelines.
+    // Deprecated since Android U, where --save-for-bugreport uses
+    // non-destructive cloning.
     bool seized_for_bugreport = 6;
   }
 }
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 70c47fc..51486c0 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -49,6 +49,7 @@
     "../../include/perfetto/ext/traced",
   ]
   deps = [
+    ":bugreport_path",
     ":gen_cc_config_descriptor",
     ":trigger_producer",
     "../../gn:default_deps",
@@ -83,6 +84,15 @@
   }
 }
 
+source_set("bugreport_path") {
+  sources = [ "bugreport_path.h" ]
+  deps = [ "../../gn:default_deps" ]
+  public_deps = [
+    "../../include/perfetto/base",
+    "../../include/perfetto/ext/base",
+  ]
+}
+
 perfetto_cc_proto_descriptor("gen_cc_config_descriptor") {
   descriptor_name = "config.descriptor"
   descriptor_target = "../../protos/perfetto/config:descriptor"
diff --git a/src/perfetto_cmd/bugreport_path.h b/src/perfetto_cmd/bugreport_path.h
new file mode 100644
index 0000000..5bc7602
--- /dev/null
+++ b/src/perfetto_cmd/bugreport_path.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_PERFETTO_CMD_BUGREPORT_PATH_H_
+#define SRC_PERFETTO_CMD_BUGREPORT_PATH_H_
+
+#include <string>
+
+#include "perfetto/base/build_config.h"
+#include "perfetto/ext/base/temp_file.h"
+
+namespace perfetto {
+
+// Expose for testing
+inline std::string GetBugreportTracePath() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
+    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+  return "/data/misc/perfetto-traces/bugreport/systrace.pftrace";
+#else
+  // Only for tests, SaveTraceForBugreport is not used on other OSes.
+  return base::GetSysTempDir() + "/bugreport.pftrace";
+#endif
+}
+
+}  // namespace perfetto
+
+#endif  // SRC_PERFETTO_CMD_BUGREPORT_PATH_H_
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 5818c59..a0234b8 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -51,6 +51,7 @@
 #include "perfetto/ext/base/getopt.h"
 #include "perfetto/ext/base/pipe.h"
 #include "perfetto/ext/base/string_view.h"
+#include "perfetto/ext/base/temp_file.h"
 #include "perfetto/ext/base/thread_utils.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/base/uuid.h"
@@ -64,9 +65,11 @@
 #include "perfetto/tracing/core/trace_config.h"
 #include "perfetto/tracing/core/tracing_service_state.h"
 #include "src/android_stats/statsd_logging_helper.h"
+#include "src/perfetto_cmd/bugreport_path.h"
 #include "src/perfetto_cmd/config.h"
 #include "src/perfetto_cmd/packet_writer.h"
 #include "src/perfetto_cmd/pbtxt_to_pb.h"
+#include "src/perfetto_cmd/rate_limiter.h"
 #include "src/perfetto_cmd/trigger_producer.h"
 
 #include "protos/perfetto/common/ftrace_descriptor.gen.h"
@@ -78,7 +81,8 @@
 
 perfetto::PerfettoCmd* g_perfetto_cmd;
 
-uint32_t kOnTraceDataTimeoutMs = 3000;
+const uint32_t kOnTraceDataTimeoutMs = 3000;
+const uint32_t kCloneTimeoutMs = 10000;
 
 class LoggingErrorReporter : public ErrorReporter {
  public:
@@ -552,6 +556,13 @@
     return 1;
   }
 
+  // --save-for-bugreport is the equivalent of:
+  // --clone kBugreportSessionId -o /data/misc/perfetto-traces/bugreport/...
+  if (bugreport_ && trace_out_path_.empty()) {
+    clone_tsid_ = kBugreportSessionId;
+    trace_out_path_ = GetBugreportTracePath();
+  }
+
   // Parse the trace config. It can be either:
   // 1) A proto-encoded file/stdin (-c ...).
   // 2) A proto-text file/stdin (-c ... --txt).
@@ -561,8 +572,7 @@
   trace_config_.reset(new TraceConfig());
 
   bool parsed = false;
-  const bool will_trace_or_trigger =
-      !is_attach() && !query_service_ && !bugreport_;
+  const bool will_trace_or_trigger = !is_attach() && !query_service_;
   if (!will_trace_or_trigger || clone_tsid_) {
     if ((!trace_config_raw.empty() || has_config_options)) {
       PERFETTO_ELOG("Cannot specify a trace config with this option");
@@ -904,12 +914,12 @@
     return finished_with_success ? 0 : 1;
   }  // if (triggers_to_activate_)
 
-  if (query_service_ || bugreport_) {
+  if (query_service_) {
     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();
@@ -997,25 +1007,14 @@
     return;
   }
 
-  if (bugreport_) {
-    consumer_endpoint_->SaveTraceForBugreport(
-        [](bool success, const std::string& msg) {
-          if (success) {
-            PERFETTO_ILOG("Trace saved into %s", msg.c_str());
-            exit(0);
-          }
-          PERFETTO_ELOG("%s", msg.c_str());
-          exit(1);
-        });
-    return;
-  }
-
   if (is_attach()) {
     consumer_endpoint_->Attach(attach_key_);
     return;
   }
 
   if (clone_tsid_.has_value()) {
+    task_runner_.PostDelayedTask(std::bind(&PerfettoCmd::OnTimeout, this),
+                                 kCloneTimeoutMs);
     consumer_endpoint_->CloneSession(*clone_tsid_);
     return;
   }
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 842ddcc..bd4a8c4 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -33,7 +33,6 @@
 #include "perfetto/ext/tracing/core/consumer.h"
 #include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
 #include "src/android_stats/perfetto_atoms.h"
-#include "src/perfetto_cmd/rate_limiter.h"
 
 namespace perfetto {
 
diff --git a/src/profiling/memory/BUILD.gn b/src/profiling/memory/BUILD.gn
index 51deba3..817c5b8 100644
--- a/src/profiling/memory/BUILD.gn
+++ b/src/profiling/memory/BUILD.gn
@@ -358,8 +358,12 @@
     "../../base",
     "../../base:test_support",
     "../../trace_processor:lib",
+    "../../tracing/test:test_support",
   ]
-  sources = [ "heapprofd_end_to_end_test.cc" ]
+  sources = [
+    "heapprofd_end_to_end_test.cc",
+    "heapprofd_producer_integrationtest.cc",
+  ]
   if (perfetto_build_with_android) {
     deps += [ ":heapprofd_client_api" ]
   } else {
diff --git a/src/profiling/memory/heapprofd_producer_integrationtest.cc b/src/profiling/memory/heapprofd_producer_integrationtest.cc
new file mode 100644
index 0000000..077a539
--- /dev/null
+++ b/src/profiling/memory/heapprofd_producer_integrationtest.cc
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2022 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_producer.h"
+
+#include "perfetto/base/proc_utils.h"
+#include "perfetto/ext/base/thread_task_runner.h"
+#include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
+#include "perfetto/ext/tracing/ipc/service_ipc_host.h"
+#include "protos/perfetto/common/data_source_descriptor.gen.h"
+#include "protos/perfetto/config/trace_config.gen.h"
+#include "src/base/test/test_task_runner.h"
+#include "src/base/test/tmp_dir_tree.h"
+#include "src/profiling/memory/client.h"
+#include "src/profiling/memory/unhooked_allocator.h"
+#include "src/tracing/test/mock_consumer.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace profiling {
+namespace {
+
+using ::testing::NiceMock;
+using ::testing::NotNull;
+
+class TracingServiceThread {
+ public:
+  TracingServiceThread(const std::string& producer_socket,
+                       const std::string& consumer_socket)
+      : runner_(base::ThreadTaskRunner::CreateAndStart("perfetto.svc")),
+        producer_socket_(producer_socket),
+        consumer_socket_(consumer_socket) {
+    runner_.PostTaskAndWaitForTesting([this]() {
+      svc_ = ServiceIPCHost::CreateInstance(&runner_);
+      bool res =
+          svc_->Start(producer_socket_.c_str(), consumer_socket_.c_str());
+      if (!res) {
+        PERFETTO_FATAL("Failed to start service listening on %s and %s",
+                       producer_socket_.c_str(), consumer_socket_.c_str());
+      }
+    });
+  }
+
+  ~TracingServiceThread() {
+    runner_.PostTaskAndWaitForTesting([this]() { svc_.reset(); });
+  }
+
+  const std::string& producer_socket() const { return producer_socket_; }
+  const std::string& consumer_socket() const { return consumer_socket_; }
+
+ private:
+  base::ThreadTaskRunner runner_;
+
+  std::string producer_socket_;
+  std::string consumer_socket_;
+  std::unique_ptr<ServiceIPCHost> svc_;
+};
+
+class HeapprofdThread {
+ public:
+  HeapprofdThread(const std::string& producer_socket,
+                  const std::string& heapprofd_socket)
+      : runner_(base::ThreadTaskRunner::CreateAndStart("heapprofd.svc")),
+        producer_socket_(producer_socket),
+        heapprofd_socket_(heapprofd_socket) {
+    runner_.PostTaskAndWaitForTesting([this]() {
+      heapprofd_.reset(new HeapprofdProducer(HeapprofdMode::kCentral, &runner_,
+                                             /* exit_when_done= */ false));
+
+      heapprofd_->ConnectWithRetries(producer_socket_.c_str());
+      listen_sock_ = base::UnixSocket::Listen(
+          heapprofd_socket_.c_str(), &heapprofd_->socket_delegate(), &runner_,
+          base::SockFamily::kUnix, base::SockType::kStream);
+      EXPECT_THAT(listen_sock_, NotNull());
+    });
+  }
+
+  void Sync() {
+    runner_.PostTaskAndWaitForTesting([]() {});
+  }
+
+  ~HeapprofdThread() {
+    runner_.PostTaskAndWaitForTesting([this]() {
+      listen_sock_.reset();
+      heapprofd_.reset();
+    });
+  }
+
+  const std::string& producer_socket() const { return producer_socket_; }
+  const std::string& heapprofd_socket() const { return heapprofd_socket_; }
+
+ private:
+  base::ThreadTaskRunner runner_;
+
+  std::string producer_socket_;
+  std::string heapprofd_socket_;
+  std::unique_ptr<HeapprofdProducer> heapprofd_;
+  std::unique_ptr<base::UnixSocket> listen_sock_;
+};
+
+TraceConfig MakeTraceConfig() {
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(10 * 1024);
+  trace_config.set_data_source_stop_timeout_ms(10000);
+
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.heapprofd");
+  ds_config->set_target_buffer(0);
+
+  protos::gen::HeapprofdConfig heapprofd_config;
+  heapprofd_config.set_sampling_interval_bytes(1);
+  heapprofd_config.add_pid(static_cast<uint64_t>(base::GetProcessId()));
+  heapprofd_config.set_all_heaps(true);
+  heapprofd_config.set_no_startup(true);
+  heapprofd_config.set_no_running(true);
+  ds_config->set_heapprofd_config_raw(heapprofd_config.SerializeAsString());
+  return trace_config;
+}
+
+bool WaitFor(std::function<bool()> predicate, long long timeout_ms = 40000) {
+  long long deadline_ms = base::GetWallTimeMs().count() + timeout_ms;
+  while (base::GetWallTimeMs().count() < deadline_ms) {
+    if (predicate())
+      return true;
+    base::SleepMicroseconds(100 * 1000);  // 0.1 s.
+  }
+  return false;
+}
+
+bool WaitForDsRegistered(MockConsumer* mock_consumer,
+                         const std::string& ds_name) {
+  return WaitFor([mock_consumer, &ds_name]() {
+    auto dss = mock_consumer->QueryServiceState().data_sources();
+    return std::any_of(dss.begin(), dss.end(),
+                       [&](const TracingServiceState::DataSource& ds) {
+                         return ds.ds_descriptor().name() == ds_name;
+                       });
+  });
+}
+
+// Waits for the heapprofd data source to be registered and starts a trace with
+// it.
+std::unique_ptr<NiceMock<MockConsumer>> StartHeapprofdTrace(
+    const std::string& consumer_socket,
+    base::TestTaskRunner* task_runner) {
+  std::unique_ptr<NiceMock<MockConsumer>> mock_consumer;
+  mock_consumer.reset(new NiceMock<MockConsumer>(task_runner));
+  mock_consumer->Connect(ConsumerIPCClient::Connect(
+      consumer_socket.c_str(), mock_consumer.get(), task_runner));
+
+  if (WaitForDsRegistered(mock_consumer.get(), "android.heapprofd") == false) {
+    ADD_FAILURE();
+    return nullptr;
+  }
+
+  mock_consumer->ObserveEvents(ObservableEvents::TYPE_ALL_DATA_SOURCES_STARTED);
+  mock_consumer->EnableTracing(MakeTraceConfig());
+  mock_consumer->WaitForObservableEvents();
+
+  return mock_consumer;
+}
+
+TEST(HeapprofdProducerIntegrationTest, Restart) {
+  base::TmpDirTree tmpdir_;
+
+  base::TestTaskRunner task_runner;
+
+  tmpdir_.TrackFile("producer.sock");
+  tmpdir_.TrackFile("consumer.sock");
+
+  base::Optional<TracingServiceThread> tracing_service;
+  tracing_service.emplace(tmpdir_.AbsolutePath("producer.sock"),
+                          tmpdir_.AbsolutePath("consumer.sock"));
+
+  tmpdir_.TrackFile("heapprofd.sock");
+  HeapprofdThread heapprofd_service(tmpdir_.AbsolutePath("producer.sock"),
+                                    tmpdir_.AbsolutePath("heapprofd.sock"));
+
+  std::unique_ptr<NiceMock<MockConsumer>> consumer =
+      StartHeapprofdTrace(tmpdir_.AbsolutePath("consumer.sock"), &task_runner);
+  ASSERT_THAT(consumer, NotNull());
+
+  base::Optional<base::UnixSocketRaw> client_sock =
+      perfetto::profiling::Client::ConnectToHeapprofd(
+          tmpdir_.AbsolutePath("heapprofd.sock"));
+  ASSERT_TRUE(client_sock.has_value());
+
+  std::shared_ptr<Client> client =
+      perfetto::profiling::Client::CreateAndHandshake(
+          std::move(client_sock.value()),
+          UnhookedAllocator<perfetto::profiling::Client>(malloc, free));
+
+  // Shutdown tracing service. This should cause HeapprofdProducer::Restart() to
+  // be executed on the heapprofd thread.
+  tracing_service.reset();
+  // Wait for the effects of the tracing service disconnect to propagate to the
+  // heapprofd thread.
+  heapprofd_service.Sync();
+
+  consumer->ForceDisconnect();
+  consumer.reset();
+
+  task_runner.RunUntilIdle();
+
+  // Start tracing service again. Heapprofd should reconnect.
+  ASSERT_EQ(remove(tmpdir_.AbsolutePath("producer.sock").c_str()), 0);
+  ASSERT_EQ(remove(tmpdir_.AbsolutePath("consumer.sock").c_str()), 0);
+  tracing_service.emplace(tmpdir_.AbsolutePath("producer.sock"),
+                          tmpdir_.AbsolutePath("consumer.sock"));
+
+  consumer =
+      StartHeapprofdTrace(tmpdir_.AbsolutePath("consumer.sock"), &task_runner);
+  ASSERT_THAT(consumer, NotNull());
+
+  consumer->ForceDisconnect();
+  consumer.reset();
+}
+
+}  // namespace
+}  // namespace profiling
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn
index 47bf6c7..b52254d 100644
--- a/src/trace_processor/importers/proto/BUILD.gn
+++ b/src/trace_processor/importers/proto/BUILD.gn
@@ -236,6 +236,7 @@
     "active_chrome_processes_tracker_unittest.cc",
     "heap_graph_tracker_unittest.cc",
     "heap_profile_tracker_unittest.cc",
+    "network_trace_module_unittest.cc",
     "perf_sample_tracker_unittest.cc",
     "proto_trace_parser_unittest.cc",
   ]
diff --git a/src/trace_processor/importers/proto/network_trace_module.cc b/src/trace_processor/importers/proto/network_trace_module.cc
index ab56358..1ea1ca7 100644
--- a/src/trace_processor/importers/proto/network_trace_module.cc
+++ b/src/trace_processor/importers/proto/network_trace_module.cc
@@ -17,7 +17,6 @@
 #include "src/trace_processor/importers/proto/network_trace_module.h"
 
 #include "perfetto/ext/base/string_writer.h"
-#include "protos/perfetto/trace/android/network_trace.pbzero.h"
 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
 #include "src/trace_processor/importers/common/async_track_set_tracker.h"
@@ -61,7 +60,8 @@
       net_arg_local_port_(context->storage->InternString("local_port")),
       net_arg_remote_port_(context->storage->InternString("remote_port")),
       net_ipproto_tcp_(context->storage->InternString("IPPROTO_TCP")),
-      net_ipproto_udp_(context->storage->InternString("IPPROTO_UDP")) {
+      net_ipproto_udp_(context->storage->InternString("IPPROTO_UDP")),
+      packet_count_(context->storage->InternString("packet_count")) {
   RegisterForField(TracePacket::kNetworkPacketFieldNumber, context);
   RegisterForField(TracePacket::kNetworkPacketBundleFieldNumber, context);
 }
@@ -132,14 +132,17 @@
     case TracePacket::kNetworkPacketFieldNumber:
       ParseNetworkPacketEvent(ts, decoder.network_packet());
       return;
+    case TracePacket::kNetworkPacketBundleFieldNumber:
+      ParseNetworkPacketBundle(ts, decoder.network_packet_bundle());
+      return;
   }
 }
 
-void NetworkTraceModule::ParseNetworkPacketEvent(int64_t ts, ConstBytes blob) {
-  using protos::pbzero::NetworkPacketEvent;
-  using protos::pbzero::TrafficDirection;
-  NetworkPacketEvent::Decoder evt(blob);
-
+void NetworkTraceModule::ParseGenericEvent(
+    int64_t ts,
+    int64_t dur,
+    protos::pbzero::NetworkPacketEvent::Decoder& evt,
+    std::function<void(ArgsTracker::BoundInserter*)> extra_args) {
   // Tracks are per interface and per direction.
   const char* track_suffix =
       evt.direction() == TrafficDirection::DIR_INGRESS  ? " Received"
@@ -167,12 +170,11 @@
   }
 
   TrackId track_id = context_->async_track_set_tracker->Scoped(
-      context_->async_track_set_tracker->InternGlobalTrackSet(name_id), ts, 0);
+      context_->async_track_set_tracker->InternGlobalTrackSet(name_id), ts,
+      dur);
 
   context_->slice_tracker->Scoped(
-      ts, track_id, name_id, title_id, 0, [&](ArgsTracker::BoundInserter* i) {
-        i->AddArg(net_arg_length_, Variadic::Integer(evt.length()));
-
+      ts, track_id, name_id, title_id, dur, [&](ArgsTracker::BoundInserter* i) {
         StringId ip_proto;
         if (evt.ip_proto() == kIpprotoTcp) {
           ip_proto = net_ipproto_tcp_;
@@ -197,9 +199,30 @@
 
         i->AddArg(net_arg_local_port_, Variadic::Integer(evt.local_port()));
         i->AddArg(net_arg_remote_port_, Variadic::Integer(evt.remote_port()));
+        extra_args(i);
       });
 }
 
+void NetworkTraceModule::ParseNetworkPacketEvent(int64_t ts, ConstBytes blob) {
+  NetworkPacketEvent::Decoder event(blob);
+  ParseGenericEvent(ts, /*dur=*/0, event, [&](ArgsTracker::BoundInserter* i) {
+    i->AddArg(net_arg_length_, Variadic::Integer(event.length()));
+  });
+}
+
+void NetworkTraceModule::ParseNetworkPacketBundle(int64_t ts, ConstBytes blob) {
+  NetworkPacketBundle::Decoder event(blob);
+  NetworkPacketEvent::Decoder ctx(event.ctx());
+  int64_t dur = static_cast<int64_t>(event.total_duration());
+
+  // Any bundle that makes it through tokenization must be aggregated bundles
+  // with total packets/total length.
+  ParseGenericEvent(ts, dur, ctx, [&](ArgsTracker::BoundInserter* i) {
+    i->AddArg(net_arg_length_, Variadic::UnsignedInteger(event.total_length()));
+    i->AddArg(packet_count_, Variadic::UnsignedInteger(event.total_packets()));
+  });
+}
+
 void NetworkTraceModule::PushPacketBufferForSort(int64_t timestamp,
                                                  PacketSequenceState* state) {
   std::vector<uint8_t> v = packet_buffer_.SerializeAsArray();
diff --git a/src/trace_processor/importers/proto/network_trace_module.h b/src/trace_processor/importers/proto/network_trace_module.h
index 843f055..e83ce7b 100644
--- a/src/trace_processor/importers/proto/network_trace_module.h
+++ b/src/trace_processor/importers/proto/network_trace_module.h
@@ -20,7 +20,9 @@
 #include <cstdint>
 
 #include "perfetto/protozero/scattered_heap_buffer.h"
+#include "protos/perfetto/trace/android/network_trace.pbzero.h"
 #include "protos/perfetto/trace/trace_packet.pbzero.h"
+#include "src/trace_processor/importers/common/args_tracker.h"
 #include "src/trace_processor/importers/common/parser_types.h"
 #include "src/trace_processor/importers/proto/proto_importer_module.h"
 #include "src/trace_processor/storage/trace_storage.h"
@@ -52,7 +54,14 @@
                             uint32_t field_id) override;
 
  private:
+  void ParseGenericEvent(
+      int64_t ts,
+      int64_t dur,
+      protos::pbzero::NetworkPacketEvent::Decoder& evt,
+      std::function<void(ArgsTracker::BoundInserter*)> extra_args);
+
   void ParseNetworkPacketEvent(int64_t ts, protozero::ConstBytes blob);
+  void ParseNetworkPacketBundle(int64_t ts, protozero::ConstBytes blob);
 
   // Helper to simplify pushing a TracePacket to the sorter. The caller fills in
   // the packet buffer and uses this to push for sorting and reset the buffer.
@@ -69,6 +78,7 @@
   const StringId net_arg_remote_port_;
   const StringId net_ipproto_tcp_;
   const StringId net_ipproto_udp_;
+  const StringId packet_count_;
 };
 
 }  // namespace trace_processor
diff --git a/src/trace_processor/importers/proto/network_trace_module_unittest.cc b/src/trace_processor/importers/proto/network_trace_module_unittest.cc
new file mode 100644
index 0000000..56118c5
--- /dev/null
+++ b/src/trace_processor/importers/proto/network_trace_module_unittest.cc
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/importers/proto/network_trace_module.h"
+
+#include "src/trace_processor/importers/common/args_tracker.h"
+#include "src/trace_processor/importers/common/args_translation_table.h"
+#include "src/trace_processor/importers/common/async_track_set_tracker.h"
+#include "src/trace_processor/importers/common/global_args_tracker.h"
+#include "src/trace_processor/importers/common/slice_tracker.h"
+#include "src/trace_processor/importers/common/slice_translation_table.h"
+#include "src/trace_processor/importers/common/track_tracker.h"
+#include "src/trace_processor/importers/proto/proto_trace_parser.h"
+#include "src/trace_processor/importers/proto/proto_trace_reader.h"
+#include "src/trace_processor/sorter/trace_sorter.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+using ::perfetto::protos::pbzero::TrafficDirection;
+
+class NetworkTraceModuleTest : public testing::Test {
+ public:
+  NetworkTraceModuleTest() {
+    context_.storage.reset(new TraceStorage());
+    storage_ = context_.storage.get();
+
+    context_.track_tracker.reset(new TrackTracker(&context_));
+    context_.slice_tracker.reset(new SliceTracker(&context_));
+    context_.args_tracker.reset(new ArgsTracker(&context_));
+    context_.global_args_tracker.reset(new GlobalArgsTracker(storage_));
+    context_.slice_translation_table.reset(new SliceTranslationTable(storage_));
+    context_.args_translation_table.reset(new ArgsTranslationTable(storage_));
+    context_.async_track_set_tracker.reset(new AsyncTrackSetTracker(&context_));
+    context_.sorter.reset(new TraceSorter(
+        &context_, std::make_unique<ProtoTraceParser>(&context_),
+        TraceSorter::SortingMode::kFullSort));
+  }
+
+  util::Status TokenizeAndParse() {
+    context_.chunk_reader.reset(new ProtoTraceReader(&context_));
+
+    trace_->Finalize();
+    std::vector<uint8_t> v = trace_.SerializeAsArray();
+    trace_.Reset();
+
+    auto status = context_.chunk_reader->Parse(
+        TraceBlobView(TraceBlob::CopyFrom(v.data(), v.size())));
+    context_.sorter->ExtractEventsForced();
+    context_.slice_tracker->FlushPendingSlices();
+    context_.args_tracker->Flush();
+    return status;
+  }
+
+  bool HasArg(ArgSetId set_id, base::StringView key, Variadic value) {
+    StringId key_id = storage_->InternString(key);
+    const auto& args = storage_->arg_table();
+    RowMap rm = args.FilterToRowMap({args.arg_set_id().eq(set_id)});
+    bool found = false;
+    for (auto it = rm.IterateRows(); it; it.Next()) {
+      if (args.key()[it.index()] == key_id) {
+        EXPECT_EQ(args.flat_key()[it.index()], key_id);
+        if (storage_->GetArgValue(it.index()) == value) {
+          found = true;
+          break;
+        }
+      }
+    }
+    return found;
+  }
+
+ protected:
+  protozero::HeapBuffered<protos::pbzero::Trace> trace_;
+  TraceProcessorContext context_;
+  TraceStorage* storage_;
+};
+
+TEST_F(NetworkTraceModuleTest, ParseAndFormatPacket) {
+  NetworkTraceModule module(&context_);
+
+  auto* packet = trace_->add_packet();
+  packet->set_timestamp(123);
+
+  auto* event = packet->set_network_packet();
+  event->set_direction(TrafficDirection::DIR_EGRESS);
+  event->set_length(72);
+  event->set_uid(1010);
+  event->set_tag(0x407);
+  event->set_local_port(5100);
+  event->set_remote_port(443);
+  event->set_tcp_flags(0b10010);
+  event->set_ip_proto(6);
+  event->set_interface("wlan");
+
+  ASSERT_TRUE(TokenizeAndParse().ok());
+
+  const auto& slices = storage_->slice_table();
+  ASSERT_EQ(slices.row_count(), 1u);
+  EXPECT_EQ(slices.ts()[0], 123);
+
+  EXPECT_TRUE(HasArg(1u, "packet_length", Variadic::Integer(72)));
+  EXPECT_TRUE(HasArg(1u, "local_port", Variadic::Integer(5100)));
+  EXPECT_TRUE(HasArg(1u, "remote_port", Variadic::Integer(443)));
+  EXPECT_TRUE(HasArg(1u, "packet_transport",
+                     Variadic::String(storage_->InternString("IPPROTO_TCP"))));
+  EXPECT_TRUE(HasArg(1u, "socket_tag",
+                     Variadic::String(storage_->InternString("0x407"))));
+  EXPECT_TRUE(HasArg(1u, "packet_tcp_flags",
+                     Variadic::String(storage_->InternString(".s..a..."))));
+}
+
+TEST_F(NetworkTraceModuleTest, TokenizeAndParsePerPacketBundle) {
+  NetworkTraceModule module(&context_);
+
+  auto* packet = trace_->add_packet();
+  packet->set_timestamp(123);
+
+  protozero::PackedVarInt timestamps;
+  timestamps.Append(0);
+  timestamps.Append(10);
+
+  protozero::PackedVarInt lengths;
+  lengths.Append(72);
+  lengths.Append(100);
+
+  auto* event = packet->set_network_packet_bundle();
+  event->set_packet_timestamps(timestamps);
+  event->set_packet_lengths(lengths);
+
+  auto* ctx = event->set_ctx();
+  ctx->set_uid(456);
+
+  ASSERT_TRUE(TokenizeAndParse().ok());
+
+  const auto& slices = storage_->slice_table();
+  ASSERT_EQ(slices.row_count(), 2u);
+  EXPECT_EQ(slices.ts()[0], 123);
+  EXPECT_EQ(slices.ts()[1], 133);
+
+  EXPECT_TRUE(HasArg(1u, "packet_length", Variadic::Integer(72)));
+  EXPECT_TRUE(HasArg(2u, "packet_length", Variadic::Integer(100)));
+}
+
+TEST_F(NetworkTraceModuleTest, TokenizeAndParseAggregateBundle) {
+  NetworkTraceModule module(&context_);
+
+  auto* packet = trace_->add_packet();
+  packet->set_timestamp(123);
+
+  auto* event = packet->set_network_packet_bundle();
+  event->set_total_packets(2);
+  event->set_total_duration(10);
+  event->set_total_length(172);
+
+  auto* ctx = event->set_ctx();
+  ctx->set_uid(456);
+
+  ASSERT_TRUE(TokenizeAndParse().ok());
+
+  const auto& slices = storage_->slice_table();
+  ASSERT_EQ(slices.row_count(), 1u);
+  EXPECT_EQ(slices.ts()[0], 123);
+  EXPECT_EQ(slices.dur()[0], 10);
+
+  EXPECT_TRUE(HasArg(1u, "packet_length", Variadic::UnsignedInteger(172)));
+  EXPECT_TRUE(HasArg(1u, "packet_count", Variadic::UnsignedInteger(2)));
+}
+
+}  // namespace
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/metrics/sql/android/android_startup.sql b/src/trace_processor/metrics/sql/android/android_startup.sql
index cb925ec..8399ddb 100644
--- a/src/trace_processor/metrics/sql/android/android_startup.sql
+++ b/src/trace_processor/metrics/sql/android/android_startup.sql
@@ -305,12 +305,12 @@
       FROM (
         SELECT 'dex2oat running during launch' AS slow_cause
         WHERE
-          DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64') > 20e6
+          DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*dex2oat64') > 0
 
         UNION ALL
         SELECT 'installd running during launch' AS slow_cause
         WHERE
-          DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*installd') > 150e6
+          DUR_OF_PROCESS_RUNNING_CONCURRENT_TO_LAUNCH(launches.startup_id, '*installd') > 0
 
         UNION ALL
         SELECT 'Main Thread - Time spent in Running state'
@@ -399,21 +399,21 @@
           SELECT COUNT(1)
           FROM ANDROID_SLICES_FOR_STARTUP_AND_SLICE_NAME(launches.startup_id, 'JIT compiling*')
           WHERE thread_name = 'Jit thread pool'
-        ) > 40
+        ) > 65
 
         UNION ALL
         SELECT 'Broadcast dispatched count'
         WHERE COUNT_SLICES_CONCURRENT_TO_LAUNCH(
           launches.startup_id,
           'Broadcast dispatched*'
-        ) > 10
+        ) > 15
 
         UNION ALL
         SELECT 'Broadcast received count'
         WHERE COUNT_SLICES_CONCURRENT_TO_LAUNCH(
           launches.startup_id,
           'broadcastReceiveReg*'
-        ) > 10
+        ) > 50
 
         UNION ALL
         SELECT 'No baseline or cloud profiles'
diff --git a/src/tracing/core/tracing_service_impl.cc b/src/tracing/core/tracing_service_impl.cc
index 5277657..0ee3122 100644
--- a/src/tracing/core/tracing_service_impl.cc
+++ b/src/tracing/core/tracing_service_impl.cc
@@ -102,15 +102,6 @@
 
 namespace perfetto {
 
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
-    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-// These are the only SELinux approved dir for trace files that are created
-// directly by traced.
-const char* kTraceDirBasePath = "/data/misc/perfetto-traces/";
-const char* kAndroidProductionBugreportTracePath =
-    "/data/misc/perfetto-traces/bugreport/systrace.pftrace";
-#endif
-
 namespace {
 constexpr int kMaxBuffersPerConsumer = 128;
 constexpr uint32_t kDefaultSnapshotsIntervalMs = 10 * 1000;
@@ -241,9 +232,7 @@
   return filter_matches || filter_regex_matches;
 }
 
-// Used when:
-// 1. TraceConfig.write_into_file == true and output_path is not empty.
-// 2. Calling SaveTraceForBugreport(), from perfetto --save-for-bugreport.
+// Used when TraceConfig.write_into_file == true and output_path is not empty.
 base::ScopedFile CreateTraceFile(const std::string& path, bool overwrite) {
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
     PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
@@ -251,6 +240,9 @@
   // It just improves the actionability of the error when people try to save the
   // trace in a location that is not SELinux-allowed (a generic "permission
   // denied" vs "don't put it here, put it there").
+  // These are the only SELinux approved dir for trace files that are created
+  // directly by traced.
+  static const char* kTraceDirBasePath = "/data/misc/perfetto-traces/";
   if (!base::StartsWith(path, kTraceDirBasePath)) {
     PERFETTO_ELOG("Invalid output_path %s. On Android it must be within %s.",
                   path.c_str(), kTraceDirBasePath);
@@ -271,10 +263,6 @@
   return fd;
 }
 
-std::string GetBugreportTmpPath() {
-  return GetBugreportPath() + ".tmp";
-}
-
 bool ShouldLogEvent(const TraceConfig& cfg) {
   switch (cfg.statsd_logging()) {
     case TraceConfig::STATSD_LOGGING_ENABLED:
@@ -320,16 +308,6 @@
 constexpr uint8_t TracingServiceImpl::kSyncMarker[];
 #endif
 
-std::string GetBugreportPath() {
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
-    PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-  return kAndroidProductionBugreportTracePath;
-#else
-  // Only for tests, SaveTraceForBugreport is not used on other OSes.
-  return base::GetSysTempDir() + "/bugreport.pftrace";
-#endif
-}
-
 // static
 std::unique_ptr<TracingService> TracingService::CreateInstance(
     std::unique_ptr<SharedMemory::Factory> shm_factory,
@@ -1692,11 +1670,6 @@
     ReadBuffersIntoFile(tracing_session->id);
   }
 
-  if (tracing_session->on_disable_callback_for_bugreport) {
-    std::move(tracing_session->on_disable_callback_for_bugreport)();
-    tracing_session->on_disable_callback_for_bugreport = nullptr;
-  }
-
   MaybeLogUploadEvent(tracing_session->config, tracing_session->trace_uuid,
                       PerfettoStatsdAtom::kTracedNotifyTracingDisabled);
 
@@ -2078,21 +2051,6 @@
     return false;
   }
 
-  // If a bugreport request happened and the trace was stolen for that, give
-  // an empty trace with a clear signal to the consumer. This deals only with
-  // the case of readback-from-IPC. A similar code-path deals with the
-  // write_into_file case in MaybeSaveTraceForBugreport().
-  if (tracing_session->seized_for_bugreport) {
-    std::vector<TracePacket> packets;
-    if (!tracing_session->config.builtin_data_sources()
-             .disable_service_events()) {
-      EmitSeizedForBugreportLifecycleEvent(&packets);
-    }
-    EmitLifecycleEvents(tracing_session, &packets);
-    consumer->consumer_->OnTraceData(std::move(packets), /*has_more=*/false);
-    return true;
-  }
-
   if (IsWaitingForTrigger(tracing_session))
     return false;
 
@@ -2141,8 +2099,7 @@
   if (!tracing_session->write_into_file)
     return false;
 
-  if (!tracing_session->seized_for_bugreport &&
-      IsWaitingForTrigger(tracing_session))
+  if (IsWaitingForTrigger(tracing_session))
     return false;
 
   // ReadBuffers() can allocate memory internally, for filtering. By limiting
@@ -2501,25 +2458,22 @@
   bool is_long_trace =
       (tracing_session->config.write_into_file() &&
        tracing_session->config.file_write_period_ms() < kMillisPerDay);
-  bool seized_for_bugreport = tracing_session->seized_for_bugreport;
   tracing_sessions_.erase(tsid);
   tracing_session = nullptr;
   UpdateMemoryGuardrail();
 
   PERFETTO_LOG("Tracing session %" PRIu64 " ended, total sessions:%zu", tsid,
                tracing_sessions_.size());
-
 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD) && \
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
-  if (notify_traceur && (seized_for_bugreport || is_long_trace)) {
+  if (notify_traceur && is_long_trace) {
     PERFETTO_LAZY_LOAD(android_internal::NotifyTraceSessionEnded, notify_fn);
-    if (!notify_fn || !notify_fn(seized_for_bugreport))
+    if (!notify_fn || !notify_fn(/*session_stolen=*/false))
       PERFETTO_ELOG("Failed to notify Traceur long tracing has ended");
   }
 #else
   base::ignore_result(notify_traceur);
   base::ignore_result(is_long_trace);
-  base::ignore_result(seized_for_bugreport);
 #endif
 }
 
@@ -2983,6 +2937,23 @@
   return &it->second;
 }
 
+TracingServiceImpl::TracingSession*
+TracingServiceImpl::FindTracingSessionWithMaxBugreportScore() {
+  TracingSession* max_session = nullptr;
+  for (auto& session_id_and_session : tracing_sessions_) {
+    auto& session = session_id_and_session.second;
+    const int32_t score = session.config.bugreport_score();
+    // Exclude sessions with 0 (or below) score. By default tracing sessions
+    // should NOT be eligible to be attached to bugreports.
+    if (score <= 0 || session.state != TracingSession::STARTED)
+      continue;
+
+    if (!max_session || score > max_session->config.bugreport_score())
+      max_session = &session;
+  }
+  return max_session;
+}
+
 ProducerID TracingServiceImpl::GetNextProducerID() {
   PERFETTO_DCHECK_THREAD(thread_checker_);
   PERFETTO_CHECK(producers_.size() < kMaxProducerID);
@@ -3411,18 +3382,6 @@
     SerializeAndAppendPacket(packets, std::move(pair.second));
 }
 
-void TracingServiceImpl::EmitSeizedForBugreportLifecycleEvent(
-    std::vector<TracePacket>* packets) {
-  protozero::HeapBuffered<protos::pbzero::TracePacket> packet;
-  packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
-  packet->set_trusted_uid(static_cast<int32_t>(uid_));
-  packet->set_trusted_packet_sequence_id(kServicePacketSequenceID);
-  auto* service_event = packet->set_service_event();
-  service_event->AppendVarInt(
-      protos::pbzero::TracingServiceEvent::kSeizedForBugreportFieldNumber, 1);
-  SerializeAndAppendPacket(packets, packet.SerializeAsArray());
-}
-
 void TracingServiceImpl::MaybeEmitReceivedTriggers(
     TracingSession* tracing_session,
     std::vector<TracePacket>* packets) {
@@ -3445,91 +3404,6 @@
   }
 }
 
-bool TracingServiceImpl::MaybeSaveTraceForBugreport(
-    std::function<void()> callback) {
-  TracingSession* max_session = nullptr;
-  TracingSessionID max_tsid = 0;
-  for (auto& session_id_and_session : tracing_sessions_) {
-    auto& session = session_id_and_session.second;
-    const int32_t score = session.config.bugreport_score();
-    // Exclude sessions with 0 (or below) score. By default tracing sessions
-    // should NOT be eligible to be attached to bugreports.
-    if (score <= 0 || session.state != TracingSession::STARTED)
-      continue;
-
-    // Also don't try to steal long traces with write_into_file if their content
-    // has been already partially written into a file, as we would get partial
-    // traces on both sides. We can't just copy the original file into the
-    // bugreport because the file could be too big (GBs) for bugreports.
-    // The only case where it's legit to steal traces with write_into_file, is
-    // when the consumer specified a very large write_period_ms (e.g. 24h),
-    // meaning that this is effectively a ring-buffer trace. Traceur (the
-    // Android System Tracing app), which uses --detach, does this to have a
-    // consistent invocation path for long-traces and ring-buffer-mode traces.
-    if (session.write_into_file && session.bytes_written_into_file > 0)
-      continue;
-
-    // If we are already in the process of finalizing another trace for
-    // bugreport, don't even start another one, as they would try to write onto
-    // the same file.
-    if (session.on_disable_callback_for_bugreport)
-      return false;
-
-    if (!max_session || score > max_session->config.bugreport_score()) {
-      max_session = &session;
-      max_tsid = session_id_and_session.first;
-    }
-  }
-
-  // No eligible trace found.
-  if (!max_session)
-    return false;
-
-  PERFETTO_LOG("Seizing trace for bugreport. tsid:%" PRIu64
-               " state:%d wf:%d score:%d name:\"%s\"",
-               max_tsid, max_session->state, !!max_session->write_into_file,
-               max_session->config.bugreport_score(),
-               max_session->config.unique_session_name().c_str());
-
-  auto br_fd = CreateTraceFile(GetBugreportTmpPath(), /*overwrite=*/true);
-  if (!br_fd)
-    return false;
-
-  if (max_session->write_into_file) {
-    auto fd = *max_session->write_into_file;
-    // If we are stealing a write_into_file session, add a marker that explains
-    // why the trace has been stolen rather than creating an empty file. This is
-    // only for write_into_file traces. A similar code path deals with the case
-    // of reading-back a seized trace from IPC in ReadBuffersIntoConsumer().
-    if (!max_session->config.builtin_data_sources().disable_service_events()) {
-      std::vector<TracePacket> packets;
-      EmitSeizedForBugreportLifecycleEvent(&packets);
-      for (auto& packet : packets) {
-        char* preamble;
-        size_t preamble_size = 0;
-        std::tie(preamble, preamble_size) = packet.GetProtoPreamble();
-        base::WriteAll(fd, preamble, preamble_size);
-        for (const Slice& slice : packet.slices()) {
-          base::WriteAll(fd, slice.start, slice.size);
-        }
-      }  // for (packets)
-    }    // if (!disable_service_events())
-  }      // if (max_session->write_into_file)
-  max_session->write_into_file = std::move(br_fd);
-  max_session->on_disable_callback_for_bugreport = std::move(callback);
-  max_session->seized_for_bugreport = true;
-
-  // Post a task to avoid that early FlushAndDisableTracing() failures invoke
-  // the callback before we return. That would re-enter in a weird way the
-  // callstack of the calling ConsumerEndpointImpl::SaveTraceForBugreport().
-  auto weak_this = weak_ptr_factory_.GetWeakPtr();
-  task_runner_->PostTask([weak_this, max_tsid] {
-    if (weak_this)
-      weak_this->FlushAndDisableTracing(max_tsid);
-  });
-  return true;
-}
-
 void TracingServiceImpl::MaybeLogUploadEvent(const TraceConfig& cfg,
                                              const base::Uuid& uuid,
                                              PerfettoStatsdAtom atom,
@@ -3571,6 +3445,17 @@
 void TracingServiceImpl::FlushAndCloneSession(ConsumerEndpointImpl* consumer,
                                               TracingSessionID tsid) {
   PERFETTO_DCHECK_THREAD(thread_checker_);
+
+  if (tsid == kBugreportSessionId) {
+    TracingSession* session = FindTracingSessionWithMaxBugreportScore();
+    if (!session) {
+      consumer->consumer_->OnSessionCloned(
+          false, "No tracing sessions eligible for bugreport found");
+      return;
+    }
+    tsid = session->id;
+  }
+
   auto weak_this = weak_ptr_factory_.GetWeakPtr();
   auto weak_consumer = consumer->GetWeakPtr();
   Flush(tsid, 0, [weak_this, tsid, weak_consumer](bool final_flush_outcome) {
@@ -3589,6 +3474,7 @@
                                                 bool final_flush_outcome) {
   PERFETTO_DLOG("CloneSession(%" PRIu64 ") started, consumer uid: %d", src_tsid,
                 static_cast<int>(consumer->uid_));
+
   TracingSession* src = GetTracingSession(src_tsid);
 
   // The session might be gone by the time we try to clone it.
@@ -3662,6 +3548,11 @@
         new protozero::MessageFilter(*src->trace_filter));
   }
 
+  SnapshotLifecyleEvent(
+      cloned_session,
+      protos::pbzero::TracingServiceEvent::kTracingDisabledFieldNumber,
+      true /* snapshot_clocks */);
+
   PERFETTO_DLOG("Consumer (uid:%d) cloned tracing session %" PRIu64
                 " -> %" PRIu64,
                 static_cast<int>(consumer->uid_), src_tsid, tsid);
@@ -3982,21 +3873,9 @@
 
 void TracingServiceImpl::ConsumerEndpointImpl::SaveTraceForBugreport(
     SaveTraceForBugreportCallback consumer_callback) {
-  PERFETTO_DCHECK_THREAD(thread_checker_);
-  auto on_complete_callback = [consumer_callback] {
-    if (rename(GetBugreportTmpPath().c_str(), GetBugreportPath().c_str())) {
-      consumer_callback(false, "rename(" + GetBugreportTmpPath() + ", " +
-                                   GetBugreportPath() + ") failed (" +
-                                   strerror(errno) + ")");
-    } else {
-      consumer_callback(true, GetBugreportPath());
-    }
-  };
-  if (!service_->MaybeSaveTraceForBugreport(std::move(on_complete_callback))) {
-    consumer_callback(false,
-                      "No trace with TraceConfig.bugreport_score > 0 eligible "
-                      "for bug reporting was found");
-  }
+  consumer_callback(false,
+                    "SaveTraceForBugreport is deprecated. Use "
+                    "CloneSession(kBugreportSessionId) instead.");
 }
 
 void TracingServiceImpl::ConsumerEndpointImpl::CloneSession(
diff --git a/src/tracing/core/tracing_service_impl.h b/src/tracing/core/tracing_service_impl.h
index 4800559..02e5639 100644
--- a/src/tracing/core/tracing_service_impl.h
+++ b/src/tracing/core/tracing_service_impl.h
@@ -632,11 +632,6 @@
     uint64_t max_file_size_bytes = 0;
     uint64_t bytes_written_into_file = 0;
 
-    // Set when using SaveTraceForBugreport(). This callback will be called
-    // when the tracing session ends and the data has been saved into the file.
-    std::function<void()> on_disable_callback_for_bugreport;
-    bool seized_for_bugreport = false;
-
     // Periodic task for snapshotting service events (e.g. clocks, sync markers
     // etc)
     base::PeriodicTask snapshot_periodic_task;
@@ -680,6 +675,10 @@
   // session doesn't exists.
   TracingSession* GetTracingSession(TracingSessionID);
 
+  // Returns a pointer to the tracing session that has the highest
+  // TraceConfig.bugreport_score, if any, or nullptr.
+  TracingSession* FindTracingSessionWithMaxBugreportScore();
+
   // Returns a pointer to the |tracing_sessions_| entry, matching the given
   // uid and detach key, or nullptr if no such session exists.
   TracingSession* GetDetachedSession(uid_t, const std::string& key);
@@ -708,12 +707,10 @@
   void EmitStats(TracingSession*, std::vector<TracePacket>*);
   TraceStats GetTraceStats(TracingSession*);
   void EmitLifecycleEvents(TracingSession*, std::vector<TracePacket>*);
-  void EmitSeizedForBugreportLifecycleEvent(std::vector<TracePacket>*);
   void MaybeEmitUuidAndTraceConfig(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitSystemInfo(TracingSession*, std::vector<TracePacket>*);
   void MaybeEmitReceivedTriggers(TracingSession*, std::vector<TracePacket>*);
   void MaybeNotifyAllDataSourcesStarted(TracingSession*);
-  bool MaybeSaveTraceForBugreport(std::function<void()> callback);
   void OnFlushTimeout(TracingSessionID, FlushRequestID);
   void OnDisableTracingTimeout(TracingSessionID);
   void DisableTracingNotifyConsumerAndFlushFile(TracingSession*);
diff --git a/src/tracing/test/mock_consumer.cc b/src/tracing/test/mock_consumer.cc
index dc44b31..e24ffcc 100644
--- a/src/tracing/test/mock_consumer.cc
+++ b/src/tracing/test/mock_consumer.cc
@@ -39,8 +39,9 @@
   task_runner_->RunUntilCheckpoint(checkpoint_name);
 }
 
-void MockConsumer::Connect(TracingService* svc, uid_t uid) {
-  service_endpoint_ = svc->ConnectConsumer(this, uid);
+void MockConsumer::Connect(
+    std::unique_ptr<TracingService::ConsumerEndpoint> service_endpoint) {
+  service_endpoint_ = std::move(service_endpoint);
   static int i = 0;
   auto checkpoint_name = "on_consumer_connect_" + std::to_string(i++);
   auto on_connect = task_runner_->CreateCheckpoint(checkpoint_name);
@@ -48,6 +49,14 @@
   task_runner_->RunUntilCheckpoint(checkpoint_name);
 }
 
+void MockConsumer::Connect(TracingService* svc, uid_t uid) {
+  Connect(svc->ConnectConsumer(this, uid));
+}
+
+void MockConsumer::ForceDisconnect() {
+  service_endpoint_.reset();
+}
+
 void MockConsumer::EnableTracing(const TraceConfig& trace_config,
                                  base::ScopedFile write_into_file) {
   service_endpoint_->EnableTracing(trace_config, std::move(write_into_file));
diff --git a/src/tracing/test/mock_consumer.h b/src/tracing/test/mock_consumer.h
index a840349..f18671e 100644
--- a/src/tracing/test/mock_consumer.h
+++ b/src/tracing/test/mock_consumer.h
@@ -47,7 +47,9 @@
   explicit MockConsumer(base::TestTaskRunner*);
   ~MockConsumer() override;
 
+  void Connect(std::unique_ptr<TracingService::ConsumerEndpoint>);
   void Connect(TracingService* svc, uid_t = 0);
+  void ForceDisconnect();
   void EnableTracing(const TraceConfig&, base::ScopedFile = base::ScopedFile());
   void StartTracing();
   void ChangeTraceConfig(const TraceConfig&);
diff --git a/test/BUILD.gn b/test/BUILD.gn
index 768b8ad..6b995ed 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -38,6 +38,7 @@
       "../protos/perfetto/trace/sys_stats:cpp",
       "../src/base:base",
       "../src/base:test_support",
+      "../src/perfetto_cmd:bugreport_path",
     ]
     if (enable_perfetto_traced_probes) {
       deps += [
diff --git a/test/android_integrationtest.cc b/test/android_integrationtest.cc
index 19e894d..9d4e543 100644
--- a/test/android_integrationtest.cc
+++ b/test/android_integrationtest.cc
@@ -63,33 +63,6 @@
 using ::testing::Property;
 using ::testing::SizeIs;
 
-// For the SaveForBugreport* tests.
-void SetTraceConfigForBugreportTest(TraceConfig* trace_config) {
-  trace_config->add_buffers()->set_size_kb(4096);
-  trace_config->set_duration_ms(60000);  // Will never hit this.
-  trace_config->set_bugreport_score(10);
-  auto* ds_config = trace_config->add_data_sources()->mutable_config();
-  ds_config->set_name("android.perfetto.FakeProducer");
-  ds_config->mutable_for_testing()->set_message_count(3);
-  ds_config->mutable_for_testing()->set_message_size(10);
-  ds_config->mutable_for_testing()->set_send_batch_on_register(true);
-}
-
-// For the SaveForBugreport* tests.
-static void VerifyBugreportTraceContents() {
-  // Read the trace written in the fixed location (/data/misc/perfetto-traces/
-  // on Android, /tmp/ on Linux/Mac) and make sure it has the right contents.
-  std::string trace_str;
-  base::ReadFile(GetBugreportPath(), &trace_str);
-  ASSERT_FALSE(trace_str.empty());
-  protos::gen::Trace trace;
-  ASSERT_TRUE(trace.ParseFromString(trace_str));
-  int test_packets = 0;
-  for (const auto& p : trace.packet())
-    test_packets += p.has_for_testing() ? 1 : 0;
-  ASSERT_EQ(test_packets, 3);
-}
-
 }  // namespace
 
 TEST(PerfettoAndroidIntegrationTest, TestKmemActivity) {
@@ -283,125 +256,6 @@
   ASSERT_TRUE(has_battery_packet);
 }
 
-TEST(PerfettoAndroidIntegrationTest, SaveForBugreport) {
-  base::TestTaskRunner task_runner;
-
-  TestHelper helper(&task_runner);
-  helper.StartServiceIfRequired();
-  helper.ConnectFakeProducer();
-  helper.ConnectConsumer();
-  helper.WaitForConsumerConnect();
-
-  TraceConfig trace_config;
-  SetTraceConfigForBugreportTest(&trace_config);
-
-  helper.StartTracing(trace_config);
-  helper.WaitForProducerEnabled();
-
-  EXPECT_TRUE(helper.SaveTraceForBugreportAndWait());
-  helper.WaitForTracingDisabled();
-
-  VerifyBugreportTraceContents();
-
-  // Now read the trace returned to the consumer via ReadBuffers. This should
-  // be always empty because --save-for-bugreport takes it over and makes the
-  // buffers unreadable by the consumer (by virtue of force-setting
-  // write_into_file, which is incompatible with ReadBuffers()). The only
-  // content should be the |seized_for_bugreport| flag.
-  helper.ReadData();
-  helper.WaitForReadData();
-  const auto& packets = helper.full_trace();
-  ASSERT_EQ(packets.size(), 1u);
-  for (const auto& p : packets) {
-    ASSERT_TRUE(p.has_service_event());
-    ASSERT_TRUE(p.service_event().seized_for_bugreport());
-  }
-}
-
-// Tests that the SaveForBugreport logic works also for traces with
-// write_into_file = true (with a passed file descriptor).
-TEST(PerfettoAndroidIntegrationTest, SaveForBugreport_WriteIntoFile) {
-  base::TestTaskRunner task_runner;
-
-  TestHelper helper(&task_runner);
-  helper.StartServiceIfRequired();
-  helper.ConnectFakeProducer();
-  helper.ConnectConsumer();
-  helper.WaitForConsumerConnect();
-
-  TraceConfig trace_config;
-  SetTraceConfigForBugreportTest(&trace_config);
-  trace_config.set_file_write_period_ms(60000);  // Will never hit this.
-  trace_config.set_write_into_file(true);
-
-  auto pipe_pair = base::Pipe::Create();
-  helper.StartTracing(trace_config, std::move(pipe_pair.wr));
-  helper.WaitForProducerEnabled();
-
-  EXPECT_TRUE(helper.SaveTraceForBugreportAndWait());
-  helper.WaitForTracingDisabled();
-
-  VerifyBugreportTraceContents();
-
-  // Now read the original file descriptor passed in.
-  std::string trace_bytes;
-  ASSERT_TRUE(base::ReadPlatformHandle(*pipe_pair.rd, &trace_bytes));
-  protos::gen::Trace trace;
-  ASSERT_TRUE(trace.ParseFromString(trace_bytes));
-  ASSERT_EQ(trace.packet().size(), 1u);
-  for (const auto& p : trace.packet()) {
-    ASSERT_TRUE(p.has_service_event());
-    ASSERT_TRUE(p.service_event().seized_for_bugreport());
-  }
-}
-
-// Tests that SaveTraceForBugreport() works also if the trace has triggers
-// defined and those triggers have not been hit. This is a regression test for
-// b/188008375 .
-#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
-// Disabled due to b/191940560
-#define MAYBE_SaveForBugreport_Triggers DISABLED_SaveForBugreport_Triggers
-#else
-#define MAYBE_SaveForBugreport_Triggers SaveForBugreport_Triggers
-#endif
-TEST(PerfettoAndroidIntegrationTest, MAYBE_SaveForBugreport_Triggers) {
-  base::TestTaskRunner task_runner;
-
-  TestHelper helper(&task_runner);
-  helper.StartServiceIfRequired();
-  helper.ConnectFakeProducer();
-  helper.ConnectConsumer();
-  helper.WaitForConsumerConnect();
-
-  TraceConfig trace_config;
-  SetTraceConfigForBugreportTest(&trace_config);
-  trace_config.set_duration_ms(0);  // set_trigger_timeout_ms is used instead.
-  auto* trigger_config = trace_config.mutable_trigger_config();
-  trigger_config->set_trigger_timeout_ms(8.64e+7);
-  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::STOP_TRACING);
-  auto* trigger = trigger_config->add_triggers();
-  trigger->set_name("trigger_name");
-  trigger->set_stop_delay_ms(1);
-
-  helper.StartTracing(trace_config);
-  helper.WaitForProducerEnabled();
-
-  EXPECT_TRUE(helper.SaveTraceForBugreportAndWait());
-  helper.WaitForTracingDisabled();
-
-  VerifyBugreportTraceContents();
-
-  // Now read the original trace.
-  helper.ReadData();
-  helper.WaitForReadData();
-  const auto& packets = helper.full_trace();
-  ASSERT_EQ(packets.size(), 1u);
-  for (const auto& p : packets) {
-    ASSERT_TRUE(p.has_service_event());
-    ASSERT_TRUE(p.service_event().seized_for_bugreport());
-  }
-}
-
 }  // namespace perfetto
 
 #endif  // PERFETTO_OS_ANDROID
diff --git a/test/cmdline_integrationtest.cc b/test/cmdline_integrationtest.cc
index 0747789..cbc3555 100644
--- a/test/cmdline_integrationtest.cc
+++ b/test/cmdline_integrationtest.cc
@@ -27,6 +27,7 @@
 #include "perfetto/protozero/scattered_heap_buffer.h"
 #include "src/base/test/test_task_runner.h"
 #include "src/base/test/utils.h"
+#include "src/perfetto_cmd/bugreport_path.h"
 #include "test/gtest_and_gmock.h"
 #include "test/test_helper.h"
 
@@ -66,6 +67,19 @@
   return path;
 }
 
+// For the SaveForBugreport* tests.
+TraceConfig CreateTraceConfigForBugreportTest() {
+  TraceConfig trace_config;
+  trace_config.add_buffers()->set_size_kb(4096);
+  trace_config.set_duration_ms(60000);  // Will never hit this.
+  trace_config.set_bugreport_score(10);
+  auto* ds_config = trace_config.add_data_sources()->mutable_config();
+  ds_config->set_name("android.perfetto.FakeProducer");
+  ds_config->mutable_for_testing()->set_message_count(3);
+  ds_config->mutable_for_testing()->set_message_size(10);
+  return trace_config;
+}
+
 class PerfettoCmdlineTest : public ::testing::Test {
  public:
   void SetUp() override {
@@ -120,6 +134,68 @@
     return Exec("trigger_perfetto", std::move(args), std::move(std_in));
   }
 
+  // This is in common to the 3 TEST_F SaveForBugreport* fixtures, which differ
+  // only in the config, passed here as input.
+  void RunBugreportTest(protos::gen::TraceConfig trace_config,
+                        bool check_original_trace = true) {
+    const std::string path = RandomTraceFileName();
+
+    auto perfetto_proc = ExecPerfetto(
+        {
+            "-o",
+            path,
+            "-c",
+            "-",
+        },
+        trace_config.SerializeAsString());
+
+    auto perfetto_br_proc = ExecPerfetto({
+        "--save-for-bugreport",
+    });
+
+    // Start the service and connect a simple fake producer.
+    StartServiceIfRequiredNoNewExecsAfterThis();
+
+    auto* fake_producer = ConnectFakeProducer();
+    ASSERT_TRUE(fake_producer);
+
+    std::thread background_trace([&perfetto_proc]() {
+      std::string stderr_str;
+      ASSERT_EQ(0, perfetto_proc.Run(&stderr_str)) << stderr_str;
+    });
+
+    // Wait for the producer to start, and then write out packets.
+    WaitForProducerEnabled();
+    auto on_data_written = task_runner_.CreateCheckpoint("data_written");
+    fake_producer->ProduceEventBatch(WrapTask(on_data_written));
+    task_runner_.RunUntilCheckpoint("data_written");
+
+    ASSERT_EQ(0, perfetto_br_proc.Run(&stderr_)) << "stderr: " << stderr_;
+    perfetto_proc.SendSigterm();
+    background_trace.join();
+
+    auto check_trace_contents = [](std::string trace_path) {
+      // Read the trace written in the fixed location
+      // (/data/misc/perfetto-traces/ on Android, /tmp/ on Linux/Mac) and make
+      // sure it has the right contents.
+      std::string trace_str;
+      base::ReadFile(trace_path, &trace_str);
+      ASSERT_FALSE(trace_str.empty());
+      protos::gen::Trace trace;
+      ASSERT_TRUE(trace.ParseFromString(trace_str));
+      int test_packets = 0;
+      for (const auto& p : trace.packet())
+        test_packets += p.has_for_testing() ? 1 : 0;
+      ASSERT_EQ(test_packets, 3) << trace_path;
+    };
+
+    // Verify that both the original trace and the cloned bugreport contain
+    // the expected contents.
+    check_trace_contents(GetBugreportTracePath());
+    if (check_original_trace)
+      check_trace_contents(path);
+  }
+
   // Tests are allowed to freely use these variables.
   std::string stderr_;
   base::TestTaskRunner task_runner_;
@@ -760,4 +836,37 @@
   }
 }
 
+TEST_F(PerfettoCmdlineTest, SaveForBugreport) {
+  TraceConfig trace_config = CreateTraceConfigForBugreportTest();
+  RunBugreportTest(std::move(trace_config));
+}
+
+TEST_F(PerfettoCmdlineTest, SaveForBugreport_WriteIntoFile) {
+  TraceConfig trace_config = CreateTraceConfigForBugreportTest();
+  trace_config.set_file_write_period_ms(60000);  // Will never hit this.
+  trace_config.set_write_into_file(true);
+  RunBugreportTest(std::move(trace_config));
+}
+
+// Tests that SaveTraceForBugreport() works also if the trace has triggers
+// defined and those triggers have not been hit. This is a regression test for
+// b/188008375 .
+#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
+// Disabled due to b/191940560
+#define MAYBE_SaveForBugreport_Triggers DISABLED_SaveForBugreport_Triggers
+#else
+#define MAYBE_SaveForBugreport_Triggers SaveForBugreport_Triggers
+#endif
+TEST_F(PerfettoCmdlineTest, MAYBE_SaveForBugreport_Triggers) {
+  TraceConfig trace_config = CreateTraceConfigForBugreportTest();
+  trace_config.set_duration_ms(0);  // set_trigger_timeout_ms is used instead.
+  auto* trigger_config = trace_config.mutable_trigger_config();
+  trigger_config->set_trigger_timeout_ms(8.64e+7);
+  trigger_config->set_trigger_mode(TraceConfig::TriggerConfig::STOP_TRACING);
+  auto* trigger = trigger_config->add_triggers();
+  trigger->set_name("trigger_name");
+  trigger->set_stop_delay_ms(1);
+  RunBugreportTest(std::move(trace_config), /*check_original_trace=*/false);
+}
+
 }  // namespace perfetto
diff --git a/test/configs/bugreport.cfg b/test/configs/bugreport.cfg
new file mode 100644
index 0000000..d42fd61
--- /dev/null
+++ b/test/configs/bugreport.cfg
@@ -0,0 +1,30 @@
+bugreport_score: 100
+duration_ms: 600000
+
+buffers {
+  size_kb: 32768
+  fill_policy: RING_BUFFER
+}
+
+data_sources {
+  config {
+    name: "linux.ftrace"
+    target_buffer: 0
+    ftrace_config {
+      ftrace_events: "sched/sched_switch"
+      ftrace_events: "power/suspend_resume"
+      ftrace_events: "sched/sched_process_exit"
+      ftrace_events: "sched/sched_process_free"
+      ftrace_events: "task/task_newtask"
+      ftrace_events: "task/task_rename"
+      ftrace_events: "sched/sched_wakeup"
+    }
+  }
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+  }
+}
diff --git a/test/test_helper.cc b/test/test_helper.cc
index 2267dc0..cbc3aca 100644
--- a/test/test_helper.cc
+++ b/test/test_helper.cc
@@ -156,18 +156,6 @@
   return success;
 }
 
-bool TestHelper::SaveTraceForBugreportAndWait() {
-  bool success = false;
-  auto checkpoint = CreateCheckpoint("bugreport");
-  auto callback = [&success, checkpoint](bool s, const std::string&) {
-    success = s;
-    checkpoint();
-  };
-  endpoint_->SaveTraceForBugreport(callback);
-  RunUntilCheckpoint("bugreport");
-  return success;
-}
-
 void TestHelper::CreateProducerProvidedSmb() {
   fake_producer_thread_.CreateProducerProvidedSmb();
 }
diff --git a/test/test_helper.h b/test/test_helper.h
index fda54b0..c8d1b48 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -40,6 +40,8 @@
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include "src/tracing/ipc/shared_memory_windows.h"
 #else
+#include <signal.h>
+
 #include "src/traced/probes/probes_producer.h"
 #include "src/tracing/ipc/posix_shared_memory.h"
 #endif
@@ -309,7 +311,6 @@
   void FreeBuffers();
   void DetachConsumer(const std::string& key);
   bool AttachConsumer(const std::string& key);
-  bool SaveTraceForBugreportAndWait();
   void CreateProducerProvidedSmb();
   bool IsShmemProvidedByProducer();
   void ProduceStartupEventBatch(const protos::gen::TestConfig& config);
@@ -427,6 +428,12 @@
     constexpr bool kUseSystemBinaries = true;
 #endif
 
+    auto pass_env = [](const std::string& var, base::Subprocess* proc) {
+      const char* val = getenv(var.c_str());
+      if (val)
+        proc->args.env.push_back(var + "=" + val);
+    };
+
     std::vector<std::string>& cmd = subprocess_.args.exec_cmd;
     if (kUseSystemBinaries) {
       PERFETTO_CHECK(TestHelper::kDefaultMode ==
@@ -442,6 +449,9 @@
       subprocess_.args.env.push_back(
           std::string("PERFETTO_CONSUMER_SOCK_NAME=") +
           TestHelper::GetDefaultModeConsumerSocketName());
+      pass_env("TMPDIR", &subprocess_);
+      pass_env("TMP", &subprocess_);
+      pass_env("TEMP", &subprocess_);
       cmd.push_back(base::GetCurExecutableDir() + "/" + argv0);
       cmd.insert(cmd.end(), args.begin(), args.end());
     }
@@ -479,6 +489,16 @@
     sync_pipe_.rd.reset();
   }
 
+  void SendSigterm() {
+#ifdef SIGTERM
+    kill(subprocess_.pid(), SIGTERM);
+#else
+    // This code is never used on Windows tests, not bothering.
+    if (subprocess_.pid())  // Always true, but avoids Wnoreturn compile errors.
+      PERFETTO_FATAL("SendSigterm() not implemented on this platform");
+#endif
+  }
+
  private:
   base::Subprocess subprocess_;
   base::Pipe sync_pipe_;
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
index 2257cb0..475edf8 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.out
@@ -32,7 +32,7 @@
         dur_ns: 40000000000
         dur_ms: 40000
       }
-      jit_compiled_methods: 41
+      jit_compiled_methods: 71
       time_jit_thread_pool_on_cpu {
         dur_ns: 20000000000
         dur_ms: 20000
diff --git a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
index aa81cca..93e652f 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
+++ b/test/trace_processor/diff_tests/startup/android_startup_attribution_slow.py
@@ -123,7 +123,7 @@
     ts=to_s(260), pid=APP_PID, tid=SECOND_APP_TID, buf='VerifyClass vp')
 trace.add_atrace_end(ts=to_s(280), pid=APP_PID, tid=SECOND_APP_TID)
 
-for t in range(100, 160, 2):
+for t in range(100, 160, 1):
   # JIT compilation slices
   trace.add_atrace_begin(
       ts=to_s(t), pid=APP_PID, tid=JIT_TID, buf='JIT compiling someting')
diff --git a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
index 8f50a7d..c0b245c 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.out
@@ -24,8 +24,8 @@
     system_state {
       dex2oat_running: false
       installd_running: false
-      broadcast_dispatched_count: 12
-      broadcast_received_count: 11
+      broadcast_dispatched_count: 24
+      broadcast_received_count: 52
       installd_dur_ns: 0
       dex2oat_dur_ns: 0
     }
diff --git a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
index 4b5f5b2..da3fab6 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
+++ b/test/trace_processor/diff_tests/startup/android_startup_broadcast_multiple.py
@@ -31,7 +31,7 @@
 trace.add_atrace_async_begin(ts=100, tid=2, pid=2, buf='launchingActivity#1')
 trace.add_atrace_async_end(ts=200, tid=2, pid=2, buf='launchingActivity#1')
 
-for t in range(105, 129, 2):
+for t in range(105, 129, 1):
   trace.add_atrace_begin(
       ts=t,
       tid=1,
@@ -39,7 +39,7 @@
       buf='Broadcast dispatched from android (2005:system/1000) x')
   trace.add_atrace_end(ts=t + 1, tid=1, pid=1)
 
-for t in range(130, 152, 2):
+for t in range(100, 152, 1):
   trace.add_atrace_begin(ts=t, tid=2, pid=2, buf='broadcastReceiveReg: x')
   trace.add_atrace_end(ts=t + 1, tid=2, pid=2)
 
diff --git a/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
index 48aa205..f2b6285 100644
--- a/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
+++ b/test/trace_processor/diff_tests/startup/android_startup_installd_dex2oat.out
@@ -61,6 +61,7 @@
       installd_dur_ns: 0
       dex2oat_dur_ns: 5
     }
+    slow_start_reason: "dex2oat running during launch"
   }
   startup {
     startup_id: 3
@@ -93,6 +94,7 @@
       installd_dur_ns: 5
       dex2oat_dur_ns: 0
     }
+    slow_start_reason: "installd running during launch"
   }
   startup {
     startup_id: 4
@@ -126,5 +128,7 @@
       installd_dur_ns: 5
       dex2oat_dur_ns: 5
     }
+    slow_start_reason: "dex2oat running during launch"
+    slow_start_reason: "installd running during launch"
   }
 }