Merge "Reapply "tp: fix build in Google3" Reapply "Refactor StackProfile related classes."" into main
diff --git a/Android.bp b/Android.bp
index 0ea862b..f4559d8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -11999,6 +11999,8 @@
         "src/trace_processor/perfetto_sql/stdlib/common/thread_states.sql",
         "src/trace_processor/perfetto_sql/stdlib/common/timestamps.sql",
         "src/trace_processor/perfetto_sql/stdlib/counters/intervals.sql",
+        "src/trace_processor/perfetto_sql/stdlib/cpu/cpus.sql",
+        "src/trace_processor/perfetto_sql/stdlib/cpu/size.sql",
         "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/args.sql",
         "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/counters.sql",
         "src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/cpus.sql",
@@ -12014,6 +12016,7 @@
         "src/trace_processor/perfetto_sql/stdlib/pkvm/hypervisor.sql",
         "src/trace_processor/perfetto_sql/stdlib/prelude/slices.sql",
         "src/trace_processor/perfetto_sql/stdlib/prelude/trace_bounds.sql",
+        "src/trace_processor/perfetto_sql/stdlib/sched/states.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/thread_state_flattened.sql",
@@ -12446,6 +12449,7 @@
         "src/traceconv/trace_to_profile.cc",
         "src/traceconv/trace_to_systrace.cc",
         "src/traceconv/trace_to_text.cc",
+        "src/traceconv/trace_unpack.cc",
     ],
 }
 
diff --git a/BUILD b/BUILD
index 09ab8b4..09790fd 100644
--- a/BUILD
+++ b/BUILD
@@ -2405,6 +2405,15 @@
     ],
 )
 
+# GN target: //src/trace_processor/perfetto_sql/stdlib/cpu:cpu
+perfetto_filegroup(
+    name = "src_trace_processor_perfetto_sql_stdlib_cpu_cpu",
+    srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/cpu/cpus.sql",
+        "src/trace_processor/perfetto_sql/stdlib/cpu/size.sql",
+    ],
+)
+
 # GN target: //src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common:common
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_deprecated_v42_common_common",
@@ -2466,6 +2475,7 @@
 perfetto_filegroup(
     name = "src_trace_processor_perfetto_sql_stdlib_sched_sched",
     srcs = [
+        "src/trace_processor/perfetto_sql/stdlib/sched/states.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/thread_executing_span.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql",
         "src/trace_processor/perfetto_sql/stdlib/sched/thread_state_flattened.sql",
@@ -2499,6 +2509,7 @@
         ":src_trace_processor_perfetto_sql_stdlib_chrome_chrome_sql",
         ":src_trace_processor_perfetto_sql_stdlib_common_common",
         ":src_trace_processor_perfetto_sql_stdlib_counters_counters",
+        ":src_trace_processor_perfetto_sql_stdlib_cpu_cpu",
         ":src_trace_processor_perfetto_sql_stdlib_deprecated_v42_common_common",
         ":src_trace_processor_perfetto_sql_stdlib_graphs_graphs",
         ":src_trace_processor_perfetto_sql_stdlib_intervals_intervals",
@@ -2909,6 +2920,8 @@
         "src/traceconv/trace_to_systrace.h",
         "src/traceconv/trace_to_text.cc",
         "src/traceconv/trace_to_text.h",
+        "src/traceconv/trace_unpack.cc",
+        "src/traceconv/trace_unpack.h",
     ],
 )
 
diff --git a/include/perfetto/ext/base/unix_socket.h b/include/perfetto/ext/base/unix_socket.h
index c5ee44c..f0e7961 100644
--- a/include/perfetto/ext/base/unix_socket.h
+++ b/include/perfetto/ext/base/unix_socket.h
@@ -25,6 +25,7 @@
 #include <utility>
 
 #include "perfetto/base/build_config.h"
+#include "perfetto/base/compiler.h"
 #include "perfetto/base/export.h"
 #include "perfetto/base/logging.h"
 #include "perfetto/base/platform_handle.h"
@@ -87,6 +88,7 @@
 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   return sock_family == SockFamily::kUnix;
 #else
+  base::ignore_result(sock_family);
   // On Windows shm is negotiated by sharing an unguessable token
   // over TCP sockets. In theory works on any socket type, in practice
   // we need to tell the difference between a local and a remote
diff --git a/perfetto.rc b/perfetto.rc
index 8a5ee38..18432dc 100644
--- a/perfetto.rc
+++ b/perfetto.rc
@@ -57,6 +57,9 @@
     # by the bug reporting UI (ls+getattr).
     mkdir /data/misc/perfetto-traces/bugreport 0773 root shell
 
+    # Traces in this directory are only accessed by system server
+    mkdir /data/misc/perfetto-traces/profiling 0773 root shell
+
     # This directory allows shell to save configs file in a place where the
     # perfetto cmdline client can read then. /data/local/tmp/ isn't safe because
     # too many other domains can write into that. See b/170404111.
diff --git a/protos/perfetto/config/android/protolog_config.proto b/protos/perfetto/config/android/protolog_config.proto
index c73256e..af61c2b 100644
--- a/protos/perfetto/config/android/protolog_config.proto
+++ b/protos/perfetto/config/android/protolog_config.proto
@@ -41,8 +41,8 @@
 }
 
 message ProtoLogGroup {
-  // The ProtoLog tag this configuration entry applies to.
-  optional string tag = 1;
+  // The ProtoLog group name this configuration entry applies to.
+  optional string group_name = 1;
   // Specify the level from which to start capturing protologs.
   // e.g. if ProtoLogLevel.WARN is specified only warning, errors and fatal log
   // message will be traced.
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index d91246b..36595af 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -594,8 +594,8 @@
 }
 
 message ProtoLogGroup {
-  // The ProtoLog tag this configuration entry applies to.
-  optional string tag = 1;
+  // The ProtoLog group name this configuration entry applies to.
+  optional string group_name = 1;
   // Specify the level from which to start capturing protologs.
   // e.g. if ProtoLogLevel.WARN is specified only warning, errors and fatal log
   // message will be traced.
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index f9370cd..dde9fdb 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -594,8 +594,8 @@
 }
 
 message ProtoLogGroup {
-  // The ProtoLog tag this configuration entry applies to.
-  optional string tag = 1;
+  // The ProtoLog group name this configuration entry applies to.
+  optional string group_name = 1;
   // Specify the level from which to start capturing protologs.
   // e.g. if ProtoLogLevel.WARN is specified only warning, errors and fatal log
   // message will be traced.
diff --git a/src/base/unix_socket_unittest.cc b/src/base/unix_socket_unittest.cc
index a0d0bb3..88383d1 100644
--- a/src/base/unix_socket_unittest.cc
+++ b/src/base/unix_socket_unittest.cc
@@ -1166,6 +1166,9 @@
 }
 
 TEST_F(UnixSocketTest, ShmemSupported) {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+  ASSERT_EQ(SockShmemSupported(""), true);
+#else  // PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
   ASSERT_EQ(SockShmemSupported(""), false);
   ASSERT_EQ(SockShmemSupported("/path/to/sock"), true);
   ASSERT_EQ(SockShmemSupported("local_dir_sock"), true);
@@ -1178,6 +1181,7 @@
     PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
   ASSERT_EQ(SockShmemSupported("vsock://-1:10000"), false);
 #endif
+#endif  // !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 }
 
 }  // namespace
diff --git a/src/ipc/host_impl_unittest.cc b/src/ipc/host_impl_unittest.cc
index 927a5a6..ce732cd 100644
--- a/src/ipc/host_impl_unittest.cc
+++ b/src/ipc/host_impl_unittest.cc
@@ -15,7 +15,7 @@
  */
 
 #include "src/ipc/host_impl.h"
-#include <sys/socket.h>
+
 #include <memory>
 
 #include "perfetto/ext/base/file_utils.h"
