Merge "tp: rewrite OrderedIndexSearch to be truly O(logn)" into main
diff --git a/Android.bp b/Android.bp
index e3312bb..972f828 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2425,6 +2425,7 @@
         ":perfetto_src_trace_processor_importers_systrace_full",
         ":perfetto_src_trace_processor_importers_systrace_systrace_line",
         ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
+        ":perfetto_src_trace_processor_importers_zip_full",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
@@ -12562,6 +12563,14 @@
     ],
 }
 
+// GN: //src/trace_processor/importers/zip:full
+filegroup {
+    name: "perfetto_src_trace_processor_importers_zip_full",
+    srcs: [
+        "src/trace_processor/importers/zip/zip_trace_reader.cc",
+    ],
+}
+
 // GN: //src/trace_processor:lib
 filegroup {
     name: "perfetto_src_trace_processor_lib",
@@ -13106,13 +13115,16 @@
         "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql",
         "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/timestamps.sql",
+        "src/trace_processor/perfetto_sql/stdlib/gpu/frequency.sql",
         "src/trace_processor/perfetto_sql/stdlib/graphs/dominator_tree.sql",
         "src/trace_processor/perfetto_sql/stdlib/graphs/partition.sql",
         "src/trace_processor/perfetto_sql/stdlib/graphs/search.sql",
         "src/trace_processor/perfetto_sql/stdlib/intervals/intersect.sql",
         "src/trace_processor/perfetto_sql/stdlib/intervals/overlap.sql",
         "src/trace_processor/perfetto_sql/stdlib/linux/cpu_idle.sql",
+        "src/trace_processor/perfetto_sql/stdlib/memory/android/gpu.sql",
         "src/trace_processor/perfetto_sql/stdlib/memory/heap_graph_dominator_tree.sql",
+        "src/trace_processor/perfetto_sql/stdlib/memory/linux/general.sql",
         "src/trace_processor/perfetto_sql/stdlib/memory/linux/high_watermark.sql",
         "src/trace_processor/perfetto_sql/stdlib/memory/linux/process.sql",
         "src/trace_processor/perfetto_sql/stdlib/pkvm/hypervisor.sql",
@@ -15086,6 +15098,7 @@
         ":perfetto_src_trace_processor_importers_systrace_systrace_line",
         ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
         ":perfetto_src_trace_processor_importers_systrace_unittests",
+        ":perfetto_src_trace_processor_importers_zip_full",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
@@ -16083,6 +16096,7 @@
         ":perfetto_src_trace_processor_importers_systrace_full",
         ":perfetto_src_trace_processor_importers_systrace_systrace_line",
         ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
+        ":perfetto_src_trace_processor_importers_zip_full",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
@@ -16480,6 +16494,7 @@
         ":perfetto_src_trace_processor_importers_systrace_full",
         ":perfetto_src_trace_processor_importers_systrace_systrace_line",
         ":perfetto_src_trace_processor_importers_systrace_systrace_parser",
+        ":perfetto_src_trace_processor_importers_zip_full",
         ":perfetto_src_trace_processor_lib",
         ":perfetto_src_trace_processor_metatrace",
         ":perfetto_src_trace_processor_metrics_metrics",
diff --git a/BUILD b/BUILD
index 02617c5..537ccc4 100644
--- a/BUILD
+++ b/BUILD
@@ -251,6 +251,7 @@
         ":src_trace_processor_importers_systrace_full",
         ":src_trace_processor_importers_systrace_systrace_line",
         ":src_trace_processor_importers_systrace_systrace_parser",
+        ":src_trace_processor_importers_zip_full",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_metrics",
@@ -2013,6 +2014,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/importers/zip:full
+perfetto_filegroup(
+    name = "src_trace_processor_importers_zip_full",
+    srcs = [
+        "src/trace_processor/importers/zip/zip_trace_reader.cc",
+        "src/trace_processor/importers/zip/zip_trace_reader.h",
+    ],
+)
+
 # GN target: //src/trace_processor/metrics/sql/android:android
 perfetto_filegroup(
     name = "src_trace_processor_metrics_sql_android_android",
@@ -2598,6 +2608,14 @@
     ],
 )
 
+# GN target: //src/trace_processor/perfetto_sql/stdlib/gpu:gpu
+perfetto_filegroup(
+    name = "src_trace_processor_perfetto_sql_stdlib_gpu_gpu",
+    srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/gpu/frequency.sql",
+    ],
+)
+
 # GN target: //src/trace_processor/perfetto_sql/stdlib/graphs:graphs
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_graphs_graphs",
@@ -2625,10 +2643,19 @@
     ],
 )
 
+# GN target: //src/trace_processor/perfetto_sql/stdlib/memory/android:android
+perfetto_filegroup(
+    name = "src_trace_processor_perfetto_sql_stdlib_memory_android_android",
+    srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/memory/android/gpu.sql",
+    ],
+)
+
 # GN target: //src/trace_processor/perfetto_sql/stdlib/memory/linux:linux
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_memory_linux_linux",
     srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/memory/linux/general.sql",
         "src/trace_processor/perfetto_sql/stdlib/memory/linux/high_watermark.sql",
         "src/trace_processor/perfetto_sql/stdlib/memory/linux/process.sql",
     ],
@@ -2746,9 +2773,11 @@
         ":src_trace_processor_perfetto_sql_stdlib_cpu_cpu",
         ":src_trace_processor_perfetto_sql_stdlib_cpu_utilization_utilization",
         ":src_trace_processor_perfetto_sql_stdlib_deprecated_v42_common_common",
+        ":src_trace_processor_perfetto_sql_stdlib_gpu_gpu",
         ":src_trace_processor_perfetto_sql_stdlib_graphs_graphs",
         ":src_trace_processor_perfetto_sql_stdlib_intervals_intervals",
         ":src_trace_processor_perfetto_sql_stdlib_linux_linux",
+        ":src_trace_processor_perfetto_sql_stdlib_memory_android_android",
         ":src_trace_processor_perfetto_sql_stdlib_memory_linux_linux",
         ":src_trace_processor_perfetto_sql_stdlib_memory_memory",
         ":src_trace_processor_perfetto_sql_stdlib_pkvm_pkvm",
@@ -5993,6 +6022,7 @@
         ":src_trace_processor_importers_systrace_full",
         ":src_trace_processor_importers_systrace_systrace_line",
         ":src_trace_processor_importers_systrace_systrace_parser",
+        ":src_trace_processor_importers_zip_full",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_metrics",
@@ -6172,6 +6202,7 @@
         ":src_trace_processor_importers_systrace_full",
         ":src_trace_processor_importers_systrace_systrace_line",
         ":src_trace_processor_importers_systrace_systrace_parser",
+        ":src_trace_processor_importers_zip_full",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_metrics",
@@ -6411,6 +6442,7 @@
         ":src_trace_processor_importers_systrace_full",
         ":src_trace_processor_importers_systrace_systrace_line",
         ":src_trace_processor_importers_systrace_systrace_parser",
+        ":src_trace_processor_importers_zip_full",
         ":src_trace_processor_lib",
         ":src_trace_processor_metatrace",
         ":src_trace_processor_metrics_metrics",
diff --git a/CHANGELOG b/CHANGELOG
index 70cb79c..8aae7f5 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,10 +1,13 @@
 Unreleased:
   Tracing service and probes:
-    * 
+    *
   SQL Standard library:
     * Added megacycles support to CPU package. Added tables:
       `cpu_cycles_per_process`, `cpu_cycles_per_thread` and
       `cpu_cycles_per_cpu`.
+    * Improved `memory` package. Added `memory.linux.process`,
+      `memory.linux.high_watermark` and `memory.android.gpu` modules.
+    * Created `gpu` package with `gpu.frequency` module.
     * Migrated `sched.utilization` package to `cpu.utilization`.
   Trace Processor:
     * Added "time to initial display" and "time to full display" metrics to
diff --git a/include/perfetto/ext/base/getopt.h b/include/perfetto/ext/base/getopt.h
index bf993fc..abf8cca 100644
--- a/include/perfetto/ext/base/getopt.h
+++ b/include/perfetto/ext/base/getopt.h
@@ -45,7 +45,7 @@
     ::perfetto::base::getopt_compat::required_argument;
 
 #else
-#include <getopt.h>
+#include <getopt.h>  // IWYU pragma: export
 #endif
 
 #endif  // INCLUDE_PERFETTO_EXT_BASE_GETOPT_H_
diff --git a/protos/perfetto/common/observable_events.proto b/protos/perfetto/common/observable_events.proto
index 85767a6..b841a0b 100644
--- a/protos/perfetto/common/observable_events.proto
+++ b/protos/perfetto/common/observable_events.proto
@@ -63,6 +63,9 @@
     // consumer has no idea of what is the TSID of its own tracing session and
     // there is no other good way to plumb it.
     optional int64 tracing_session_id = 1;
+
+    // The trigger name of the CLONE_SNAPSHOT trigger which was hit.
+    optional string trigger_name = 2;
   }
 
   repeated DataSourceInstanceStateChange instance_state_changes = 1;
diff --git a/src/android_internal/statsd_logging.cc b/src/android_internal/statsd_logging.cc
index a164adf..09b55a2 100644
--- a/src/android_internal/statsd_logging.cc
+++ b/src/android_internal/statsd_logging.cc
@@ -16,12 +16,11 @@
 
 #include "src/android_internal/statsd_logging.h"
 
-#include <string.h>
+#include <cstdint>
 
 #include <statslog_perfetto.h>
 
-namespace perfetto {
-namespace android_internal {
+namespace perfetto::android_internal {
 
 void StatsdLogUploadEvent(PerfettoStatsdAtom atom,
                           int64_t uuid_lsb,
@@ -35,5 +34,4 @@
   stats_write(PERFETTO_TRIGGER, static_cast<int32_t>(atom), trigger_name);
 }
 
-}  // namespace android_internal
-}  // namespace perfetto
+}  // namespace perfetto::android_internal
diff --git a/src/android_stats/perfetto_atoms.h b/src/android_stats/perfetto_atoms.h
index 1981a77..6352042 100644
--- a/src/android_stats/perfetto_atoms.h
+++ b/src/android_stats/perfetto_atoms.h
@@ -27,6 +27,8 @@
   // Checkpoints inside perfetto_cmd before tracing is finished.
   kTraceBegin = 1,
   kBackgroundTraceBegin = 2,
+  kCloneTraceBegin = 55,
+  kCloneTriggerTraceBegin = 56,
   kOnConnect = 3,
 
   // Guardrails inside perfetto_cmd before tracing is finished.
@@ -105,7 +107,7 @@
   // longer supports uploading traces using Dropbox.
   // reserved 5, 6, 7;
 
-  // Contained status of guardrail state initalization and upload limit in
+  // Contained status of guardrail state initialization and upload limit in
   // perfetto_cmd. Removed as perfetto no longer manages stateful guardrails
   // reserved 44, 45, 46;
 };
diff --git a/src/android_stats/statsd_logging_helper.cc b/src/android_stats/statsd_logging_helper.cc
index c943027..1f5011a 100644
--- a/src/android_stats/statsd_logging_helper.cc
+++ b/src/android_stats/statsd_logging_helper.cc
@@ -16,10 +16,12 @@
 
 #include "src/android_stats/statsd_logging_helper.h"
 
+#include <cstdint>
 #include <string>
+#include <vector>
 
 #include "perfetto/base/build_config.h"
-#include "perfetto/base/compiler.h"
+#include "src/android_stats/perfetto_atoms.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && \
     PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
@@ -27,8 +29,7 @@
 #include "src/android_internal/statsd_logging.h"       // nogncheck
 #endif
 
-namespace perfetto {
-namespace android_stats {
+namespace perfetto::android_stats {
 
 // Make sure we don't accidentally log on non-Android tree build. Note that even
 // removing this ifdef still doesn't make uploads work on OS_ANDROID.
@@ -75,5 +76,4 @@
                            const std::vector<std::string>&) {}
 #endif
 
-}  // namespace android_stats
-}  // namespace perfetto
+}  // namespace perfetto::android_stats
diff --git a/src/android_stats/statsd_logging_helper.h b/src/android_stats/statsd_logging_helper.h
index 5f0911f..0ba2d65 100644
--- a/src/android_stats/statsd_logging_helper.h
+++ b/src/android_stats/statsd_logging_helper.h
@@ -18,6 +18,7 @@
 #define SRC_ANDROID_STATS_STATSD_LOGGING_HELPER_H_
 
 #include <stdint.h>
+#include <optional>
 #include <string>
 #include <vector>
 
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 2ccd24e..e11a485 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -16,46 +16,47 @@
 
 #include "src/perfetto_cmd/perfetto_cmd.h"
 
-#include "perfetto/base/build_config.h"
-#include "perfetto/base/proc_utils.h"
-#include "perfetto/ext/base/scoped_file.h"
-#include "perfetto/ext/base/string_splitter.h"
-
 #include <fcntl.h>
 #include <stdio.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <time.h>
 
-// For dup() (and _setmode() on windows).
-#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
-#include <fcntl.h>
-#include <io.h>
-#else
-#include <unistd.h>
-#endif
-
+#include <algorithm>
+#include <array>
 #include <atomic>
 #include <chrono>
-#include <fstream>
+#include <cinttypes>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <functional>
 #include <iostream>
 #include <iterator>
+#include <memory>
 #include <mutex>
+#include <optional>
 #include <random>
-#include <sstream>
+#include <string>
 #include <thread>
+#include <utility>
+#include <vector>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/base/compiler.h"
 #include "perfetto/base/logging.h"
-#include "perfetto/base/time.h"
-#include "perfetto/ext/base/android_utils.h"
+#include "perfetto/base/proc_utils.h"         // IWYU pragma: keep
+#include "perfetto/ext/base/android_utils.h"  // IWYU pragma: keep
 #include "perfetto/ext/base/ctrl_c_handler.h"
 #include "perfetto/ext/base/file_utils.h"
-#include "perfetto/ext/base/getopt.h"
+#include "perfetto/ext/base/getopt.h"  // IWYU pragma: keep
 #include "perfetto/ext/base/no_destructor.h"
 #include "perfetto/ext/base/pipe.h"
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/string_splitter.h"
+#include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
-#include "perfetto/ext/base/temp_file.h"
+#include "perfetto/ext/base/thread_task_runner.h"
 #include "perfetto/ext/base/thread_utils.h"
 #include "perfetto/ext/base/utils.h"
 #include "perfetto/ext/base/uuid.h"
@@ -64,11 +65,14 @@
 #include "perfetto/ext/traced/traced.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/trace_packet.h"
-#include "perfetto/protozero/proto_utils.h"
-#include "perfetto/tracing/core/data_source_descriptor.h"
+#include "perfetto/ext/tracing/core/tracing_service.h"
+#include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
+#include "perfetto/tracing/core/flush_flags.h"
+#include "perfetto/tracing/core/forward_decls.h"
 #include "perfetto/tracing/core/trace_config.h"
-#include "perfetto/tracing/core/tracing_service_state.h"
 #include "perfetto/tracing/default_socket.h"
+#include "protos/perfetto/common/data_source_descriptor.gen.h"
+#include "src/android_stats/perfetto_atoms.h"
 #include "src/android_stats/statsd_logging_helper.h"
 #include "src/perfetto_cmd/bugreport_path.h"
 #include "src/perfetto_cmd/config.h"
@@ -81,6 +85,14 @@
 #include "protos/perfetto/common/tracing_service_state.gen.h"
 #include "protos/perfetto/common/track_event_descriptor.gen.h"
 