@@ -34,6 +34,11 @@
 #include "protos/perfetto/ipc/wire_protocol.gen.h"
 #include "src/ipc/test/client_unittest_messages.gen.h"
 
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
+    PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
+#include <sys/socket.h>
+#endif
+
 namespace perfetto {
 namespace ipc {
 namespace {
@@ -644,7 +649,8 @@
   EXPECT_CALL(*cli, OnInvokeMethodReply(_)).WillOnce(Return());
   task_runner->RunUntilIdle();
 }
-#endif  // OS_WIN
+#endif  // PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) ||
+        // PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID)
 
 // TODO(primiano): add the tests below in next CLs.
 // TEST(HostImplTest, ManyClients) {}
diff --git a/src/perfetto_cmd/perfetto_cmd.cc b/src/perfetto_cmd/perfetto_cmd.cc
index 9d77c4b..e356b0d 100644
--- a/src/perfetto_cmd/perfetto_cmd.cc
+++ b/src/perfetto_cmd/perfetto_cmd.cc
@@ -35,6 +35,7 @@
 #include <unistd.h>
 #endif
 
+#include <atomic>
 #include <chrono>
 #include <fstream>
 #include <iostream>
@@ -82,7 +83,7 @@
 namespace perfetto {
 namespace {
 
-perfetto::PerfettoCmd* g_perfetto_cmd;
+std::atomic<perfetto::PerfettoCmd*> g_perfetto_cmd;
 
 const uint32_t kOnTraceDataTimeoutMs = 3000;
 const uint32_t kCloneTimeoutMs = 10000;
@@ -209,13 +210,13 @@
 
 PerfettoCmd::PerfettoCmd() {
   // Only the main thread instance on the main thread will receive ctrl-c.
-  if (!g_perfetto_cmd)
-    g_perfetto_cmd = this;
+  PerfettoCmd* set_if_null = nullptr;
+  g_perfetto_cmd.compare_exchange_strong(set_if_null, this);
 }
 
 PerfettoCmd::~PerfettoCmd() {
-  if (g_perfetto_cmd == this) {
-    g_perfetto_cmd = nullptr;
+  PerfettoCmd* self = this;
+  if (g_perfetto_cmd.compare_exchange_strong(self, nullptr)) {
     if (ctrl_c_handler_installed_) {
       task_runner_.RemoveFileDescriptorWatch(ctrl_c_evt_.fd());
     }
@@ -1297,9 +1298,8 @@
     return;
   ctrl_c_handler_installed_ = true;
   base::InstallCtrlCHandler([] {
-    if (!g_perfetto_cmd)
-      return;
-    g_perfetto_cmd->SignalCtrlC();
+    if (PerfettoCmd* main_thread = g_perfetto_cmd.load())
+      main_thread->SignalCtrlC();
   });
   auto weak_this = weak_factory_.GetWeakPtr();
   task_runner_.AddFileDescriptorWatch(ctrl_c_evt_.fd(), [weak_this] {
diff --git a/src/trace_processor/db/column/arrangement_overlay.cc b/src/trace_processor/db/column/arrangement_overlay.cc
index 281071f..b9e3000 100644
--- a/src/trace_processor/db/column/arrangement_overlay.cc
+++ b/src/trace_processor/db/column/arrangement_overlay.cc
@@ -54,13 +54,6 @@
   return inner_->SingleSearch(op, sql_val, (*arrangement_)[index]);
 }
 
-UniqueSearchResult ArrangementOverlay::ChainImpl::UniqueSearch(
-    FilterOp,
-    SqlValue,
-    uint32_t*) const {
-  return UniqueSearchResult::kNeedsFullSearch;
-}
-
 SearchValidationResult ArrangementOverlay::ChainImpl::ValidateSearchConstraints(
     FilterOp op,
     SqlValue value) const {
diff --git a/src/trace_processor/db/column/arrangement_overlay.h b/src/trace_processor/db/column/arrangement_overlay.h
index 6138918..bca884f 100644
--- a/src/trace_processor/db/column/arrangement_overlay.h
+++ b/src/trace_processor/db/column/arrangement_overlay.h
@@ -54,10 +54,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/data_layer.h b/src/trace_processor/db/column/data_layer.h
index 5ec7909..8f34845 100644
--- a/src/trace_processor/db/column/data_layer.h
+++ b/src/trace_processor/db/column/data_layer.h
@@ -111,29 +111,13 @@
 
   // Start of public API.
 
-  // Checks whether element at the the provided index match |op| and |value|.
+  // Checks whether element at the provided index match |op| and |value|.
   //
-  // Returns one of the following:
-  //  * kMatch if the element matches.
-  //  * kNoMatch if the element does not match.
-  //  * kNeedsFullSearch if one of the "full" search algorithms need to be
-  //    run to determine if the element matches.
+  // Returns true if the element matches, false otherwise.
   virtual SingleSearchResult SingleSearch(FilterOp op,
                                           SqlValue value,
                                           uint32_t row) const = 0;
 
-  // Searches for a *unique* element in chain which matches |op| and |value|.
-  //
-  // The return value is one of the following:
-  //  * kMatch if an element matches. |row| should be set to the index of
-  //    the element.
-  //  * kNoMatch if no element matches.
-  //  * kNeedsFullSearch if one of the "full" search algorithms need to be
-  //    run for this constraint.
-  virtual UniqueSearchResult UniqueSearch(FilterOp op,
-                                          SqlValue value,
-                                          uint32_t* row) const = 0;
-
   // Searches for elements which match |op| and |value| between |range.start|
   // and |range.end|.
   //
diff --git a/src/trace_processor/db/column/dense_null_overlay.cc b/src/trace_processor/db/column/dense_null_overlay.cc
index e7251a3..7dff76f 100644
--- a/src/trace_processor/db/column/dense_null_overlay.cc
+++ b/src/trace_processor/db/column/dense_null_overlay.cc
@@ -61,27 +61,6 @@
   PERFETTO_FATAL("For GCC");
 }
 
-UniqueSearchResult DenseNullOverlay::ChainImpl::UniqueSearch(
-    FilterOp op,
-    SqlValue sql_val,
-    uint32_t* index) const {
-  switch (inner_->UniqueSearch(op, sql_val, index)) {
-    case UniqueSearchResult::kMatch:
-      if (*index >= non_null_->size()) {
-        return UniqueSearchResult::kNoMatch;
-      }
-      // If non_null_[index] is not set, then any result returned by |inner_| is
-      // meaningless as the value in |inner_| is not a "real" value.
-      return non_null_->IsSet(*index) ? UniqueSearchResult::kMatch
-                                      : UniqueSearchResult::kNeedsFullSearch;
-    case UniqueSearchResult::kNoMatch:
-      return UniqueSearchResult::kNoMatch;
-    case UniqueSearchResult::kNeedsFullSearch:
-      return UniqueSearchResult::kNeedsFullSearch;
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
 SearchValidationResult DenseNullOverlay::ChainImpl::ValidateSearchConstraints(
     FilterOp op,
     SqlValue sql_val) const {
diff --git a/src/trace_processor/db/column/dense_null_overlay.h b/src/trace_processor/db/column/dense_null_overlay.h
index 4acec46..0f881fb 100644
--- a/src/trace_processor/db/column/dense_null_overlay.h
+++ b/src/trace_processor/db/column/dense_null_overlay.h
@@ -49,10 +49,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/dense_null_overlay_unittest.cc b/src/trace_processor/db/column/dense_null_overlay_unittest.cc
index 1678857..0cf6276 100644
--- a/src/trace_processor/db/column/dense_null_overlay_unittest.cc
+++ b/src/trace_processor/db/column/dense_null_overlay_unittest.cc
@@ -17,7 +17,6 @@
 #include "src/trace_processor/db/column/dense_null_overlay.h"
 
 #include <cstdint>
-#include <limits>
 #include <memory>
 #include <vector>
 
@@ -202,40 +201,6 @@
             SingleSearchResult::kMatch);
 }
 
-TEST(DenseNullOverlay, UniqueSearchNonNull) {
-  BitVector bv{0, 1, 0, 1, 1};
-  DenseNullOverlay storage(&bv);
-  auto fake = FakeStorageChain::SearchSubset(5, Range(1, 2));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kMatch);
-  ASSERT_EQ(row, 1u);
-}
-
-TEST(DenseNullOverlay, UniqueSearchNull) {
-  BitVector bv{0, 0, 0, 1, 1};
-  DenseNullOverlay storage(&bv);
-  auto fake = FakeStorageChain::SearchSubset(5, Range(1, 2));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kNeedsFullSearch);
-}
-
-TEST(DenseNullOverlay, UniqueSearchOutOfBounds) {
-  BitVector bv{0, 0, 0, 1, 1};
-  DenseNullOverlay storage(&bv);
-  auto fake = FakeStorageChain::SearchSubset(6, Range(5, 6));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kNoMatch);
-}
-
 TEST(DenseNullOverlay, StableSort) {
   std::vector<uint32_t> numeric_data{0, 3, 0, 1, 0, 2, 4};
   NumericStorage<uint32_t> numeric(&numeric_data, ColumnType::kUint32, false);
diff --git a/src/trace_processor/db/column/dummy_storage.cc b/src/trace_processor/db/column/dummy_storage.cc
index 0f182f8..54d7c61 100644
--- a/src/trace_processor/db/column/dummy_storage.cc
+++ b/src/trace_processor/db/column/dummy_storage.cc
@@ -31,12 +31,6 @@
   PERFETTO_FATAL("Shouldn't be called");
 }
 
-UniqueSearchResult DummyStorage::ChainImpl::UniqueSearch(FilterOp,
-                                                         SqlValue,
-                                                         uint32_t*) const {
-  PERFETTO_FATAL("Shouldn't be called");
-}
-
 SearchValidationResult DummyStorage::ChainImpl::ValidateSearchConstraints(
     FilterOp,
     SqlValue) const {
diff --git a/src/trace_processor/db/column/dummy_storage.h b/src/trace_processor/db/column/dummy_storage.h
index 286c1ea..ced41a9 100644
--- a/src/trace_processor/db/column/dummy_storage.h
+++ b/src/trace_processor/db/column/dummy_storage.h
@@ -38,10 +38,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/fake_storage.cc b/src/trace_processor/db/column/fake_storage.cc
index 1bab025..9fbc3ae 100644
--- a/src/trace_processor/db/column/fake_storage.cc
+++ b/src/trace_processor/db/column/fake_storage.cc
@@ -56,34 +56,6 @@
   PERFETTO_FATAL("For GCC");
 }
 
-UniqueSearchResult FakeStorageChain::UniqueSearch(FilterOp,
-                                                  SqlValue,
-                                                  uint32_t* i) const {
-  switch (strategy_) {
-    case kAll:
-      if (size_ != 1) {
-        return UniqueSearchResult::kNeedsFullSearch;
-      }
-      *i = 0;
-      return UniqueSearchResult::kMatch;
-    case kNone:
-      return UniqueSearchResult::kNoMatch;
-    case kBitVector:
-      if (bit_vector_.CountSetBits() != 1) {
-        return UniqueSearchResult::kNeedsFullSearch;
-      }
-      *i = bit_vector_.IndexOfNthSet(0);
-      return UniqueSearchResult::kMatch;
-    case kRange:
-      if (range_.size() != 1) {
-        return UniqueSearchResult::kNeedsFullSearch;
-      }
-      *i = range_.start;
-      return UniqueSearchResult::kMatch;
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
 SearchValidationResult FakeStorageChain::ValidateSearchConstraints(
     FilterOp,
     SqlValue) const {
diff --git a/src/trace_processor/db/column/fake_storage.h b/src/trace_processor/db/column/fake_storage.h
index 8d4d6b5..aca6890 100644
--- a/src/trace_processor/db/column/fake_storage.h
+++ b/src/trace_processor/db/column/fake_storage.h
@@ -77,8 +77,6 @@
   // Implementation of DataLayerChain.
   SingleSearchResult SingleSearch(FilterOp, SqlValue, uint32_t) const override;
 
-  UniqueSearchResult UniqueSearch(FilterOp, SqlValue, uint32_t*) const override;
-
   SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                    SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/id_storage.cc b/src/trace_processor/db/column/id_storage.cc
index 1c32194..6fdb800 100644
--- a/src/trace_processor/db/column/id_storage.cc
+++ b/src/trace_processor/db/column/id_storage.cc
@@ -191,19 +191,6 @@
   PERFETTO_FATAL("For GCC");
 }
 
-UniqueSearchResult IdStorage::ChainImpl::UniqueSearch(FilterOp op,
-                                                      SqlValue sql_val,
-                                                      uint32_t* index) const {
-  if (sql_val.type != SqlValue::kLong ||
-      sql_val.long_value >= std::numeric_limits<uint32_t>::max() ||
-      sql_val.long_value <= std::numeric_limits<uint32_t>::min() ||
-      op != FilterOp::kEq) {
-    return UniqueSearchResult::kNeedsFullSearch;
-  }
-  *index = static_cast<uint32_t>(sql_val.long_value);
-  return UniqueSearchResult::kMatch;
-}
-
 RangeOrBitVector IdStorage::ChainImpl::SearchValidated(
     FilterOp op,
     SqlValue sql_val,
diff --git a/src/trace_processor/db/column/id_storage.h b/src/trace_processor/db/column/id_storage.h
index bb0d7d5..e6e25ab 100644
--- a/src/trace_processor/db/column/id_storage.h
+++ b/src/trace_processor/db/column/id_storage.h
@@ -48,10 +48,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/id_storage_unittest.cc b/src/trace_processor/db/column/id_storage_unittest.cc
index 32b0583..48ce18e 100644
--- a/src/trace_processor/db/column/id_storage_unittest.cc
+++ b/src/trace_processor/db/column/id_storage_unittest.cc
@@ -145,25 +145,6 @@
             SingleSearchResult::kNoMatch);
 }
 
-TEST(IdStorage, UniqueSearch) {
-  IdStorage storage;
-  auto chain = storage.MakeChain();
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kEq, SqlValue::Long(5), &row),
-            UniqueSearchResult::kMatch);
-  ASSERT_EQ(row, 5u);
-
-  row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(
-      chain->UniqueSearch(
-          FilterOp::kEq,
-          SqlValue::Long(std::numeric_limits<uint32_t>::max() + 1ll), &row),
-      UniqueSearchResult::kNeedsFullSearch);
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kEq, SqlValue::Double(0), &row),
-            UniqueSearchResult::kNeedsFullSearch);
-}
-
 TEST(IdStorage, SearchEqSimple) {
   IdStorage storage;
   auto chain = storage.MakeChain();
diff --git a/src/trace_processor/db/column/null_overlay.cc b/src/trace_processor/db/column/null_overlay.cc
index fe489e4..af31ac5 100644
--- a/src/trace_processor/db/column/null_overlay.cc
+++ b/src/trace_processor/db/column/null_overlay.cc
@@ -104,24 +104,6 @@
   PERFETTO_FATAL("For GCC");
 }
 
-UniqueSearchResult NullOverlay::ChainImpl::UniqueSearch(FilterOp op,
-                                                        SqlValue sql_val,
-                                                        uint32_t* index) const {
-  switch (inner_->UniqueSearch(op, sql_val, index)) {
-    case UniqueSearchResult::kMatch:
-      if (*index >= non_null_->CountSetBits()) {
-        return UniqueSearchResult::kNoMatch;
-      }
-      *index = non_null_->IndexOfNthSet(*index);
-      return UniqueSearchResult::kMatch;
-    case UniqueSearchResult::kNoMatch:
-      return UniqueSearchResult::kNoMatch;
-    case UniqueSearchResult::kNeedsFullSearch:
-      return UniqueSearchResult::kNeedsFullSearch;
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
 NullOverlay::ChainImpl::ChainImpl(std::unique_ptr<DataLayerChain> innner,
                                   const BitVector* non_null)
     : inner_(std::move(innner)), non_null_(non_null) {
diff --git a/src/trace_processor/db/column/null_overlay.h b/src/trace_processor/db/column/null_overlay.h
index 35c5e52..99f6256 100644
--- a/src/trace_processor/db/column/null_overlay.h
+++ b/src/trace_processor/db/column/null_overlay.h
@@ -48,10 +48,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/null_overlay_unittest.cc b/src/trace_processor/db/column/null_overlay_unittest.cc
index 73934a6..ceb6c9f 100644
--- a/src/trace_processor/db/column/null_overlay_unittest.cc
+++ b/src/trace_processor/db/column/null_overlay_unittest.cc
@@ -17,7 +17,6 @@
 #include "src/trace_processor/db/column/null_overlay.h"
 
 #include <cstdint>
-#include <limits>
 #include <memory>
 #include <vector>
 
@@ -74,29 +73,6 @@
             SingleSearchResult::kNoMatch);
 }
 
-TEST(NullOverlay, UniqueSearch) {
-  BitVector bv{0, 0, 0, 1, 1};
-  NullOverlay storage(&bv);
-  auto fake = FakeStorageChain::SearchSubset(5, Range(1, 2));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kMatch);
-  ASSERT_EQ(row, 4u);
-}
-
-TEST(NullOverlay, UniqueSearchOutOfBounds) {
-  BitVector bv{0, 0, 0, 1, 1};
-  NullOverlay storage(&bv);
-  auto fake = FakeStorageChain::SearchSubset(5, Range(4, 5));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kNoMatch);
-}
-
 TEST(NullOverlay, SearchInputInsideBoundary) {
   BitVector bv{0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0};
   auto fake = FakeStorageChain::SearchAll(4u);
diff --git a/src/trace_processor/db/column/numeric_storage.cc b/src/trace_processor/db/column/numeric_storage.cc
index 27f6204..529e93d 100644
--- a/src/trace_processor/db/column/numeric_storage.cc
+++ b/src/trace_processor/db/column/numeric_storage.cc
@@ -27,7 +27,6 @@
 #include <string>
 #include <utility>
 #include <variant>
-#include <vector>
 
 #include "perfetto/base/logging.h"
 #include "perfetto/public/compiler.h"
@@ -259,7 +258,7 @@
   auto i_as_d = static_cast<double>(i);
 
   // Case when |sql_val| can be interpreted as a SqlValue::Long.
-  if (std::equal_to<>()(i, static_cast<int64_t>(i_as_d))) {
+  if (std::equal_to<int64_t>()(i, static_cast<int64_t>(i_as_d))) {
     *sql_val = SqlValue::Double(i_as_d);
     return SearchValidationResult::kOk;
   }
@@ -299,13 +298,6 @@
                                          bool is_sorted)
     : vector_ptr_(vector_ptr), storage_type_(type), is_sorted_(is_sorted) {}
 
-UniqueSearchResult NumericStorageBase::ChainImpl::UniqueSearch(
-    FilterOp,
-    SqlValue,
-    uint32_t*) const {
-  return UniqueSearchResult::kNeedsFullSearch;
-}
-
 SearchValidationResult NumericStorageBase::ChainImpl::ValidateSearchConstraints(
     FilterOp op,
     SqlValue val) const {
diff --git a/src/trace_processor/db/column/numeric_storage.h b/src/trace_processor/db/column/numeric_storage.h
index af69e28..2dfafc6 100644
--- a/src/trace_processor/db/column/numeric_storage.h
+++ b/src/trace_processor/db/column/numeric_storage.h
@@ -37,10 +37,6 @@
  protected:
   class ChainImpl : public DataLayerChain {
    public:
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/range_overlay.cc b/src/trace_processor/db/column/range_overlay.cc
index 308bf1c..0b8275b 100644
--- a/src/trace_processor/db/column/range_overlay.cc
+++ b/src/trace_processor/db/column/range_overlay.cc
@@ -47,25 +47,6 @@
   return inner_->SingleSearch(op, sql_val, i + range_->start);
 }
 
-UniqueSearchResult RangeOverlay::ChainImpl::UniqueSearch(
-    FilterOp op,
-    SqlValue sql_val,
-    uint32_t* index) const {
-  switch (inner_->UniqueSearch(op, sql_val, index)) {
-    case UniqueSearchResult::kMatch:
-      if (!range_->Contains(*index)) {
-        return UniqueSearchResult::kNoMatch;
-      }
-      *index -= range_->start;
-      return UniqueSearchResult::kMatch;
-    case UniqueSearchResult::kNoMatch:
-      return UniqueSearchResult::kNoMatch;
-    case UniqueSearchResult::kNeedsFullSearch:
-      return UniqueSearchResult::kNeedsFullSearch;
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
 SearchValidationResult RangeOverlay::ChainImpl::ValidateSearchConstraints(
     FilterOp op,
     SqlValue sql_val) const {
diff --git a/src/trace_processor/db/column/range_overlay.h b/src/trace_processor/db/column/range_overlay.h
index 366b2d3..200d5d4 100644
--- a/src/trace_processor/db/column/range_overlay.h
+++ b/src/trace_processor/db/column/range_overlay.h
@@ -45,10 +45,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/range_overlay_unittest.cc b/src/trace_processor/db/column/range_overlay_unittest.cc
index bf13cbb..96c2f80 100644
--- a/src/trace_processor/db/column/range_overlay_unittest.cc
+++ b/src/trace_processor/db/column/range_overlay_unittest.cc
@@ -17,8 +17,6 @@
 #include "src/trace_processor/db/column/range_overlay.h"
 
 #include <cstdint>
-#include <limits>
-#include <utility>
 #include <vector>
 
 #include "perfetto/trace_processor/basic_types.h"
@@ -37,7 +35,7 @@
 using testing::IsEmpty;
 using Range = Range;
 
-TEST(SelectorOverlay, SingleSearch) {
+TEST(RangeOverlay, SearchSingle) {
   Range range(3, 8);
   RangeOverlay storage(&range);
   auto fake = FakeStorageChain::SearchSubset(
@@ -50,40 +48,6 @@
             SingleSearchResult::kNoMatch);
 }
 
-TEST(SelectorOverlay, UniqueSearch) {
-  Range range(1, 3);
-  RangeOverlay storage(&range);
-  auto fake = FakeStorageChain::SearchSubset(5, Range(2, 3));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kMatch);
-  ASSERT_EQ(row, 1u);
-}
-
-TEST(SelectorOverlay, UniqueSearchLowOutOfBounds) {
-  Range range(3, 8);
-  RangeOverlay storage(&range);
-  auto fake = FakeStorageChain::SearchSubset(8, Range(1, 2));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kNoMatch);
-}
-
-TEST(SelectorOverlay, UniqueSearchHighOutOfBounds) {
-  Range range(3, 8);
-  RangeOverlay storage(&range);
-  auto fake = FakeStorageChain::SearchSubset(9, Range(8, 9));
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kIsNotNull, SqlValue(), &row),
-            UniqueSearchResult::kNoMatch);
-}
-
 TEST(RangeOverlay, SearchAll) {
   Range range(3, 8);
   RangeOverlay storage(&range);
diff --git a/src/trace_processor/db/column/selector_overlay.cc b/src/trace_processor/db/column/selector_overlay.cc
index 935c4e2..ff1e463 100644
--- a/src/trace_processor/db/column/selector_overlay.cc
+++ b/src/trace_processor/db/column/selector_overlay.cc
@@ -44,25 +44,6 @@
   return inner_->SingleSearch(op, sql_val, selector_->IndexOfNthSet(i));
 }
 
-UniqueSearchResult SelectorOverlay::ChainImpl::UniqueSearch(
-    FilterOp op,
-    SqlValue sql_val,
-    uint32_t* index) const {
-  switch (inner_->UniqueSearch(op, sql_val, index)) {
-    case UniqueSearchResult::kMatch:
-      if (*index >= selector_->size() || !selector_->IsSet(*index)) {
-        return UniqueSearchResult::kNoMatch;
-      }
-      *index = selector_->CountSetBits(*index);
-      return UniqueSearchResult::kMatch;
-    case UniqueSearchResult::kNoMatch:
-      return UniqueSearchResult::kNoMatch;
-    case UniqueSearchResult::kNeedsFullSearch:
-      return UniqueSearchResult::kNeedsFullSearch;
-  }
-  PERFETTO_FATAL("For GCC");
-}
-
 SearchValidationResult SelectorOverlay::ChainImpl::ValidateSearchConstraints(
     FilterOp op,
     SqlValue sql_val) const {
diff --git a/src/trace_processor/db/column/selector_overlay.h b/src/trace_processor/db/column/selector_overlay.h
index 04bc2e7..04f178a 100644
--- a/src/trace_processor/db/column/selector_overlay.h
+++ b/src/trace_processor/db/column/selector_overlay.h
@@ -49,10 +49,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/selector_overlay_unittest.cc b/src/trace_processor/db/column/selector_overlay_unittest.cc
index b9151b8..11d20f2 100644
--- a/src/trace_processor/db/column/selector_overlay_unittest.cc
+++ b/src/trace_processor/db/column/selector_overlay_unittest.cc
@@ -17,12 +17,11 @@
 #include "src/trace_processor/db/column/selector_overlay.h"
 
 #include <cstdint>
-#include <limits>
 #include <vector>
 
+#include "data_layer.h"
 #include "perfetto/trace_processor/basic_types.h"
 #include "src/trace_processor/containers/bit_vector.h"
-#include "src/trace_processor/db/column/data_layer.h"
 #include "src/trace_processor/db/column/fake_storage.h"
 #include "src/trace_processor/db/column/numeric_storage.h"
 #include "src/trace_processor/db/column/types.h"
@@ -47,38 +46,6 @@
             SingleSearchResult::kNoMatch);
 }
 
-TEST(SelectorOverlay, UniqueSearch) {
-  BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
-  auto fake = FakeStorageChain::SearchSubset(8, Range(2, 3));
-  SelectorOverlay storage(&selector);
-  auto chain = storage.MakeChain(std::move(fake));
-
-  uint32_t row = std::numeric_limits<uint32_t>::max();
-  ASSERT_EQ(chain->UniqueSearch(FilterOp::kGe, SqlValue::Long(0u), &row),
-            UniqueSearchResult::kMatch);
-  ASSERT_EQ(row, 1u);
-}
-
-TEST(SelectorOverlay, UniqueSearchNotSet) {
-  BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
-  auto fake = FakeStorageChain::SearchSubset(8, Range(4, 5));
-  SelectorOverlay storage(&selector);
-  auto chain = storage.MakeChain(std::move(fake));
-
-  ASSERT_EQ(chain->SingleSearch(FilterOp::kGe, SqlValue::Long(0u), 1),
-            SingleSearchResult::kNoMatch);
-}
-
-TEST(SelectorOverlay, UniqueSearchOutOfBounds) {
-  BitVector selector{0, 1, 1, 0, 0, 1, 1, 0};
-  auto fake = FakeStorageChain::SearchSubset(9, Range(8, 9));
-  SelectorOverlay storage(&selector);
-  auto chain = storage.MakeChain(std::move(fake));
-
-  ASSERT_EQ(chain->SingleSearch(FilterOp::kGe, SqlValue::Long(0u), 1),
-            SingleSearchResult::kNoMatch);
-}
-
 TEST(SelectorOverlay, SearchAll) {
   BitVector selector{0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1};
   auto fake = FakeStorageChain::SearchAll(10);
diff --git a/src/trace_processor/db/column/set_id_storage.cc b/src/trace_processor/db/column/set_id_storage.cc
index 3e9d497..780761c 100644
--- a/src/trace_processor/db/column/set_id_storage.cc
+++ b/src/trace_processor/db/column/set_id_storage.cc
@@ -83,12 +83,6 @@
                                     static_cast<uint32_t>(sql_val.long_value));
 }
 
-UniqueSearchResult SetIdStorage::ChainImpl::UniqueSearch(FilterOp,
-                                                         SqlValue,
-                                                         uint32_t*) const {
-  return UniqueSearchResult::kNeedsFullSearch;
-}
-
 SearchValidationResult SetIdStorage::ChainImpl::ValidateSearchConstraints(
     FilterOp op,
     SqlValue val) const {
diff --git a/src/trace_processor/db/column/set_id_storage.h b/src/trace_processor/db/column/set_id_storage.h
index 86d2508..64f14a3 100644
--- a/src/trace_processor/db/column/set_id_storage.h
+++ b/src/trace_processor/db/column/set_id_storage.h
@@ -47,10 +47,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/string_storage.cc b/src/trace_processor/db/column/string_storage.cc
index ae6a797..05f5932 100644
--- a/src/trace_processor/db/column/string_storage.cc
+++ b/src/trace_processor/db/column/string_storage.cc
@@ -280,12 +280,6 @@
   PERFETTO_FATAL("For GCC");
 }
 
-UniqueSearchResult StringStorage::ChainImpl::UniqueSearch(FilterOp,
-                                                          SqlValue,
-                                                          uint32_t*) const {
-  return UniqueSearchResult::kNeedsFullSearch;
-}
-
 SearchValidationResult StringStorage::ChainImpl::ValidateSearchConstraints(
     FilterOp op,
     SqlValue val) const {
diff --git a/src/trace_processor/db/column/string_storage.h b/src/trace_processor/db/column/string_storage.h
index 03541aa..d61aef5 100644
--- a/src/trace_processor/db/column/string_storage.h
+++ b/src/trace_processor/db/column/string_storage.h
@@ -50,10 +50,6 @@
                                     SqlValue,
                                     uint32_t) const override;
 
-    UniqueSearchResult UniqueSearch(FilterOp,
-                                    SqlValue,
-                                    uint32_t*) const override;
-
     SearchValidationResult ValidateSearchConstraints(FilterOp,
                                                      SqlValue) const override;
 
diff --git a/src/trace_processor/db/column/types.h b/src/trace_processor/db/column/types.h
index f27daa7..803c6ce 100644
--- a/src/trace_processor/db/column/types.h
+++ b/src/trace_processor/db/column/types.h
@@ -37,17 +37,6 @@
                      // the crtiteria, a call to *Search is required.
 };
 
-// Result of calling Storage::UniqueSearch function.
-enum class UniqueSearchResult {
-  kMatch,            // The returned row matches the constraint.
-  kNoMatch,          // The returned row does not matches the constraint.
-  kNeedsFullSearch,  // UniqueSearch was unable to determine if a row meets
-                     // the crtiteria, a call to *Search is required. This
-                     // does not mean there >1 row necessarily, just that
-                     // UniqueSearch was unable to quickly identify a single
-                     // row.
-};
-
 // Result of calling Storage::ValidateSearchResult function.
 enum class SearchValidationResult {
   kOk,       // It makes sense to run search
diff --git a/src/trace_processor/db/query_executor.cc b/src/trace_processor/db/query_executor.cc
index 30214e4..bd15245 100644
--- a/src/trace_processor/db/query_executor.cc
+++ b/src/trace_processor/db/query_executor.cc
@@ -57,21 +57,6 @@
     }
   }
 
-  // Given the chain a chance to return a single row which uniquely matches
-  // this constraint. This is a relatively frequent occurence e.g. id equality
-  // constraints so it is valuable to have an explicit fastpath.
-  uint32_t unique_row;
-  switch (chain.UniqueSearch(c.op, c.value, &unique_row)) {
-    case UniqueSearchResult::kMatch:
-      rm->IntersectExact(unique_row);
-      return;
-    case UniqueSearchResult::kNoMatch:
-      rm->Clear();
-      return;
-    case UniqueSearchResult::kNeedsFullSearch:
-      break;
-  }
-
   // Comparison of NULL with any operation apart from |IS_NULL| and
   // |IS_NOT_NULL| should return no rows.
   if (c.value.is_null() && c.op != FilterOp::kIsNull &&
diff --git a/src/trace_processor/importers/ftrace/ftrace_parser.h b/src/trace_processor/importers/ftrace/ftrace_parser.h
index 75f2130..f3234bd 100644
--- a/src/trace_processor/importers/ftrace/ftrace_parser.h
+++ b/src/trace_processor/importers/ftrace/ftrace_parser.h
@@ -233,7 +233,7 @@
   void ParseWakeSourceDeactivate(int64_t timestamp, protozero::ConstBytes);
   void ParseSuspendResume(int64_t timestamp, protozero::ConstBytes);
   void ParseSuspendResumeMinimal(int64_t timestamp, protozero::ConstBytes);
-  void ParseSchedCpuUtilCfs(int64_t timestap, protozero::ConstBytes);
+  void ParseSchedCpuUtilCfs(int64_t timestamp, protozero::ConstBytes);
 
   void ParseFuncgraphEntry(int64_t timestamp,
                            uint32_t pid,
diff --git a/src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql b/src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql
index 66a1aa8..61e34c4 100644
--- a/src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_reliable_range.sql
@@ -21,7 +21,14 @@
 --   b. The event rate for the thread is at or above 75p.
 -- Note: this metric considers only chrome processes and their threads, i.e. the ones coming
 -- from track_event's.
-INCLUDE PERFETTO MODULE common.metadata;
+
+-- Extracts an int value with the given name from the metadata table.
+CREATE OR REPLACE PERFETTO FUNCTION _extract_int_metadata(
+  -- The name of the metadata entry.
+  name STRING)
+-- int_value for the given name. NULL if there's no such entry.
+RETURNS LONG AS
+SELECT int_value FROM metadata WHERE name = ($name);
 
 DROP VIEW IF EXISTS chrome_event_stats_per_thread;
 
@@ -153,9 +160,9 @@
 SELECT
   -- If the trace has a cropping packet, we don't want to recompute the reliable
   -- based on cropped track events - the result might be incorrect.
-  IFNULL(extract_int_metadata('range_of_interest_start_us') * 1000,
+  IFNULL(_extract_int_metadata('range_of_interest_start_us') * 1000,
          MAX(thread_start, data_loss_free_start)) AS start,
-  IIF(extract_int_metadata('range_of_interest_start_us') IS NOT NULL,
+  IIF(_extract_int_metadata('range_of_interest_start_us') IS NOT NULL,
       'Range of interest packet',
       IIF(limiting_upid IN (SELECT upid FROM chrome_processes_with_missing_main),
           'Missing main thread for upid=' || limiting_upid,
diff --git a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
index fa871e6..4ad989b 100644
--- a/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/BUILD.gn
@@ -23,6 +23,7 @@
     "chrome:chrome_sql",
     "common",
     "counters",
+    "cpu",
     "deprecated/v42/common",
     "graphs",
     "intervals",
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 314e612..3ce017e 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/oom_adjuster.sql
@@ -14,7 +14,7 @@
 -- limitations under the License.
 --
 
-INCLUDE PERFETTO MODULE common.slices;
+INCLUDE PERFETTO MODULE slices.with_context;
 INCLUDE PERFETTO MODULE counters.intervals;
 
 -- Converts an oom_adj score Integer to String bucket name.
diff --git a/src/trace_processor/perfetto_sql/stdlib/android/suspend.sql b/src/trace_processor/perfetto_sql/stdlib/android/suspend.sql
index 9e604b5..0d31b0d 100644
--- a/src/trace_processor/perfetto_sql/stdlib/android/suspend.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/android/suspend.sql
@@ -14,8 +14,6 @@
 -- limitations under the License.
 --
 
-INCLUDE PERFETTO MODULE common.timestamps;
-
 -- Table of suspended and awake slices.
 --
 -- Selects either the minimal or full ftrace source depending on what's
diff --git a/src/trace_processor/perfetto_sql/stdlib/cpu/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/cpu/BUILD.gn
new file mode 100644
index 0000000..e17e6b2
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/cpu/BUILD.gn
@@ -0,0 +1,22 @@
+# 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("cpu") {
+  sources = [
+    "cpus.sql",
+    "size.sql",
+  ]
+}
diff --git a/src/trace_processor/perfetto_sql/stdlib/cpu/cpus.sql b/src/trace_processor/perfetto_sql/stdlib/cpu/cpus.sql
new file mode 100644
index 0000000..0d13879
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/cpu/cpus.sql
@@ -0,0 +1,28 @@
+--
+-- 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 cpu.size;
+
+-- All of the CPUs with their core type as a descriptive size ('little', 'mid', 'big', etc).
+CREATE PERFETTO TABLE cpu_core_types(
+  -- Index of the CPU.
+  cpu_index INT,
+  -- A descriptive size ('little', 'mid', 'big', etc) or NULL if we have insufficient information.
+  size STRING
+) AS
+SELECT
+  cpu as cpu_index,
+  cpu_guess_core_type(cpu) AS size
+FROM _ranked_cpus;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/cpu/size.sql b/src/trace_processor/perfetto_sql/stdlib/cpu/size.sql
new file mode 100644
index 0000000..6536172
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/cpu/size.sql
@@ -0,0 +1,55 @@
+--
+-- 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 TABLE _cpu_sizes AS
+SELECT 0 AS n, 'little' AS size
+UNION
+SELECT 1 AS n, 'mid' AS size
+UNION
+SELECT 2 AS n, 'big' AS size;
+
+CREATE PERFETTO TABLE _ranked_cpus AS
+SELECT
+ (DENSE_RANK() OVER win) - 1 AS n,
+ cpu
+FROM (
+  SELECT
+    track.cpu AS cpu,
+    MAX(counter.value) AS maxfreq
+  FROM counter
+  JOIN cpu_counter_track AS track
+  ON (counter.track_id = track.id)
+  WHERE track.name = "cpufreq"
+  GROUP BY track.cpu
+)
+WINDOW win AS (ORDER BY maxfreq);
+
+-- Guess size of CPU.
+-- On some multicore devices the cores are heterogeneous and divided
+-- into two or more 'sizes'. In a typical case a device might have 8
+-- cores of which 4 are 'little' (low power & low performance) and 4
+-- are 'big' (high power & high performance). This functions attempts
+-- to map a given CPU index onto the relevant descriptor. For
+-- homogeneous systems this returns NULL.
+CREATE PERFETTO FUNCTION cpu_guess_core_type(
+  -- Index of the CPU whose size we will guess.
+  cpu_index INT)
+-- A descriptive size ('little', 'mid', 'big', etc) or NULL if we have insufficient information.
+RETURNS STRING AS
+SELECT
+  IIF((SELECT COUNT(DISTINCT n) FROM _ranked_cpus) >= 2, size, null) as size
+FROM _ranked_cpus
+LEFT JOIN _cpu_sizes USING(n)
+WHERE cpu = $cpu_index;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/cpus.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/cpus.sql
index d897503..0870603 100644
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/cpus.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/cpus.sql
@@ -1,5 +1,5 @@
 --
--- Copyright 2023 The Android Open Source Project
+-- 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.
@@ -13,57 +13,14 @@
 -- See the License for the specific language governing permissions and
 -- limitations under the License.
 
+INCLUDE PERFETTO MODULE cpu.size;
+INCLUDE PERFETTO MODULE cpu.cpus;
 
-CREATE PERFETTO TABLE _cpu_sizes AS
-SELECT 0 AS n, 'little' AS size
-UNION
-SELECT 1 AS n, 'mid' AS size
-UNION
-SELECT 2 AS n, 'big' AS size;
+CREATE PERFETTO TABLE cpus(cpu_index INT, size STRING)
+AS
+SELECT * FROM cpu_core_types;
 
-CREATE PERFETTO TABLE _ranked_cpus AS
-SELECT
- (DENSE_RANK() OVER win) - 1 AS n,
- cpu
-FROM (
-  SELECT
-    track.cpu AS cpu,
-    MAX(counter.value) AS maxfreq
-  FROM counter
-  JOIN cpu_counter_track AS track
-  ON (counter.track_id = track.id)
-  WHERE track.name = "cpufreq"
-  GROUP BY track.cpu
-)
-WINDOW win AS (ORDER BY maxfreq);
 
--- Guess size of CPU.
--- On some multicore devices the cores are heterogeneous and divided
--- into two or more 'sizes'. In a typical case a device might have 8
--- cores of which 4 are 'little' (low power & low performance) and 4
--- are 'big' (high power & high performance). This functions attempts
--- to map a given CPU index onto the relevant descriptor. For
--- homogeneous systems this returns NULL.
-CREATE PERFETTO FUNCTION guess_cpu_size(
-  -- Index of the CPU whose size we will guess.
-  cpu_index INT)
--- A descriptive size ('little', 'mid', 'big', etc) or NULL if we have insufficient information.
+CREATE PERFETTO FUNCTION guess_cpu_size(cpu_index INT)
 RETURNS STRING AS
-SELECT
-  IIF((SELECT COUNT(DISTINCT n) FROM _ranked_cpus) >= 2, size, null) as size
-FROM _ranked_cpus
-LEFT JOIN _cpu_sizes USING(n)
-WHERE cpu = $cpu_index;
-
-
--- A list of CPUs with their sizes.
-CREATE PERFETTO TABLE cpus(
-  -- Index of the CPU.
-  cpu_index INT,
-  -- A descriptive size ('little', 'mid', 'big', etc) or NULL if we have insufficient information.
-  size STRING
-) AS
-SELECT
-  cpu as cpu_index,
-  guess_cpu_size(cpu) AS size
-FROM _ranked_cpus;
\ No newline at end of file
+SELECT cpu_guess_core_type($cpu_index);
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql
index 78e1ed1..e807a78 100644
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/percentiles.sql
@@ -24,6 +24,57 @@
     WHERE num < $upper_limit)
 SELECT num FROM nums;
 
+CREATE PERFETTO FUNCTION _earliest_timestamp_for_counter_track(
+  -- Id of a counter track with a counter.
+  counter_track_id INT)
+-- Timestamp of first counter value. Null if doesn't exist.
+RETURNS LONG AS
+SELECT MIN(ts) FROM counter WHERE counter.track_id = $counter_track_id;
+
+-- COUNTER_WITH_DUR_FOR_TRACK but in a specified time.
+-- Does calculation over the table ends - creates an artificial counter value at
+-- the start if needed and chops the duration of the last timestamps in range.
+CREATE PERFETTO FUNCTION _counter_for_time_range(
+  -- Id of track counter track.
+  counter_track_id INT,
+  -- Timestamp of the timerange start.
+  -- Can be earlier than the first counter value.
+  start_ts LONG,
+  -- Timestamp of the timerange end.
+  end_ts LONG)
+RETURNS TABLE(
+  -- Timestamp of the counter value.
+  ts LONG,
+  -- Duration of the counter value.
+  dur LONG,
+  -- Counter value.
+  value DOUBLE,
+  -- If of the counter track.
+  track_id INT,
+  -- Name of the counter track.
+  track_name STRING,
+  -- Counter track set id.
+  track_arg_set_id INT,
+  -- Counter arg set id.
+  arg_set_id INT
+) AS
+SELECT
+  IIF(ts < $start_ts, $start_ts, ts) AS ts,
+  IIF(
+    ts < $start_ts,
+    dur - ($start_ts - ts),
+    IIF(ts + dur > $end_ts, $end_ts - ts, dur)) AS dur,
+  value,
+  track_id,
+  track_name,
+  track_arg_set_id,
+  arg_set_id
+FROM counter_with_dur_for_track($counter_track_id)
+WHERE TRUE
+  AND ts + dur >= $start_ts
+  AND ts < $end_ts
+ORDER BY ts ASC;
+
 --
 -- Get durations for percentile
 --
@@ -55,9 +106,9 @@
     SELECT
         value,
         (CAST(SUM(dur) OVER(ORDER BY value ASC) AS DOUBLE) /
-            ($end_ts - MAX($start_ts, earliest_timestamp_for_counter_track($counter_track_id)))) * 100
+            ($end_ts - MAX($start_ts, _earliest_timestamp_for_counter_track($counter_track_id)))) * 100
         AS percentile_for_value
-    FROM COUNTER_FOR_TIME_RANGE($counter_track_id, $start_ts, $end_ts)
+    FROM _COUNTER_FOR_TIME_RANGE($counter_track_id, $start_ts, $end_ts)
     ORDER BY value ASC
 ),
 with_gaps AS (
diff --git a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql
index a0c63e9..952fb1d 100644
--- a/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/deprecated/v42/common/thread_states.sql
@@ -14,33 +14,13 @@
 -- limitations under the License.
 
 INCLUDE PERFETTO MODULE deprecated.v42.common.timestamps;
-INCLUDE PERFETTO MODULE deprecated.v42.common.cpus;
+INCLUDE PERFETTO MODULE sched.states;
+INCLUDE PERFETTO MODULE cpu.size;
 
--- TODO(altimin): this doesn't handle some corner cases which thread_state.ts
--- handles (as complex strings manipulations in SQL are pretty painful),
--- but they are pretty niche.
--- Translates the thread state name from a single-letter shorthard to
--- a human-readable name.
 CREATE PERFETTO FUNCTION _translate_thread_state_name(name STRING)
 RETURNS STRING AS
-SELECT CASE $name
-WHEN 'Running' THEN 'Running'
-WHEN 'R' THEN 'Runnable'
-WHEN 'R+' THEN 'Runnable (Preempted)'
-WHEN 'S' THEN 'Sleeping'
-WHEN 'D' THEN 'Uninterruptible Sleep'
-WHEN 'T' THEN 'Stopped'
-WHEN 't' THEN 'Traced'
-WHEN 'X' THEN 'Exit (Dead)'
-WHEN 'Z' THEN 'Exit (Zombie)'
-WHEN 'x' THEN 'Task Dead'
-WHEN 'I' THEN 'Idle'
-WHEN 'K' THEN 'Wakekill'
-WHEN 'W' THEN 'Waking'
-WHEN 'P' THEN 'Parked'
-WHEN 'N' THEN 'No Load'
-ELSE $name
-END;
+SELECT sched_state_to_human_readable_string($name);
+
 
 -- Returns a human-readable name for a thread state.
 CREATE PERFETTO FUNCTION human_readable_thread_state_name(
@@ -107,13 +87,14 @@
   SELECT * FROM first_state_starting_before
 )
 SELECT
-  human_readable_thread_state_name(id) as state,
+  full_name.sched_state_full_name as state,
   state as raw_state,
-  guess_cpu_size(cpu) as cpu_type,
+  cpu_guess_core_type(cpu) as cpu_type,
   cpu,
   blocked_function,
   sum(spans_overlapping_dur($ts, $dur, ts, dur)) as dur
 FROM thread_state
+JOIN sched_state_full_name!(thread_state, state, io_wait) full_name USING (id)
 JOIN relevant_states USING (id)
 GROUP BY state, raw_state, cpu_type, cpu, blocked_function
 ORDER BY dur desc;
\ No newline at end of file
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
index 8e83d6f..fa5ba77 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/BUILD.gn
@@ -16,6 +16,7 @@
 
 perfetto_sql_source_set("sched") {
   sources = [
+    "states.sql",
     "thread_executing_span.sql",
     "thread_level_parallelism.sql",
     "thread_state_flattened.sql",
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/states.sql b/src/trace_processor/perfetto_sql/stdlib/sched/states.sql
new file mode 100644
index 0000000..03f3d9c
--- /dev/null
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/states.sql
@@ -0,0 +1,90 @@
+--
+-- 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.
+
+-- TODO(altimin): `sched_humanly_readable_name` doesn't handle some corner
+-- cases which thread_state.ts handles (as complex strings manipulations in
+-- SQL are pretty painful), but they are pretty niche.
+
+-- Translates the thread state name from a single-letter shorthard to
+-- a human-readable name.
+CREATE PERFETTO FUNCTION sched_state_to_human_readable_string(
+  -- An individual character string representing the scheduling state of the
+  -- kernel thread at the end of the slice.
+  short_name STRING
+)
+-- Humanly readable string representing the scheduling state of the kernel
+-- thread. The individual characters in the string mean the following: R
+-- (runnable), S (awaiting a wakeup), D (in an uninterruptible sleep), T
+-- (suspended), t (being traced), X (exiting), P (parked), W (waking), I
+-- (idle), N (not contributing to the load average), K (wakeable on fatal
+-- signals) and Z (zombie, awaiting cleanup).
+RETURNS STRING AS
+SELECT CASE $short_name
+WHEN 'Running' THEN 'Running'
+WHEN 'R' THEN 'Runnable'
+WHEN 'R+' THEN 'Runnable (Preempted)'
+WHEN 'S' THEN 'Sleeping'
+WHEN 'D' THEN 'Uninterruptible Sleep'
+WHEN 'T' THEN 'Stopped'
+WHEN 't' THEN 'Traced'
+WHEN 'X' THEN 'Exit (Dead)'
+WHEN 'Z' THEN 'Exit (Zombie)'
+WHEN 'x' THEN 'Task Dead'
+WHEN 'I' THEN 'Idle'
+WHEN 'K' THEN 'Wakekill'
+WHEN 'W' THEN 'Waking'
+WHEN 'P' THEN 'Parked'
+WHEN 'N' THEN 'No Load'
+ELSE $short_name
+END;
+
+-- Creates a table with humanly readable sched state names with IO waits.
+-- Translates the individual characters in the string to the following: R
+-- (runnable), S (awaiting a wakeup), D (in an uninterruptible sleep), T
+-- (suspended), t (being traced), X (exiting), P (parked), W (waking), I
+-- (idle), N (not contributing to the load average), K (wakeable on fatal
+-- signals) and Z (zombie, awaiting cleanup). Adds the IO wait (IO/non
+-- IO/nothing) based on the value in the `io_wait_column`.
+CREATE PERFETTO MACRO sched_state_full_name(
+  -- Table with columns required for translation and `id` column for joins.
+  states_table TableOrSubquery,
+  -- Column in `states_table` with single character version of sched state
+  -- string.
+  sched_name_column ColumnName,
+  -- Column in `states_table` with 0 for IO and 1 for non-IO states. Can be
+  -- a dummy (no real values), and no value from there would be added to the
+  -- resulting strings.
+  io_wait_column ColumnName
+)
+-- Table with the schema (id UINT32, ts UINT64, sched_state_full_name STRING).
+RETURNS TableOrSubquery AS
+(
+  WITH data AS
+  (
+    SELECT
+      id,
+      sched_state_to_human_readable_string($sched_name_column) AS sched_state_name,
+      (CASE $io_wait_column
+        WHEN 1 THEN ' (IO)'
+        WHEN 0 THEN ' (non-IO)'
+        ELSE ''
+      END) AS io_wait
+    FROM $states_table
+  )
+  SELECT
+    id,
+    printf('%s%s', sched_state_name, io_wait) AS sched_state_full_name
+  FROM data
+);
diff --git a/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql b/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql
index 0b3a470..19b0508 100644
--- a/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/sched/thread_level_parallelism.sql
@@ -17,8 +17,8 @@
 -- including how many threads were runnable at a given time and how many threads
 -- where running at a given point in time.
 
-INCLUDE PERFETTO MODULE common.cpus;
 INCLUDE PERFETTO MODULE intervals.overlap;
+INCLUDE PERFETTO MODULE cpu.cpus;
 
 -- The count of runnable threads over time.
 CREATE PERFETTO TABLE sched_runnable_thread_count(
@@ -74,7 +74,7 @@
 -- Materialise the relevant cores to avoid calling a function for each row of the sched table.
 cores AS MATERIALIZED (
   SELECT cpu_index
-  FROM cpus
+  FROM cpu_core_types
   WHERE size = $core_type
 ),
 -- Filter sched events corresponding to running tasks.
diff --git a/src/traceconv/BUILD.gn b/src/traceconv/BUILD.gn
index 97c7524..8cde4f4 100644
--- a/src/traceconv/BUILD.gn
+++ b/src/traceconv/BUILD.gn
@@ -108,6 +108,8 @@
     "trace_to_text.cc",
     "trace_to_text.h",
     "trace_to_text.h",
+    "trace_unpack.cc",
+    "trace_unpack.h",
   ]
 }
 
diff --git a/src/traceconv/main.cc b/src/traceconv/main.cc
index 1570281..d7e6ac4 100644
--- a/src/traceconv/main.cc
+++ b/src/traceconv/main.cc
@@ -31,6 +31,7 @@
 #include "src/traceconv/trace_to_profile.h"
 #include "src/traceconv/trace_to_systrace.h"
 #include "src/traceconv/trace_to_text.h"
+#include "src/traceconv/trace_unpack.h"
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <fcntl.h>
@@ -47,7 +48,8 @@
   fprintf(stderr,
           "Usage: %s MODE [OPTIONS] [input file] [output file]\n"
           "modes:\n"
-          "  systrace|json|ctrace|text|profile|hprof|symbolize|deobfuscate\n"
+          "  systrace|json|ctrace|text|profile|hprof|symbolize|deobfuscate"
+          "|decompress_packets\n"
           "options:\n"
           "  [--truncate start|end]\n"
           "  [--full-sort]\n"
@@ -186,13 +188,15 @@
 
   if (truncate_keep != Keep::kAll) {
     PERFETTO_ELOG(
-        "--truncate is unsupported for text|profile|symbolize format.");
+        "--truncate is unsupported for "
+        "text|profile|symbolize|decompress_packets format.");
     return 1;
   }
 
   if (full_sort) {
     PERFETTO_ELOG(
-        "--full-sort is unsupported for text|profile|symbolize format.");
+        "--full-sort is unsupported for "
+        "text|profile|symbolize|decompress_packets format.");
     return 1;
   }
 
@@ -216,6 +220,10 @@
 
   if (format == "deobfuscate")
     return DeobfuscateProfile(input_stream, output_stream);
+
+  if (format == "decompress_packets")
+    return UnpackCompressedPackets(input_stream, output_stream);
+
   return Usage(argv[0]);
 }
 
diff --git a/src/traceconv/trace_unpack.cc b/src/traceconv/trace_unpack.cc
new file mode 100644
index 0000000..060a7b2
--- /dev/null
+++ b/src/traceconv/trace_unpack.cc
@@ -0,0 +1,47 @@
+/*
+ * 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/traceconv/trace_unpack.h"
+
+#include <iostream>
+#include <iterator>
+#include <memory>
+#include <vector>
+
+#include "perfetto/trace_processor/read_trace.h"
+#include "src/traceconv/utils.h"
+
+namespace perfetto {
+namespace trace_to_text {
+
+// Naive: puts multiple copies of the trace in memory, but good enough for
+// manual workflows.
+bool UnpackCompressedPackets(std::istream* input, std::ostream* output) {
+  std::vector<char> packed(std::istreambuf_iterator<char>{*input},
+                           std::istreambuf_iterator<char>{});
+  std::vector<uint8_t> unpacked;
+  auto status = trace_processor::DecompressTrace(
+      reinterpret_cast<uint8_t*>(packed.data()), packed.size(), &unpacked);
+  if (!status.ok())
+    return false;
+
+  TraceWriter trace_writer(output);
+  trace_writer.Write(reinterpret_cast<char*>(unpacked.data()), unpacked.size());
+  return true;
+}
+
+}  // namespace trace_to_text
+}  // namespace perfetto
diff --git a/src/traceconv/trace_unpack.h b/src/traceconv/trace_unpack.h
new file mode 100644
index 0000000..00e2537
--- /dev/null
+++ b/src/traceconv/trace_unpack.h
@@ -0,0 +1,32 @@
+/*
+ * 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_TRACECONV_TRACE_UNPACK_H_
+#define SRC_TRACECONV_TRACE_UNPACK_H_
+
+#include <iostream>
+
+namespace perfetto {
+namespace trace_to_text {
+
+// Serialised trace with compressed_packets -> serialised trace with those
+// packets in their decompressed form. Mostly for use with protoprofile.
+bool UnpackCompressedPackets(std::istream* input, std::ostream* output);
+
+}  // namespace trace_to_text
+}  // namespace perfetto
+
+#endif  // SRC_TRACECONV_TRACE_UNPACK_H_
diff --git a/src/traced/probes/ftrace/cpu_reader.cc b/src/traced/probes/ftrace/cpu_reader.cc
index 30b58cf..2f1f121 100644
--- a/src/traced/probes/ftrace/cpu_reader.cc
+++ b/src/traced/probes/ftrace/cpu_reader.cc
@@ -596,7 +596,7 @@
       }
       // Data record:
       default: {
-        // If type_or_length <=28, the the record length is 4x that value.
+        // If type_or_length <=28, the record length is 4x that value.
         // If type_or_length == 0, the length of the record is stored in the
         // first uint32_t word of the payload.
         uint32_t event_size = 0;
diff --git a/src/tracing/test/api_integrationtest.cc b/src/tracing/test/api_integrationtest.cc
index fefab5b..ba0d010 100644
--- a/src/tracing/test/api_integrationtest.cc
+++ b/src/tracing/test/api_integrationtest.cc
@@ -16,9 +16,6 @@
 
 #include <fcntl.h>
 
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
 #include <chrono>
 #include <condition_variable>
 #include <fstream>
@@ -39,6 +36,10 @@
 
 #if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
 #include <Windows.h>  // For CreateFile().
+#else
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
 #endif
 
 // Deliberately not pulling any non-public perfetto header to spot accidental
diff --git a/test/cmdline_integrationtest.cc b/test/cmdline_integrationtest.cc
index f8c7a43..74d405b 100644
--- a/test/cmdline_integrationtest.cc
+++ b/test/cmdline_integrationtest.cc
@@ -90,19 +90,6 @@
 
 class PerfettoCmdlineTest : public ::testing::Test {
  public:
-  void SetUp() override {
-#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \
-    defined(MEMORY_SANITIZER) || defined(LEAK_SANITIZER)
-    // Disable cmdline tests on sanitizets because they use fork() and that
-    // messes up leak / races detections, which has been fixed only recently
-    // (see https://github.com/google/sanitizers/issues/836 ).
-    PERFETTO_LOG("Skipping cmdline integration tests on sanitizers");
-    GTEST_SKIP();
-#endif
-  }
-
-  void TearDown() override {}
-
   void StartServiceIfRequiredNoNewExecsAfterThis() {
     exec_allowed_ = false;
     test_helper_.StartServiceIfRequired();
diff --git a/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
index 35808b9..8dc7bd1 100644
--- a/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
+++ b/test/cts/test_apps/src/android/perfetto/cts/app/JavaOomActivity.java
@@ -18,22 +18,23 @@
 
 import android.app.Activity;
 import android.os.Bundle;
+import android.util.Log;
 
 public class JavaOomActivity extends Activity {
+    public static final String TAG = "JavaOomActivity";
+
     @Override
     public void onCreate(Bundle state) {
         super.onCreate(state);
         new Thread(() -> {
             try {
+                Log.i(TAG, "Before the allocation");
+                // Try to allocate a big array: it should cause ART to run out of memory.
                 byte[] alloc = new byte[Integer.MAX_VALUE];
-                // The return statement below is required to keep the allocation
-                // above when generating DEX. Without the return statement there is
-                // no way for a debugger to break where `alloc` is in scope and
-                // therefore javac will not generate local variable information for it.
-                // Without local variable information dexers (both D8 and R8) will
-                // remove the dead allocation as without local variable information it
-                // is dead even in debug mode. See b/322478366#comment3.
-                return;
+                // Use the array, otherwise R8 might optimize the allocation away. (b/322478366,
+                // b/325467497).
+                alloc[5] = 42;
+                Log.i(TAG, "After the allocation " + alloc[5]);
             } catch (OutOfMemoryError e) {
             }
         }).start();
diff --git a/test/test_helper.h b/test/test_helper.h
index b43ae0a..462861d 100644
--- a/test/test_helper.h
+++ b/test/test_helper.h
@@ -468,6 +468,7 @@
       pass_env("TMPDIR", &subprocess_);
       pass_env("TMP", &subprocess_);
       pass_env("TEMP", &subprocess_);
+      pass_env("LD_LIBRARY_PATH", &subprocess_);
       cmd.push_back(base::GetCurExecutableDir() + "/" + argv0);
       cmd.insert(cmd.end(), args.begin(), args.end());
     }
diff --git a/test/trace_processor/diff_tests/stdlib/common/tests.py b/test/trace_processor/diff_tests/stdlib/common/tests.py
index 8e85918..453fd08 100644
--- a/test/trace_processor/diff_tests/stdlib/common/tests.py
+++ b/test/trace_processor/diff_tests/stdlib/common/tests.py
@@ -25,7 +25,7 @@
     return DiffTestBlueprint(
         trace=Path('../../common/synth_1.py'),
         query="""
-        INCLUDE PERFETTO MODULE common.thread_states;
+        INCLUDE PERFETTO MODULE deprecated.v42.common.thread_states;
 
         SELECT
           state,
diff --git a/tools/check_sql_metrics.py b/tools/check_sql_metrics.py
index 73f0d70..3819c27 100755
--- a/tools/check_sql_metrics.py
+++ b/tools/check_sql_metrics.py
@@ -72,6 +72,24 @@
   with open(path) as f:
     sql = f.read()
 
+  # Check that each function/macro is using "CREATE OR REPLACE"
+  lines = [l.strip() for l in sql.split('\n')]
+  for line in lines:
+    if line.startswith('--'):
+      continue
+    if 'create perfetto function' in line.casefold():
+      errors.append(
+          f'Use "CREATE OR REPLACE PERFETTO FUNCTION" in Perfetto metrics, '
+          f'to prevent the file from crashing if the metric is rerun.\n'
+          f'Offending file: {path}\n')
+    if 'create perfetto macro' in line.casefold():
+      errors.append(
+          f'Use "CREATE OR REPLACE PERFETTO MACRO" in Perfetto metrics, to '
+          f'prevent the file from crashing if the metric is rerun.\n'
+          f'Offending file: {path}\n')
+
+
+
   # Check that CREATE VIEW/TABLE has a matching DROP VIEW/TABLE before it.
   create_table_view_dir = match_create_table_pattern_to_dict(
       sql, CREATE_TABLE_VIEW_PATTERN)
diff --git a/tools/check_sql_modules.py b/tools/check_sql_modules.py
index b4ffc1f..c09261b 100755
--- a/tools/check_sql_modules.py
+++ b/tools/check_sql_modules.py
@@ -111,6 +111,10 @@
       if 'RUN_METRIC' in line:
         errors.append(f"RUN_METRIC is banned in standard library.\n"
                       f"Offending file: {path}\n")
+      if 'include perfetto module common.' in line.casefold():
+        errors.append(
+            f"Common module has been deprecated in the standard library.\n"
+            f"Offending file: {path}\n")
       if 'insert into' in line.casefold():
         errors.append(f"INSERT INTO table is not allowed in standard library.\n"
                       f"Offending file: {path}\n")
diff --git a/tools/gen_cc_proto_descriptor.py b/tools/gen_cc_proto_descriptor.py
index 45354b1..0c2dc66 100755
--- a/tools/gen_cc_proto_descriptor.py
+++ b/tools/gen_cc_proto_descriptor.py
@@ -68,7 +68,7 @@
 
 namespace perfetto {{
 
-constexpr inline std::array<uint8_t, {size}> k{proto_name}Descriptor{{
+constexpr std::array<uint8_t, {size}> k{proto_name}Descriptor{{
 {binary}}};
 
 }}  // namespace perfetto
diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js
index c5120f4..3036628 100644
--- a/ui/.eslintrc.js
+++ b/ui/.eslintrc.js
@@ -28,7 +28,7 @@
     }],
 
     // Max line length is 80 with 2 space tabs.
-    // This matches the the old clang-format definition for consistency.
+    // This matches the old clang-format definition for consistency.
     'max-len': [
       'error',
       {
diff --git a/ui/src/common/commands.ts b/ui/src/common/commands.ts
index 2f19198..7b44775 100644
--- a/ui/src/common/commands.ts
+++ b/ui/src/common/commands.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Disposable} from '../base/disposable';
 import {FuzzyFinder, FuzzySegment} from '../base/fuzzy';
 import {Command} from '../public';
 import {Registry} from './registry';
@@ -21,12 +22,16 @@
 }
 
 export class CommandManager {
-  readonly registry = new Registry<Command>((cmd) => cmd.id);
+  private readonly registry = new Registry<Command>((cmd) => cmd.id);
 
   get commands(): Command[] {
     return Array.from(this.registry.values());
   }
 
+  registerCommand(cmd: Command): Disposable {
+    return this.registry.register(cmd);
+  }
+
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   runCommand(id: string, ...args: any[]): any {
     const cmd = this.registry.get(id);
diff --git a/ui/src/common/event_set.ts b/ui/src/common/event_set.ts
index 47e4e1b..e815eb7 100644
--- a/ui/src/common/event_set.ts
+++ b/ui/src/common/event_set.ts
@@ -479,7 +479,7 @@
   constructor(keys: P, events: Event<P>[]) {
     super();
     // TODO(hjd): Add some paranoid mode where we crash here if
-    // `events` and `keys` missmatch?
+    // `events` and `keys` mismatch?
     this.events = events;
     this.keys = keys;
   }
diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts
index fc996a2..bd7d5ef 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -75,7 +75,7 @@
     // Silently ignore if context is dead.
     if (!this.alive) return;
 
-    const disposable = globals.commandManager.registry.register(cmd);
+    const disposable = globals.commandManager.registerCommand(cmd);
     this.trash.add(disposable);
   };
 
@@ -108,22 +108,24 @@
     // Silently ignore if context is dead.
     if (!this.alive) return;
 
-    const disposable = globals.commandManager.registry.register(cmd);
-    this.trash.add(disposable);
+    const dispose = globals.commandManager.registerCommand(cmd);
+    this.trash.add(dispose);
   }
 
   registerTrack(trackDesc: TrackDescriptor): void {
     // Silently ignore if context is dead.
     if (!this.alive) return;
-    globals.trackManager.registerTrack(trackDesc);
-    this.trash.addCallback(
-      () => globals.trackManager.unregisterTrack(trackDesc.uri));
+
+    const dispose = globals.trackManager.registerTrack(trackDesc);
+    this.trash.add(dispose);
   }
 
   addDefaultTrack(track: TrackRef): void {
-    globals.trackManager.addDefaultTrack(track);
-    this.trash.addCallback(
-      () => globals.trackManager.removeDefaultTrack(track));
+    // Silently ignore if context is dead.
+    if (!this.alive) return;
+
+    const dispose = globals.trackManager.addPotentialTrack(track);
+    this.trash.add(dispose);
   }
 
   registerStaticTrack(track: TrackDescriptor&TrackRef): void {
diff --git a/ui/src/common/timestamp_format.ts b/ui/src/common/timestamp_format.ts
index 45f38ff..83c3107 100644
--- a/ui/src/common/timestamp_format.ts
+++ b/ui/src/common/timestamp_format.ts
@@ -29,13 +29,17 @@
 const DEFAULT_TIMESTAMP_FORMAT = TimestampFormat.Timecode;
 
 export function timestampFormat(): TimestampFormat {
-  const storedFormat = localStorage.getItem(TIMESTAMP_FORMAT_KEY);
-  if (storedFormat && isEnumValue(TimestampFormat, storedFormat)) {
-    timestampFormatCached = storedFormat;
+  if (timestampFormatCached !== undefined) {
+    return timestampFormatCached;
   } else {
-    timestampFormatCached = DEFAULT_TIMESTAMP_FORMAT;
+    const storedFormat = localStorage.getItem(TIMESTAMP_FORMAT_KEY);
+    if (storedFormat && isEnumValue(TimestampFormat, storedFormat)) {
+      timestampFormatCached = storedFormat;
+    } else {
+      timestampFormatCached = DEFAULT_TIMESTAMP_FORMAT;
+    }
+    return timestampFormatCached;
   }
-  return timestampFormatCached;
 }
 
 export function setTimestampFormat(format: TimestampFormat) {
@@ -54,13 +58,17 @@
 const DEFAULT_DURATION_FORMAT = DurationPrecision.Full;
 
 export function durationPrecision(): DurationPrecision {
-  const storedFormat = localStorage.getItem(DURATION_FORMAT_KEY);
-  if (storedFormat && isEnumValue(DurationPrecision, storedFormat)) {
-    durationFormatCached = storedFormat;
+  if (durationFormatCached !== undefined) {
+    return durationFormatCached;
   } else {
-    durationFormatCached = DEFAULT_DURATION_FORMAT;
+    const storedFormat = localStorage.getItem(DURATION_FORMAT_KEY);
+    if (storedFormat && isEnumValue(DurationPrecision, storedFormat)) {
+      durationFormatCached = storedFormat;
+    } else {
+      durationFormatCached = DEFAULT_DURATION_FORMAT;
+    }
+    return durationFormatCached;
   }
-  return durationFormatCached;
 }
 
 export function setDurationPrecision(format: DurationPrecision) {
diff --git a/ui/src/common/track_cache.ts b/ui/src/common/track_cache.ts
index 43b1e70..90c004e 100644
--- a/ui/src/common/track_cache.ts
+++ b/ui/src/common/track_cache.ts
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import {Disposable, DisposableCallback} from '../base/disposable';
 import {PanelSize} from '../frontend/panel';
 import {Store} from '../frontend/store';
 import {
@@ -21,6 +22,7 @@
   TrackDescriptor,
   TrackRef,
 } from '../public';
+import {Registry} from './registry';
 
 import {State} from './state';
 
@@ -55,44 +57,39 @@
 // Third cycle
 //   flushTracks() <-- 'foo' is destroyed.
 export class TrackManager {
+  private readonly registry = new Registry<TrackDescriptor>(({uri}) => uri);
+  private readonly potentialTracks = new Set<TrackRef>();
   private safeCache = new Map<string, TrackCacheEntry>();
   private recycleBin = new Map<string, TrackCacheEntry>();
-  private trackRegistry = new Map<string, TrackDescriptor>();
-  private defaultTracks = new Set<TrackRef>();
   private store: Store<State>;
 
   constructor(store: Store<State>) {
     this.store = store;
   }
 
-  registerTrack(trackDesc: TrackDescriptor): void {
-    this.trackRegistry.set(trackDesc.uri, trackDesc);
+  registerTrack(trackDesc: TrackDescriptor): Disposable {
+    return this.registry.register(trackDesc);
   }
 
-  unregisterTrack(uri: string): void {
-    this.trackRegistry.delete(uri);
-  }
-
-  addDefaultTrack(track: TrackRef): void {
-    this.defaultTracks.add(track);
-  }
-
-  removeDefaultTrack(track: TrackRef): void {
-    this.defaultTracks.delete(track);
+  addPotentialTrack(track: TrackRef): Disposable {
+    this.potentialTracks.add(track);
+    return new DisposableCallback(() => {
+      this.potentialTracks.delete(track);
+    });
   }
 
   findPotentialTracks(): TrackRef[] {
-    return Array.from(this.defaultTracks);
+    return Array.from(this.potentialTracks);
   }
 
   getAllTracks(): TrackDescriptor[] {
-    return Array.from(this.trackRegistry.values());
+    return Array.from(this.registry.values());
   }
 
   // Look up track into for a given track's URI.
   // Returns |undefined| if no track can be found.
   resolveTrackInfo(uri: string): TrackDescriptor|undefined {
-    return this.trackRegistry.get(uri);
+    return this.registry.get(uri);
   }
 
   // Creates a new track using |uri| and |params| or retrieves a cached track if
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index c3f0cbe..2c5388d 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -108,10 +108,10 @@
   async guessCpuSizes(): Promise<Map<number, string>> {
     const cpuToSize = new Map<number, string>();
     await this.engine.query(`
-      INCLUDE PERFETTO MODULE common.cpus;
+      INCLUDE PERFETTO MODULE cpu.size;
     `);
     const result = await this.engine.query(`
-      SELECT cpu, GUESS_CPU_SIZE(cpu) as size FROM cpu_counter_track;
+      SELECT cpu, cpu_guess_core_type(cpu) as size FROM cpu_counter_track;
     `);
 
     const it = result.iter({
@@ -371,7 +371,7 @@
 
     for (const track of ionTracks) {
       if (!foundSummary &&
-          [MEM_DMA_COUNTER_NAME, MEM_ION].includes(track.name)) {
+        [MEM_DMA_COUNTER_NAME, MEM_ION].includes(track.name)) {
         foundSummary = true;
         track.key = summaryTrackKey;
         track.trackGroup = undefined;
@@ -491,9 +491,9 @@
       // Group all the frequency tracks together (except the CPU and GPU
       // frequency ones).
       if (track.name.endsWith('Frequency') && !track.name.startsWith('Cpu') &&
-          !track.name.startsWith('Gpu')) {
+        !track.name.startsWith('Gpu')) {
         if (track.trackGroup !== undefined &&
-            track.trackGroup !== SCROLLING_TRACK_GROUP) {
+          track.trackGroup !== SCROLLING_TRACK_GROUP) {
           continue;
         }
         if (track.uri === NULL_TRACK_URI) {
@@ -539,7 +539,7 @@
     let groupUuid = undefined;
     for (const track of this.tracksToAdd) {
       if (track.trackGroup !== undefined &&
-          track.trackGroup !== SCROLLING_TRACK_GROUP) {
+        track.trackGroup !== SCROLLING_TRACK_GROUP) {
         continue;
       }
       if (track.uri === NULL_TRACK_URI) {
@@ -584,7 +584,7 @@
     for (const track of this.tracksToAdd) {
       if (regex.test(track.name)) {
         if (track.trackGroup !== undefined &&
-            track.trackGroup !== SCROLLING_TRACK_GROUP) {
+          track.trackGroup !== SCROLLING_TRACK_GROUP) {
           continue;
         }
         if (track.uri === NULL_TRACK_URI) {
@@ -619,7 +619,7 @@
 
   async addLogsTrack(engine: EngineProxy): Promise<void> {
     const result =
-        await engine.query(`select count(1) as cnt from android_logs`);
+      await engine.query(`select count(1) as cnt from android_logs`);
     const count = result.firstRow({cnt: NUM}).cnt;
 
     if (count > 0) {
@@ -702,7 +702,7 @@
 
       let summaryTrackKey = undefined;
       let trackGroupId =
-          upid === 0 ? SCROLLING_TRACK_GROUP : this.upidToUuid.get(upid);
+        upid === 0 ? SCROLLING_TRACK_GROUP : this.upidToUuid.get(upid);
 
       if (groupName) {
         // If this is the first track encountered for a certain group,
@@ -801,7 +801,7 @@
 
       const priority = InThreadTrackSortKey.THREAD_SCHEDULING_STATE_TRACK;
       const name =
-          getTrackName({utid, tid, threadName, kind: THREAD_STATE_TRACK_KIND});
+        getTrackName({utid, tid, threadName, kind: THREAD_STATE_TRACK_KIND});
 
       this.tracksToAdd.push({
         uri: `perfetto.ThreadState#${utid}`,
@@ -1014,7 +1014,7 @@
       const rawName = it.name;
       const uid = it.uid === null ? undefined : it.uid;
       const userName =
-          it.package_name === null ? `UID: ${uid}` : it.package_name;
+        it.package_name === null ? `UID: ${uid}` : it.package_name;
 
       const groupUuid = `uid-track-group${rawName}`;
       if (groupMap.get(rawName) === undefined) {
@@ -1095,7 +1095,7 @@
 
       const kind = ACTUAL_FRAMES_SLICE_TRACK_KIND;
       const name =
-          getTrackName({name: trackName, upid, pid, processName, kind});
+        getTrackName({name: trackName, upid, pid, processName, kind});
 
       this.tracksToAdd.push({
         uri: `perfetto.ActualFrames#${upid}`,
@@ -1153,7 +1153,7 @@
 
       const kind = EXPECTED_FRAMES_SLICE_TRACK_KIND;
       const name =
-          getTrackName({name: trackName, upid, pid, processName, kind});
+        getTrackName({name: trackName, upid, pid, processName, kind});
 
       this.tracksToAdd.push({
         uri: `perfetto.ExpectedFrames#${upid}`,
@@ -1295,16 +1295,16 @@
     }
   }
 
-  getUuidUnchecked(utid: number, upid: number|null) {
+  getUuidUnchecked(utid: number, upid: number | null) {
     return upid === null ? this.utidToUuid.get(utid) :
       this.upidToUuid.get(upid);
   }
 
-  getUuid(utid: number, upid: number|null) {
+  getUuid(utid: number, upid: number | null) {
     return assertExists(this.getUuidUnchecked(utid, upid));
   }
 
-  getOrCreateUuid(utid: number, upid: number|null) {
+  getOrCreateUuid(utid: number, upid: number | null) {
     let uuid = this.getUuidUnchecked(utid, upid);
     if (uuid === undefined) {
       uuid = uuidv4();
@@ -1531,7 +1531,7 @@
 
       // Group by upid if present else by utid.
       let pUuid =
-          upid === null ? this.utidToUuid.get(utid) : this.upidToUuid.get(upid);
+        upid === null ? this.utidToUuid.get(utid) : this.upidToUuid.get(upid);
       // These should only happen once for each track group.
       if (pUuid === undefined) {
         pUuid = this.getOrCreateUuid(utid, upid);
@@ -1550,7 +1550,7 @@
         });
 
         const name =
-            getTrackName({utid, processName, pid, threadName, tid, upid});
+          getTrackName({utid, processName, pid, threadName, tid, upid});
         const addTrackGroup = Actions.addTrackGroup({
           summaryTrackKey,
           name,
@@ -1793,8 +1793,8 @@
   }
 
   private static getThreadSortKey(
-    threadName?: string|null, tid?: number|null,
-    pid?: number|null): PrimaryTrackSortKey {
+    threadName?: string | null, tid?: number | null,
+    pid?: number | null): PrimaryTrackSortKey {
     if (pid !== undefined && pid !== null && pid === tid) {
       return PrimaryTrackSortKey.MAIN_THREAD;
     }
@@ -1804,13 +1804,13 @@
 
     // Chrome main threads should always come first within their process.
     if (threadName === 'CrBrowserMain' || threadName === 'CrRendererMain' ||
-        threadName === 'CrGpuMain') {
+      threadName === 'CrGpuMain') {
       return PrimaryTrackSortKey.MAIN_THREAD;
     }
 
     // Chrome IO threads should always come immediately after the main thread.
     if (threadName === 'Chrome_ChildIOThread' ||
-        threadName === 'Chrome_IOThread') {
+      threadName === 'Chrome_IOThread') {
       return PrimaryTrackSortKey.CHROME_IO_THREAD;
     }
 
diff --git a/ui/src/frontend/track_cache.ts b/ui/src/core/timeline_cache.ts
similarity index 98%
rename from ui/src/frontend/track_cache.ts
rename to ui/src/core/timeline_cache.ts
index 09ddf41..d8cede9 100644
--- a/ui/src/frontend/track_cache.ts
+++ b/ui/src/core/timeline_cache.ts
@@ -127,10 +127,10 @@
 }
 
 
-// LRU cache for the tracks.
+// LRU cache for the timeline.
 // T is all the data needed for a displaying the track in a given
 // CacheKey area - generally an array of slices.
-export class TrackCache<T> {
+export class TimelineCache<T> {
   private cacheSize: number;
   private cache: Map<string, CacheItem<T>>;
   private lastAccessId: number;
diff --git a/ui/src/frontend/track_cache_unittest.ts b/ui/src/core/timeline_cache_unittest.ts
similarity index 95%
rename from ui/src/frontend/track_cache_unittest.ts
rename to ui/src/core/timeline_cache_unittest.ts
index 2be853b..f1d24e8 100644
--- a/ui/src/frontend/track_cache_unittest.ts
+++ b/ui/src/core/timeline_cache_unittest.ts
@@ -14,7 +14,7 @@
 
 import {Time} from '../base/time';
 
-import {CacheKey, TrackCache} from './track_cache';
+import {CacheKey, TimelineCache} from './timeline_cache';
 
 test('cacheKeys', () => {
   const k = CacheKey.create(Time.fromRaw(201n), Time.fromRaw(302n), 123);
@@ -45,7 +45,7 @@
     .normalize();
   const key7 = (CacheKey.create(Time.fromRaw(7000n), Time.fromRaw(7100n), 100))
     .normalize();
-  const cache = new TrackCache<string>(5);
+  const cache = new TimelineCache<string>(5);
 
   cache.insert(key1, 'v1');
   expect(cache.lookup(key1)).toEqual('v1');
diff --git a/ui/src/frontend/aggregation_tab.ts b/ui/src/frontend/aggregation_tab.ts
index 603599d..0288b91 100644
--- a/ui/src/frontend/aggregation_tab.ts
+++ b/ui/src/frontend/aggregation_tab.ts
@@ -178,7 +178,7 @@
       });
       this.trash.add(unregister);
 
-      const unregisterCmd = globals.commandManager.registry.register({
+      const unregisterCmd = globals.commandManager.registerCommand({
         id: uri,
         name: `Show ${title} Aggregation Tab`,
         callback: () => {
diff --git a/ui/src/frontend/app.ts b/ui/src/frontend/app.ts
index 0819338..d48eab6 100644
--- a/ui/src/frontend/app.ts
+++ b/ui/src/frontend/app.ts
@@ -839,7 +839,7 @@
 
     // Register each command with the command manager
     this.cmds.forEach((cmd) => {
-      const dispose = globals.commandManager.registry.register(cmd);
+      const dispose = globals.commandManager.registerCommand(cmd);
       this.trash.add(dispose);
     });
   }
diff --git a/ui/src/frontend/base_counter_track.ts b/ui/src/frontend/base_counter_track.ts
index a0025c6..dbc9456 100644
--- a/ui/src/frontend/base_counter_track.ts
+++ b/ui/src/frontend/base_counter_track.ts
@@ -32,7 +32,7 @@
 import {PanelSize} from './panel';
 import {constraintsToQuerySuffix} from './sql_utils';
 import {NewTrackArgs} from './track';
-import {CacheKey, TrackCache} from './track_cache';
+import {CacheKey, TimelineCache} from '../core/timeline_cache';
 
 interface CounterData {
   timestamps: BigInt64Array;
@@ -85,7 +85,7 @@
     minimumRate: 0,
   };
 
-  private cache: TrackCache<CounterData> = new TrackCache(5);
+  private cache: TimelineCache<CounterData> = new TimelineCache(5);
 
   private sqlState: 'UNINITIALIZED'|'INITIALIZING'|'QUERY_PENDING'|
       'QUERY_DONE' = 'UNINITIALIZED';
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index 83f678d..5974811 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -41,7 +41,7 @@
 import {DEFAULT_SLICE_LAYOUT, SliceLayout} from './slice_layout';
 import {constraintsToQuerySuffix} from './sql_utils';
 import {NewTrackArgs} from './track';
-import {BUCKETS_PER_PIXEL, CacheKey, TrackCache} from './track_cache';
+import {BUCKETS_PER_PIXEL, CacheKey, TimelineCache} from '../core/timeline_cache';
 
 // The common class that underpins all tracks drawing slices.
 
@@ -184,8 +184,8 @@
   private slices = new Array<CastInternal<T['slice']>>();
 
   // This is the slices cache:
-  private cache: TrackCache<Array<CastInternal<T['slice']>>> =
-    new TrackCache(5);
+  private cache: TimelineCache<Array<CastInternal<T['slice']>>> =
+    new TimelineCache(5);
 
   // Incomplete slices (dur = -1). Rather than adding a lot of logic to
   // the SQL queries to handle this case we materialise them one off
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index a86448b..9b19db0 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -254,11 +254,9 @@
     this.panelByKey.set(key, node);
     const mithril = node.mithril;
 
-    return m(
-      `.panel${extraClass}`,
-      {key, 'data-key': key},
+    return m(`.panel${extraClass}`, {key, 'data-key': key},
       perfDebug() ?
-        [mithril, m('.debug-panel-border', {key: 'debug-panel-border'})] :
+        [mithril, m('.debug-panel-border')] :
         mithril);
   }
 
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index 257d2d8..627d888 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -754,8 +754,7 @@
             href: `${GITILES_URL}/+/${SCM_REVISION}/ui`,
             title: `Channel: ${getCurrentChannel()}`,
             target: '_blank',
-          },
-          `${VERSION.substr(0, 11)}`),
+          }, VERSION),
       ),
     );
   },
diff --git a/ui/src/frontend/sql/thread_state.ts b/ui/src/frontend/sql/thread_state.ts
index 2274c15..24de373 100644
--- a/ui/src/frontend/sql/thread_state.ts
+++ b/ui/src/frontend/sql/thread_state.ts
@@ -72,7 +72,7 @@
   // TODO(altimin): this probably should share some code with pivot tables when
   // we actually get some pivot tables we like.
   const query = await engine.query(`
-    INCLUDE PERFETTO MODULE common.thread_states;
+    INCLUDE PERFETTO MODULE deprecated.v42.common.thread_states;
 
     SELECT
       state,
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index da06f31..697e45a 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -400,7 +400,6 @@
 }
 
 interface TrackPanelAttrs {
-  key: string;
   trackKey: string;
   title: string;
   tags?: TrackTags;
@@ -415,7 +414,7 @@
   constructor(private readonly attrs: TrackPanelAttrs) {}
 
   get key(): string {
-    return this.attrs.key;
+    return this.attrs.trackKey;
   }
 
   get trackKey(): string {
@@ -435,7 +434,6 @@
         });
       }
       return m(TrackComponent, {
-        key: attrs.key,
         trackKey: attrs.trackKey,
         title: attrs.title,
         heightPx: attrs.trackFSM.track.getHeight(),
@@ -447,7 +445,6 @@
       });
     } else {
       return m(TrackComponent, {
-        key: attrs.key,
         trackKey: attrs.trackKey,
         title: attrs.title,
         revealOnCreate: attrs.revealOnCreate,
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 693a54f..57d623c 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -227,7 +227,6 @@
         globals.state.scrollingTracks.map((key) => {
           const trackBundle = this.resolveTrack(key);
           return new TrackPanel({
-            key,
             trackKey: key,
             title: trackBundle.title,
             tags: trackBundle.tags,
@@ -256,7 +255,6 @@
           const key = group.tracks[i];
           const trackBundle = this.resolveTrack(key);
           const panel = new TrackPanel({
-            key: `track-${group.id}-${key}`,
             trackKey: key,
             title: trackBundle.title,
             tags: trackBundle.tags,
@@ -312,7 +310,6 @@
             panels: globals.state.pinnedTracks.map((key) => {
               const trackBundle = this.resolveTrack(key);
               return new TrackPanel({
-                key,
                 trackKey: key,
                 title: trackBundle.title,
                 tags: trackBundle.tags,
diff --git a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
index 7ef32d1..c8ffaf6 100644
--- a/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
+++ b/ui/src/plugins/dev.perfetto.AndroidPerf/index.ts
@@ -21,7 +21,7 @@
   async addAppProcessStartsDebugTrack(
     engine: EngineProxy, reason: string, sliceName: string): Promise<void> {
     const sliceColumns =
-        ['id', 'ts', 'dur', 'reason', 'process_name', 'intent', 'table_name'];
+      ['id', 'ts', 'dur', 'reason', 'process_name', 'intent', 'table_name'];
     await addDebugSliceTrack(
       engine,
       {
@@ -91,7 +91,7 @@
           if (tid === null) return;
         }
         ctx.tabs.openQuery(`
-          INCLUDE PERFETTO MODULE common.cpus;
+          INCLUDE PERFETTO MODULE cpu.cpus;
           WITH
             total_runtime AS (
               SELECT sum(dur) AS total_runtime
@@ -107,7 +107,7 @@
             FROM sched s
             LEFT JOIN thread t
               USING (utid)
-            LEFT JOIN cpus c
+            LEFT JOIN cpu_core_types c
               ON s.cpu = c.cpu_index
             WHERE t.tid = ${tid}
             GROUP BY 1`, `runtime cluster distrubtion for tid ${tid}`);
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 068142e..74e7555 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -61,7 +61,7 @@
 const TRACK_HEIGHT = MARGIN_TOP * 2 + RECT_HEIGHT;
 
 class CpuSliceTrack implements Track {
-  private mousePos?: {x: number, y: number};
+  private mousePos?: { x: number, y: number };
   private utidHoveredInThisTrack = -1;
   private fetcher = new TimelineFetcher<Data>(this.onBoundsChange.bind(this));
 
@@ -140,7 +140,7 @@
   }
 
   async onBoundsChange(start: time, end: time, resolution: duration):
-      Promise<Data> {
+    Promise<Data> {
     assertTrue(BIMath.popcount(resolution) === 1, `${resolution} not pow of 2`);
 
     const isCached = this.cachedBucketSize <= resolution;
@@ -148,7 +148,7 @@
       `cached_tsq / ${resolution} * ${resolution}` :
       `(ts + ${resolution / 2n}) / ${resolution} * ${resolution}`;
     const queryTable =
-        isCached ? this.tableName('sched_cached') : this.tableName('sched');
+      isCached ? this.tableName('sched_cached') : this.tableName('sched');
     const constraintColumn = isCached ? 'cached_tsq' : 'ts';
 
     const queryRes = await this.engine.query(`
@@ -411,7 +411,7 @@
       // Draw diamond if the track being drawn is the cpu of the waker.
       if (this.cpu === details.wakerCpu && details.wakeupTs) {
         const wakeupPos =
-            Math.floor(visibleTimeScale.timeToPx(details.wakeupTs));
+          Math.floor(visibleTimeScale.timeToPx(details.wakeupTs));
         ctx.beginPath();
         ctx.moveTo(wakeupPos, MARGIN_TOP + RECT_HEIGHT / 2 + 8);
         ctx.fillStyle = 'black';
@@ -439,7 +439,7 @@
     }
   }
 
-  onMouseMove(pos: {x: number, y: number}) {
+  onMouseMove(pos: { x: number, y: number }) {
     const data = this.fetcher.data;
     this.mousePos = pos;
     if (data === undefined) return;
@@ -475,7 +475,7 @@
     this.mousePos = undefined;
   }
 
-  onMouseClick({x}: {x: number}) {
+  onMouseClick({x}: { x: number }) {
     const data = this.fetcher.data;
     if (data === undefined) return false;
     const {visibleTimeScale} = globals.timeline;
@@ -525,10 +525,11 @@
   async guessCpuSizes(engine: EngineProxy): Promise<Map<number, string>> {
     const cpuToSize = new Map<number, string>();
     await engine.query(`
-      INCLUDE PERFETTO MODULE common.cpus;
+      INCLUDE PERFETTO MODULE cpu.size;
     `);
     const result = await engine.query(`
-      SELECT cpu, GUESS_CPU_SIZE(cpu) as size FROM cpu_counter_track;
+      SELECT cpu, cpu_guess_core_type(cpu) as size
+      FROM cpu_counter_track;
     `);
 
     const it = result.iter({