+// For dup() (and _setmode() on windows).
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <fcntl.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
 namespace perfetto {
 namespace {
 
@@ -92,7 +104,7 @@
 class LoggingErrorReporter : public ErrorReporter {
  public:
   LoggingErrorReporter(std::string file_name, const char* config)
-      : file_name_(file_name), config_(config) {}
+      : file_name_(std::move(file_name)), config_(config) {}
 
   void AddError(size_t row,
                 size_t column,
@@ -1014,7 +1026,14 @@
     std::this_thread::sleep_for(std::chrono::milliseconds(dist(minstd)));
   }
 
-  if (trace_config_->trigger_config().trigger_timeout_ms() == 0) {
+  if (clone_tsid_) {
+    if (snapshot_trigger_name_.empty()) {
+      LogUploadEvent(PerfettoStatsdAtom::kCloneTraceBegin);
+    } else {
+      LogUploadEvent(PerfettoStatsdAtom::kCloneTriggerTraceBegin,
+                     snapshot_trigger_name_);
+    }
+  } else if (trace_config_->trigger_config().trigger_timeout_ms() == 0) {
     LogUploadEvent(PerfettoStatsdAtom::kTraceBegin);
   } else {
     LogUploadEvent(PerfettoStatsdAtom::kBackgroundTraceBegin);
@@ -1533,11 +1552,15 @@
   }
   if (observable_events.has_clone_trigger_hit()) {
     int64_t tsid = observable_events.clone_trigger_hit().tracing_session_id();
-    OnCloneSnapshotTriggerReceived(static_cast<TracingSessionID>(tsid));
+    std::string trigger_name =
+        observable_events.clone_trigger_hit().trigger_name();
+    OnCloneSnapshotTriggerReceived(static_cast<TracingSessionID>(tsid),
+                                   std::move(trigger_name));
   }
 }
 
-void PerfettoCmd::OnCloneSnapshotTriggerReceived(TracingSessionID tsid) {
+void PerfettoCmd::OnCloneSnapshotTriggerReceived(TracingSessionID tsid,
+                                                 std::string trigger_name) {
   std::string cmdline;
   cmdline.reserve(128);
   ArgsAppend(&cmdline, "perfetto");
@@ -1555,13 +1578,15 @@
   } else {
     PERFETTO_FATAL("Cannot use CLONE_SNAPSHOT with the current cmdline args");
   }
-  CloneSessionOnThread(tsid, cmdline, kSingleExtraThread, nullptr);
+  CloneSessionOnThread(tsid, cmdline, kSingleExtraThread,
+                       std::move(trigger_name), nullptr);
 }
 
 void PerfettoCmd::CloneSessionOnThread(
     TracingSessionID tsid,
     const std::string& cmdline,
     CloneThreadMode thread_mode,
+    std::string trigger_name,
     std::function<void()> on_clone_callback) {
   PERFETTO_DLOG("Creating snapshot for tracing session %" PRIu64, tsid);
 
@@ -1583,7 +1608,7 @@
   std::string trace_config_copy = trace_config_->SerializeAsString();
 
   snapshot_threads_.back().PostTask(
-      [tsid, cmdline, trace_config_copy, on_clone_callback] {
+      [tsid, cmdline, trace_config_copy, trigger_name, on_clone_callback] {
         int argc = 0;
         char* argv[32];
         // `splitter` needs to live on the stack for the whole scope as it owns
@@ -1595,6 +1620,7 @@
         }
         perfetto::PerfettoCmd cmd;
         cmd.snapshot_config_ = std::move(trace_config_copy);
+        cmd.snapshot_trigger_name_ = std::move(trigger_name);
         cmd.on_session_cloned_ = on_clone_callback;
         auto cmdline_res = cmd.ParseCmdlineAndMaybeDaemonize(argc, argv);
         PERFETTO_CHECK(!cmdline_res.has_value());  // No daemonization expected.
@@ -1686,7 +1712,7 @@
     ArgsAppend(&cmdline, "--clone-for-bugreport");
     ArgsAppend(&cmdline, "--out");
     ArgsAppend(&cmdline, out_path);
-    CloneSessionOnThread(it->tsid, cmdline, kNewThreadPerRequest, sync_fn);
+    CloneSessionOnThread(it->tsid, cmdline, kNewThreadPerRequest, "", sync_fn);
   }  // for(sessions)
 
   PERFETTO_DLOG("Issuing %zu CloneSession requests", num_sessions);
@@ -1720,6 +1746,15 @@
   android_stats::MaybeLogUploadEvent(atom, uuid.lsb(), uuid.msb());
 }
 
+void PerfettoCmd::LogUploadEvent(PerfettoStatsdAtom atom,
+                                 const std::string& trigger_name) {
+  if (!statsd_logging_)
+    return;
+  base::Uuid uuid(uuid_);
+  android_stats::MaybeLogUploadEvent(atom, uuid.lsb(), uuid.msb(),
+                                     trigger_name);
+}
+
 void PerfettoCmd::LogTriggerEvents(
     PerfettoTriggerAtom atom,
     const std::vector<std::string>& trigger_names) {
diff --git a/src/perfetto_cmd/perfetto_cmd.h b/src/perfetto_cmd/perfetto_cmd.h
index 844afaf..160a701 100644
--- a/src/perfetto_cmd/perfetto_cmd.h
+++ b/src/perfetto_cmd/perfetto_cmd.h
@@ -17,8 +17,7 @@
 #ifndef SRC_PERFETTO_CMD_PERFETTO_CMD_H_
 #define SRC_PERFETTO_CMD_PERFETTO_CMD_H_
 
-#include <time.h>
-
+#include <cstdint>
 #include <functional>
 #include <list>
 #include <memory>
@@ -32,9 +31,12 @@
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/thread_task_runner.h"
 #include "perfetto/ext/base/unix_task_runner.h"
+#include "perfetto/ext/base/uuid.h"
 #include "perfetto/ext/base/weak_ptr.h"
+#include "perfetto/ext/tracing/core/basic_types.h"
 #include "perfetto/ext/tracing/core/consumer.h"
 #include "perfetto/ext/tracing/ipc/consumer_ipc_client.h"
+#include "perfetto/tracing/core/forward_decls.h"
 #include "src/android_stats/perfetto_atoms.h"
 #include "src/perfetto_cmd/packet_writer.h"
 
@@ -86,6 +88,7 @@
   void CloneSessionOnThread(TracingSessionID,
                             const std::string& cmdline,  // \0 separated.
                             CloneThreadMode,
+                            std::string clone_trigger_name,
                             std::function<void()> on_clone_callback);
   void OnTimeout();
   bool is_detach() const { return !detach_key_.empty(); }
@@ -126,7 +129,8 @@
   // will have no effect.
   void NotifyBgProcessPipe(BgProcessStatus status);
 
-  void OnCloneSnapshotTriggerReceived(TracingSessionID);
+  void OnCloneSnapshotTriggerReceived(TracingSessionID,
+                                      std::string trigger_name);
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
   static base::ScopedFile CreateUnlinkedTmpFile();
@@ -135,6 +139,7 @@
   void ReportTraceToAndroidFrameworkOrCrash();
 #endif
   void LogUploadEvent(PerfettoStatsdAtom atom);
+  void LogUploadEvent(PerfettoStatsdAtom atom, const std::string& trigger_name);
   void LogTriggerEvents(PerfettoTriggerAtom atom,
                         const std::vector<std::string>& trigger_names);
 
@@ -185,6 +190,7 @@
   std::list<base::ThreadTaskRunner> snapshot_threads_;
   int snapshot_count_ = 0;
   std::string snapshot_config_;
+  std::string snapshot_trigger_name_;
 
   base::WeakPtrFactory<PerfettoCmd> weak_factory_{this};
 };
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 2a61ec6..802f9bc 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -177,6 +177,7 @@
       "importers/proto:full",
       "importers/proto:minimal",
       "importers/systrace:full",
+      "importers/zip:full",
       "metrics",
       "perfetto_sql/engine",
       "perfetto_sql/intrinsics/functions",
diff --git a/src/trace_processor/forwarding_trace_parser.cc b/src/trace_processor/forwarding_trace_parser.cc
index 369917d..a3b6a7f 100644
--- a/src/trace_processor/forwarding_trace_parser.cc
+++ b/src/trace_processor/forwarding_trace_parser.cc
@@ -29,6 +29,7 @@
 #include "src/trace_processor/trace_reader_registry.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/trace_type.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -53,7 +54,7 @@
     case kSystraceTraceType:
     case kGzipTraceType:
     case kCtraceTraceType:
-    case kAndroidBugreportTraceType:
+    case kZipFile:
       return std::nullopt;
 
     case kPerfDataTraceType:
@@ -102,26 +103,11 @@
   reader_ = std::move(*reader_or);
 
   PERFETTO_DLOG("%s detected", ToString(trace_type));
-  std::optional<TraceSorter::SortingMode> minimum_sorting_mode =
-      GetMinimumSortingMode(trace_type, *context_);
+  UpdateSorterForTraceType(trace_type);
 
-  if (minimum_sorting_mode.has_value()) {
-    if (!context_->sorter) {
-      context_->sorter.reset(new TraceSorter(context_, *minimum_sorting_mode));
-    }
-
-    switch (context_->sorter->sorting_mode()) {
-      case TraceSorter::SortingMode::kDefault:
-        PERFETTO_CHECK(minimum_sorting_mode ==
-                       TraceSorter::SortingMode::kDefault);
-        break;
-      case TraceSorter::SortingMode::kFullSort:
-        break;
-    }
-  }
-
-  // TODO(carlscab) Make sure kProtoTraceType and kSystraceTraceType are parsed
-  // first so that we do not get issues with SetPidZeroIsUpidZeroIdleProcess()
+  // TODO(b/334978369) Make sure kProtoTraceType and kSystraceTraceType are
+  // parsed first so that we do not get issues with
+  // SetPidZeroIsUpidZeroIdleProcess()
   if (trace_type == kProtoTraceType || trace_type == kSystraceTraceType) {
     context_->process_tracker->SetPidZeroIsUpidZeroIdleProcess();
   }
@@ -129,6 +115,27 @@
   return base::OkStatus();
 }
 
+void ForwardingTraceParser::UpdateSorterForTraceType(TraceType trace_type) {
+  std::optional<TraceSorter::SortingMode> minimum_sorting_mode =
+      GetMinimumSortingMode(trace_type, *context_);
+  if (!minimum_sorting_mode.has_value()) {
+    return;
+  }
+
+  if (!context_->sorter) {
+    context_->sorter.reset(new TraceSorter(context_, *minimum_sorting_mode));
+  }
+
+  switch (context_->sorter->sorting_mode()) {
+    case TraceSorter::SortingMode::kDefault:
+      PERFETTO_CHECK(minimum_sorting_mode ==
+                     TraceSorter::SortingMode::kDefault);
+      break;
+    case TraceSorter::SortingMode::kFullSort:
+      break;
+  }
+}
+
 base::Status ForwardingTraceParser::Parse(TraceBlobView blob) {
   // If this is the first Parse() call, guess the trace type and create the
   // appropriate parser.
diff --git a/src/trace_processor/forwarding_trace_parser.h b/src/trace_processor/forwarding_trace_parser.h
index 1f32938..12fe447 100644
--- a/src/trace_processor/forwarding_trace_parser.h
+++ b/src/trace_processor/forwarding_trace_parser.h
@@ -20,6 +20,7 @@
 #include "perfetto/base/status.h"
 #include "perfetto/trace_processor/trace_blob_view.h"
 #include "src/trace_processor/importers/common/chunked_trace_reader.h"
+#include "src/trace_processor/util/trace_type.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -37,6 +38,7 @@
 
  private:
   base::Status Init(const TraceBlobView&);
+  void UpdateSorterForTraceType(TraceType trace_type);
   TraceProcessorContext* const context_;
   std::unique_ptr<ChunkedTraceReader> reader_;
 };
diff --git a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
index 87aa0f5..1d46efc 100644
--- a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
+++ b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.cc
@@ -17,54 +17,90 @@
 #include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h"
 
 #include <algorithm>
+#include <cstddef>
 #include <optional>
+#include <string>
+#include <vector>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
 #include "perfetto/ext/base/string_utils.h"
-#include "perfetto/trace_processor/trace_blob.h"
-#include "perfetto/trace_processor/trace_blob_view.h"
+#include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "src/trace_processor/importers/android_bugreport/android_log_parser.h"
 #include "src/trace_processor/importers/common/clock_tracker.h"
 #include "src/trace_processor/importers/common/process_tracker.h"
-#include "src/trace_processor/importers/common/track_tracker.h"
 #include "src/trace_processor/types/trace_processor_context.h"
 #include "src/trace_processor/util/zip_reader.h"
 
-#include "protos/perfetto/common/builtin_clock.pbzero.h"
-
 namespace perfetto {
 namespace trace_processor {
+namespace {
+const util::ZipFile* FindBugReportFile(
+    const std::vector<util::ZipFile>& zip_file_entries) {
+  for (const auto& zf : zip_file_entries) {
+    if (base::StartsWith(zf.name(), "bugreport-") &&
+        base::EndsWith(zf.name(), ".txt")) {
+      return &zf;
+    }
+  }
+  return nullptr;
+}
 
-AndroidBugreportParser::AndroidBugreportParser(TraceProcessorContext* ctx)
-    : context_(ctx), zip_reader_(new util::ZipReader()) {}
+std::optional<int32_t> ExtractYearFromBugReportFilename(
+    const std::string& filename) {
+  // Typical name: "bugreport-product-TP1A.220623.001-2022-06-24-16-24-37.txt".
+  auto year_str =
+      filename.substr(filename.size() - strlen("2022-12-31-23-59-00.txt"), 4);
+  return base::StringToInt32(year_str);
+}
+
+}  // namespace
+
+// static
+bool AndroidBugreportParser::IsAndroidBugReport(
+    const std::vector<util::ZipFile>& zip_file_entries) {
+  if (const util::ZipFile* file = FindBugReportFile(zip_file_entries);
+      file != nullptr) {
+    return ExtractYearFromBugReportFilename(file->name()).has_value();
+  }
+
+  return false;
+}
+
+// static
+util::Status AndroidBugreportParser::Parse(
+    TraceProcessorContext* context,
+    std::vector<util::ZipFile> zip_file_entries) {
+  return AndroidBugreportParser(context, std::move(zip_file_entries))
+      .ParseImpl();
+}
+
+AndroidBugreportParser::AndroidBugreportParser(
+    TraceProcessorContext* context,
+    std::vector<util::ZipFile> zip_file_entries)
+    : context_(context), zip_file_entries_(std::move(zip_file_entries)) {}
 
 AndroidBugreportParser::~AndroidBugreportParser() = default;
 
-util::Status AndroidBugreportParser::Parse(TraceBlobView tbv) {
-  if (!first_chunk_seen_) {
-    first_chunk_seen_ = true;
-    // All logs in Android bugreports use wall time (which creates problems
-    // in case of early boot events before NTP kicks in, which get emitted as
-    // 1970), but that is the state of affairs.
-    context_->clock_tracker->SetTraceTimeClock(
-        protos::pbzero::BUILTIN_CLOCK_REALTIME);
-  }
-
-  return zip_reader_->Parse(tbv.data(), tbv.size());
-}
-
-void AndroidBugreportParser::NotifyEndOfFile() {
+util::Status AndroidBugreportParser::ParseImpl() {
+  // All logs in Android bugreports use wall time (which creates problems
+  // in case of early boot events before NTP kicks in, which get emitted as
+  // 1970), but that is the state of affairs.
+  context_->clock_tracker->SetTraceTimeClock(
+      protos::pbzero::BUILTIN_CLOCK_REALTIME);
   if (!DetectYearAndBrFilename()) {
     context_->storage->IncrementStats(stats::android_br_parse_errors);
-    return;
+    return base::ErrStatus("Zip file does not contain bugreport file.");
   }
 
   ParsePersistentLogcat();
   ParseDumpstateTxt();
   SortAndStoreLogcat();
+  return base::OkStatus();
 }
 
 void AndroidBugreportParser::ParseDumpstateTxt() {
+  PERFETTO_CHECK(dumpstate_file_);
   // Dumpstate is organized in a two level hierarchy, beautifully flattened into
   // one text file with load bearing ----- markers:
   // 1. Various dumpstate sections, examples:
@@ -95,80 +131,81 @@
   // Here we put each line in a dedicated table, android_dumpstate, keeping
   // track of the dumpstate `section` and dumpsys `service`.
   AndroidLogParser log_parser(br_year_, context_->storage.get());
-  util::ZipFile* zf = zip_reader_->Find(dumpstate_fname_);
   StringId section_id = StringId::Null();  // The current dumpstate section.
   StringId service_id = StringId::Null();  // The current dumpsys service.
   static constexpr size_t npos = base::StringView::npos;
   enum { OTHER = 0, DUMPSYS, LOG } cur_sect = OTHER;
-  zf->DecompressLines([&](const std::vector<base::StringView>& lines) {
-    // Optimization for ParseLogLines() below. Avoids ctor/dtor-ing a new vector
-    // on every line.
-    std::vector<base::StringView> log_line(1);
-    for (const base::StringView& line : lines) {
-      if (line.StartsWith("------ ") && line.EndsWith(" ------")) {
-        // These lines mark the beginning and end of dumpstate sections:
-        // ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
-        // ------ 0.356s was the duration of 'DUMPSYS CRITICAL' ------
-        base::StringView section = line.substr(7);
-        section = section.substr(0, section.size() - 7);
-        bool end_marker = section.find("was the duration of") != npos;
-        service_id = StringId::Null();
-        if (end_marker) {
-          section_id = StringId::Null();
-        } else {
-          section_id = context_->storage->InternString(section);
-          cur_sect = OTHER;
-          if (section.StartsWith("DUMPSYS")) {
-            cur_sect = DUMPSYS;
-          } else if (section.StartsWith("SYSTEM LOG") ||
-                     section.StartsWith("EVENT LOG") ||
-                     section.StartsWith("RADIO LOG")) {
-            // KERNEL LOG is deliberately omitted because SYSTEM LOG is a
-            // superset. KERNEL LOG contains all dupes.
-            cur_sect = LOG;
-          } else if (section.StartsWith("BLOCK STAT")) {
-            // Coalesce all the block stats into one section. Otherwise they
-            // pollute the table with one section per block device.
-            section_id = context_->storage->InternString("BLOCK STAT");
+  dumpstate_file_->DecompressLines(
+      [&](const std::vector<base::StringView>& lines) {
+        // Optimization for ParseLogLines() below. Avoids ctor/dtor-ing a new
+        // vector on every line.
+        std::vector<base::StringView> log_line(1);
+        for (const base::StringView& line : lines) {
+          if (line.StartsWith("------ ") && line.EndsWith(" ------")) {
+            // These lines mark the beginning and end of dumpstate sections:
+            // ------ DUMPSYS CRITICAL (/system/bin/dumpsys) ------
+            // ------ 0.356s was the duration of 'DUMPSYS CRITICAL' ------
+            base::StringView section = line.substr(7);
+            section = section.substr(0, section.size() - 7);
+            bool end_marker = section.find("was the duration of") != npos;
+            service_id = StringId::Null();
+            if (end_marker) {
+              section_id = StringId::Null();
+            } else {
+              section_id = context_->storage->InternString(section);
+              cur_sect = OTHER;
+              if (section.StartsWith("DUMPSYS")) {
+                cur_sect = DUMPSYS;
+              } else if (section.StartsWith("SYSTEM LOG") ||
+                         section.StartsWith("EVENT LOG") ||
+                         section.StartsWith("RADIO LOG")) {
+                // KERNEL LOG is deliberately omitted because SYSTEM LOG is a
+                // superset. KERNEL LOG contains all dupes.
+                cur_sect = LOG;
+              } else if (section.StartsWith("BLOCK STAT")) {
+                // Coalesce all the block stats into one section. Otherwise they
+                // pollute the table with one section per block device.
+                section_id = context_->storage->InternString("BLOCK STAT");
+              }
+            }
+            continue;
           }
+          // Skip end marker lines for dumpsys sections.
+          if (cur_sect == DUMPSYS && line.StartsWith("--------- ") &&
+              line.find("was the duration of dumpsys") != npos) {
+            service_id = StringId::Null();
+            continue;
+          }
+          if (cur_sect == DUMPSYS && service_id.is_null() &&
+              line.StartsWith(
+                  "----------------------------------------------")) {
+            continue;
+          }
+          if (cur_sect == DUMPSYS && line.StartsWith("DUMP OF SERVICE")) {
+            // DUMP OF SERVICE [CRITICAL|HIGH] ServiceName:
+            base::StringView svc = line.substr(line.rfind(' ') + 1);
+            svc = svc.substr(0, svc.size() - 1);
+            service_id = context_->storage->InternString(svc);
+          } else if (cur_sect == LOG) {
+            // Parse the non-persistent logcat and append to `log_events_`,
+            // together with the persistent one previously parsed by
+            // ParsePersistentLogcat(). Skips entries that are already seen in
+            // the persistent logcat, handling us vs ms truncation.
+            PERFETTO_DCHECK(log_line.size() == 1);
+            log_line[0] = line;
+            log_parser.ParseLogLines(log_line, &log_events_,
+                                     log_events_last_sorted_idx_);
+          }
+
+          if (build_fpr_.empty() && line.StartsWith("Build fingerprint:")) {
+            build_fpr_ = line.substr(20, line.size() - 20).ToStdString();
+          }
+
+          // Append the line to the android_dumpstate table.
+          context_->storage->mutable_android_dumpstate_table()->Insert(
+              {section_id, service_id, context_->storage->InternString(line)});
         }
-        continue;
-      }
-      // Skip end marker lines for dumpsys sections.
-      if (cur_sect == DUMPSYS && line.StartsWith("--------- ") &&
-          line.find("was the duration of dumpsys") != npos) {
-        service_id = StringId::Null();
-        continue;
-      }
-      if (cur_sect == DUMPSYS && service_id.is_null() &&
-          line.StartsWith("----------------------------------------------")) {
-        continue;
-      }
-      if (cur_sect == DUMPSYS && line.StartsWith("DUMP OF SERVICE")) {
-        // DUMP OF SERVICE [CRITICAL|HIGH] ServiceName:
-        base::StringView svc = line.substr(line.rfind(' ') + 1);
-        svc = svc.substr(0, svc.size() - 1);
-        service_id = context_->storage->InternString(svc);
-      } else if (cur_sect == LOG) {
-        // Parse the non-persistent logcat and append to `log_events_`, together
-        // with the persistent one previously parsed by ParsePersistentLogcat().
-        // Skips entries that are already seen in the persistent logcat,
-        // handling us vs ms truncation.
-        PERFETTO_DCHECK(log_line.size() == 1);
-        log_line[0] = line;
-        log_parser.ParseLogLines(log_line, &log_events_,
-                                 log_events_last_sorted_idx_);
-      }
-
-      if (build_fpr_.empty() && line.StartsWith("Build fingerprint:")) {
-        build_fpr_ = line.substr(20, line.size() - 20).ToStdString();
-      }
-
-      // Append the line to the android_dumpstate table.
-      context_->storage->mutable_android_dumpstate_table()->Insert(
-          {section_id, service_id, context_->storage->InternString(line)});
-    }
-  });
+      });
 }
 
 void AndroidBugreportParser::ParsePersistentLogcat() {
@@ -182,22 +219,23 @@
   // Sort files to ease the job of the subsequent line-based sort. Unfortunately
   // lines within each file are not 100% timestamp-ordered, due to things like
   // kernel messages where log time != event time.
-  std::vector<std::pair<uint64_t, std::string>> log_paths;
-  for (const util::ZipFile& zf : zip_reader_->files()) {
+  std::vector<std::pair<uint64_t, const util::ZipFile*>> log_files;
+  for (const util::ZipFile& zf : zip_file_entries_) {
     if (base::StartsWith(zf.name(), "FS/data/misc/logd/logcat") &&
         !base::EndsWith(zf.name(), "logcat.id")) {
-      log_paths.emplace_back(std::make_pair(zf.GetDatetime(), zf.name()));
+      log_files.push_back(std::make_pair(zf.GetDatetime(), &zf));
     }
   }
-  std::sort(log_paths.begin(), log_paths.end());
+
+  std::sort(log_files.begin(), log_files.end());
 
   // Push all events into the AndroidLogParser. It will take care of string
   // interning into the pool. Appends entries into `log_events`.
-  for (const auto& kv : log_paths) {
-    util::ZipFile* zf = zip_reader_->Find(kv.second);
-    zf->DecompressLines([&](const std::vector<base::StringView>& lines) {
-      log_parser.ParseLogLines(lines, &log_events_);
-    });
+  for (const auto& log_file : log_files) {
+    log_file.second->DecompressLines(
+        [&](const std::vector<base::StringView>& lines) {
+          log_parser.ParseLogLines(lines, &log_events_);
+        });
   }
 
   // Do an initial sorting pass. This is not the final sorting because we
@@ -230,30 +268,20 @@
 // This is obviously bugged for cases of bugreports collected across new year
 // but we'll live with that.
 bool AndroidBugreportParser::DetectYearAndBrFilename() {
-  const util::ZipFile* br_file = nullptr;
-  for (const auto& zf : zip_reader_->files()) {
-    if (base::StartsWith(zf.name(), "bugreport-") &&
-        base::EndsWith(zf.name(), ".txt")) {
-      br_file = &zf;
-      break;
-    }
-  }
-
+  const util::ZipFile* br_file = FindBugReportFile(zip_file_entries_);
   if (!br_file) {
     PERFETTO_ELOG("Could not find bugreport-*.txt in the zip file");
     return false;
   }
 
-  // Typical name: "bugreport-product-TP1A.220623.001-2022-06-24-16-24-37.txt".
-  auto year_str = br_file->name().substr(
-      br_file->name().size() - strlen("2022-12-31-23-59-00.txt"), 4);
-  std::optional<int32_t> year = base::StringToInt32(year_str);
+  std::optional<int32_t> year =
+      ExtractYearFromBugReportFilename(br_file->name());
   if (!year.has_value()) {
     PERFETTO_ELOG("Could not parse the year from %s", br_file->name().c_str());
     return false;
   }
   br_year_ = *year;
-  dumpstate_fname_ = br_file->name();
+  dumpstate_file_ = br_file;
   return true;
 }
 
diff --git a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
index 9969159..a9ca322 100644
--- a/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
+++ b/src/trace_processor/importers/android_bugreport/android_bugreport_parser.h
@@ -17,8 +17,11 @@
 #ifndef SRC_TRACE_PROCESSOR_IMPORTERS_ANDROID_BUGREPORT_ANDROID_BUGREPORT_PARSER_H_
 #define SRC_TRACE_PROCESSOR_IMPORTERS_ANDROID_BUGREPORT_ANDROID_BUGREPORT_PARSER_H_
 
-#include "src/trace_processor/importers/common/chunked_trace_reader.h"
-#include "src/trace_processor/storage/trace_storage.h"
+#include <cstddef>
+#include <vector>
+
+#include "perfetto/trace_processor/status.h"
+#include "src/trace_processor/util/zip_reader.h"
 
 namespace perfetto {
 namespace trace_processor {
@@ -31,16 +34,19 @@
 class TraceProcessorContext;
 
 // Trace importer for Android bugreport.zip archives.
-class AndroidBugreportParser : public ChunkedTraceReader {
+class AndroidBugreportParser {
  public:
-  explicit AndroidBugreportParser(TraceProcessorContext*);
-  ~AndroidBugreportParser() override;
-
-  // ChunkedTraceReader implementation.
-  util::Status Parse(TraceBlobView) override;
-  void NotifyEndOfFile() override;
+  static bool IsAndroidBugReport(
+      const std::vector<util::ZipFile>& zip_file_entries);
+  static util::Status Parse(TraceProcessorContext* context,
+                            std::vector<util::ZipFile> zip_file_entries);
 
  private:
+  AndroidBugreportParser(TraceProcessorContext* context,
+                         std::vector<util::ZipFile> zip_file_entries);
+  ~AndroidBugreportParser();
+  util::Status ParseImpl();
+
   bool DetectYearAndBrFilename();
   void ParsePersistentLogcat();
   void ParseDumpstateTxt();
@@ -48,11 +54,11 @@
   void SortLogEvents();
 
   TraceProcessorContext* const context_;
+  std::vector<util::ZipFile> zip_file_entries_;
   int br_year_ = 0;  // The year when the bugreport has been taken.
-  std::string dumpstate_fname_;  // The name of bugreport-xxx-2022-08-04....txt
+  const util::ZipFile* dumpstate_file_ =
+      nullptr;  // The bugreport-xxx-2022-08-04....txt file
   std::string build_fpr_;
-  bool first_chunk_seen_ = false;
-  std::unique_ptr<util::ZipReader> zip_reader_;
   std::vector<AndroidLogEvent> log_events_;
   size_t log_events_last_sorted_idx_ = 0;
 };
diff --git a/src/trace_processor/importers/zip/BUILD.gn b/src/trace_processor/importers/zip/BUILD.gn
new file mode 100644
index 0000000..a938fdd
--- /dev/null
+++ b/src/trace_processor/importers/zip/BUILD.gn
@@ -0,0 +1,33 @@
+# 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.
+
+source_set("full") {
+  sources = [
+    "zip_trace_reader.cc",
+    "zip_trace_reader.h",
+  ]
+  deps = [
+    "../../../../gn:default_deps",
+    "../../../../gn:zlib",
+    "../../../../include/perfetto/ext/base:base",
+    "../../../trace_processor:storage_minimal",
+    "../../types",
+    "../../util:trace_type",
+    "../../util:util",
+    "../../util:zip_reader",
+    "../android_bugreport",
+    "../common",
+    "../proto:minimal",
+  ]
+}
diff --git a/src/trace_processor/importers/zip/zip_trace_reader.cc b/src/trace_processor/importers/zip/zip_trace_reader.cc
new file mode 100644
index 0000000..20f019f
--- /dev/null
+++ b/src/trace_processor/importers/zip/zip_trace_reader.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2024 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/zip/zip_trace_reader.h"
+
+#include <algorithm>
+#include <cinttypes>
+#include <cstdint>
+#include <cstring>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/trace_blob.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/forwarding_trace_parser.h"
+#include "src/trace_processor/importers/android_bugreport/android_bugreport_parser.h"
+#include "src/trace_processor/importers/proto/proto_trace_tokenizer.h"
+#include "src/trace_processor/types/trace_processor_context.h"
+#include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/trace_type.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace {
+
+// Proto traces should always parsed first as they might contains clock sync
+// data needed to correctly parse other traces.
+// The rest of the types are sorted by position in the enum but this is not
+// something users should rely on.
+// TODO(carlscab): Proto traces with just ModuleSymbols packets should be an
+// exception. We actually need those are the very end (once whe have all the
+// Frames). Alternatively we could build a map address -> symbol during
+// tokenization and use this during parsing to resolve symbols.
+bool CompareTraceType(TraceType lhs, TraceType rhs) {
+  if (rhs == TraceType::kProtoTraceType) {
+    return false;
+  }
+  if (lhs == TraceType::kProtoTraceType) {
+    return true;
+  }
+  return lhs < rhs;
+}
+
+bool HasSymbols(const TraceBlobView& blob) {
+  bool has_symbols = false;
+  ProtoTraceTokenizer().Tokenize(blob.copy(), [&](TraceBlobView raw) {
+    protos::pbzero::TracePacket::Decoder packet(raw.data(), raw.size());
+    has_symbols = packet.has_module_symbols();
+    return base::ErrStatus("break");
+  });
+  return has_symbols;
+}
+
+}  // namespace
+
+ZipTraceReader::ZipTraceReader(TraceProcessorContext* context)
+    : context_(context) {}
+ZipTraceReader::~ZipTraceReader() = default;
+
+bool ZipTraceReader::Entry::operator<(const Entry& rhs) const {
+  // Traces with symbols should be the last ones to be read.
+  if (has_symbols) {
+    return false;
+  }
+  if (rhs.has_symbols) {
+    return true;
+  }
+  if (CompareTraceType(trace_type, rhs.trace_type)) {
+    return true;
+  }
+  if (CompareTraceType(rhs.trace_type, trace_type)) {
+    return false;
+  }
+  return std::tie(name, index) < std::tie(rhs.name, rhs.index);
+}
+
+util::Status ZipTraceReader::Parse(TraceBlobView blob) {
+  zip_reader_.Parse(blob.data(), blob.size());
+  return base::OkStatus();
+}
+
+void ZipTraceReader::NotifyEndOfFile() {
+  base::Status status = NotifyEndOfFileImpl();
+  if (!status.ok()) {
+    PERFETTO_ELOG("ZipTraceReader failed: %s", status.c_message());
+  }
+}
+
+base::Status ZipTraceReader::NotifyEndOfFileImpl() {
+  std::vector<util::ZipFile> files = zip_reader_.TakeFiles();
+
+  // Android bug reports are ZIP files and its files do not get handled
+  // separately.
+  if (AndroidBugreportParser::IsAndroidBugReport(files)) {
+    return AndroidBugreportParser::Parse(context_, std::move(files));
+  }
+
+  base::StatusOr<std::vector<Entry>> entries = ExtractEntries(std::move(files));
+  if (!entries.ok()) {
+    return entries.status();
+  }
+  std::sort(entries->begin(), entries->end());
+
+  for (Entry& e : *entries) {
+    parsers_.push_back(std::make_unique<ForwardingTraceParser>(context_));
+    auto& parser = *parsers_.back();
+    RETURN_IF_ERROR(parser.Parse(std::move(e.uncompressed_data)));
+    parser.NotifyEndOfFile();
+  }
+  return base::OkStatus();
+}
+
+base::StatusOr<std::vector<ZipTraceReader::Entry>>
+ZipTraceReader::ExtractEntries(std::vector<util::ZipFile> files) const {
+  // TODO(carlsacab): There is a lot of unnecessary copying going on here.
+  // ZipTraceReader can directly parse the ZIP file and given that we know the
+  // decompressed size we could directly decompress into TraceBlob chunks and
+  // send them to the tokenizer.
+  std::vector<Entry> entries;
+  std::vector<uint8_t> buffer;
+  for (size_t i = 0; i < files.size(); ++i) {
+    const util::ZipFile& zip_file = files[i];
+    Entry entry;
+    entry.name = zip_file.name();
+    entry.index = i;
+    RETURN_IF_ERROR(files[i].Decompress(&buffer));
+    entry.uncompressed_data =
+        TraceBlobView(TraceBlob::CopyFrom(buffer.data(), buffer.size()));
+    entry.trace_type = GuessTraceType(entry.uncompressed_data.data(),
+                                      entry.uncompressed_data.size());
+    entry.has_symbols = entry.trace_type == TraceType::kProtoTraceType &&
+                        HasSymbols(entry.uncompressed_data);
+    entries.push_back(std::move(entry));
+  }
+  return std::move(entries);
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/importers/zip/zip_trace_reader.h b/src/trace_processor/importers/zip/zip_trace_reader.h
new file mode 100644
index 0000000..d4f3d88
--- /dev/null
+++ b/src/trace_processor/importers/zip/zip_trace_reader.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 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_TRACE_PROCESSOR_IMPORTERS_ZIP_ZIP_TRACE_READER_H_
+#define SRC_TRACE_PROCESSOR_IMPORTERS_ZIP_ZIP_TRACE_READER_H_
+
+#include <cstddef>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/trace_blob_view.h"
+#include "src/trace_processor/importers/common/chunked_trace_reader.h"
+#include "src/trace_processor/util/trace_type.h"
+#include "src/trace_processor/util/zip_reader.h"
+
+namespace perfetto::trace_processor {
+
+class ForwardingTraceParser;
+class TraceProcessorContext;
+
+// Forwards files contained in a ZIP to the appropiate ChunkedTraceReader. It is
+// guaranteed that proto traces will be parsed first.
+class ZipTraceReader : public ChunkedTraceReader {
+ public:
+  explicit ZipTraceReader(TraceProcessorContext* context);
+  ~ZipTraceReader() override;
+
+  // ChunkedTraceReader implementation
+  util::Status Parse(TraceBlobView) override;
+  void NotifyEndOfFile() override;
+
+ private:
+  // Represents a file in the ZIP file. Used to sort them before sending the
+  // files one by one to a `ForwardingTraceParser` instance.
+  struct Entry {
+    // File name. Used to break ties.
+    std::string name;
+    // Position in the zip file. Used to break ties.
+    size_t index;
+    // Trace type. This is the main attribute traces are ordered by. Proto
+    // traces are always parsed first as they might contains clock sync
+    // data needed to correctly parse other traces.
+    TraceType trace_type;
+    TraceBlobView uncompressed_data;
+    // True for proto trace_types whose fist message is a ModuleSymbols packet
+    bool has_symbols = false;
+    // Comparator used to determine the order in which files in the ZIP will be
+    // read.
+    bool operator<(const Entry& rhs) const;
+  };
+
+  base::Status NotifyEndOfFileImpl();
+  base::StatusOr<std::vector<Entry>> ExtractEntries(
+      std::vector<util::ZipFile> files) const;
+  base::Status ParseEntry(Entry entry);
+
+  TraceProcessorContext* const context_;
+  util::ZipReader zip_reader_;
+  // For every file in the ZIP we will create a `ForwardingTraceParser`instance
+  // and send that file to it for tokenization. The instances are kept around
+  // here as some tokenizers might keep state that is later needed after
+  // sorting.
+  std::vector<std::unique_ptr<ForwardingTraceParser>> parsers_;
+};
+
+}  // namespace perfetto::trace_processor
+
+#endif  // SRC_TRACE_PROCESSOR_IMPORTERS_ZIP_ZIP_TRACE_READER_H_
diff --git a/src/trace_processor/metrics/sql/android/android_boot.sql b/src/trace_processor/metrics/sql/android/android_boot.sql
index 9140387..5738bff 100644
--- a/src/trace_processor/metrics/sql/android/android_boot.sql
+++ b/src/trace_processor/metrics/sql/android/android_boot.sql
@@ -19,6 +19,24 @@
 INCLUDE PERFETTO MODULE android.garbage_collection;
 INCLUDE PERFETTO MODULE android.oom_adjuster;
 
+DROP VIEW IF EXISTS android_oom_adj_intervals_with_detailed_bucket_name;
+CREATE PERFETTO VIEW android_oom_adj_intervals_with_detailed_bucket_name AS
+SELECT
+  ts,
+  dur,
+  score,
+  android_oom_adj_score_to_detailed_bucket_name(score, android_appid) AS bucket,
+  upid,
+  process_name,
+  oom_adj_id,
+  oom_adj_ts,
+  oom_adj_dur,
+  oom_adj_track_id,
+  oom_adj_thread_name,
+  oom_adj_reason,
+  oom_adj_trigger
+FROM _oom_adjuster_intervals;
+
 CREATE OR REPLACE PERFETTO FUNCTION get_durations(process_name STRING)
 RETURNS TABLE(uint_sleep_dur LONG, total_dur LONG) AS
 SELECT
@@ -41,7 +59,7 @@
   bucket,
   process_name,
   oom_adj_reason
-FROM android_oom_adj_intervals;
+FROM android_oom_adj_intervals_with_detailed_bucket_name;
 
 DROP VIEW IF EXISTS oom_adj_events_by_process_name;
 CREATE PERFETTO VIEW oom_adj_events_by_process_name AS
@@ -280,7 +298,7 @@
           NULL as name,
           bucket,
           SUM(dur) as total_dur
-        FROM android_oom_adj_intervals
+        FROM android_oom_adj_intervals_with_detailed_bucket_name
           WHERE ts > first_user_unlocked()
         GROUP BY bucket)
     ),
@@ -296,7 +314,7 @@
         process_name as name,
         bucket,
         SUM(dur) as total_dur
-      FROM android_oom_adj_intervals
+      FROM android_oom_adj_intervals_with_detailed_bucket_name
       WHERE ts > first_user_unlocked()
       AND process_name IS NOT NULL
       GROUP BY process_name, bucket)
@@ -318,7 +336,7 @@
         AVG(oom_adj_dur) as avg_oom_adj_dur,
         COUNT(DISTINCT(oom_adj_id)) oom_adj_event_count,
         oom_adj_reason
-      FROM android_oom_adj_intervals
+      FROM android_oom_adj_intervals_with_detailed_bucket_name
       WHERE ts > first_user_unlocked()
       GROUP BY oom_adj_reason
     )
diff --git a/src/trace_processor/metrics/sql/android/android_lmk_reason.sql b/src/trace_processor/metrics/sql/android/android_lmk_reason.sql
index 0ca2258..b7c7ca5 100644
--- a/src/trace_processor/metrics/sql/android/android_lmk_reason.sql
+++ b/src/trace_processor/metrics/sql/android/android_lmk_reason.sql
@@ -64,8 +64,8 @@
   FROM lmk_events
   JOIN rss_and_swap_span
   WHERE lmk_events.ts
-    BETWEEN rss_and_swap_span.ts
-    AND rss_and_swap_span.ts + MAX(rss_and_swap_span.dur - 1, 0)
+  BETWEEN rss_and_swap_span.ts
+  AND rss_and_swap_span.ts + MAX(rss_and_swap_span.dur - 1, 0)
 ),
 lmk_process_sizes_output AS (
   SELECT ts, RepeatedField(AndroidLmkReasonMetric_Process(
diff --git a/src/trace_processor/metrics/sql/android/android_oom_adjuster.sql b/src/trace_processor/metrics/sql/android/android_oom_adjuster.sql
index 7b752a8..1564586 100644
--- a/src/trace_processor/metrics/sql/android/android_oom_adjuster.sql
+++ b/src/trace_processor/metrics/sql/android/android_oom_adjuster.sql
@@ -15,6 +15,51 @@
 --
 INCLUDE PERFETTO MODULE android.oom_adjuster;
 
+DROP VIEW IF EXISTS android_oom_adj_intervals_with_detailed_bucket_name;
+CREATE PERFETTO VIEW android_oom_adj_intervals_with_detailed_bucket_name (
+  -- Timestamp the oom_adj score of the process changed
+  ts INT,
+  -- Duration until the next oom_adj score change of the process.
+  dur INT,
+  -- oom_adj score of the process.
+  score INT,
+  -- oom_adj bucket of the process.
+  bucket STRING,
+  -- Upid of the process having an oom_adj update.
+  upid INT,
+  -- Name of the process having an oom_adj update.
+  process_name STRING,
+  -- Slice id of the latest oom_adj update in the system_server.
+  oom_adj_id INT,
+  -- Timestamp of the latest oom_adj update in the system_server.
+  oom_adj_ts INT,
+  -- Duration of the latest oom_adj update in the system_server.
+  oom_adj_dur INT,
+  -- Track id of the latest oom_adj update in the system_server
+  oom_adj_track_id INT,
+  -- Thread name of the latest oom_adj update in the system_server.
+  oom_adj_thread_name STRING,
+  -- Reason for the latest oom_adj update in the system_server.
+  oom_adj_reason STRING,
+  -- Trigger for the latest oom_adj update in the system_server.
+  oom_adj_trigger STRING
+  ) AS
+SELECT
+  ts,
+  dur,
+  score,
+  android_oom_adj_score_to_detailed_bucket_name(score, android_appid) AS bucket,
+  upid,
+  process_name,
+  oom_adj_id,
+  oom_adj_ts,
+  oom_adj_dur,
+  oom_adj_track_id,
+  oom_adj_thread_name,
+  oom_adj_reason,
+  oom_adj_trigger
+FROM _oom_adjuster_intervals;
+
 DROP TABLE IF EXISTS _oom_adj_events_with_src_bucket;
 CREATE PERFETTO TABLE _oom_adj_events_with_src_bucket
 AS
@@ -24,7 +69,7 @@
   bucket,
   process_name,
   oom_adj_reason
-FROM android_oom_adj_intervals;
+FROM android_oom_adj_intervals_with_detailed_bucket_name;
 
 DROP VIEW IF EXISTS oom_adj_events_by_process_name;
 CREATE PERFETTO VIEW oom_adj_events_by_process_name AS
@@ -100,7 +145,7 @@
     )
     FROM (
         SELECT NULL as name, bucket, SUM(dur) as total_dur
-        FROM android_oom_adj_intervals GROUP BY bucket
+        FROM android_oom_adj_intervals_with_detailed_bucket_name GROUP BY bucket
     )
   ),
   'oom_adj_bucket_duration_agg_by_process',(SELECT RepeatedField(
@@ -112,7 +157,7 @@
     )
     FROM (
       SELECT process_name as name, bucket, SUM(dur) as total_dur
-      FROM android_oom_adj_intervals
+      FROM android_oom_adj_intervals_with_detailed_bucket_name
       WHERE process_name IS NOT NULL
       GROUP BY process_name, bucket
     )
@@ -133,7 +178,7 @@
         AVG(oom_adj_dur) as avg_oom_adj_dur,
         COUNT(DISTINCT(oom_adj_id)) oom_adj_event_count,
         oom_adj_reason
-      FROM android_oom_adj_intervals GROUP BY oom_adj_reason
+      FROM android_oom_adj_intervals_with_detailed_bucket_name GROUP BY oom_adj_reason
     )
   )
 );
diff --git a/src/trace_processor/metrics/sql/android/process_mem.sql b/src/trace_processor/metrics/sql/android/process_mem.sql
index b26bf05..f3b1c88 100644
--- a/src/trace_processor/metrics/sql/android/process_mem.sql
+++ b/src/trace_processor/metrics/sql/android/process_mem.sql
@@ -47,7 +47,7 @@
   ts,
   dur,
   upid,
-  file_rss AS file_tss_val,
+  file_rss AS file_rss_val,
   anon_rss AS anon_rss_val,
   shmem_rss AS shmem_rss_val,
   swap AS swap_val,
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
index 8f163a0..3048e68 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_engine.cc
@@ -32,6 +32,7 @@
 
 #include "perfetto/base/logging.h"
 #include "perfetto/base/status.h"
+#include "perfetto/ext/base/flat_hash_map.h"
 #include "perfetto/ext/base/status_or.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/ext/base/string_view.h"
@@ -848,7 +849,18 @@
     const std::vector<std::string>& column_names,
     const std::vector<sql_argument::ArgumentDefinition>& schema,
     const char* tag) {
-  // If the user has not provided a schema, we have nothing to validate.
+  std::vector<std::string> duplicate_columns;
+  for (auto it = column_names.begin(); it != column_names.end(); ++it) {
+    if (std::count(it + 1, column_names.end(), *it) > 0) {
+      duplicate_columns.push_back(*it);
+    }
+  }
+  if (!duplicate_columns.empty()) {
+    return base::ErrStatus("%s: multiple columns are named: %s", tag,
+                           base::Join(duplicate_columns, ", ").c_str());
+  }
+
+  // If the user has not provided a schema, we have nothing further to validate.
   if (schema.empty()) {
     return base::OkStatus();
   }
diff --git a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
index 349e2f6..e746a1d 100644
--- a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
@@ -25,6 +25,7 @@
     "counters",
     "cpu",
     "deprecated/v42/common",
+    "gpu",
     "graphs",
     "intervals",
     "linux",
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql b/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql
index 3ce017e..c09414e 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql
@@ -17,8 +17,30 @@
 INCLUDE PERFETTO MODULE slices.with_context;
 INCLUDE PERFETTO MODULE counters.intervals;
 
--- Converts an oom_adj score Integer to String bucket name.
+-- Converts an oom_adj score Integer to String sample name.
+-- One of: cached, background, job, foreground_service, bfgs, foreground and
+-- system.
 CREATE PERFETTO FUNCTION android_oom_adj_score_to_bucket_name(
+  -- `oom_score` value
+  oom_score INT
+)
+-- Returns the sample bucket based on the oom score.
+RETURNS STRING
+AS
+SELECT
+  CASE
+    WHEN $oom_score >= 900 THEN 'cached'
+    WHEN $oom_score BETWEEN 250 AND 900 THEN 'background'
+    WHEN $oom_score BETWEEN 201 AND 250 THEN 'job'
+    WHEN $oom_score = 200 THEN 'foreground_service'
+    WHEN $oom_score BETWEEN 100 AND 200 THEN 'bfgs'
+    WHEN $oom_score BETWEEN 0 AND 100 THEN 'foreground'
+    WHEN $oom_score < 0 THEN 'system'
+END;
+
+-- Converts an oom_adj score Integer to String bucket name.
+-- Deprecated: use `android_oom_adj_score_to_bucket_name` instead.
+CREATE PERFETTO FUNCTION android_oom_adj_score_to_detailed_bucket_name(
   -- oom_adj score.
   value INT,
   -- android_app id of the process.
@@ -52,8 +74,58 @@
     ELSE 'unknown_app'
   END;
 
+CREATE PERFETTO TABLE _oom_adjuster_intervals AS
+WITH reason AS (
+  SELECT
+    thread_slice.id AS oom_adj_id,
+    thread_slice.ts AS oom_adj_ts,
+    thread_slice.dur AS oom_adj_dur,
+    thread_slice.track_id AS oom_adj_track_id,
+    utid AS oom_adj_utid,
+    thread_name AS oom_adj_thread_name,
+    str_split(thread_slice.name, '_', 1) AS oom_adj_reason,
+    slice.name AS oom_adj_trigger,
+    LEAD(thread_slice.ts) OVER (ORDER BY thread_slice.ts) AS oom_adj_next_ts
+  FROM thread_slice
+  LEFT JOIN slice ON slice.id = thread_slice.parent_id AND slice.dur != -1
+  WHERE thread_slice.name GLOB 'updateOomAdj_*' AND process_name = 'system_server'
+)
+SELECT
+  ts,
+  dur,
+  cast_int!(value) AS score,
+  process.upid,
+  process.name AS process_name,
+  reason.oom_adj_id,
+  reason.oom_adj_ts,
+  reason.oom_adj_dur,
+  reason.oom_adj_track_id,
+  reason.oom_adj_thread_name,
+  reason.oom_adj_utid,
+  reason.oom_adj_reason,
+  reason.oom_adj_trigger,
+  android_appid
+FROM
+  counter_leading_intervals
+    !(
+      (
+        SELECT counter.*
+        FROM counter
+        JOIN counter_track track
+          ON track.id = counter.track_id AND track.name = 'oom_score_adj'
+      ))
+      counter
+JOIN process_counter_track track
+  ON counter.track_id = track.id
+JOIN process
+  USING (upid)
+LEFT JOIN reason
+  ON counter.ts BETWEEN oom_adj_ts AND COALESCE(oom_adj_next_ts, trace_end())
+WHERE track.name = 'oom_score_adj';
+
+
 -- All oom adj state intervals across all processes along with the reason for the state update.
-CREATE PERFETTO TABLE android_oom_adj_intervals (
+CREATE PERFETTO VIEW android_oom_adj_intervals (
   -- Timestamp the oom_adj score of the process changed
   ts INT,
   -- Duration until the next oom_adj score change of the process.
@@ -81,49 +153,18 @@
   -- Trigger for the latest oom_adj update in the system_server.
   oom_adj_trigger STRING
   ) AS
-WITH
-  reason AS (
-    SELECT
-      thread_slice.id AS oom_adj_id,
-      thread_slice.ts AS oom_adj_ts,
-      thread_slice.dur AS oom_adj_dur,
-      thread_slice.track_id AS oom_adj_track_id,
-      thread_name AS oom_adj_thread_name,
-      str_split(thread_slice.name, '_', 1) AS oom_adj_reason,
-      slice.name AS oom_adj_trigger,
-      LEAD(thread_slice.ts) OVER (ORDER BY thread_slice.ts) AS oom_adj_next_ts
-    FROM thread_slice
-    LEFT JOIN slice ON slice.id = thread_slice.parent_id AND slice.dur != -1
-    WHERE thread_slice.name GLOB 'updateOomAdj_*' AND process_name = 'system_server'
-  )
 SELECT
   ts,
   dur,
-  value AS score,
-  android_oom_adj_score_to_bucket_name(value, android_appid) AS bucket,
-  process.upid,
-  process.name AS process_name,
-  reason.oom_adj_id,
-  reason.oom_adj_ts,
-  reason.oom_adj_dur,
-  reason.oom_adj_track_id,
-  reason.oom_adj_thread_name,
-  reason.oom_adj_reason,
-  reason.oom_adj_trigger
-FROM
-  counter_leading_intervals
-    !(
-      (
-        SELECT counter.*
-        FROM counter
-        JOIN counter_track track
-          ON track.id = counter.track_id AND track.name = 'oom_score_adj'
-      ))
-      counter
-JOIN process_counter_track track
-  ON counter.track_id = track.id
-JOIN process
-  USING (upid)
-LEFT JOIN reason
-  ON counter.ts BETWEEN oom_adj_ts AND COALESCE(oom_adj_next_ts, trace_end())
-WHERE track.name = 'oom_score_adj';
+  score,
+  android_oom_adj_score_to_bucket_name(score) AS bucket,
+  upid,
+  process_name,
+  oom_adj_id,
+  oom_adj_ts,
+  oom_adj_dur,
+  oom_adj_track_id,
+  oom_adj_thread_name,
+  oom_adj_reason,
+  oom_adj_trigger
+FROM _oom_adjuster_intervals;
diff --git a/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql b/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql
index 82427f7..7bb0c96 100644
--- a/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql
@@ -62,7 +62,7 @@
     ts,
     track_id,
     LEAD(ts, 1, trace_end()) OVER(PARTITION BY track_id ORDER BY ts) - ts AS dur,
-    CAST(value AS INT) AS value
+    value
   FROM base
   WHERE value != lag_value OR lag_value IS NULL
 );
diff --git a/src/trace_processor/perfetto_sql/stdlib/cpu/freq.sql b/src/trace_processor/perfetto_sql/stdlib/cpu/freq.sql
index c0226bf..5dd2a48 100644
--- a/src/trace_processor/perfetto_sql/stdlib/cpu/freq.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/cpu/freq.sql
@@ -37,7 +37,7 @@
   count_w_dur.track_id,
   count_w_dur.ts,
   count_w_dur.dur,
-  count_w_dur.value as freq,
+  cast_int!(count_w_dur.value) as freq,
   cct.cpu
 FROM
 counter_leading_intervals!((
diff --git a/src/trace_processor/perfetto_sql/stdlib/cpu/idle.sql b/src/trace_processor/perfetto_sql/stdlib/cpu/idle.sql
index 7f837a2..703a913 100644
--- a/src/trace_processor/perfetto_sql/stdlib/cpu/idle.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/cpu/idle.sql
@@ -39,7 +39,7 @@
   count_w_dur.track_id,
   count_w_dur.ts,
   count_w_dur.dur,
-  IIF(count_w_dur.value = 4294967295, -1, count_w_dur.value) AS idle,
+  cast_int!(IIF(count_w_dur.value = 4294967295, -1, count_w_dur.value)) AS idle,
   cct.cpu
 FROM
 counter_leading_intervals!((
diff --git a/src/trace_processor/perfetto_sql/stdlib/gpu/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/gpu/BUILD.gn
new file mode 100644
index 0000000..a5f431d
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/gpu/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright (C) 2024 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_sql.gni")
+
+perfetto_sql_source_set("gpu") {
+  sources = [ "frequency.sql" ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/gpu/frequency.sql b/src/trace_processor/perfetto_sql/stdlib/gpu/frequency.sql
new file mode 100644
index 0000000..2bd87b2
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/gpu/frequency.sql
@@ -0,0 +1,42 @@
+--
+-- Copyright 2024 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
+--
+--     https://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 MODULE counters.intervals;
+
+-- GPU frequency counter per GPU.
+CREATE PERFETTO TABLE gpu_frequency(
+  -- Timestamp
+  ts INT,
+  -- Duration
+  dur INT,
+  -- GPU id. Joinable with `gpu_counter_track.gpu_id`.
+  gpu_id INT,
+  -- GPU frequency
+  gpu_freq INT
+) AS
+SELECT
+  ts,
+  dur,
+  gpu_id,
+  cast_int!(value) AS gpu_freq
+FROM counter_leading_intervals!((
+    SELECT c.*
+    FROM counter c
+    JOIN gpu_counter_track t
+    ON t.id = c.track_id AND t.name = 'gpufreq'
+    WHERE gpu_id IS NOT NULL
+))
+JOIN gpu_counter_track t ON t.id = track_id;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/memory/BUILD.gn
index da9685e..ddc6419 100644
--- a/src/trace_processor/perfetto_sql/stdlib/memory/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/BUILD.gn
@@ -15,6 +15,9 @@
 import("../../../../../gn/perfetto_sql.gni")
 
 perfetto_sql_source_set("memory") {
-  deps = ["linux"]
+  deps = [
+    "android",
+    "linux",
+  ]
   sources = [ "heap_graph_dominator_tree.sql" ]
 }
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/android/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/memory/android/BUILD.gn
new file mode 100644
index 0000000..55123b6
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/android/BUILD.gn
@@ -0,0 +1,19 @@
+# Copyright (C) 2024 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_sql.gni")
+
+perfetto_sql_source_set("android") {
+  sources = [ "gpu.sql" ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/android/gpu.sql b/src/trace_processor/perfetto_sql/stdlib/memory/android/gpu.sql
new file mode 100644
index 0000000..39abaec
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/android/gpu.sql
@@ -0,0 +1,35 @@
+--
+-- Copyright 2024 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
+--
+--     https://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 MODULE memory.linux.general;
+
+-- Counter for GPU memory per process with duration.
+CREATE PERFETTO TABLE memory_gpu_per_process(
+    -- Timestamp
+    ts INT,
+    -- Duration
+    dur INT,
+    -- Upid of the process
+    upid INT,
+    -- GPU memory
+    gpu_memory INT
+) AS
+SELECT
+  ts,
+  dur,
+  upid,
+  cast_int!(value) AS gpu_memory
+FROM _all_counters_per_process
+WHERE name = 'GPU Memory';
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/linux/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/memory/linux/BUILD.gn
index e436ca6..0711197 100644
--- a/src/trace_processor/perfetto_sql/stdlib/memory/linux/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/linux/BUILD.gn
@@ -16,6 +16,7 @@
 
 perfetto_sql_source_set("linux") {
   sources = [
+    "general.sql",
     "high_watermark.sql",
     "process.sql",
   ]
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/linux/general.sql b/src/trace_processor/perfetto_sql/stdlib/memory/linux/general.sql
new file mode 100644
index 0000000..372cbf9
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/linux/general.sql
@@ -0,0 +1,30 @@
+--
+-- Copyright 2024 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
+--
+--     https://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.
+
+CREATE PERFETTO VIEW _all_counters_per_process AS
+SELECT
+  ts,
+  LEAD(
+    ts, 1,
+    (SELECT COALESCE(end_ts, trace_end())
+    FROM process p WHERE p.upid = t.upid) + 1)
+    OVER (PARTITION BY track_id ORDER BY ts) - ts AS dur,
+  upid,
+  value,
+  track_id,
+  name
+FROM counter c JOIN process_counter_track t
+ON t.id = c.track_id
+WHERE upid IS NOT NULL;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/linux/high_watermark.sql b/src/trace_processor/perfetto_sql/stdlib/memory/linux/high_watermark.sql
index bc5ca67..b01f2df 100644
--- a/src/trace_processor/perfetto_sql/stdlib/memory/linux/high_watermark.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/linux/high_watermark.sql
@@ -36,7 +36,7 @@
     0 AS id
 FROM with_rss
 )
-SELECT ts, dur, track_id AS upid, value AS rss_high_watermark
+SELECT ts, dur, track_id AS upid, cast_int!(value) AS rss_high_watermark
 FROM counter_leading_intervals!(high_watermark_as_counter);
 
 -- For each process fetches the memory high watermark until or during
diff --git a/src/trace_processor/perfetto_sql/stdlib/memory/linux/process.sql b/src/trace_processor/perfetto_sql/stdlib/memory/linux/process.sql
index 8a81f43..4601c21 100644
--- a/src/trace_processor/perfetto_sql/stdlib/memory/linux/process.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/memory/linux/process.sql
@@ -13,20 +13,8 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
-CREATE PERFETTO VIEW _all_counters_for_with_upid AS
-SELECT
-  ts,
-  LEAD(
-    ts, 1,
-    (SELECT COALESCE(end_ts, trace_end())
-    FROM process p WHERE p.upid = t.upid) + 1)
-    OVER (PARTITION BY track_id ORDER BY ts) - ts AS dur,
-  upid,
-  value,
-  name
-FROM counter c JOIN process_counter_track t
-ON t.id = c.track_id
-WHERE upid IS NOT NULL;
+INCLUDE PERFETTO MODULE android.oom_adjuster;
+INCLUDE PERFETTO MODULE memory.linux.general;
 
 -- All memory counters tables.
 
@@ -36,7 +24,7 @@
   dur,
   upid,
   value AS anon_rss_val
-FROM _all_counters_for_with_upid
+FROM _all_counters_per_process
 WHERE name = 'mem.rss.anon';
 
 CREATE PERFETTO VIEW _file_rss AS
@@ -45,7 +33,7 @@
   dur,
   upid,
   value AS file_rss_val
-FROM _all_counters_for_with_upid
+FROM _all_counters_per_process
 WHERE name = 'mem.rss.file';
 
 CREATE PERFETTO VIEW _shmem_rss AS
@@ -54,7 +42,7 @@
   dur,
   upid,
   value AS shmem_rss_val
-FROM _all_counters_for_with_upid
+FROM _all_counters_per_process
 WHERE name = 'mem.rss.shmem';
 
 CREATE PERFETTO VIEW _swap AS
@@ -63,7 +51,7 @@
   dur,
   upid,
   value AS swap_val
-FROM _all_counters_for_with_upid
+FROM _all_counters_per_process
 WHERE name = 'mem.swap';
 
 -- Span joins
@@ -145,3 +133,85 @@
   anon_rss + file_rss  + COALESCE(shmem_rss, 0) + COALESCE(swap, 0) AS rss_and_swap
 FROM _memory_rss_and_swap_per_process_table
 JOIN process USING (upid);
+
+-- OOM score tables
+
+CREATE VIRTUAL TABLE _mem_ooms_sj
+USING SPAN_OUTER_JOIN(
+  android_oom_adj_intervals PARTITIONED upid,
+  _memory_rss_and_swap_per_process_table PARTITIONED upid);
+
+-- Process memory and it's OOM adjuster scores. Detects transitions, each new
+-- interval means that either the memory or OOM adjuster score of the process changed.
+CREATE PERFETTO TABLE memory_oom_score_with_rss_and_swap_per_process(
+  -- Timestamp the oom_adj score or memory of the process changed
+  ts INT,
+  -- Duration until the next oom_adj score or memory change of the process.
+  dur INT,
+  -- oom adjuster score of the process.
+  score INT,
+  -- oom adjuster bucket of the process.
+  bucket STRING,
+  -- Upid of the process having an oom_adj update.
+  upid INT,
+  -- Name of the process having an oom_adj update.
+  process_name STRING,
+  -- Pid of the process having an oom_adj update.
+  pid INT,
+  -- Slice of the latest oom_adj update in the system_server. Alias of
+  -- `slice.id`.
+  oom_adj_id INT,
+  -- Timestamp of the latest oom_adj update in the system_server.
+  oom_adj_ts INT,
+  -- Duration of the latest oom_adj update in the system_server.
+  oom_adj_dur INT,
+  -- Track of the latest oom_adj update in the system_server. Alias of
+  -- `track.id`.
+  oom_adj_track_id INT,
+  -- Thread name of the latest oom_adj update in the system_server.
+  oom_adj_thread_name STRING,
+  -- Reason for the latest oom_adj update in the system_server.
+  oom_adj_reason STRING,
+  -- Trigger for the latest oom_adj update in the system_server.
+  oom_adj_trigger STRING,
+  -- Anon RSS counter value
+  anon_rss INT,
+  -- File RSS counter value
+  file_rss INT,
+  -- Shared memory RSS counter value
+  shmem_rss INT,
+  -- Total RSS value. Sum of `anon_rss`, `file_rss` and `shmem_rss`. Returns
+  -- value even if one of the values is NULL.
+  rss INT,
+  -- Swap counter value
+  swap INT,
+  -- Sum or `anon_rss` and `swap`. Returns value even if one of the values is
+  -- NULL.
+  anon_rss_and_swap INT,
+  -- Sum or `rss` and `swap`. Returns value even if one of the values is NULL.
+  rss_and_swap INT
+) AS
+SELECT
+  ts,
+  dur,
+  score,
+  bucket,
+  upid,
+  process_name,
+  pid,
+  oom_adj_id,
+  oom_adj_ts,
+  oom_adj_dur,
+  oom_adj_track_id,
+  oom_adj_thread_name,
+  oom_adj_reason,
+  oom_adj_trigger,
+  anon_rss,
+  file_rss,
+  shmem_rss,
+  file_rss + anon_rss + COALESCE(shmem_rss, 0) AS rss,
+  swap,
+  anon_rss + COALESCE(swap, 0) AS anon_rss_and_swap,
+  anon_rss + file_rss  + COALESCE(shmem_rss, 0) + COALESCE(swap, 0) AS rss_and_swap
+FROM _mem_ooms_sj
+JOIN process USING (upid);
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span_with_slice.sql b/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span_with_slice.sql
index 89085f3..74f73a5 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span_with_slice.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span_with_slice.sql
@@ -213,7 +213,7 @@
       self_thread_state_id AS id,
       ts,
       dur,
-      utid,
+      critical_path_utid AS utid,
       0 AS stack_depth,
       'thread_state: ' || self_state AS name,
       'thread_state' AS table_name,
@@ -225,7 +225,7 @@
       self_thread_state_id AS id,
       ts,
       dur,
-      utid,
+      critical_path_utid AS utid,
       1 AS stack_depth,
       IIF(self_state GLOB 'R*', NULL, 'kernel function: ' || self_function) AS name,
       'thread_state' AS table_name,
@@ -237,7 +237,7 @@
       self_thread_state_id AS id,
       ts,
       dur,
-      utid,
+      critical_path_utid AS utid,
       2 AS stack_depth,
       IIF(self_state GLOB 'R*', NULL, 'io_wait: ' || self_io_wait) AS name,
       'thread_state' AS table_name,
@@ -281,7 +281,7 @@
       anc.id,
       slice.ts,
       slice.dur,
-      slice.utid,
+      critical_path_utid AS utid,
       anc.depth + 5 AS stack_depth,
       IIF($enable_self_slice, anc.name, NULL) AS name,
       'slice' AS table_name,
@@ -294,7 +294,7 @@
       self_slice_id AS id,
       ts,
       dur,
-      utid,
+      critical_path_utid AS utid,
       self_slice_depth + 5 AS stack_depth,
       IIF($enable_self_slice, self_slice_name, NULL) AS name,
       'slice' AS table_name,
diff --git a/src/trace_processor/rpc/wasm_bridge.cc b/src/trace_processor/rpc/wasm_bridge.cc
index ca27abc..2473e4c 100644
--- a/src/trace_processor/rpc/wasm_bridge.cc
+++ b/src/trace_processor/rpc/wasm_bridge.cc
@@ -15,8 +15,13 @@
  */
 
 #include <emscripten/emscripten.h>
-#include <cstdint>
 
+#include <cstdint>
+#include <cstdio>
+#include <cstdlib>
+#include <new>
+
+#include "perfetto/base/compiler.h"
 #include "src/trace_processor/rpc/rpc.h"
 
 namespace perfetto::trace_processor {
@@ -29,6 +34,12 @@
 // The buffer used to pass the request arguments. The caller (JS) decides how
 // big this buffer should be in the Initialize() call.
 uint8_t* g_req_buf;
+
+PERFETTO_NO_INLINE void OutOfMemoryHandler() {
+  fprintf(stderr, "\nCannot enlarge memory\n");
+  abort();
+}
+
 }  // namespace
 
 // +---------------------------------------------------------------------------+
@@ -41,6 +52,13 @@
 trace_processor_rpc_init(RpcResponseFn* RpcResponseFn, uint32_t);
 uint8_t* trace_processor_rpc_init(RpcResponseFn* resp_function,
                                   uint32_t req_buffer_size) {
+  // Usually OOMs manifest as a failure in dlmalloc() -> sbrk() ->
+  //_emscripten_resize_heap() which aborts itself. However in some rare cases
+  // sbrk() can fail outside of _emscripten_resize_heap and just return null.
+  // When that happens, just abort with the same message that
+  // _emscripten_resize_heap uses, so error_dialog.ts shows a OOM message.
+  std::set_new_handler(&OutOfMemoryHandler);
+
   g_trace_processor_rpc = new Rpc();
 
   // |resp_function| is a JS-bound function passed by wasm_bridge.ts. It will
diff --git a/src/trace_processor/trace_database_integrationtest.cc b/src/trace_processor/trace_database_integrationtest.cc
index d09c8a7..731f4e3 100644
--- a/src/trace_processor/trace_database_integrationtest.cc
+++ b/src/trace_processor/trace_database_integrationtest.cc
@@ -14,17 +14,23 @@
  * limitations under the License.
  */
 
-#include <algorithm>
+#include <cstdint>
 #include <cstdio>
-#include <map>
-#include <optional>
+#include <cstring>
+#include <memory>
 #include <random>
 #include <string>
+#include <utility>
+#include <vector>
 
+#include "perfetto/base/build_config.h"
 #include "perfetto/base/logging.h"
+#include "perfetto/base/status.h"
 #include "perfetto/ext/base/scoped_file.h"
 #include "perfetto/ext/base/string_utils.h"
 #include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/trace_processor/iterator.h"
+#include "perfetto/trace_processor/status.h"
 #include "perfetto/trace_processor/trace_processor.h"
 #include "protos/perfetto/common/descriptor.pbzero.h"
 #include "protos/perfetto/trace_processor/trace_processor.pbzero.h"
@@ -32,11 +38,12 @@
 #include "src/base/test/utils.h"
 #include "test/gtest_and_gmock.h"
 
-namespace perfetto {
-namespace trace_processor {
+namespace perfetto::trace_processor {
 namespace {
 
-constexpr size_t kMaxChunkSize = 4 * 1024 * 1024;
+using testing::HasSubstr;
+
+constexpr size_t kMaxChunkSize = 4ul * 1024 * 1024;
 
 TEST(TraceProcessorCustomConfigTest, SkipInternalMetricsMatchingMountPath) {
   auto config = Config();
@@ -97,12 +104,13 @@
       : processor_(TraceProcessor::CreateInstance(Config())) {}
 
  protected:
-  util::Status LoadTrace(const char* name,
+  base::Status LoadTrace(const char* name,
                          size_t min_chunk_size = 512,
                          size_t max_chunk_size = kMaxChunkSize) {
     EXPECT_LE(min_chunk_size, max_chunk_size);
-    base::ScopedFstream f(fopen(
-        base::GetTestDataPath(std::string("test/data/") + name).c_str(), "rb"));
+    base::ScopedFstream f(
+        fopen(base::GetTestDataPath(std::string("test/data/") + name).c_str(),
+              "rbe"));
     std::minstd_rand0 rnd_engine(0);
     std::uniform_int_distribution<size_t> dist(min_chunk_size, max_chunk_size);
     while (!feof(*f)) {
@@ -118,7 +126,7 @@
   }
 
   Iterator Query(const std::string& query) {
-    return processor_->ExecuteQuery(query.c_str());
+    return processor_->ExecuteQuery(query);
   }
 
   TraceProcessor* Processor() { return processor_.get(); }
@@ -280,7 +288,7 @@
 
 TEST_F(TraceProcessorIntegrationTest, ComputeMetricsFormattedExtension) {
   std::string metric_output;
-  util::Status status = Processor()->ComputeMetricText(
+  base::Status status = Processor()->ComputeMetricText(
       std::vector<std::string>{"test_chrome_metric"},
       TraceProcessor::MetricResultFormat::kProtoText, &metric_output);
   ASSERT_TRUE(status.ok());
@@ -293,7 +301,7 @@
 
 TEST_F(TraceProcessorIntegrationTest, ComputeMetricsFormattedNoExtension) {
   std::string metric_output;
-  util::Status status = Processor()->ComputeMetricText(
+  base::Status status = Processor()->ComputeMetricText(
       std::vector<std::string>{"trace_metadata"},
       TraceProcessor::MetricResultFormat::kProtoText, &metric_output);
   ASSERT_TRUE(status.ok());
@@ -321,21 +329,21 @@
 }
 
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14762) {
-  ASSERT_TRUE(LoadTrace("clusterfuzz_14762", 4096 * 1024).ok());
+  ASSERT_TRUE(LoadTrace("clusterfuzz_14762", 4096ul * 1024).ok());
   auto it = Query("select sum(value) from stats where severity = 'error';");
   ASSERT_TRUE(it.Next());
   ASSERT_GT(it.Get(0).long_value, 0);
 }
 
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14767) {
-  ASSERT_TRUE(LoadTrace("clusterfuzz_14767", 4096 * 1024).ok());
+  ASSERT_TRUE(LoadTrace("clusterfuzz_14767", 4096ul * 1024).ok());
   auto it = Query("select sum(value) from stats where severity = 'error';");
   ASSERT_TRUE(it.Next());
   ASSERT_GT(it.Get(0).long_value, 0);
 }
 
 TEST_F(TraceProcessorIntegrationTest, Clusterfuzz14799) {
-  ASSERT_TRUE(LoadTrace("clusterfuzz_14799", 4096 * 1024).ok());
+  ASSERT_TRUE(LoadTrace("clusterfuzz_14799", 4096ul * 1024).ok());
   auto it = Query("select sum(value) from stats where severity = 'error';");
   ASSERT_TRUE(it.Next());
   ASSERT_GT(it.Get(0).long_value, 0);
@@ -793,6 +801,15 @@
   ASSERT_TRUE(it.Status().ok());
 }
 
+TEST_F(TraceProcessorIntegrationTest, CreateTableDuplicateNames) {
+  auto it = Query(
+      "create perfetto table foo select 1 as duplicate_a, 2 as duplicate_a, 3 "
+      "as duplicate_b, 4 as duplicate_b");
+  ASSERT_FALSE(it.Next());
+  ASSERT_FALSE(it.Status().ok());
+  ASSERT_THAT(it.Status().message(), HasSubstr("duplicate_a"));
+  ASSERT_THAT(it.Status().message(), HasSubstr("duplicate_b"));
+}
+
 }  // namespace
-}  // namespace trace_processor
-}  // namespace perfetto
+}  // namespace perfetto::trace_processor
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index fb9b294..117a737 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -59,6 +59,7 @@
 #include "src/trace_processor/importers/proto/additional_modules.h"
 #include "src/trace_processor/importers/proto/content_analyzer.h"
 #include "src/trace_processor/importers/systrace/systrace_trace_parser.h"
+#include "src/trace_processor/importers/zip/zip_trace_reader.h"
 #include "src/trace_processor/iterator_impl.h"
 #include "src/trace_processor/metrics/all_chrome_metrics.descriptor.h"
 #include "src/trace_processor/metrics/all_webview_metrics.descriptor.h"
@@ -121,6 +122,7 @@
 #include "src/trace_processor/util/regex.h"
 #include "src/trace_processor/util/sql_modules.h"
 #include "src/trace_processor/util/status_macros.h"
+#include "src/trace_processor/util/trace_type.h"
 
 #include "protos/perfetto/common/builtin_clock.pbzero.h"
 #include "protos/perfetto/trace/clock_snapshot.pbzero.h"
@@ -308,8 +310,8 @@
       return "ctrace";
     case kNinjaLogTraceType:
       return "ninja_log";
-    case kAndroidBugreportTraceType:
-      return "android_bugreport";
+    case kZipFile:
+      return "zip";
     case kPerfDataTraceType:
       return "perf_data";
   }
@@ -362,8 +364,7 @@
         kGzipTraceType);
     context_.reader_registry->RegisterTraceReader<GzipTraceParser>(
         kCtraceTraceType);
-    context_.reader_registry->RegisterTraceReader<AndroidBugreportParser>(
-        kAndroidBugreportTraceType);
+    context_.reader_registry->RegisterTraceReader<ZipTraceReader>(kZipFile);
   }
 
   if (json::IsJsonSupported()) {
diff --git a/src/trace_processor/trace_processor_storage_impl.cc b/src/trace_processor/trace_processor_storage_impl.cc
index 32f2452..09d5386 100644
--- a/src/trace_processor/trace_processor_storage_impl.cc
+++ b/src/trace_processor/trace_processor_storage_impl.cc
@@ -107,6 +107,8 @@
     return;
   Flush();
   context_.chunk_reader->NotifyEndOfFile();
+  // NotifyEndOfFile might have pushed packets to the sorter.
+  Flush();
   for (std::unique_ptr<ProtoImporterModule>& module : context_.modules) {
     module->NotifyEndOfFile();
   }
diff --git a/src/trace_processor/trace_reader_registry.cc b/src/trace_processor/trace_reader_registry.cc
index 09c5c7b..065f6f6 100644
--- a/src/trace_processor/trace_reader_registry.cc
+++ b/src/trace_processor/trace_reader_registry.cc
@@ -30,7 +30,7 @@
   switch (type) {
     case kGzipTraceType:
     case kCtraceTraceType:
-    case kAndroidBugreportTraceType:
+    case kZipFile:
       return true;
 
     case kNinjaLogTraceType:
diff --git a/src/trace_processor/util/trace_type.cc b/src/trace_processor/util/trace_type.cc
index d265b90..ee5a56a 100644
--- a/src/trace_processor/util/trace_type.cc
+++ b/src/trace_processor/util/trace_type.cc
@@ -58,8 +58,8 @@
       return "gzip trace";
     case kCtraceTraceType:
       return "ctrace trace";
-    case kAndroidBugreportTraceType:
-      return "Android Bugreport";
+    case kZipFile:
+      return "ZIP file";
     case kPerfDataTraceType:
       return "perf data";
     case kUnknownTraceType:
@@ -124,12 +124,8 @@
   if (base::StartsWith(start, "\x0a"))
     return kProtoTraceType;
 
-  // Android bugreport.zip
-  // TODO(primiano). For now we assume any .zip file is a bugreport. In future,
-  // if we want to support different trace formats based on a .zip arachive we
-  // will need an extra layer similar to what we did kGzipTraceType.
   if (base::StartsWith(start, "PK\x03\x04"))
-    return kAndroidBugreportTraceType;
+    return kZipFile;
 
   return kUnknownTraceType;
 }
diff --git a/src/trace_processor/util/trace_type.h b/src/trace_processor/util/trace_type.h
index fec7a74..2d40d83 100644
--- a/src/trace_processor/util/trace_type.h
+++ b/src/trace_processor/util/trace_type.h
@@ -31,7 +31,7 @@
   kGzipTraceType,
   kCtraceTraceType,
   kNinjaLogTraceType,
-  kAndroidBugreportTraceType,
+  kZipFile,
   kPerfDataTraceType,
 };
 
diff --git a/src/traceconv/symbolize_profile.cc b/src/traceconv/symbolize_profile.cc
index 00cc7b9..4efaa10 100644
--- a/src/traceconv/symbolize_profile.cc
+++ b/src/traceconv/symbolize_profile.cc
@@ -55,12 +55,12 @@
     PERFETTO_FATAL("Failed to read trace.");
 
   tp->Flush();
+  tp->NotifyEndOfFile();
 
   SymbolizeDatabase(
       tp.get(), symbolizer.get(),
       [output](const std::string& trace_proto) { *output << trace_proto; });
 
-  tp->NotifyEndOfFile();
   return 0;
 }
 
diff --git a/src/tracing/service/tracing_service_impl.cc b/src/tracing/service/tracing_service_impl.cc
index d667767..6631d68 100644
--- a/src/tracing/service/tracing_service_impl.cc
+++ b/src/tracing/service/tracing_service_impl.cc
@@ -24,6 +24,7 @@
 #include <limits>
 #include <optional>
 #include <regex>
+#include <string>
 #include <unordered_set>
 #include "perfetto/base/time.h"
 #include "perfetto/ext/tracing/core/client_identity.h"
@@ -1689,13 +1690,14 @@
               tracing_session.config, tracing_session.trace_uuid,
               PerfettoStatsdAtom::kTracedTriggerCloneSnapshot, iter->name());
           task_runner_->PostDelayedTask(
-              [weak_this, tsid] {
+              [weak_this, tsid, trigger_name = iter->name()] {
                 if (!weak_this)
                   return;
                 auto* tsess = weak_this->GetTracingSession(tsid);
                 if (!tsess || !tsess->consumer_maybe_null)
                   return;
-                tsess->consumer_maybe_null->NotifyCloneSnapshotTrigger();
+                tsess->consumer_maybe_null->NotifyCloneSnapshotTrigger(
+                    trigger_name);
               },
               iter->stop_delay_ms());
           break;
@@ -4265,13 +4267,15 @@
   observable_events->set_all_data_sources_started(true);
 }
 
-void TracingServiceImpl::ConsumerEndpointImpl::NotifyCloneSnapshotTrigger() {
+void TracingServiceImpl::ConsumerEndpointImpl::NotifyCloneSnapshotTrigger(
+    const std::string& trigger_name) {
   if (!(observable_events_mask_ & ObservableEvents::TYPE_CLONE_TRIGGER_HIT)) {
     return;
   }
   auto* observable_events = AddObservableEvents();
   auto* clone_trig = observable_events->mutable_clone_trigger_hit();
   clone_trig->set_tracing_session_id(static_cast<int64_t>(tracing_session_id_));
+  clone_trig->set_trigger_name(trigger_name);
 }
 
 ObservableEvents*
diff --git a/src/tracing/service/tracing_service_impl.h b/src/tracing/service/tracing_service_impl.h
index 9fa84fe..1dbcbe6 100644
--- a/src/tracing/service/tracing_service_impl.h
+++ b/src/tracing/service/tracing_service_impl.h
@@ -32,6 +32,7 @@
 #include "perfetto/base/time.h"
 #include "perfetto/ext/base/circular_queue.h"
 #include "perfetto/ext/base/periodic_task.h"
+#include "perfetto/ext/base/string_view.h"
 #include "perfetto/ext/base/uuid.h"
 #include "perfetto/ext/base/weak_ptr.h"
 #include "perfetto/ext/tracing/core/basic_types.h"
@@ -211,7 +212,7 @@
     ~ConsumerEndpointImpl() override;
 
     void NotifyOnTracingDisabled(const std::string& error);
-    void NotifyCloneSnapshotTrigger();
+    void NotifyCloneSnapshotTrigger(const std::string& trigger_name);
 
     // TracingService::ConsumerEndpoint implementation.
     void EnableTracing(const TraceConfig&, base::ScopedFile) override;
diff --git a/test/.gitignore b/test/.gitignore
index 369c917..47a77bd 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -22,3 +22,7 @@
 !/data/simpleperf
 /data/simpleperf/*
 !/data/simpleperf/*.sha256
+
+!/data/zip
+/data/zip/*
+!/data/zip/*.sha256
\ No newline at end of file
diff --git a/test/data/zip/perf_track_sym.zip.sha256 b/test/data/zip/perf_track_sym.zip.sha256
new file mode 100644
index 0000000..53dfcb3
--- /dev/null
+++ b/test/data/zip/perf_track_sym.zip.sha256
@@ -0,0 +1 @@
+146b1d8f48743323e91fd63d6ab5a1f6d8fcca8c5ea8455ebe8f087667a23c3f
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py
index 871abaf..e70dfa3 100644
--- a/test/trace_processor/diff_tests/include_index.py
+++ b/test/trace_processor/diff_tests/include_index.py
@@ -96,6 +96,7 @@
 from diff_tests.parser.track_event.tests import TrackEvent
 from diff_tests.parser.translated_args.tests import TranslatedArgs
 from diff_tests.parser.ufs.tests import Ufs
+from diff_tests.parser.zip.tests import Zip
 from diff_tests.stdlib.android.frames_tests import Frames
 from diff_tests.stdlib.android.startups_tests import Startups
 from diff_tests.stdlib.android.tests import AndroidStdlib
@@ -105,6 +106,7 @@
 from diff_tests.stdlib.counters.tests import StdlibCounterIntervals
 from diff_tests.stdlib.cpu.tests import Cpu
 from diff_tests.stdlib.dynamic_tables.tests import DynamicTables
+from diff_tests.stdlib.gpu.tests import Gpu
 from diff_tests.stdlib.graphs.dominator_tree_tests import DominatorTree
 from diff_tests.stdlib.graphs.partition_tests import GraphPartitionTests
 from diff_tests.stdlib.graphs.search_tests import GraphSearchTests
@@ -219,6 +221,7 @@
       *FtraceCrop(index_path, 'parser/ftrace', 'FtraceCrop').fetch(),
       *ParsingTracedStats(index_path, 'parser/parsing',
                           'ParsingTracedStats').fetch(),
+      *Zip(index_path, 'parser/zip', 'Zip').fetch(),
   ]
 
   metrics_tests = [
@@ -265,6 +268,7 @@
       *Cpu(index_path, 'stdlib/cpu', 'Cpu').fetch(),
       *DominatorTree(index_path, 'stdlib/graphs', 'DominatorTree').fetch(),
       *Frames(index_path, 'stdlib/android', 'Frames').fetch(),
+      *Gpu(index_path, 'stdlib/gpu', 'Gpu').fetch(),
       *GraphSearchTests(index_path, 'stdlib/graphs',
                         'GraphSearchTests').fetch(),
       *GraphPartitionTests(index_path, 'stdlib/graphs',
diff --git a/test/trace_processor/diff_tests/metrics/memory/tests.py b/test/trace_processor/diff_tests/metrics/memory/tests.py
index 7ea5fda..a5a5cec 100644
--- a/test/trace_processor/diff_tests/metrics/memory/tests.py
+++ b/test/trace_processor/diff_tests/metrics/memory/tests.py
@@ -248,6 +248,17 @@
         }
         """))
 
+  def test_android_lmk_reason(self):
+    return DiffTestBlueprint(
+        trace=DataPath('lmk_userspace.pb'),
+        query=Metric('android_lmk_reason'),
+        # TODO(mayzner): Find a trace that returns results. This is still
+        # beneficial though, as at least this metric is run.
+        out=TextProto(r"""
+        android_lmk_reason {
+        }
+        """))
+
   def test_android_mem_delta(self):
     return DiffTestBlueprint(
         trace=Path('android_mem_delta.py'),
diff --git a/test/trace_processor/diff_tests/parser/simpleperf/clocks_align_test.sql b/test/trace_processor/diff_tests/parser/simpleperf/clocks_align_test.sql
new file mode 100644
index 0000000..f5a65ee
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/simpleperf/clocks_align_test.sql
@@ -0,0 +1,99 @@
+--
+-- Copyright 2024 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
+--
+--     https://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.
+--
+
+CREATE PERFETTO VIEW perf_sample_in(ts INT, dur INT)
+AS
+SELECT ts, 0 AS dur FROM perf_sample;
+
+CREATE VIRTUAL TABLE span
+USING
+  SPAN_JOIN(perf_sample_in, slice PARTITIONED depth);
+
+CREATE PERFETTO TABLE slice_stack
+AS
+WITH
+  tmp AS (
+    SELECT
+      ts,
+      parent_stack_id,
+      string_AGG(IIF(name = 'Main loop', 'main', name), ',')
+        OVER (
+          PARTITION BY ts
+          ORDER BY depth ASC
+          RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+        ) AS stack
+    FROM span
+  )
+SELECT ts, stack FROM tmp WHERE parent_stack_id = 0 ORDER BY TS ASC;
+
+CREATE PERFETTO TABLE perf_stack
+AS
+WITH
+  symbol AS (
+    SELECT
+      id,
+      symbol_set_id,
+      replace(replace(name, '(anonymous namespace)::', ''), '()', '') AS name
+    FROM stack_profile_symbol
+  ),
+  symbol_agg AS (
+    SELECT
+      id,
+      symbol_set_id,
+      string_agg(name, ',')
+        OVER (
+          PARTITION BY symbol_set_id
+          ORDER BY id DESC
+          RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+        ) AS name
+    FROM symbol
+    WHERE name IN ('main', 'A', 'B', 'C', 'D', 'E')
+  ),
+  inline AS (
+    SELECT symbol_set_id, name FROM symbol_agg WHERE id = symbol_set_id
+  ),
+  frame AS (
+    SELECT f.id AS frame_id, i.name
+    FROM STACK_PROFILE_FRAME f, inline i
+    USING (symbol_set_id)
+  ),
+  child AS (
+    SELECT
+      s.ts,
+      spc.id,
+      spc.parent_id,
+      name
+    FROM perf_sample s, stack_profile_callsite spc
+    ON (s.callsite_id = spc.id),
+    frame USING (frame_id)
+    UNION ALL
+    SELECT
+      child.ts,
+      parent.id,
+      parent.parent_id,
+      COALESCE(f.name || ',', '') || child.name AS name
+    FROM child, stack_profile_callsite parent
+    ON (child.parent_id = parent.id)
+    LEFT JOIN frame f
+      USING (frame_id)
+  )
+SELECT ts, name AS stack FROM child WHERE parent_id IS NULL ORDER BY ts ASC;
+
+SELECT COUNT(*) AS misaligned_count
+FROM slice_stack s
+FULL JOIN perf_stack p
+  USING (ts)
+WHERE s.stack <> p.stack;
diff --git a/test/trace_processor/diff_tests/parser/simpleperf/tests.py b/test/trace_processor/diff_tests/parser/simpleperf/tests.py
index c98239d..4f5a22b 100644
--- a/test/trace_processor/diff_tests/parser/simpleperf/tests.py
+++ b/test/trace_processor/diff_tests/parser/simpleperf/tests.py
@@ -142,3 +142,12 @@
         "0b12a384a9f4a3f3659b7171ca615dbec3a81f71","/t1"
         "0b12a384a9f4a3f3659b7171ca615dbec3a81f71","/t2"
         '''))
+
+  def test_clocks_align(self):
+    return DiffTestBlueprint(
+        trace=DataPath('zip/perf_track_sym.zip'),
+        query=Path('clocks_align_test.sql'),
+        out=Csv('''
+        "misaligned_count"
+        0
+        '''))
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/parser/zip/stacks_test.sql b/test/trace_processor/diff_tests/parser/zip/stacks_test.sql
new file mode 100644
index 0000000..68543f1
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/zip/stacks_test.sql
@@ -0,0 +1,48 @@
+WITH
+  symbol AS (
+    SELECT
+      id,
+      symbol_set_id,
+      replace(replace(name, '(anonymous namespace)::', ''), '()', '') AS name
+    FROM stack_profile_symbol
+  ),
+  symbol_agg AS (
+    SELECT
+      id,
+      symbol_set_id,
+      string_agg(name, ',')
+        OVER (
+          PARTITION BY symbol_set_id
+          ORDER BY id DESC
+          RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
+        ) AS name
+    FROM symbol
+    WHERE name IN ('main', 'A', 'B', 'C', 'D', 'E')
+  ),
+  inline AS (
+    SELECT symbol_set_id, name FROM symbol_agg WHERE id = symbol_set_id
+  ),
+  frame AS (
+    SELECT f.id AS frame_id, i.name
+    FROM STACK_PROFILE_FRAME f, inline i
+    USING (symbol_set_id)
+  ),
+  child AS (
+    SELECT
+      spc.id,
+      spc.parent_id,
+      name
+    FROM perf_sample s, stack_profile_callsite spc
+    ON (s.callsite_id = spc.id),
+    frame USING (frame_id)
+    UNION ALL
+    SELECT
+      parent.id,
+      parent.parent_id,
+      COALESCE(f.name || ',', '') || child.name AS name
+    FROM child, stack_profile_callsite parent
+    ON (child.parent_id = parent.id)
+    LEFT JOIN frame f
+      USING (frame_id)
+  )
+SELECT DISTINCT name FROM child WHERE parent_id IS NULL ORDER BY name
\ No newline at end of file
diff --git a/test/trace_processor/diff_tests/parser/zip/tests.py b/test/trace_processor/diff_tests/parser/zip/tests.py
new file mode 100644
index 0000000..9827072
--- /dev/null
+++ b/test/trace_processor/diff_tests/parser/zip/tests.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+# 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 a
+#
+#      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.
+
+from python.generators.diff_tests.testing import Csv, Path, DataPath
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+
+
+class Zip(TestSuite):
+
+  def test_perf_proto_sym(self):
+    return DiffTestBlueprint(
+        trace=DataPath('zip/perf_track_sym.zip'),
+        query=Path('stacks_test.sql'),
+        out=Csv('''
+        "name"
+        "main,A"
+        "main,A,B"
+        "main,A,B,C"
+        "main,A,B,C,D"
+        "main,A,B,C,D,E"
+        "main,A,B,C,E"
+        "main,A,B,D"
+        "main,A,B,D,E"
+        "main,A,B,E"
+        "main,A,C"
+        "main,A,C,D"
+        "main,A,C,D,E"
+        "main,A,C,E"
+        "main,A,D"
+        "main,A,D,E"
+        "main,A,E"
+        "main,B"
+        "main,B,C"
+        "main,B,C,D"
+        "main,B,C,D,E"
+        "main,B,C,E"
+        "main,B,D"
+        "main,B,D,E"
+        "main,B,E"
+        "main,C"
+        "main,C,D"
+        "main,C,D,E"
+        "main,C,E"
+        "main,D"
+        "main,D,E"
+        "main,E"
+        '''))
diff --git a/test/trace_processor/diff_tests/stdlib/android/tests.py b/test/trace_processor/diff_tests/stdlib/android/tests.py
index 59a8877..6781e3e 100644
--- a/test/trace_processor/diff_tests/stdlib/android/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/android/tests.py
@@ -1244,16 +1244,16 @@
       """,
         out=Csv("""
         "ts","dur","score","bucket","process_name","oom_adj_ts","oom_adj_dur","oom_adj_thread_name","oom_adj_reason","oom_adj_trigger"
-        1737065264829,701108081,925,"cached_app","com.android.providers.calendar",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737066678827,3470211742,935,"cached_app","com.android.imsserviceentitlement",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737066873002,3470017567,945,"cached_app","com.android.carrierconfig",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737067058812,3469831757,955,"cached_app_lmk_first","com.android.messaging",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737067246975,699224817,955,"cached_app_lmk_first","android.process.acore",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737068421919,3468468650,965,"cached_app_lmk_first","com.android.shell",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737068599673,697908135,965,"cached_app_lmk_first","android.process.media",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737068933602,3467956967,975,"cached_app_lmk_first","com.android.gallery3d",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737069091010,3467799559,975,"cached_app_lmk_first","com.android.packageinstaller",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
-        1737069240534,3467650035,985,"cached_app_lmk_first","com.android.managedprovisioning",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737065264829,701108081,925,"cached","com.android.providers.calendar",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737066678827,3470211742,935,"cached","com.android.imsserviceentitlement",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737066873002,3470017567,945,"cached","com.android.carrierconfig",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737067058812,3469831757,955,"cached","com.android.messaging",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737067246975,699224817,955,"cached","android.process.acore",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737068421919,3468468650,965,"cached","com.android.shell",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737068599673,697908135,965,"cached","android.process.media",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737068933602,3467956967,975,"cached","com.android.gallery3d",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737069091010,3467799559,975,"cached","com.android.packageinstaller",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
+1737069240534,3467650035,985,"cached","com.android.managedprovisioning",1737064421516,29484835,"binder:642_1","processEnd","IActivityManager#1598246212"
       """))
 
   def test_broadcast_minsdk_u(self):
diff --git a/test/trace_processor/diff_tests/stdlib/gpu/tests.py b/test/trace_processor/diff_tests/stdlib/gpu/tests.py
new file mode 100644
index 0000000..2c23e80
--- /dev/null
+++ b/test/trace_processor/diff_tests/stdlib/gpu/tests.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# 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 a
+#
+#      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.
+
+from python.generators.diff_tests.testing import Path, DataPath, Metric, Systrace
+from python.generators.diff_tests.testing import Csv, Json, TextProto, BinaryProto
+from python.generators.diff_tests.testing import DiffTestBlueprint
+from python.generators.diff_tests.testing import TestSuite
+from python.generators.diff_tests.testing import PrintProfileProto
+
+
+class Gpu(TestSuite):
+
+  def test_gpu_frequency(self):
+    return DiffTestBlueprint(
+        trace=Path('../../metrics/graphics/gpu_frequency_metric.textproto'),
+        query="""
+        INCLUDE PERFETTO MODULE gpu.frequency;
+        SELECT *
+        FROM gpu_frequency;
+      """,
+        out=Csv("""
+        "ts","dur","gpu_id","gpu_freq"
+        200001000000,2000000,0,585000
+        200003000000,1000000,0,0
+        200004000000,2000000,0,603000
+        200002000000,3000000,1,400000
+        200005000000,1000000,1,758000
+      """))
diff --git a/test/trace_processor/diff_tests/stdlib/memory/tests.py b/test/trace_processor/diff_tests/stdlib/memory/tests.py
index bc8f16d..d781b54 100644
--- a/test/trace_processor/diff_tests/stdlib/memory/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/memory/tests.py
@@ -67,3 +67,45 @@
         38090817604,11930827,1,1982,"com.android.systemui",373714944
               """))
 
+  def test_memory_oom_score_with_rss_and_swap_per_process(self):
+    return DiffTestBlueprint(
+        trace=DataPath('sched_wakeup_trace.atr'),
+        query="""
+        INCLUDE PERFETTO MODULE memory.linux.process;
+        SELECT *
+        FROM memory_oom_score_with_rss_and_swap_per_process
+        WHERE oom_adj_reason IS NOT NULL
+        ORDER BY ts
+        LIMIT 10;
+      """,
+        out=Csv("""
+        "ts","dur","score","bucket","upid","process_name","pid","oom_adj_id","oom_adj_ts","oom_adj_dur","oom_adj_track_id","oom_adj_thread_name","oom_adj_reason","oom_adj_trigger","anon_rss","file_rss","shmem_rss","rss","swap","anon_rss_and_swap","rss_and_swap"
+        1737065264829,701108081,925,"cached",269,"com.android.providers.calendar",1937,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",49229824,57495552,835584,107560960,0,49229824,107560960
+        1737066678827,2934486383,935,"cached",287,"com.android.imsserviceentitlement",2397,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",48881664,57081856,831488,106795008,0,48881664,106795008
+        1737066873002,2934292208,945,"cached",292,"com.android.carrierconfig",2593,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",48586752,49872896,823296,99282944,0,48586752,99282944
+        1737067058812,2934106398,955,"cached",288,"com.android.messaging",2416,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",54956032,71417856,843776,127217664,0,54956032,127217664
+        1737067246975,699224817,955,"cached",267,"android.process.acore",1866,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",52498432,72048640,856064,125403136,0,52498432,125403136
+        1737068421919,2932743291,965,"cached",273,"com.android.shell",2079,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",48738304,52056064,823296,101617664,0,48738304,101617664
+        1737068599673,970398,965,"cached",271,"android.process.media",2003,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",49917952,60444672,839680,111202304,0,49917952,111202304
+        1737068933602,2932231608,975,"cached",286,"com.android.gallery3d",2371,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",49561600,54521856,831488,104914944,0,49561600,104914944
+        1737069091010,682459310,975,"cached",289,"com.android.packageinstaller",2480,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",49364992,52539392,827392,102731776,0,49364992,102731776
+        1737069240534,489635,985,"cached",268,"com.android.managedprovisioning",1868,332,1737064421516,29484835,1217,"binder:642_1","processEnd","IActivityManager#1598246212",50683904,53985280,815104,105484288,0,50683904,105484288
+         """))
+
+  def test_memory_gpu_per_process(self):
+    return DiffTestBlueprint(
+        trace=Path('../../metrics/graphics/gpu_metric.py'),
+        query="""
+        INCLUDE PERFETTO MODULE memory.android.gpu;
+        SELECT *
+        FROM memory_gpu_per_process;
+        """,
+        out=Csv("""
+        "ts","dur","upid","gpu_memory"
+        2,2,2,6
+        4,6,2,8
+        4,5,1,2
+        9,1,1,8
+        6,1,3,7
+        7,3,3,10
+         """))
diff --git a/test/trace_processor/diff_tests/tables/tests_sched.py b/test/trace_processor/diff_tests/tables/tests_sched.py
index ad23a5d..f13d42c 100644
--- a/test/trace_processor/diff_tests/tables/tests_sched.py
+++ b/test/trace_processor/diff_tests/tables/tests_sched.py
@@ -487,22 +487,24 @@
           table_name,
           critical_path_utid
         FROM _thread_executing_span_critical_path_stack((select utid from thread where tid = 3487), start_ts, end_ts), trace_bounds
-        ORDER BY ts
-        LIMIT 11
+        WHERE ts = 1737500355691
+        ORDER BY utid, id
         """,
         out=Csv("""
         "id","ts","dur","utid","stack_depth","name","table_name","critical_path_utid"
-        11889,1737349401439,57188,1477,0,"thread_state: R","thread_state",1477
-        11889,1737349401439,57188,1477,1,"[NULL]","thread_state",1477
-        11889,1737349401439,57188,1477,2,"[NULL]","thread_state",1477
-        11889,1737349401439,57188,1477,3,"process_name: com.android.providers.media.module","thread_state",1477
-        11889,1737349401439,57188,1477,4,"thread_name: rs.media.module","thread_state",1477
-        11891,1737349458627,1884896,1477,0,"thread_state: Running","thread_state",1477
-        11891,1737349458627,1884896,1477,1,"[NULL]","thread_state",1477
-        11891,1737349458627,1884896,1477,2,"[NULL]","thread_state",1477
-        11891,1737349458627,1884896,1477,3,"process_name: com.android.providers.media.module","thread_state",1477
-        11891,1737349458627,1884896,1477,4,"thread_name: rs.media.module","thread_state",1477
-        11891,1737349458627,1884896,1477,5,"cpu: 0","thread_state",1477
+        4271,1737500355691,1456753,1477,5,"bindApplication","slice",1477
+        13120,1737500355691,1456753,1477,0,"thread_state: S","thread_state",1477
+        13120,1737500355691,1456753,1477,1,"[NULL]","thread_state",1477
+        13120,1737500355691,1456753,1477,2,"[NULL]","thread_state",1477
+        13120,1737500355691,1456753,1477,3,"process_name: com.android.providers.media.module","thread_state",1477
+        13120,1737500355691,1456753,1477,4,"thread_name: rs.media.module","thread_state",1477
+        4800,1737500355691,1456753,1498,11,"HIDL::IComponentStore::getStructDescriptors::client","slice",1477
+        4801,1737500355691,1456753,1498,12,"binder transaction","slice",1477
+        13648,1737500355691,1456753,1498,6,"blocking thread_state: R+","thread_state",1477
+        13648,1737500355691,1456753,1498,7,"blocking process_name: com.android.providers.media.module","thread_state",1477
+        13648,1737500355691,1456753,1498,8,"blocking thread_name: CodecLooper","thread_state",1477
+        13648,1737500355691,1456753,1498,9,"[NULL]","thread_state",1477
+        13648,1737500355691,1456753,1498,10,"[NULL]","thread_state",1477
         """))
 
   def test_thread_executing_span_critical_path_graph(self):
diff --git a/ui/src/core/default_plugins.ts b/ui/src/core/default_plugins.ts
index 4f5460e..44b4e21 100644
--- a/ui/src/core/default_plugins.ts
+++ b/ui/src/core/default_plugins.ts
@@ -58,4 +58,5 @@
   'perfetto.VisualisedArgs',
   'org.kernel.LinuxKernelDevices',
   'perfetto.TrackUtils',
+  'com.google.PixelMemory',
 ];
diff --git a/ui/src/frontend/error_dialog.ts b/ui/src/frontend/error_dialog.ts
index f2cea37..835292e 100644
--- a/ui/src/frontend/error_dialog.ts
+++ b/ui/src/frontend/error_dialog.ts
@@ -36,6 +36,7 @@
   // Here we rely on the exception message from onCannotGrowMemory function
   if (
     err.message.includes('Cannot enlarge memory') ||
+    err.stack.some((entry) => entry.name.includes('OutOfMemoryHandler')) ||
     err.stack.some((entry) => entry.name.includes('_emscripten_resize_heap')) ||
     err.stack.some((entry) => entry.name.includes('sbrk')) ||
     /^out of memory$/m.exec(err.message)
diff --git a/ui/src/plugins/com.google.PixelMemory/OWNERS b/ui/src/plugins/com.google.PixelMemory/OWNERS
new file mode 100644
index 0000000..249152a
--- /dev/null
+++ b/ui/src/plugins/com.google.PixelMemory/OWNERS
@@ -0,0 +1 @@
+liumartin@google.com
diff --git a/ui/src/plugins/com.google.PixelMemory/index.ts b/ui/src/plugins/com.google.PixelMemory/index.ts
new file mode 100644
index 0000000..c79faf2
--- /dev/null
+++ b/ui/src/plugins/com.google.PixelMemory/index.ts
@@ -0,0 +1,63 @@
+// Copyright (C) 2024 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 {Plugin, PluginContextTrace, PluginDescriptor} from '../../public';
+
+import {addDebugCounterTrack} from '../../frontend/debug_tracks';
+
+class PixelMemory implements Plugin {
+  async onTraceLoad(ctx: PluginContextTrace): Promise<void> {
+    ctx.registerCommand({
+      id: 'dev.perfetto.PixelMemory#ShowTotalMemory',
+      name: 'Add tracks: show a process total memory',
+      callback: async (pid) => {
+        if (pid === undefined) {
+          pid = prompt('Enter a process pid', '');
+          if (pid === null) return;
+        }
+        const RSS_ALL = `
+          INCLUDE PERFETTO MODULE memory.linux.process;
+          INCLUDE PERFETTO MODULE memory.android.gpu;
+
+          DROP TABLE IF EXISTS process_mem_rss_anon_file_shmem_swap_gpu;
+
+          CREATE VIRTUAL TABLE process_mem_rss_anon_file_shmem_swap_gpu
+          USING
+            SPAN_OUTER_JOIN(
+              memory_gpu_per_process PARTITIONED upid, memory_rss_and_swap_per_process PARTITIONED upid);
+        `;
+        await ctx.engine.query(RSS_ALL);
+        await addDebugCounterTrack(
+          {
+            sqlSource: `
+                SELECT
+                  ts,
+                  COALESCE(rss_and_swap, 0) + COALESCE(gpu_memory, 0) AS value
+                FROM process_mem_rss_anon_file_shmem_swap_gpu
+                WHERE pid = ${pid}
+            `,
+            columns: ['ts', 'value'],
+          },
+          pid + '_rss_anon_file_swap_shmem_gpu',
+          {ts: 'ts', value: 'value'},
+        );
+      },
+    });
+  }
+}
+
+export const plugin: PluginDescriptor = {
+  pluginId: 'com.google.PixelMemory',
+  plugin: PixelMemory,
+};