Merge "Use bigint time (almost) everywhere - Pt.2"
diff --git a/Android.bp b/Android.bp
index f4f9a2d..af0f50d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2035,6 +2035,7 @@
         ":perfetto_src_shared_lib_test_utils",
         ":perfetto_src_trace_processor_containers_containers",
         ":perfetto_src_trace_processor_db_db",
+        ":perfetto_src_trace_processor_db_overlays_overlays",
         ":perfetto_src_trace_processor_export_json",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
         ":perfetto_src_trace_processor_importers_common_common",
@@ -9408,6 +9409,11 @@
     ],
 }
 
+// GN: //src/trace_processor/db/overlays:overlays
+filegroup {
+    name: "perfetto_src_trace_processor_db_overlays_overlays",
+}
+
 // GN: //src/trace_processor/db:unittests
 filegroup {
     name: "perfetto_src_trace_processor_db_unittests",
@@ -12033,6 +12039,7 @@
         ":perfetto_src_trace_processor_containers_containers",
         ":perfetto_src_trace_processor_containers_unittests",
         ":perfetto_src_trace_processor_db_db",
+        ":perfetto_src_trace_processor_db_overlays_overlays",
         ":perfetto_src_trace_processor_db_unittests",
         ":perfetto_src_trace_processor_export_json",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
@@ -12730,6 +12737,7 @@
         ":perfetto_src_protozero_protozero",
         ":perfetto_src_trace_processor_containers_containers",
         ":perfetto_src_trace_processor_db_db",
+        ":perfetto_src_trace_processor_db_overlays_overlays",
         ":perfetto_src_trace_processor_export_json",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
         ":perfetto_src_trace_processor_importers_common_common",
@@ -12955,6 +12963,7 @@
         ":perfetto_src_protozero_protozero",
         ":perfetto_src_trace_processor_containers_containers",
         ":perfetto_src_trace_processor_db_db",
+        ":perfetto_src_trace_processor_db_overlays_overlays",
         ":perfetto_src_trace_processor_export_json",
         ":perfetto_src_trace_processor_importers_android_bugreport_android_bugreport",
         ":perfetto_src_trace_processor_importers_common_common",
@@ -13095,15 +13104,22 @@
         "src/traced/service/main.cc",
     ],
     shared_libs: [
-        "liblog",
         "libperfetto",
     ],
+    host_supported: true,
     init_rc: [
         "perfetto.rc",
     ],
     defaults: [
         "perfetto_defaults",
     ],
+    target: {
+        android: {
+            shared_libs: [
+                "liblog",
+            ],
+        },
+    },
 }
 
 // GN: //src/profiling/perf:traced_perf
diff --git a/BUILD b/BUILD
index 195292f..e350b2c 100644
--- a/BUILD
+++ b/BUILD
@@ -73,6 +73,7 @@
         ":src_kernel_utils_syscall_table",
         ":src_protozero_proto_ring_buffer",
         ":src_trace_processor_db_db",
+        ":src_trace_processor_db_overlays_overlays",
         ":src_trace_processor_export_json",
         ":src_trace_processor_importers_android_bugreport_android_bugreport",
         ":src_trace_processor_importers_common_common",
@@ -1271,6 +1272,14 @@
     linkstatic = True,
 )
 
+# GN target: //src/trace_processor/db/overlays:overlays
+perfetto_filegroup(
+    name = "src_trace_processor_db_overlays_overlays",
+    srcs = [
+        "src/trace_processor/db/overlays/column_overlay.h",
+    ],
+)
+
 # GN target: //src/trace_processor/db:db
 perfetto_filegroup(
     name = "src_trace_processor_db_db",
@@ -4991,6 +5000,7 @@
     srcs = [
         ":src_kernel_utils_syscall_table",
         ":src_trace_processor_db_db",
+        ":src_trace_processor_db_overlays_overlays",
         ":src_trace_processor_export_json",
         ":src_trace_processor_importers_android_bugreport_android_bugreport",
         ":src_trace_processor_importers_common_common",
@@ -5147,6 +5157,7 @@
         ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_proto_ring_buffer",
         ":src_trace_processor_db_db",
+        ":src_trace_processor_db_overlays_overlays",
         ":src_trace_processor_export_json",
         ":src_trace_processor_importers_android_bugreport_android_bugreport",
         ":src_trace_processor_importers_common_common",
@@ -5362,6 +5373,7 @@
         ":src_profiling_symbolizer_symbolizer",
         ":src_protozero_proto_ring_buffer",
         ":src_trace_processor_db_db",
+        ":src_trace_processor_db_overlays_overlays",
         ":src_trace_processor_export_json",
         ":src_trace_processor_importers_android_bugreport_android_bugreport",
         ":src_trace_processor_importers_common_common",
diff --git a/src/base/utils.cc b/src/base/utils.cc
index 198daa9..82386fa 100644
--- a/src/base/utils.cc
+++ b/src/base/utils.cc
@@ -55,9 +55,16 @@
 #define PERFETTO_M_PURGE -101
 #endif  // M_PURGE
 
+#ifdef M_PURGE_ALL
+#define PERFETTO_M_PURGE_ALL M_PURGE_ALL
+#else
+// Only available in in-tree builds and on newer SDKs.
+#define PERFETTO_M_PURGE_ALL -104
+#endif  // M_PURGE
+
 namespace {
 extern "C" {
-using MalloptType = void (*)(int, int);
+using MalloptType = int (*)(int, int);
 }
 }  // namespace
 #endif  // OS_ANDROID
@@ -140,7 +147,9 @@
       reinterpret_cast<MalloptType>(dlsym(RTLD_DEFAULT, "mallopt"));
   if (!mallopt_fn)
     return;
-  mallopt_fn(PERFETTO_M_PURGE, 0);
+  if (mallopt_fn(PERFETTO_M_PURGE_ALL, 0) == 0) {
+    mallopt_fn(PERFETTO_M_PURGE, 0);
+  }
 #endif
 }
 
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index 043b2af..848db44 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -50,6 +50,7 @@
     "../../../include/perfetto/trace_processor",
     "../containers",
     "../util:glob",
+    "overlays",
   ]
 }
 
diff --git a/src/trace_processor/db/column_overlay.cc b/src/trace_processor/db/column_overlay.cc
index 599a3b2..690256f 100644
--- a/src/trace_processor/db/column_overlay.cc
+++ b/src/trace_processor/db/column_overlay.cc
@@ -20,7 +20,7 @@
 namespace trace_processor {
 namespace column {
 
-ColumnOverlay::~ColumnOverlay() = default;
+ColumnOverlayOld::~ColumnOverlayOld() = default;
 
 }  // namespace column
 }  // namespace trace_processor
diff --git a/src/trace_processor/db/column_overlay.h b/src/trace_processor/db/column_overlay.h
index e3bad53..14c6123 100644
--- a/src/trace_processor/db/column_overlay.h
+++ b/src/trace_processor/db/column_overlay.h
@@ -31,9 +31,9 @@
 // done on the storage. This is a composable design - one ColumnOverlay
 // subclass might hold another subclass, and each of them implements all of the
 // functions in it's own specific way.
-class ColumnOverlay {
+class ColumnOverlayOld {
  public:
-  virtual ~ColumnOverlay();
+  virtual ~ColumnOverlayOld();
 
   // Clears the rows of RowMap, on which data don't match the FilterOp operation
   // with SqlValue. Efficient.
diff --git a/src/trace_processor/db/null_overlay.h b/src/trace_processor/db/null_overlay.h
index 43d2fed..cd573a4 100644
--- a/src/trace_processor/db/null_overlay.h
+++ b/src/trace_processor/db/null_overlay.h
@@ -28,9 +28,9 @@
 namespace column {
 
 // Overlay responsible for operations related to column nullability.
-class NullOverlay : public ColumnOverlay {
+class NullOverlay : public ColumnOverlayOld {
  public:
-  explicit NullOverlay(std::unique_ptr<ColumnOverlay> inner,
+  explicit NullOverlay(std::unique_ptr<ColumnOverlayOld> inner,
                        const BitVector* null_bv)
       : inner_(std::move(inner)), null_bv_(null_bv) {}
 
@@ -38,7 +38,7 @@
   void StableSort(uint32_t* rows, uint32_t rows_size) const override;
 
  private:
-  std::unique_ptr<ColumnOverlay> inner_;
+  std::unique_ptr<ColumnOverlayOld> inner_;
 
   // Vector of data nullability.
   const BitVector* null_bv_;
diff --git a/src/trace_processor/db/numeric_storage.cc b/src/trace_processor/db/numeric_storage.cc
index 3b66e22..1957dc1 100644
--- a/src/trace_processor/db/numeric_storage.cc
+++ b/src/trace_processor/db/numeric_storage.cc
@@ -93,12 +93,14 @@
       val);
 }
 
+void NumericStorage::Sort(uint32_t*, uint32_t) const {}
+
 // Responsible for invoking templated version of FastPathComparison.
-void NumericStorage::CompareFast(FilterOp op,
-                                 SqlValue sql_val,
-                                 uint32_t offset,
-                                 uint32_t num_elements,
-                                 BitVector::Builder& builder) const {
+void NumericStorage::LinearSearchAligned(FilterOp op,
+                                         SqlValue sql_val,
+                                         uint32_t offset,
+                                         uint32_t num_elements,
+                                         BitVector::Builder& builder) const {
   PERFETTO_DCHECK(num_elements % BitVector::kBitsInWord == 0);
   std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
 
@@ -119,11 +121,11 @@
 }
 
 // Responsible for invoking templated version of SlowPathComparison.
-void NumericStorage::CompareSlow(FilterOp op,
-                                 SqlValue sql_val,
-                                 uint32_t offset,
-                                 uint32_t num_elements,
-                                 BitVector::Builder& builder) const {
+void NumericStorage::LinearSearchUnaligned(FilterOp op,
+                                           SqlValue sql_val,
+                                           uint32_t offset,
+                                           uint32_t num_elements,
+                                           BitVector::Builder& builder) const {
   std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
 
   // If the value is invalid we should just ignore those elements.
@@ -142,13 +144,14 @@
       *val);
 }
 
-uint32_t NumericStorage::UpperBoundIndex(NumericValue val) const {
+uint32_t NumericStorage::UpperBoundIndex(NumericValue val,
+                                         Range search_range) const {
   return std::visit(
-      [this](auto val_data) {
+      [this, search_range](auto val_data) {
         using T = decltype(val_data);
         const T* typed_start = static_cast<const T*>(data_);
-        auto upper =
-            std::upper_bound(typed_start, typed_start + size_, val_data);
+        auto upper = std::upper_bound(typed_start + search_range.start,
+                                      typed_start + search_range.end, val_data);
         return static_cast<uint32_t>(std::distance(typed_start, upper));
       },
       val);
@@ -156,76 +159,59 @@
 
 // As we don't template those functions, we need to use std::visitor to type
 // `start`, hence this wrapping.
-uint32_t NumericStorage::LowerBoundIndex(NumericValue val) const {
+uint32_t NumericStorage::LowerBoundIndex(NumericValue val,
+                                         Range search_range) const {
   return std::visit(
-      [this](auto val_data) {
+      [this, search_range](auto val_data) {
         using T = decltype(val_data);
         const T* typed_start = static_cast<const T*>(data_);
-        auto lower =
-            std::lower_bound(typed_start, typed_start + size_, val_data);
+        auto lower = std::lower_bound(typed_start + search_range.start,
+                                      typed_start + search_range.end, val_data);
         return static_cast<uint32_t>(std::distance(typed_start, lower));
       },
       val);
 }
 
-void NumericStorage::CompareSorted(FilterOp op,
-                                   SqlValue sql_val,
-                                   RowMap& rm) const {
+std::optional<Range> NumericStorage::BinarySearch(FilterOp op,
+                                                  SqlValue sql_val,
+                                                  Range search_range) const {
   std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
-  if (!val.has_value() || op == FilterOp::kIsNotNull ||
-      op == FilterOp::kIsNull || op == FilterOp::kGlob) {
-    rm.Clear();
-    return;
-  }
+  if (op == FilterOp::kIsNotNull)
+    return Range(0, size());
+
+  if (!val)
+    return std::nullopt;
 
   switch (op) {
-    case FilterOp::kEq: {
-      uint32_t beg = LowerBoundIndex(*val);
-      uint32_t end = UpperBoundIndex(*val);
-      RowMap sec(beg, end);
-      rm.Intersect(sec);
-      return;
-    }
-    case FilterOp::kLe: {
-      uint32_t end = UpperBoundIndex(*val);
-      RowMap sec(0, end);
-      rm.Intersect(sec);
-      return;
-    }
-    case FilterOp::kLt: {
-      uint32_t end = LowerBoundIndex(*val);
-      RowMap sec(0, end);
-      rm.Intersect(sec);
-      return;
-    }
-    case FilterOp::kGe: {
-      uint32_t beg = LowerBoundIndex(*val);
-      RowMap sec(beg, size_);
-      rm.Intersect(sec);
-      return;
-    }
-    case FilterOp::kGt: {
-      uint32_t beg = UpperBoundIndex(*val);
-      RowMap sec(beg, size_);
-      rm.Intersect(sec);
-      return;
-    }
+    case FilterOp::kEq:
+      return Range(LowerBoundIndex(*val, search_range),
+                   UpperBoundIndex(*val, search_range));
+    case FilterOp::kLe:
+      return Range(0, UpperBoundIndex(*val, search_range));
+    case FilterOp::kLt:
+      return Range(0, LowerBoundIndex(*val, search_range));
+    case FilterOp::kGe:
+      return Range(LowerBoundIndex(*val, search_range), size_);
+    case FilterOp::kGt:
+      return Range(UpperBoundIndex(*val, search_range), size_);
     case FilterOp::kNe:
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
     case FilterOp::kGlob:
-      rm.Clear();
+      return std::nullopt;
   }
-  return;
+  return std::nullopt;
 }
 
 uint32_t NumericStorage::UpperBoundIndex(NumericValue val,
-                                         uint32_t* order) const {
+                                         uint32_t* order,
+                                         Range search_range) const {
   return std::visit(
-      [this, order](auto val_data) {
+      [this, order, search_range](auto val_data) {
         using T = decltype(val_data);
         const T* typed_start = static_cast<const T*>(data_);
-        auto upper = std::upper_bound(order, order + size_, val_data,
+        auto upper = std::upper_bound(order + search_range.start,
+                                      order + search_range.end, val_data,
                                       [typed_start](T val, uint32_t index) {
                                         return val < *(typed_start + index);
                                       });
@@ -237,12 +223,14 @@
 // As we don't template those functions, we need to use std::visitor to type
 // `start`, hence this wrapping.
 uint32_t NumericStorage::LowerBoundIndex(NumericValue val,
-                                         uint32_t* order) const {
+                                         uint32_t* order,
+                                         Range search_range) const {
   return std::visit(
-      [this, order](auto val_data) {
+      [this, order, search_range](auto val_data) {
         using T = decltype(val_data);
         const T* typed_start = static_cast<const T*>(data_);
-        auto lower = std::lower_bound(order, order + size_, val_data,
+        auto lower = std::lower_bound(order + search_range.start,
+                                      order + search_range.end, val_data,
                                       [typed_start](uint32_t index, T val) {
                                         return *(typed_start + index) < val;
                                       });
@@ -251,56 +239,38 @@
       val);
 }
 
-void NumericStorage::CompareSortedIndexes(FilterOp op,
-                                          SqlValue sql_val,
-                                          uint32_t* order,
-                                          RowMap& rm) const {
+std::optional<Range> NumericStorage::BinarySearchWithIndex(
+    FilterOp op,
+    SqlValue sql_val,
+    uint32_t* order,
+    Range search_range) const {
   std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
-  if (!val.has_value() || op == FilterOp::kIsNotNull ||
-      op == FilterOp::kIsNull || op == FilterOp::kGlob) {
-    rm.Clear();
-    return;
-  }
+
+  if (op == FilterOp::kIsNotNull)
+    return Range(0, size());
+
+  if (!val.has_value())
+    return std::nullopt;
 
   switch (op) {
-    case FilterOp::kEq: {
-      uint32_t beg = LowerBoundIndex(*val, order);
-      uint32_t end = UpperBoundIndex(*val, order);
-      std::vector<uint32_t> index(order + beg, order + end);
-      rm.Intersect(RowMap(std::move(index)));
-      return;
-    }
-    case FilterOp::kLe: {
-      uint32_t end = UpperBoundIndex(*val, order);
-      std::vector<uint32_t> index(order, order + end);
-      rm.Intersect(RowMap(std::move(index)));
-      return;
-    }
-    case FilterOp::kLt: {
-      uint32_t end = LowerBoundIndex(*val, order);
-      std::vector<uint32_t> index(order, order + end);
-      rm.Intersect(RowMap(std::move(index)));
-      return;
-    }
-    case FilterOp::kGe: {
-      uint32_t beg = LowerBoundIndex(*val, order);
-      std::vector<uint32_t> index(order + beg, order + size_);
-      rm.Intersect(RowMap(std::move(index)));
-      return;
-    }
-    case FilterOp::kGt: {
-      uint32_t beg = UpperBoundIndex(*val, order);
-      std::vector<uint32_t> index(order + beg, order + size_);
-      rm.Intersect(RowMap(std::move(index)));
-      return;
-    }
+    case FilterOp::kEq:
+      return Range(LowerBoundIndex(*val, order, search_range),
+                   UpperBoundIndex(*val, order, search_range));
+    case FilterOp::kLe:
+      return Range(0, UpperBoundIndex(*val, order, search_range));
+    case FilterOp::kLt:
+      return Range(0, LowerBoundIndex(*val, order, search_range));
+    case FilterOp::kGe:
+      return Range(LowerBoundIndex(*val, order, search_range), size_);
+    case FilterOp::kGt:
+      return Range(UpperBoundIndex(*val, order, search_range), size_);
     case FilterOp::kNe:
     case FilterOp::kIsNull:
     case FilterOp::kIsNotNull:
     case FilterOp::kGlob:
-      rm.Clear();
+      return std::nullopt;
   }
-  return;
+  return std::nullopt;
 }
 
 }  // namespace column
diff --git a/src/trace_processor/db/numeric_storage.h b/src/trace_processor/db/numeric_storage.h
index a85044b..0ed8852 100644
--- a/src/trace_processor/db/numeric_storage.h
+++ b/src/trace_processor/db/numeric_storage.h
@@ -33,43 +33,51 @@
 
   void StableSort(uint32_t* rows, uint32_t rows_size) const override;
 
-  void CompareFast(FilterOp op,
-                   SqlValue val,
-                   uint32_t offset,
-                   uint32_t num_elements,
-                   BitVector::Builder& builder) const override;
+  void Sort(uint32_t* rows, uint32_t rows_size) const override;
 
-  void CompareSlow(FilterOp op,
-                   SqlValue val,
-                   uint32_t offset,
-                   uint32_t num_elements,
-                   BitVector::Builder& builder) const override;
+  void LinearSearchAligned(FilterOp op,
+                           SqlValue val,
+                           uint32_t offset,
+                           uint32_t num_elements,
+                           BitVector::Builder& builder) const override;
 
-  void CompareSorted(FilterOp op, SqlValue val, RowMap&) const override;
+  void LinearSearchUnaligned(FilterOp op,
+                             SqlValue val,
+                             uint32_t offset,
+                             uint32_t num_elements,
+                             BitVector::Builder& builder) const override;
 
-  void CompareSortedIndexes(FilterOp op,
-                            SqlValue val,
-                            uint32_t* order,
-                            RowMap&) const override;
+  std::optional<Range> BinarySearch(FilterOp op,
+                                    SqlValue val,
+                                    Range search_range) const override;
+
+  std::optional<Range> BinarySearchWithIndex(FilterOp op,
+                                             SqlValue val,
+                                             uint32_t* order,
+                                             Range search_range) const override;
 
   uint32_t size() const override { return size_; }
 
  private:
   // As we don't template those functions, we need to use std::visitor to type
   // `start`, hence this wrapping.
-  uint32_t UpperBoundIndex(NumericValue val) const;
+  uint32_t UpperBoundIndex(NumericValue val, Range search_range) const;
 
   // As we don't template those functions, we need to use std::visitor to type
   // `start`, hence this wrapping.
-  uint32_t LowerBoundIndex(NumericValue val) const;
+  uint32_t LowerBoundIndex(NumericValue val, Range search_range) const;
 
   // As we don't template those functions, we need to use std::visitor to type
   // `start`, hence this wrapping.
-  uint32_t UpperBoundIndex(NumericValue val, uint32_t* order) const;
+  uint32_t UpperBoundIndex(NumericValue val,
+                           uint32_t* order,
+                           Range search_range) const;
 
   // As we don't template those functions, we need to use std::visitor to type
   // `start`, hence this wrapping.
-  uint32_t LowerBoundIndex(NumericValue val, uint32_t* order) const;
+  uint32_t LowerBoundIndex(NumericValue val,
+                           uint32_t* order,
+                           Range search_range) const;
 
   const ColumnType type_;
   const void* data_;
diff --git a/src/trace_processor/db/overlays/BUILD.gn b/src/trace_processor/db/overlays/BUILD.gn
new file mode 100644
index 0000000..abc47d7
--- /dev/null
+++ b/src/trace_processor/db/overlays/BUILD.gn
@@ -0,0 +1,18 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+source_set("overlays") {
+  sources = [ "column_overlay.h" ]
+  deps = [ "../../containers" ]
+}
diff --git a/src/trace_processor/db/overlays/column_overlay.h b/src/trace_processor/db/overlays/column_overlay.h
new file mode 100644
index 0000000..661debe
--- /dev/null
+++ b/src/trace_processor/db/overlays/column_overlay.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_DB_OVERLAYS_COLUMN_OVERLAY_H_
+#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_COLUMN_OVERLAY_H_
+
+#include <variant>
+#include "perfetto/ext/base/status_or.h"
+#include "src/trace_processor/db/column.h"
+#include "src/trace_processor/db/storage.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace column {
+
+enum class SearchAlgorithm {
+  kLinearSearch,
+  kBinarySearch,
+};
+
+// Column overlay introduce separation between column storage (vector of data)
+// and state (nullability, sorting) and actions (filtering, expanding, joining)
+// done on the storage. This is a composable design - one Overlay is
+// DecidingSearchAlgorithm based on the result of the same function from
+// previous Overlay.
+class ColumnOverlay {
+ public:
+  virtual ~ColumnOverlay();
+
+  // Returns the RowMap without information added by overlay. The result would
+  // be used by the overlay one step closer to Storage. For example, for
+  // NullOverlay it would be returning RowMap that would be only of the length
+  // of set bits in a bit vector.
+  virtual RowMap TranslateDown(RowMap) const = 0;
+
+  // Returns the RowMap with information added by overlay. The result would be
+  // used by the overlay one step closer to the Table. For example, for
+  // NullOverlay it would be returning RowMap that would be the length of bit
+  // vector, nulls included.
+  virtual RowMap TranslateUp(RowMap) const = 0;
+
+  // Decides what search algorithm should be called based on the type of overlay
+  // and passed SearchAlgorithm.
+  virtual SearchAlgorithm DecideSearchAlgorithm(SearchAlgorithm) const = 0;
+};
+}  // namespace column
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_DB_OVERLAYS_COLUMN_OVERLAY_H_
diff --git a/src/trace_processor/db/row_order_overlay.cc b/src/trace_processor/db/row_order_overlay.cc
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/trace_processor/db/row_order_overlay.cc
diff --git a/src/trace_processor/db/storage.h b/src/trace_processor/db/storage.h
index 41e616f..8157a13 100644
--- a/src/trace_processor/db/storage.h
+++ b/src/trace_processor/db/storage.h
@@ -24,43 +24,55 @@
 namespace trace_processor {
 namespace column {
 
-// Most base column interpreting layer - responsible for implementing operations
-// that require looking at the data, such as comparison or sorting.
+using Range = RowMap::Range;
+
+// Most base column interpreting layer - responsible for implementing searches
+// and sorting.
 class Storage {
  public:
   virtual ~Storage();
 
-  // Changes the vector of indices to represent the sorted state of the column.
+  // Changes the vector of indices to represent the sorted (stable sort) state
+  // of the column.
   virtual void StableSort(uint32_t* rows, uint32_t rows_size) const = 0;
 
+  // Changes the vector of indices to represent the sorted (not stable) state of
+  // the column.
+  virtual void Sort(uint32_t* rows, uint32_t rows_size) const = 0;
+
   // Efficiently compares series of |num_elements| of data from |data_start| to
   // comparator value and appends results to BitVector::Builder. Should be used
-  // on as much data as possible.
-  virtual void CompareFast(FilterOp op,
-                           SqlValue value,
-                           uint32_t offset,
-                           uint32_t compare_elements_count,
-                           BitVector::Builder&) const = 0;
+  // where possible
+  virtual void LinearSearchAligned(FilterOp op,
+                                   SqlValue value,
+                                   uint32_t offset,
+                                   uint32_t compare_elements_count,
+                                   BitVector::Builder&) const = 0;
 
   // Inefficiently compares series of |num_elements| of data from |data_start|
   // to comparator value and appends results to BitVector::Builder. Should be
-  // avoided if possible, with `FastSeriesComparison` used instead.
-  virtual void CompareSlow(FilterOp op,
-                           SqlValue value,
-                           uint32_t offset,
-                           uint32_t compare_elements_count,
-                           BitVector::Builder&) const = 0;
+  // avoided if possible, with `LinearSearchAligned` used instead.
+  virtual void LinearSearchUnaligned(FilterOp op,
+                                     SqlValue value,
+                                     uint32_t offset,
+                                     uint32_t compare_elements_count,
+                                     BitVector::Builder&) const = 0;
 
-  // Compares sorted (asc) series data with comparator value. Should be used
-  // where possible.
-  virtual void CompareSorted(FilterOp op, SqlValue value, RowMap&) const = 0;
+  // Compares sorted (asc) series data in |range| with comparator value. Should
+  // be used where possible. Returns the Range of indices which match the
+  // constraint.
+  virtual std::optional<Range> BinarySearch(FilterOp op,
+                                            SqlValue value,
+                                            Range search_range) const = 0;
 
-  // Compares sorted (asc) with `order` vector series with comparator value.
-  // Should be used where possible.
-  virtual void CompareSortedIndexes(FilterOp op,
-                                    SqlValue value,
-                                    uint32_t* order,
-                                    RowMap&) const = 0;
+  // Compares sorted (asc) with |order| vector series in |range| with comparator
+  // value. Should be used where possible. Returns the Range of indices
+  // inside |order| vector which match the constraint.
+  virtual std::optional<Range> BinarySearchWithIndex(
+      FilterOp op,
+      SqlValue value,
+      uint32_t* order,
+      Range search_range) const = 0;
 
   // Number of elements in stored data.
   virtual uint32_t size() const = 0;
diff --git a/src/trace_processor/db/storage_overlay.cc b/src/trace_processor/db/storage_overlay.cc
index 2ab4fde..daefc34 100644
--- a/src/trace_processor/db/storage_overlay.cc
+++ b/src/trace_processor/db/storage_overlay.cc
@@ -35,18 +35,19 @@
   // Slow path: we compare <64 elements and append to get us to a word
   // boundary.
   uint32_t front_elements = builder.BitsUntilWordBoundaryOrFull();
-  storage_->CompareSlow(op, value, 0, front_elements, builder);
+  storage_->LinearSearchUnaligned(op, value, 0, front_elements, builder);
   uint32_t cur_index = front_elements;
 
   // Fast path: we compare as many groups of 64 elements as we can.
   // This should be very easy for the compiler to auto-vectorize.
   uint32_t fast_path_elements = builder.BitsInCompleteWordsUntilFull();
-  storage_->CompareFast(op, value, cur_index, fast_path_elements, builder);
+  storage_->LinearSearchAligned(op, value, cur_index, fast_path_elements,
+                                builder);
   cur_index += fast_path_elements;
 
   // Slow path: we compare <64 elements and append to fill the Builder.
   uint32_t back_elements = builder.BitsUntilFull();
-  storage_->CompareSlow(op, value, cur_index, back_elements, builder);
+  storage_->LinearSearchUnaligned(op, value, cur_index, back_elements, builder);
 
   BitVector bv = std::move(builder).Build();
   rm.Intersect(RowMap(std::move(bv)));
diff --git a/src/trace_processor/db/storage_overlay.h b/src/trace_processor/db/storage_overlay.h
index 9b0b5ec..1c3a7de 100644
--- a/src/trace_processor/db/storage_overlay.h
+++ b/src/trace_processor/db/storage_overlay.h
@@ -28,7 +28,7 @@
 namespace column {
 
 // Overlay responsible for doing operations on storage.
-class StorageOverlay : public ColumnOverlay {
+class StorageOverlay : public ColumnOverlayOld {
  public:
   explicit StorageOverlay(const Storage* storage) : storage_(storage) {}
   void Filter(FilterOp, SqlValue, RowMap&) const override;
diff --git a/src/trace_processor/db/storage_unittest.cc b/src/trace_processor/db/storage_unittest.cc
index 8988c00..05e5e4e 100644
--- a/src/trace_processor/db/storage_unittest.cc
+++ b/src/trace_processor/db/storage_unittest.cc
@@ -57,7 +57,8 @@
   std::iota(data_vec.begin(), data_vec.end(), 0);
   NumericStorage storage(data_vec.data(), size, ColumnType::kUint32);
   BitVector::Builder builder(size);
-  storage.CompareSlow(FilterOp::kGe, SqlValue::Long(5), 0, size, builder);
+  storage.LinearSearchUnaligned(FilterOp::kGe, SqlValue::Long(5), 0, size,
+                                builder);
   BitVector bv = std::move(builder).Build();
 
   ASSERT_EQ(bv.CountSetBits(), 5u);
@@ -70,7 +71,8 @@
   std::iota(data_vec.begin(), data_vec.end(), 0);
   NumericStorage storage(data_vec.data(), size, ColumnType::kUint32);
   BitVector::Builder builder(size);
-  storage.CompareSlow(FilterOp::kGe, SqlValue::Long(5), 0, size, builder);
+  storage.LinearSearchUnaligned(FilterOp::kGe, SqlValue::Long(5), 0, size,
+                                builder);
   BitVector bv = std::move(builder).Build();
 
   ASSERT_EQ(bv.CountSetBits(), 1020u);
@@ -82,7 +84,8 @@
   std::iota(data_vec.begin(), data_vec.end(), 0);
   NumericStorage storage(data_vec.data(), 128, ColumnType::kUint32);
   BitVector::Builder builder(128);
-  storage.CompareFast(FilterOp::kGe, SqlValue::Long(100), 0, 128, builder);
+  storage.LinearSearchAligned(FilterOp::kGe, SqlValue::Long(100), 0, 128,
+                              builder);
   BitVector bv = std::move(builder).Build();
 
   ASSERT_EQ(bv.CountSetBits(), 28u);
@@ -93,11 +96,12 @@
   std::vector<uint32_t> data_vec(128);
   std::iota(data_vec.begin(), data_vec.end(), 0);
   NumericStorage storage(data_vec.data(), 128, ColumnType::kUint32);
-  RowMap rm(0, 128);
-  storage.CompareSorted(FilterOp::kGe, SqlValue::Long(100), rm);
+  std::optional<Range> range =
+      storage.BinarySearch(FilterOp::kGe, SqlValue::Long(100), Range(0, 128));
 
-  ASSERT_EQ(rm.size(), 28u);
-  ASSERT_EQ(rm.Get(0), 100u);
+  ASSERT_EQ(range->size(), 28u);
+  ASSERT_EQ(range->start, 100u);
+  ASSERT_EQ(range->end, 128u);
 }
 
 TEST(NumericStorageUnittest, CompareSortedIndexesGreaterEqual) {
@@ -105,16 +109,13 @@
   std::vector<uint32_t> sorted_order{7, 8, 9, 0, 1, 2, 3, 6, 5, 4};
 
   NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
-  RowMap rm(0, 10);
 
-  storage.CompareSortedIndexes(FilterOp::kGe, SqlValue::Long(60),
-                               sorted_order.data(), rm);
+  std::optional<Range> range = storage.BinarySearchWithIndex(
+      FilterOp::kGe, SqlValue::Long(60), sorted_order.data(), Range(0, 10));
 
-  ASSERT_EQ(rm.size(), 4u);
-  ASSERT_EQ(rm.Get(0), 3u);
-  ASSERT_EQ(rm.Get(1), 6u);
-  ASSERT_EQ(rm.Get(2), 5u);
-  ASSERT_EQ(rm.Get(3), 4u);
+  ASSERT_EQ(range->size(), 4u);
+  ASSERT_EQ(range->start, 6u);
+  ASSERT_EQ(range->end, 10u);
 }
 
 TEST(NumericStorageUnittest, CompareSortedIndexesLess) {
@@ -122,13 +123,13 @@
   std::vector<uint32_t> sorted_order{7, 8, 9, 0, 1, 2, 3, 6, 5, 4};
 
   NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
-  RowMap rm(0, 10);
 
-  storage.CompareSortedIndexes(FilterOp::kLt, SqlValue::Long(60),
-                               sorted_order.data(), rm);
+  std::optional<Range> range = storage.BinarySearchWithIndex(
+      FilterOp::kLt, SqlValue::Long(60), sorted_order.data(), Range(0, 10));
 
-  ASSERT_EQ(rm.size(), 6u);
-  ASSERT_EQ(rm.Get(0), 7u);
+  ASSERT_EQ(range->size(), 6u);
+  ASSERT_EQ(range->start, 0u);
+  ASSERT_EQ(range->end, 6u);
 }
 
 TEST(NumericStorageUnittest, CompareSortedIndexesEqual) {
@@ -136,13 +137,13 @@
   std::vector<uint32_t> sorted_order{7, 8, 9, 0, 1, 2, 3, 6, 5, 4};
 
   NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
-  RowMap rm(0, 10);
 
-  storage.CompareSortedIndexes(FilterOp::kEq, SqlValue::Long(60),
-                               sorted_order.data(), rm);
+  std::optional<Range> range = storage.BinarySearchWithIndex(
+      FilterOp::kEq, SqlValue::Long(60), sorted_order.data(), Range(0, 10));
 
-  ASSERT_EQ(rm.size(), 1u);
-  ASSERT_EQ(rm.Get(0), 3u);
+  ASSERT_EQ(range->size(), 1u);
+  ASSERT_EQ(range->start, 6u);
+  ASSERT_EQ(range->end, 7u);
 }
 
 TEST(StorageOverlayUnittests, FilterIsNull) {
@@ -201,7 +202,8 @@
   BitVector bv = BitVector::Range(0, 20, [](uint32_t t) { return t % 2 == 0; });
 
   NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
-  std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage));
+  std::unique_ptr<ColumnOverlayOld> storage_overlay(
+      new StorageOverlay(&storage));
   NullOverlay overlay(std::move(storage_overlay), &bv);
 
   RowMap rm(0, 10);
@@ -215,7 +217,8 @@
   std::iota(data_vec.begin(), data_vec.end(), 0);
   NumericStorage storage(data_vec.data(), 10, ColumnType::kUint32);
   BitVector bv = BitVector::Range(0, 20, [](uint32_t t) { return t % 2 == 0; });
-  std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage));
+  std::unique_ptr<ColumnOverlayOld> storage_overlay(
+      new StorageOverlay(&storage));
   NullOverlay overlay(std::move(storage_overlay), &bv);
 
   RowMap rm(0, 10);
@@ -237,7 +240,8 @@
   // Prepare NullOverlay
   BitVector bv =
       BitVector::Range(0, bv_size, [](uint32_t t) { return t % 2 == 0; });
-  std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage));
+  std::unique_ptr<ColumnOverlayOld> storage_overlay(
+      new StorageOverlay(&storage));
   NullOverlay overlay(std::move(storage_overlay), &bv);
 
   RowMap rm(0, bv_size);
@@ -259,7 +263,8 @@
   // Prepare NullOverlay
   BitVector bv =
       BitVector::Range(800, bv_size, [](uint32_t t) { return t % 2 == 0; });
-  std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage));
+  std::unique_ptr<ColumnOverlayOld> storage_overlay(
+      new StorageOverlay(&storage));
   NullOverlay overlay(std::move(storage_overlay), &bv);
 
   RowMap rm(0, bv_size);
@@ -278,7 +283,8 @@
 
   // Prepare NullOverlay
   BitVector bv = BitVector::Range(0, 18, [](uint32_t t) { return t % 2 == 0; });
-  std::unique_ptr<ColumnOverlay> storage_overlay(new StorageOverlay(&storage));
+  std::unique_ptr<ColumnOverlayOld> storage_overlay(
+      new StorageOverlay(&storage));
   NullOverlay overlay(std::move(storage_overlay), &bv);
 
   overlay.StableSort(out.data(), 18);
diff --git a/src/trace_processor/metrics/sql/android/android_batt.sql b/src/trace_processor/metrics/sql/android/android_batt.sql
index b88d7ed..a5cfd5a 100644
--- a/src/trace_processor/metrics/sql/android/android_batt.sql
+++ b/src/trace_processor/metrics/sql/android/android_batt.sql
@@ -14,6 +14,7 @@
 -- limitations under the License.
 --
 SELECT IMPORT('android.battery');
+SELECT IMPORT('android.battery_stats');
 
 DROP VIEW IF EXISTS battery_view;
 CREATE VIEW battery_view AS
@@ -182,7 +183,26 @@
        END AS slice_name,
        'Plug type' AS track_name,
        'slice' AS track_type
-FROM plug_type_span;
+FROM plug_type_span
+UNION ALL
+SELECT *
+FROM (
+  SELECT ts,
+         dur,
+         value_name AS slice_name,
+         CASE track_name
+         WHEN 'battery_stats.mobile_radio' THEN 'Cellular radio'
+         WHEN 'battery_stats.data_conn' THEN 'Cellular connection'
+         WHEN 'battery_stats.phone_signal_strength' THEN 'Cellular strength'
+         WHEN 'battery_stats.wifi_radio' THEN 'WiFi radio'
+         WHEN 'battery_stats.wifi_suppl' THEN 'Wifi supplicant state'
+         WHEN 'battery_stats.wifi_signal_strength' THEN 'WiFi strength'
+         ELSE NULL
+         END AS track_name,
+         'slice' AS track_type
+  FROM android_battery_stats_state
+)
+WHERE track_name IS NOT NULL;
 
 DROP VIEW IF EXISTS android_batt_output;
 CREATE VIEW android_batt_output AS
diff --git a/src/trace_processor/metrics/sql/android/network_activity_template.sql b/src/trace_processor/metrics/sql/android/network_activity_template.sql
index 2dcfa31..aa163c2 100644
--- a/src/trace_processor/metrics/sql/android/network_activity_template.sql
+++ b/src/trace_processor/metrics/sql/android/network_activity_template.sql
@@ -34,6 +34,7 @@
 -- @column dur           The duration of the current segment.
 -- @column packet_count  The total number of packets in this segment.
 -- @column packet_length The total number of bytes for packets in this segment.
+DROP VIEW IF EXISTS {{view_name}};
 CREATE VIEW {{view_name}} AS
 WITH quantized AS (
   SELECT
diff --git a/src/trace_processor/stdlib/android/battery_stats.sql b/src/trace_processor/stdlib/android/battery_stats.sql
index 2507cb1..30cea54 100644
--- a/src/trace_processor/stdlib/android/battery_stats.sql
+++ b/src/trace_processor/stdlib/android/battery_stats.sql
@@ -18,10 +18,10 @@
 -- Converts a battery_stats counter value to human readable string.
 --
 -- @arg track STRING  The counter track name (e.g. 'battery_stats.audio').
--- @arg value LONG    The counter value.
+-- @arg value FLOAT   The counter value.
 -- @ret STRING        The human-readable name for the counter value.
 SELECT CREATE_FUNCTION(
-  'BATTERY_STATS_COUNTER_TO_STRING(track STRING, value FLOAT)',
+  'ANDROID_BATTERY_STATS_COUNTER_TO_STRING(track STRING, value FLOAT)',
   'STRING',
   '
   SELECT
@@ -71,7 +71,7 @@
         THEN
           CASE $value
             WHEN 0 THEN "invalid"
-            WHEN 1 THEN "disconn"
+            WHEN 1 THEN "disconnected"
             WHEN 2 THEN "disabled"
             WHEN 3 THEN "inactive"
             WHEN 4 THEN "scanning"
@@ -82,35 +82,35 @@
             WHEN 9 THEN "group-handshake"
             WHEN 10 THEN "completed"
             WHEN 11 THEN "dormant"
-            WHEN 12 THEN "uninit"
+            WHEN 12 THEN "uninitialized"
             ELSE "unknown"
           END
       WHEN $track = "battery_stats.data_conn"
         THEN
           CASE $value
-            WHEN 0 THEN "oos"
-            WHEN 1 THEN "gprs"
-            WHEN 2 THEN "edge"
-            WHEN 3 THEN "umts"
-            WHEN 4 THEN "cdma"
-            WHEN 5 THEN "evdo_0"
-            WHEN 6 THEN "evdo_A"
-            WHEN 7 THEN "1xrtt"
-            WHEN 8 THEN "hsdpa"
-            WHEN 9 THEN "hsupa"
-            WHEN 10 THEN "hspa"
-            WHEN 11 THEN "iden"
-            WHEN 12 THEN "evdo_b"
-            WHEN 13 THEN "lte"
-            WHEN 14 THEN "ehrpd"
-            WHEN 15 THEN "hspap"
-            WHEN 16 THEN "gsm"
-            WHEN 17 THEN "td_scdma"
-            WHEN 18 THEN "iwlan"
-            WHEN 19 THEN "lte_ca"
-            WHEN 20 THEN "nr"
-            WHEN 21 THEN "emngcy"
-            WHEN 22 THEN "other"
+            WHEN 0 THEN "Out of service"
+            WHEN 1 THEN "2.5G (GPRS)"
+            WHEN 2 THEN "2.7G (EDGE)"
+            WHEN 3 THEN "3G (UMTS)"
+            WHEN 4 THEN "3G (CDMA)"
+            WHEN 5 THEN "3G (EVDO Rel 0)"
+            WHEN 6 THEN "3G (EVDO Rev A)"
+            WHEN 7 THEN "3G (LXRTT)"
+            WHEN 8 THEN "3.5G (HSDPA)"
+            WHEN 9 THEN "3.5G (HSUPA)"
+            WHEN 10 THEN "3.5G (HSPA)"
+            WHEN 11 THEN "2G (IDEN)"
+            WHEN 12 THEN "3G (EVDO Rev B)"
+            WHEN 13 THEN "4G (LTE)"
+            WHEN 14 THEN "3.5G (eHRPD)"
+            WHEN 15 THEN "3.7G (HSPA+)"
+            WHEN 16 THEN "2G (GSM)"
+            WHEN 17 THEN "3G (TD SCDMA)"
+            WHEN 18 THEN "Wifi calling (IWLAN)"
+            WHEN 19 THEN "4.5G (LTE CA)"
+            WHEN 20 THEN "5G (NR)"
+            WHEN 21 THEN "Emergency calls only"
+            WHEN 22 THEN "Other"
             ELSE "unknown"
           END
       ELSE CAST($value AS text)
@@ -133,8 +133,8 @@
   ts,
   name AS track_name,
   CAST(value AS INT64) AS value,
-  BATTERY_STATS_COUNTER_TO_STRING(name, value) AS value_name,
-  IFNULL(LEAD(ts) OVER (PARTITION BY track_id ORDER BY ts) - ts, -1) AS dur
+  ANDROID_BATTERY_STATS_COUNTER_TO_STRING(name, value) AS value_name,
+  IFNULL(LEAD(ts) OVER (PARTITION BY name ORDER BY ts) - ts, -1) AS dur
 FROM counter
 JOIN counter_track
   ON counter.track_id = counter_track.id
diff --git a/src/trace_processor/stdlib/android/monitor_contention.sql b/src/trace_processor/stdlib/android/monitor_contention.sql
index 595c1fe..9fae0c7 100644
--- a/src/trace_processor/stdlib/android/monitor_contention.sql
+++ b/src/trace_processor/stdlib/android/monitor_contention.sql
@@ -145,7 +145,7 @@
 AS
 SELECT ancestor.parent_id AS id FROM slice
     JOIN slice ancestor ON ancestor.id = slice.parent_id
-    WHERE ancestor.name LIKE 'Lock contention on a monitor lock%'
+    WHERE ancestor.name GLOB 'Lock contention on a monitor lock*'
     GROUP BY ancestor.id;
 
 -- Contains parsed monitor contention slices.
@@ -211,7 +211,7 @@
 LEFT JOIN thread_track binder_reply_thread_track ON binder_reply.track_id = binder_reply_thread_track.id
 LEFT JOIN thread binder_reply_thread ON binder_reply_thread_track.utid = binder_reply_thread.utid
 JOIN thread blocking_thread ON blocking_thread.tid = blocking_tid AND blocking_thread.upid = thread.upid
-WHERE slice.name LIKE 'monitor contention%'
+WHERE slice.name GLOB 'monitor contention*'
   AND slice.dur != -1
   AND internal_broken_android_monitor_contention.id IS NULL
   AND short_blocking_method IS NOT NULL
@@ -247,13 +247,34 @@
 LEFT JOIN android_monitor_contention parent ON child.blocked_utid = parent.blocking_utid
     AND parent.ts BETWEEN child.ts AND child.ts + child.dur;
 
+-- First blocked node on a lock, i.e nodes with |waiter_count| = 0. The |dur| here is adjusted
+-- to only account for the time between the first thread waiting and the first thread to acquire
+-- the lock. That way, the thread state span joins below only compute the thread states where
+-- the blocking thread is actually holding the lock. This avoids counting the time when another
+-- waiter acquired the lock before the first waiter.
+CREATE VIEW internal_first_blocked_contention
+  AS
+SELECT start.id, start.blocking_utid, start.ts, MIN(end.ts + end.dur) - start.ts AS dur
+FROM android_monitor_contention_chain start
+JOIN android_monitor_contention_chain END
+  ON
+    start.blocking_utid = end.blocking_utid
+    AND start.blocking_method = end.blocking_method
+    AND end.ts BETWEEN start.ts AND start.ts + start.dur
+WHERE start.waiter_count = 0
+GROUP BY start.id;
+
 CREATE VIEW internal_blocking_thread_state
 AS
 SELECT utid AS blocking_utid, ts, dur, state, blocked_function
 FROM thread_state;
 
--- Contains the span join of the |android_monitor_contention_chain| with their
--- blocking thread thread state.
+-- Contains the span join of the first waiters in the |android_monitor_contention_chain| with their
+-- blocking_thread thread state.
+
+-- Note that we only span join the duration where the lock was actually held and contended.
+-- This can be less than the duration the lock was 'waited on' when a different waiter acquired the
+-- lock earlier than the first waiter.
 --
 -- @column parent_id Id of slice blocking the blocking_thread.
 -- @column blocking_method Name of the method holding the lock.
@@ -282,14 +303,16 @@
 -- @column blocked_function Blocked kernel function of the blocking thread.
 CREATE VIRTUAL TABLE android_monitor_contention_chain_thread_state
 USING
-  SPAN_JOIN(android_monitor_contention_chain PARTITIONED blocking_utid,
+  SPAN_JOIN(internal_first_blocked_contention PARTITIONED blocking_utid,
             internal_blocking_thread_state PARTITIONED blocking_utid);
 
--- Aggregated blocked_functions on the 'blocking thread', the thread holding the lock.
+-- Aggregated thread_states on the 'blocking thread', the thread holding the lock.
 -- This builds on the data from |android_monitor_contention_chain| and
 -- for each contention slice, it returns the aggregated sum of all the thread states on the
 -- blocking thread.
 --
+-- Note that this data is only available for the first waiter on a lock.
+--
 -- @column id Slice id of the monitor contention.
 -- @column thread_state A |thread_state| that occurred in the blocking thread during the contention.
 -- @column thread_state_dur Total time the blocking thread spent in the |thread_state| during
@@ -311,6 +334,8 @@
 -- for each contention, it returns the aggregated sum of all the kernel
 -- blocked function durations on the blocking thread.
 --
+-- Note that this data is only available for the first waiter on a lock.
+--
 -- @column id Slice id of the monitor contention.
 -- @column blocked_function Blocked kernel function in a thread state in the blocking thread during
 -- the contention.
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index da08183..44df9a9 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -763,7 +763,7 @@
                                       builds. The features behind this flag can
                                       break at any time without any warning.
 
-Standard library: 
+Standard library:
  --add-sql-module MODULE_PATH         Files from the directory will be treated
                                       as a new SQL module and can be used for
                                       IMPORT. The name of the directory is the
@@ -1016,10 +1016,11 @@
   return command_line_options;
 }
 
-void ExtendPoolWithBinaryDescriptor(google::protobuf::DescriptorPool& pool,
-                                    const void* data,
-                                    int size,
-                                    std::vector<std::string>& skip_prefixes) {
+void ExtendPoolWithBinaryDescriptor(
+    google::protobuf::DescriptorPool& pool,
+    const void* data,
+    int size,
+    const std::vector<std::string>& skip_prefixes) {
   google::protobuf::FileDescriptorSet desc_set;
   PERFETTO_CHECK(desc_set.ParseFromArray(data, size));
   for (const auto& file_desc : desc_set.file()) {
@@ -1237,7 +1238,8 @@
 }
 
 base::Status LoadMetricExtensionProtos(const std::string& proto_root,
-                                       const std::string& mount_path) {
+                                       const std::string& mount_path,
+                                       google::protobuf::DescriptorPool& pool) {
   if (!base::FileExists(proto_root)) {
     return base::ErrStatus(
         "Directory %s does not exist. Metric extension directory must contain "
@@ -1262,6 +1264,11 @@
       serialized_filedescset.data(),
       static_cast<int>(serialized_filedescset.size()));
 
+  // Extend the pool for any subsequent reflection-based operations
+  // (e.g. output json)
+  ExtendPoolWithBinaryDescriptor(
+      pool, serialized_filedescset.data(),
+      static_cast<int>(serialized_filedescset.size()), {});
   RETURN_IF_ERROR(g_tp->ExtendMetricsProto(serialized_filedescset.data(),
                                            serialized_filedescset.size()));
 
@@ -1293,7 +1300,8 @@
   return base::OkStatus();
 }
 
-base::Status LoadMetricExtension(const MetricExtension& extension) {
+base::Status LoadMetricExtension(const MetricExtension& extension,
+                                 google::protobuf::DescriptorPool& pool) {
   const std::string& disk_path = extension.disk_path();
   const std::string& virtual_path = extension.virtual_path();
 
@@ -1305,8 +1313,8 @@
   // Note: Proto files must be loaded first, because we determine whether an SQL
   // file is a metric or not by checking if the name matches a field of the root
   // TraceMetrics proto.
-  RETURN_IF_ERROR(LoadMetricExtensionProtos(disk_path + "protos/",
-                                            kMetricProtoRoot + virtual_path));
+  RETURN_IF_ERROR(LoadMetricExtensionProtos(
+      disk_path + "protos/", kMetricProtoRoot + virtual_path, pool));
   RETURN_IF_ERROR(LoadMetricExtensionSql(disk_path + "sql/", virtual_path));
 
   return base::OkStatus();
@@ -1585,11 +1593,21 @@
     tp->EnableMetatrace(metatrace_config);
   }
 
+  // Descriptor pool used for printing output as textproto. Building on top of
+  // generated pool so default protos in google.protobuf.descriptor.proto are
+  // available.
+  // For some insane reason, the descriptor pool is not movable so we need to
+  // create it here so we can create references and pass it everywhere.
+  google::protobuf::DescriptorPool pool(
+      google::protobuf::DescriptorPool::generated_pool());
+  RETURN_IF_ERROR(PopulateDescriptorPool(pool, metric_extensions));
+
   // We load all the metric extensions even when --run-metrics arg is not there,
   // because we want the metrics to be available in interactive mode or when
   // used in UI using httpd.
+  // Metric extensions are also used to populate the descriptor pool.
   for (const auto& extension : metric_extensions) {
-    RETURN_IF_ERROR(LoadMetricExtension(extension));
+    RETURN_IF_ERROR(LoadMetricExtension(extension, pool));
   }
 
   base::TimeNanos t_load{};
@@ -1616,14 +1634,6 @@
     RETURN_IF_ERROR(RunQueries(options.pre_metrics_path, false));
   }
 
-  // Descriptor pool used for printing output as textproto. Building on top of
-  // generated pool so default protos in google.protobuf.descriptor.proto are
-  // available.
-  // For some insane reason, the descriptor pool is not movable so we need to
-  // create it here so we can create references and pass it everywhere.
-  google::protobuf::DescriptorPool pool(
-      google::protobuf::DescriptorPool::generated_pool());
-  RETURN_IF_ERROR(PopulateDescriptorPool(pool, metric_extensions));
 
   std::vector<MetricNameAndPath> metrics;
   if (!options.metric_names.empty()) {
diff --git a/src/trace_processor/util/interned_message_view.h b/src/trace_processor/util/interned_message_view.h
index 3422b8b..f6a9a13 100644
--- a/src/trace_processor/util/interned_message_view.h
+++ b/src/trace_processor/util/interned_message_view.h
@@ -71,7 +71,9 @@
     if (PERFETTO_TYPE_IDENTIFIER &&
         strcmp(decoder_type_,
                // GCC complains if this arg can be null.
-               PERFETTO_TYPE_IDENTIFIER ? PERFETTO_TYPE_IDENTIFIER : "") != 0) {
+               static_cast<bool>(PERFETTO_TYPE_IDENTIFIER)
+                   ? PERFETTO_TYPE_IDENTIFIER
+                   : "") != 0) {
       PERFETTO_FATAL(
           "Interning entry accessed under different types! previous type: "
           "%s. new type: %s.",
diff --git a/test/trace_processor/diff_tests/android/android_battery_stats_state.out b/test/trace_processor/diff_tests/android/android_battery_stats_state.out
index 04ea953..eb16850 100644
--- a/test/trace_processor/diff_tests/android/android_battery_stats_state.out
+++ b/test/trace_processor/diff_tests/android/android_battery_stats_state.out
@@ -1,4 +1,4 @@
 "ts","track_name","value","value_name","dur"
 1000,"battery_stats.audio",1,"active",-1
-1000,"battery_stats.data_conn",13,"lte",3000
-4000,"battery_stats.data_conn",20,"nr",-1
+1000,"battery_stats.data_conn",13,"4G (LTE)",3000
+4000,"battery_stats.data_conn",20,"5G (NR)",-1
diff --git a/test/trace_processor/diff_tests/android/android_monitor_contention.out b/test/trace_processor/diff_tests/android/android_monitor_contention.out
index 163815d..8940be9 100644
--- a/test/trace_processor/diff_tests/android/android_monitor_contention.out
+++ b/test/trace_processor/diff_tests/android/android_monitor_contention.out
@@ -1215,11 +1215,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "R"
-      thread_state_dur: 122815
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 914
@@ -1373,8 +1368,8 @@
     is_blocking_thread_main: false
     thread_states {
       thread_state: "R+"
-      thread_state_dur: 7143444
-      thread_state_count: 4
+      thread_state_dur: 7127675
+      thread_state_count: 3
     }
     thread_states {
       thread_state: "Running"
@@ -1404,8 +1399,8 @@
     is_blocking_thread_main: false
     thread_states {
       thread_state: "R+"
-      thread_state_dur: 7143444
-      thread_state_count: 4
+      thread_state_dur: 7127675
+      thread_state_count: 3
     }
     thread_states {
       thread_state: "Running"
@@ -1432,21 +1427,6 @@
     pid: 642
     is_blocked_thread_main: true
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "R+"
-      thread_state_dur: 7100855
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "Running"
-      thread_state_dur: 40711
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 2814860
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 983
@@ -1467,11 +1447,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 3054413
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 994
@@ -4504,11 +4479,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 3567249
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 1778
@@ -4529,11 +4499,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 8523903
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 1786
@@ -4554,11 +4519,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 9607080
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 1819
@@ -4614,11 +4574,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 10733654
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 1825
@@ -5221,26 +5176,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "R"
-      thread_state_dur: 59191
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "R+"
-      thread_state_dur: 223496
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "Running"
-      thread_state_dur: 36279
-      thread_state_count: 2
-    }
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 583889
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 2289
@@ -5261,11 +5196,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 1514707
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 2352
@@ -6873,26 +6803,6 @@
     is_blocking_thread_main: false
     binder_reply_ts: 1739927686578
     binder_reply_tid: 657
-    thread_states {
-      thread_state: "R"
-      thread_state_dur: 819671
-      thread_state_count: 3
-    }
-    thread_states {
-      thread_state: "R+"
-      thread_state_dur: 600233
-      thread_state_count: 2
-    }
-    thread_states {
-      thread_state: "Running"
-      thread_state_dur: 890957
-      thread_state_count: 5
-    }
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 623113
-      thread_state_count: 2
-    }
   }
   node {
     node_id: 13948
@@ -6935,16 +6845,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "Running"
-      thread_state_dur: 480064
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 1685676
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 13979
@@ -7222,16 +7122,6 @@
     is_blocking_thread_main: false
     binder_reply_ts: 1739956996641
     binder_reply_tid: 2721
-    thread_states {
-      thread_state: "R"
-      thread_state_dur: 70662
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "Running"
-      thread_state_dur: 92726
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 14154
@@ -7629,26 +7519,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: true
-    thread_states {
-      thread_state: "R"
-      thread_state_dur: 105075
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "R+"
-      thread_state_dur: 69326
-      thread_state_count: 1
-    }
-    thread_states {
-      thread_state: "Running"
-      thread_state_dur: 722485
-      thread_state_count: 3
-    }
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 267110
-      thread_state_count: 2
-    }
   }
   node {
     node_id: 14623
@@ -7669,11 +7539,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: true
-    thread_states {
-      thread_state: "S"
-      thread_state_dur: 1129545
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 14626
@@ -7716,11 +7581,6 @@
     pid: 642
     is_blocked_thread_main: true
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "Running"
-      thread_state_dur: 388360
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 14730
@@ -8045,11 +7905,6 @@
     pid: 642
     is_blocked_thread_main: false
     is_blocking_thread_main: false
-    thread_states {
-      thread_state: "R+"
-      thread_state_dur: 473837
-      thread_state_count: 1
-    }
   }
   node {
     node_id: 15361
diff --git a/test/trace_processor/diff_tests/android/tests.py b/test/trace_processor/diff_tests/android/tests.py
index 84bafc1..2dc6615 100644
--- a/test/trace_processor/diff_tests/android/tests.py
+++ b/test/trace_processor/diff_tests/android/tests.py
@@ -436,41 +436,19 @@
         13934,"D",11950576,1
       """))
 
-  def test_monitor_contention_chain_extraction(self):
+  def test_monitor_contention_chain_extraction_parent(self):
     return DiffTestBlueprint(
         trace=DataPath('android_monitor_contention_trace.atr'),
         query="""
       SELECT IMPORT('android.monitor_contention');
-      SELECT
-        IIF(parent_id IS NULL, "", parent_id) AS parent_id,
-        blocking_method,
-        blocked_method,
-        short_blocking_method,
-        short_blocked_method,
-        blocking_src,
-        blocked_src,
-        waiter_count,
-        blocked_utid,
-        blocked_thread_name,
-        blocking_utid,
-        blocking_thread_name,
-        upid,
-        process_name,
-        id,
-        ts,
-        dur,
-        is_blocked_thread_main,
-        is_blocking_thread_main,
-        IIF(binder_reply_id IS NULL, "", binder_reply_id) AS binder_reply_id,
-        IIF(binder_reply_ts IS NULL, "", binder_reply_ts) AS binder_reply_ts,
-        IIF(binder_reply_tid IS NULL, "", binder_reply_tid) AS binder_reply_tid
-      FROM android_monitor_contention_chain
+      SELECT * FROM android_monitor_contention_chain
+        WHERE parent_id IS NOT NULL
       ORDER BY dur DESC
       LIMIT 1;
       """,
         out=Csv("""
-        "parent_id","blocking_method","blocked_method","short_blocking_method","short_blocked_method","blocking_src","blocked_src","waiter_count","blocked_utid","blocked_thread_name","blocking_utid","blocking_thread_name","upid","process_name","id","ts","dur","is_blocked_thread_main","is_blocking_thread_main","binder_reply_id","binder_reply_ts","binder_reply_tid"
-        "","void com.android.server.am.ActivityManagerService.forceStopPackage(java.lang.String, int)","boolean com.android.server.am.ActivityManagerService.unbindService(android.app.IServiceConnection)","com.android.server.am.ActivityManagerService.forceStopPackage","com.android.server.am.ActivityManagerService.unbindService","ActivityManagerService.java:3992","ActivityManagerService.java:12719",0,640,"StorageUserConn",495,"binder:642_1",250,"system_server",327,1737063410007,46114664,0,0,"","",""
+        "parent_id","blocking_method","blocked_method","short_blocking_method","short_blocked_method","blocking_src","blocked_src","waiter_count","blocked_utid","blocked_thread_name","blocking_utid","blocking_thread_name","blocking_tid","upid","process_name","id","ts","dur","track_id","is_blocked_thread_main","blocked_thread_tid","is_blocking_thread_main","blocking_thread_tid","binder_reply_id","binder_reply_ts","binder_reply_tid","pid"
+        956,"void com.android.server.am.AppProfiler.collectPssInBackground()","void com.android.server.am.ProcessRecord.setPid(int)","com.android.server.am.AppProfiler.collectPssInBackground","com.android.server.am.ProcessRecord.setPid","AppProfiler.java:514","ProcessRecord.java:596",0,656,"binder:642_12",506,"android.bg",670,250,"system_server",949,1737122781871,7301144,1236,0,2720,0,670,"[NULL]","[NULL]","[NULL]",642
       """))
 
   def test_monitor_contention_metric(self):
diff --git a/test/trace_processor/diff_tests/atrace/tests_general.py b/test/trace_processor/diff_tests/atrace/tests_general.py
deleted file mode 100644
index 59417c0..0000000
--- a/test/trace_processor/diff_tests/atrace/tests_general.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class AtraceGeneral(DiffTestModule):
-
-  def test_android_b2b_async_begin_list_slices(self):
-    return DiffTestBlueprint(
-        trace=Path('android_b2b_async_begin.textproto'),
-        query="""
-SELECT ts, dur, name
-FROM slice;
-""",
-        out=Csv("""
-"ts","dur","name"
-1000,30,"multistart"
-1015,45,"multistart"
-1030,20,"multistart"
-"""))
-
-  def test_process_track_slices_android_async_slice(self):
-    return DiffTestBlueprint(
-        trace=Path('android_async_slice.textproto'),
-        query="""
-SELECT
-  ts,
-  dur,
-  pid,
-  slice.name AS slice_name,
-  process_track.name AS track_name
-FROM slice
-JOIN process_track ON slice.track_id = process_track.id
-JOIN process USING (upid);
-""",
-        out=Path('process_track_slices_android_async_slice.out'))
-
-  def test_async_track_atrace_process_track_slices(self):
-    return DiffTestBlueprint(
-        trace=Path('async_track_atrace.py'),
-        query="""
-SELECT
-  ts,
-  dur,
-  pid,
-  slice.name AS slice_name,
-  process_track.name AS track_name
-FROM slice
-JOIN process_track ON slice.track_id = process_track.id
-JOIN process USING (upid);
-""",
-        out=Csv("""
-"ts","dur","pid","slice_name","track_name"
-50,25,1,"ev","track"
-55,15,1,"ev","track"
-60,5,2,"ev","track"
-"""))
-
-  def test_sys_write_and_atrace(self):
-    return DiffTestBlueprint(
-        trace=Path('sys_write_and_atrace.py'),
-        query="""
-SELECT slice.ts, slice.dur, slice.name, slice.depth
-FROM slice
-JOIN thread_track ON (slice.track_id = thread_track.id)
-JOIN thread USING (utid)
-WHERE tid = 42;
-""",
-        out=Csv("""
-"ts","dur","name","depth"
-100,100,"sys_write",0
-300,50,"sys_write",0
-350,300,"test",0
-600,50,"sys_write",1
-"""))
diff --git a/test/trace_processor/diff_tests/camera/tests_general.py b/test/trace_processor/diff_tests/camera/tests_general.py
deleted file mode 100644
index 7277796..0000000
--- a/test/trace_processor/diff_tests/camera/tests_general.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class CameraGeneral(DiffTestModule):
-
-  def test_camera_ion_mem_trace_android_camera(self):
-    return DiffTestBlueprint(
-        trace=DataPath('camera-ion-mem-trace'),
-        query=Metric('android_camera'),
-        out=TextProto(r"""
-android_camera {
-  gc_rss_and_dma {
-    min: 47779840.0
-    max: 2529583104.0
-    avg: 1459479416.3297353
-  }
-}
-"""))
-
-  def test_camera_ion_mem_trace_android_camera_unagg(self):
-    return DiffTestBlueprint(
-        trace=DataPath('camera-ion-mem-trace'),
-        query=Metric('android_camera_unagg'),
-        out=Path('camera-ion-mem-trace_android_camera_unagg.out'))
diff --git a/test/trace_processor/diff_tests/chrome/index.py b/test/trace_processor/diff_tests/chrome/index.py
deleted file mode 100644
index 67e8ca4..0000000
--- a/test/trace_processor/diff_tests/chrome/index.py
+++ /dev/null
@@ -1,1427 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class DiffTestModule_Chrome(DiffTestModule):
-
-  def test_scroll_jank_general_validation(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query=Path('scroll_jank_general_validation_test.sql'),
-        out=Path('scroll_jank_general_validation.out'))
-
-  def test_scroll_jank(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_jank.sql');
-
-SELECT
-  gesture_scroll_id,
-  trace_id,
-  jank,
-  ts,
-  dur,
-  jank_budget
-FROM scroll_jank;
-""",
-        out=Path('scroll_jank.out'))
-
-  def test_event_latency_to_breakdowns(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/event_latency_with_args.perfetto-trace'),
-        query="""
-SELECT RUN_METRIC('chrome/event_latency_to_breakdowns.sql');
-
-SELECT
-  event_latency_ts,
-  event_latency_dur,
-  event_type,
-  GenerationToRendererCompositorNs,
-  GenerationToBrowserMainNs,
-  BrowserMainToRendererCompositorNs,
-  RendererCompositorQueueingDelayNs,
-  unknown_stages_seen
-FROM event_latency_to_breakdowns
-ORDER BY event_latency_id
-LIMIT 30;
-""",
-        out=Path('event_latency_to_breakdowns.out'))
-
-  def test_event_latency_scroll_jank(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/event_latency_with_args.perfetto-trace'),
-        query="""
-SELECT RUN_METRIC('chrome/event_latency_scroll_jank.sql');
-
-SELECT
-  jank,
-  next_jank,
-  prev_jank,
-  gesture_begin_ts,
-  gesture_end_ts,
-  ts,
-  dur,
-  event_type,
-  next_ts,
-  next_dur,
-  prev_ts,
-  prev_dur
-FROM scroll_event_latency_jank
-ORDER BY jank DESC
-LIMIT 10;
-""",
-        out=Path('event_latency_scroll_jank.out'))
-
-  def test_event_latency_scroll_jank_cause(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/event_latency_with_args.perfetto-trace'),
-        query="""
-SELECT RUN_METRIC('chrome/event_latency_scroll_jank_cause.sql');
-
-SELECT
-  dur,
-  ts,
-  event_type,
-  next_jank,
-  prev_jank,
-  next_delta_dur_ns,
-  prev_delta_dur_ns,
-  cause_of_jank,
-  max_delta_dur_ns,
-  sub_cause_of_jank
-FROM event_latency_scroll_jank_cause
-ORDER by ts;
-""",
-        out=Path('event_latency_scroll_jank_cause.out'))
-
-  def test_scroll_flow_event(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
-
-SELECT
-  trace_id,
-  ts,
-  dur,
-  jank,
-  step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  next_ts,
-  next_trace_id,
-  next_step
-FROM scroll_flow_event
-ORDER BY gesture_scroll_id, trace_id, ts;
-""",
-        out=Path('scroll_flow_event.out'))
-
-  def test_scroll_flow_event_general_validation(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_flow_event.sql');
-
-SELECT
-  -- Each trace_id (in our example trace not true in general) has 8 steps. There
-  -- are 139 scrolls. So we expect 1112 rows in total 72 of which are janky.
-  (
-    SELECT
-      COUNT(*)
-    FROM (
-      SELECT
-        trace_id,
-        COUNT(*)
-      FROM scroll_flow_event
-      GROUP BY trace_id
-    )
-  ) AS total_scroll_updates,
-  (
-    SELECT COUNT(*) FROM scroll_flow_event
-  ) AS total_flow_event_steps,
-  (
-    SELECT COUNT(*) FROM scroll_flow_event WHERE jank
-  ) AS total_janky_flow_event_steps,
-  (
-    SELECT COUNT(*) FROM (SELECT step FROM scroll_flow_event GROUP BY step)
-  ) AS number_of_unique_steps;
-""",
-        out=Path('scroll_flow_event_general_validation.out'))
-
-  def test_scroll_jank_cause(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause.sql');
-
-SELECT
-  COUNT(*) AS total,
-  SUM(jank) AS total_jank,
-  SUM(explained_jank + unexplained_jank) AS sum_explained_and_unexplained,
-  SUM(
-    CASE WHEN explained_jank THEN
-      unexplained_jank
-      ELSE
-        CASE WHEN jank AND NOT unexplained_jank THEN
-          1
-          ELSE
-            0
-        END
-    END
-  ) AS error_rows
-FROM scroll_jank_cause;
-""",
-        out=Csv("""
-"total","total_jank","sum_explained_and_unexplained","error_rows"
-139,7,7,0
-"""))
-
-  def test_scroll_flow_event_queuing_delay(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_flow_event_queuing_delay.sql');
-
-SELECT
-  trace_id,
-  jank,
-  step,
-  next_step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  queuing_time_ns
-FROM scroll_flow_event_queuing_delay
-WHERE trace_id = 2954 OR trace_id = 2956 OR trace_id = 2960
-ORDER BY trace_id, ts;
-""",
-        out=Path('scroll_flow_event_queuing_delay.out'))
-
-  def test_scroll_flow_event_general_validation_2(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query=Path(
-            'scroll_flow_event_queuing_delay_general_validation_test.sql'),
-        out=Path('scroll_flow_event_general_validation.out'))
-
-  def test_scroll_jank_cause_queuing_delay(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
-  process_name,
-  thread_name,
-  trace_id,
-  jank,
-  dur_overlapping_ns,
-  metric_name
-FROM scroll_jank_cause_queuing_delay
-WHERE trace_id = 2918 OR trace_id = 2926
-ORDER BY trace_id ASC, ts ASC;
-""",
-        out=Path('scroll_jank_cause_queuing_delay.out'))
-
-  def test_scroll_jank_cause_queuing_delay_restricted(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
-  process_name,
-  thread_name,
-  trace_id,
-  jank,
-  dur_overlapping_ns,
-  restricted_metric_name
-FROM scroll_jank_cause_queuing_delay
-WHERE trace_id = 2918 OR trace_id = 2926
-ORDER BY trace_id ASC, ts ASC;
-""",
-        out=Path('scroll_jank_cause_queuing_delay_restricted.out'))
-
-  def test_scroll_jank_cause_queuing_delay_general_validation(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
-  COUNT(*) AS total,
-  (
-    SELECT DISTINCT
-      (avg_no_jank_dur_overlapping_ns)
-    FROM scroll_jank_cause_queuing_delay
-    WHERE
-      location = "LatencyInfo.Flow"
-      AND jank
-  ) AS janky_latency_info_non_jank_avg_dur,
-  (
-    SELECT DISTINCT
-      (avg_no_jank_dur_overlapping_ns)
-    FROM scroll_jank_cause_queuing_delay
-    WHERE
-      location = "LatencyInfo.Flow"
-      AND NOT jank
-  ) AS non_janky_latency_info_non_jank_avg_dur
-FROM (
-  SELECT
-    trace_id
-  FROM scroll_jank_cause_queuing_delay
-  GROUP BY trace_id
-);
-""",
-        out=Path('scroll_jank_cause_queuing_delay_general_validation.out'))
-
-  def test_chrome_thread_slice(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
-
-SELECT
-  EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') AS trace_id,
-  dur,
-  thread_dur
-FROM chrome_thread_slice
-WHERE
-  name = 'LatencyInfo.Flow'
-  AND EXTRACT_ARG(arg_set_id, 'chrome_latency_info.trace_id') = 2734;
-""",
-        out=Csv("""
-"trace_id","dur","thread_dur"
-2734,25000,25000
-2734,1000,2000
-2734,2000,2000
-2734,258000,171000
-2734,1000,1000
-"""))
-
-  def test_chrome_input_to_browser_intervals(self):
-    return DiffTestBlueprint(
-        trace=Path(
-            '../../data/scrolling_with_blocked_nonblocked_frames.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_input_to_browser_intervals.sql');
-
-SELECT
-  *
-FROM chrome_input_to_browser_intervals
-WHERE window_start_ts >= 60934320005158
-  AND window_start_ts <= 60934338798158;
-""",
-        out=Path('chrome_input_to_browser_intervals.out'))
-
-  def test_chrome_scroll_jank_caused_by_scheduling_test(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/fling_with_input_delay.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_scroll_jank_caused_by_scheduling.sql',
-  'dur_causes_jank_ms',
-/* dur_causes_jank_ms = */ '5');
-
-SELECT
-  full_name,
-  total_duration_ms,
-  total_thread_duration_ms,
-  count,
-  window_start_ts,
-  window_end_ts,
-  scroll_type
-FROM chrome_scroll_jank_caused_by_scheduling;
-""",
-        out=Path('chrome_scroll_jank_caused_by_scheduling_test.out'))
-
-  def test_chrome_tasks_delaying_input_processing_test(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/fling_with_input_delay.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_tasks_delaying_input_processing.sql',
-  'duration_causing_jank_ms',
- /* duration_causing_jank_ms = */ '8');
-
-SELECT
-  full_name,
-  duration_ms,
-  thread_dur_ms
-FROM chrome_tasks_delaying_input_processing;
-""",
-        out=Path('chrome_tasks_delaying_input_processing_test.out'))
-
-  def test_long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test(
-      self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/long_task_tracking_trace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_long_tasks_delaying_input_processing.sql');
-
-SELECT
-  full_name,
-  duration_ms,
-  slice_id
-FROM chrome_tasks_delaying_input_processing
-ORDER BY slice_id;
-""",
-        out=Path(
-            'long_task_tracking_trace_chrome_long_tasks_delaying_input_processing_test.out'
-        ))
-
-  def test_experimental_reliable_chrome_tasks_delaying_input_processing_test(
-      self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/fling_with_input_delay.pftrace'),
-        query="""
-SELECT RUN_METRIC(
-    'chrome/experimental_reliable_chrome_tasks_delaying_input_processing.sql',
-    'duration_causing_jank_ms', '8');
-
-SELECT
-  full_name,
-  duration_ms,
-  thread_dur_ms
-FROM chrome_tasks_delaying_input_processing;
-""",
-        out=Path(
-            'experimental_reliable_chrome_tasks_delaying_input_processing_test.out'
-        ))
-
-  def test_chrome_scroll_inputs_per_frame_test(self):
-    return DiffTestBlueprint(
-        trace=Path(
-            '../../data/scrolling_with_blocked_nonblocked_frames.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_scroll_inputs_per_frame.sql');
-
-SELECT
-  count_for_frame,
-  ts
-FROM chrome_scroll_inputs_per_frame
-WHERE ts = 60934316798158;
-""",
-        out=Csv("""
-"count_for_frame","ts"
-4,60934316798158
-"""))
-
-  def test_chrome_thread_slice_repeated(self):
-    return DiffTestBlueprint(
-        trace=Path('../track_event/track_event_counters.textproto'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_thread_slice.sql');
-
-SELECT
-  name,
-  ts,
-  dur,
-  thread_dur
-FROM chrome_thread_slice;
-""",
-        out=Csv("""
-"name","ts","dur","thread_dur"
-"event1_on_t1",1000,100,10000
-"event2_on_t1",2000,200,30000
-"event3_on_t1",2000,200,10000
-"event4_on_t1",4000,0,0
-"float_counter_on_t1",4300,0,"[NULL]"
-"float_counter_on_t1",4500,0,"[NULL]"
-"event1_on_t3",4000,100,5000
-"""))
-
-  def test_frame_times_metric(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_rendering_desktop.pftrace'),
-        query=Metric('frame_times'),
-        out=Path('frame_times_metric.out'))
-
-  def test_chrome_dropped_frames_metric(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_rendering_desktop.pftrace'),
-        query=Metric('chrome_dropped_frames'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_dropped_frames]: {
-  dropped_frame: {
-    ts: 166479338462000
-    process_name: "Renderer"
-    pid: 12743
-  }
-  dropped_frame: {
-    ts: 166479355302000
-    process_name: "Renderer"
-    pid: 12743
-  }
-}"""))
-
-  def test_chrome_long_latency_metric(self):
-    return DiffTestBlueprint(
-        trace=Path('../chrome/long_event_latency.textproto'),
-        query="""
-SELECT RUN_METRIC('experimental/chrome_long_latency.sql');
-
-SELECT * FROM long_latency_with_process_info;
-""",
-        out=Csv("""
-"ts","event_type","process_name","process_id"
-200111000,"FirstGestureScrollUpdate,GestureScrollUpdate","Renderer",1001
-200111000,"GestureScrollUpdate","Renderer",1002
-280111001,"GestureScrollUpdate","Renderer",1001
-"""))
-
-  def test_scroll_jank_mojo_simple_watcher(self):
-    return DiffTestBlueprint(
-        trace=Path('scroll_jank_mojo_simple_watcher.py'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_jank_cause_queuing_delay.sql');
-
-SELECT
-  trace_id,
-  jank,
-  dur_overlapping_ns,
-  metric_name
-FROM scroll_jank_cause_queuing_delay
-ORDER BY trace_id ASC, ts ASC;
-""",
-        out=Path('scroll_jank_mojo_simple_watcher.out'))
-
-  def test_scroll_jank_gpu_check(self):
-    return DiffTestBlueprint(
-        trace=Path('scroll_jank_gpu_check.py'),
-        query="""
-SELECT RUN_METRIC('chrome/scroll_jank.sql');
-
-SELECT ts, jank
-FROM scroll_jank
-ORDER BY ts ASC;
-""",
-        out=Csv("""
-"ts","jank"
-15000000,0
-30000000,1
-115000000,0
-"""))
-
-  def test_touch_jank(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/touch_jank.sql');
-
-SELECT
-  touch_id,
-  trace_id,
-  jank,
-  ts,
-  dur,
-  jank_budget
-FROM touch_jank;
-""",
-        out=Path('touch_jank.out'))
-
-  def test_touch_flow_event(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/touch_flow_event.sql');
-
-SELECT
-  trace_id,
-  ts,
-  dur,
-  jank,
-  step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  next_ts,
-  next_trace_id,
-  next_step
-FROM touch_flow_event
-ORDER BY touch_id, trace_id, ts;
-""",
-        out=Path('touch_flow_event.out'))
-
-  def test_touch_flow_event_queuing_delay(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_touch_gesture_scroll.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
-
-SELECT
-  trace_id,
-  jank,
-  step,
-  next_step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  queuing_time_ns
-FROM touch_flow_event_queuing_delay
-WHERE trace_id = 6915 OR trace_id = 6911 OR trace_id = 6940
-ORDER BY trace_id, ts;
-""",
-        out=Path('touch_flow_event_queuing_delay.out'))
-
-  def test_touch_jank_synth(self):
-    return DiffTestBlueprint(
-        trace=Path('touch_jank.py'),
-        query="""
-SELECT RUN_METRIC('chrome/touch_jank.sql');
-
-SELECT
-  touch_id,
-  trace_id,
-  jank,
-  ts,
-  dur,
-  jank_budget
-FROM touch_jank;
-""",
-        out=Csv("""
-"touch_id","trace_id","jank","ts","dur","jank_budget"
-87654,34577,0,0,10000000,-31333333.350000
-87654,34578,1,16000000,33000000,14666666.650000
-87654,34579,0,55000000,33000000,-8333333.350000
-"""))
-
-  def test_touch_flow_event_synth(self):
-    return DiffTestBlueprint(
-        trace=Path('touch_jank.py'),
-        query="""
-SELECT RUN_METRIC('chrome/touch_flow_event.sql');
-
-SELECT
-  trace_id,
-  ts,
-  dur,
-  jank,
-  step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  next_ts,
-  next_trace_id,
-  next_step
-FROM touch_flow_event
-ORDER BY touch_id, trace_id, ts;
-""",
-        out=Path('touch_flow_event_synth.out'))
-
-  def test_touch_flow_event_queuing_delay_synth(self):
-    return DiffTestBlueprint(
-        trace=Path('touch_jank.py'),
-        query="""
-SELECT RUN_METRIC('chrome/touch_flow_event_queuing_delay.sql');
-
-SELECT
-  trace_id,
-  jank,
-  step,
-  next_step,
-  ancestor_end,
-  maybe_next_ancestor_ts,
-  queuing_time_ns
-FROM touch_flow_event_queuing_delay
-ORDER BY trace_id, ts;
-""",
-        out=Path('touch_flow_event_queuing_delay_synth.out'))
-
-  def test_memory_snapshot_general_validation(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
-        query="""
-SELECT
-  (
-    SELECT COUNT(*) FROM memory_snapshot
-  ) AS total_snapshots,
-  (
-    SELECT COUNT(*) FROM process
-  ) AS total_processes,
-  (
-    SELECT COUNT(*) FROM process_memory_snapshot
-  ) AS total_process_snapshots,
-  (
-    SELECT COUNT(*) FROM memory_snapshot_node
-  ) AS total_nodes,
-  (
-    SELECT COUNT(*) FROM memory_snapshot_edge
-  ) AS total_edges,
-  (
-    SELECT COUNT(DISTINCT args.id)
-    FROM args
-    JOIN memory_snapshot_node
-      ON args.arg_set_id = memory_snapshot_node.arg_set_id
-  ) AS total_node_args,
-  (
-    SELECT COUNT(*) FROM profiler_smaps
-    JOIN memory_snapshot ON timestamp = ts
-  ) AS total_smaps;
-""",
-        out=Path('memory_snapshot_general_validation.out'))
-
-  def test_memory_snapshot_os_dump_events(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
-        query="""
-SELECT
-  p.upid,
-  pid,
-  p.name,
-  timestamp,
-  detail_level,
-  pf.value AS private_footprint_kb,
-  prs.value AS peak_resident_set_kb,
-  EXTRACT_ARG(p.arg_set_id, 'is_peak_rss_resettable') AS is_peak_rss_resettable
-FROM process p
-LEFT JOIN memory_snapshot
-LEFT JOIN (
-  SELECT id, upid
-  FROM process_counter_track
-  WHERE name = 'chrome.private_footprint_kb'
-  ) AS pct_pf
-  ON p.upid = pct_pf.upid
-LEFT JOIN counter pf ON timestamp = pf.ts AND pct_pf.id = pf.track_id
-LEFT JOIN (
-  SELECT id, upid
-  FROM process_counter_track
-  WHERE name = 'chrome.peak_resident_set_kb'
-  ) AS pct_prs
-  ON p.upid = pct_prs.upid
-LEFT JOIN counter prs ON timestamp = prs.ts AND pct_prs.id = prs.track_id
-ORDER BY timestamp;
-""",
-        out=Path('memory_snapshot_os_dump_events.out'))
-
-  def test_memory_snapshot_chrome_dump_events(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
-        query="""
-SELECT
-  pms.id AS process_snapshot_id,
-  upid,
-  snapshot_id,
-  timestamp,
-  detail_level
-FROM memory_snapshot ms
-LEFT JOIN process_memory_snapshot pms
-  ON ms.id = pms.snapshot_id;
-""",
-        out=Path('memory_snapshot_chrome_dump_events.out'))
-
-  def test_memory_snapshot_nodes(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
-        query="""
-SELECT
-  id,
-  process_snapshot_id,
-  parent_node_id,
-  path,
-  size,
-  effective_size
-FROM memory_snapshot_node
-LIMIT 20;
-""",
-        out=Path('memory_snapshot_nodes.out'))
-
-  def test_memory_snapshot_edges(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
-        query="""
-SELECT
-  id,
-  source_node_id,
-  target_node_id,
-  importance
-FROM memory_snapshot_edge
-LIMIT 20;
-""",
-        out=Path('memory_snapshot_edges.out'))
-
-  def test_memory_snapshot_node_args(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
-        query="""
-SELECT
-  node.id AS node_id,
-  key,
-  value_type,
-  int_value,
-  string_value
-FROM memory_snapshot_node node
-JOIN args ON node.arg_set_id = args.arg_set_id
-LIMIT 20;
-""",
-        out=Path('memory_snapshot_node_args.out'))
-
-  def test_memory_snapshot_smaps(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_memory_snapshot.pftrace'),
-        query="""
-SELECT
-  process.upid,
-  process.name,
-  smap.ts,
-  path,
-  size_kb,
-  private_dirty_kb,
-  swap_kb,
-  file_name,
-  start_address,
-  module_timestamp,
-  module_debugid,
-  module_debug_path,
-  protection_flags,
-  private_clean_resident_kb,
-  shared_dirty_resident_kb,
-  shared_clean_resident_kb,
-  locked_kb,
-  proportional_resident_kb
-FROM process
-JOIN profiler_smaps smap ON process.upid = smap.upid
-JOIN memory_snapshot ms ON ms.timestamp = smap.ts
-LIMIT 20;
-""",
-        out=Path('memory_snapshot_smaps.out'))
-
-  def test_combined_rail_modes(self):
-    return DiffTestBlueprint(
-        trace=Path('combined_rail_modes.py'),
-        query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM combined_overall_rail_slices;
-""",
-        out=Csv("""
-"id","ts","dur","rail_mode"
-1,0,10000,"response"
-2,10000,25000,"animation"
-3,35000,10000,"background"
-"""))
-
-  def test_cpu_time_by_combined_rail_mode(self):
-    return DiffTestBlueprint(
-        trace=Path('cpu_time_by_combined_rail_mode.py'),
-        query="""
-SELECT RUN_METRIC('chrome/cpu_time_by_rail_mode.sql');
-SELECT * FROM cpu_time_by_rail_mode;
-""",
-        out=Csv("""
-"id","ts","dur","rail_mode","cpu_dur"
-1,0,10000,"response",26000
-2,10000,20000,"animation",20000
-3,30000,5000,"background",8000
-4,35000,10000,"animation",21000
-5,45000,10000,"background",1000
-"""))
-
-  def test_actual_power_by_combined_rail_mode(self):
-    return DiffTestBlueprint(
-        trace=Path('actual_power_by_combined_rail_mode.py'),
-        query="""
-SELECT RUN_METRIC('chrome/actual_power_by_rail_mode.sql');
-SELECT * FROM real_power_by_rail_mode;
-""",
-        out=Csv("""
-"id","ts","dur","rail_mode","subsystem","joules","drain_w"
-1,0,10000000,"response","cellular",0.000000,0.000000
-1,0,10000000,"response","cpu_little",0.000140,0.014000
-2,10000000,20000000,"animation","cellular",0.000350,0.017500
-2,10000000,20000000,"animation","cpu_little",0.000140,0.007000
-3,30000000,5000000,"background","cellular",0.000018,0.003500
-3,30000000,5000000,"background","cpu_little",0.000007,0.001400
-4,35000000,10000000,"animation","cellular",0.000021,0.002100
-4,35000000,10000000,"animation","cpu_little",0.000070,0.007000
-5,45000000,10000000,"background","cellular",0.000003,0.000350
-5,45000000,10000000,"background","cpu_little",0.000070,0.007000
-"""))
-
-  def test_estimated_power_by_combined_rail_mode(self):
-    return DiffTestBlueprint(
-        trace=Path('estimated_power_by_combined_rail_mode.py'),
-        query="""
-SELECT RUN_METRIC('chrome/estimated_power_by_rail_mode.sql');
-SELECT * FROM power_by_rail_mode;
-""",
-        out=Csv("""
-"id","ts","dur","rail_mode","mas","ma"
-1,0,10000000,"response",0.554275,55.427500
-2,10000000,20000000,"animation",0.284850,14.242500
-3,30000000,5000000,"background",0.076233,15.246667
-4,35000000,10000000,"animation",0.536850,53.685000
-5,45000000,10000000,"background",0.071580,7.158000
-"""))
-
-  def test_modified_rail_modes(self):
-    return DiffTestBlueprint(
-        trace=Path('modified_rail_modes.py'),
-        query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
-        out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1950000000,"foreground_idle"
-4,2950000000,333333324,"animation"
-5,3283333324,216666676,"foreground_idle"
-6,3500000000,1000000000,"background"
-"""))
-
-  def test_modified_rail_modes_no_vsyncs(self):
-    return DiffTestBlueprint(
-        trace=Path('modified_rail_modes_no_vsyncs.py'),
-        query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
-        out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,2500000000,"foreground_idle"
-4,3500000000,1000000000,"background"
-"""))
-
-  def test_modified_rail_modes_with_input(self):
-    return DiffTestBlueprint(
-        trace=Path('modified_rail_modes_with_input.py'),
-        query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
-        out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1950000000,"foreground_idle"
-4,2950000000,50000000,"animation"
-5,3000000000,66666674,"response"
-6,3066666674,216666650,"animation"
-7,3283333324,216666676,"foreground_idle"
-8,3500000000,1000000000,"background"
-"""))
-
-  def test_modified_rail_modes_long(self):
-    return DiffTestBlueprint(
-        trace=Path('modified_rail_modes_long.py'),
-        query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
-        out=Csv("""
-"id","ts","dur","mode"
-2,0,1000000000,"response"
-3,1000000000,1,"background"
-"""))
-
-  def test_modified_rail_modes_extra_long(self):
-    return DiffTestBlueprint(
-        trace=Path('modified_rail_modes_extra_long.py'),
-        query="""
-SELECT RUN_METRIC('chrome/rail_modes.sql');
-SELECT * FROM modified_rail_slices;
-""",
-        out=Csv("""
-"id","ts","dur","mode"
-"""))
-
-  def test_chrome_processes(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT pid, name, process_type FROM chrome_process;
-""",
-        out=Csv("""
-"pid","name","process_type"
-18250,"Renderer","Renderer"
-17547,"Browser","Browser"
-18277,"GPU Process","Gpu"
-17578,"Browser","Browser"
-"""))
-
-  def test_chrome_processes_android_systrace(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_android_systrace.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT pid, name, process_type FROM chrome_process;
-""",
-        out=Path('chrome_processes_android_systrace.out'))
-
-  def test_chrome_threads(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT tid, name, is_main_thread, canonical_name
-FROM chrome_thread
-ORDER BY tid, name;
-""",
-        out=Path('chrome_threads.out'))
-
-  def test_chrome_threads_android_systrace(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_android_systrace.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_processes.sql');
-SELECT tid, name, is_main_thread, canonical_name
-FROM chrome_thread
-ORDER BY tid, name;
-""",
-        out=Path('chrome_threads_android_systrace.out'))
-
-  def test_chrome_processes_type(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT pid, name, string_value AS chrome_process_type
-FROM
-  process
-JOIN
-  (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
-  ON
-    process.arg_set_id = chrome_process_args.arg_set_id
-ORDER BY pid;
-""",
-        out=Csv("""
-"pid","name","chrome_process_type"
-17547,"Browser","Browser"
-17578,"Browser","Browser"
-18250,"Renderer","Renderer"
-18277,"GPU Process","Gpu"
-"""))
-
-  def test_chrome_processes_type_android_systrace(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_android_systrace.pftrace'),
-        query="""
-SELECT pid, name, string_value AS chrome_process_type
-FROM
-  process
-JOIN
-  (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
-  ON
-    process.arg_set_id = chrome_process_args.arg_set_id
-ORDER BY pid;
-""",
-        out=Path('chrome_processes_type_android_systrace.out'))
-
-  def test_track_with_chrome_process(self):
-    return DiffTestBlueprint(
-        trace=Path('track_with_chrome_process.textproto'),
-        query="""
-SELECT pid, name, string_value AS chrome_process_type
-FROM
-  process
-JOIN
-  (SELECT * FROM args WHERE key = "chrome.process_type") chrome_process_args
-  ON
-    process.arg_set_id = chrome_process_args.arg_set_id
-ORDER BY pid;
-""",
-        out=Csv("""
-"pid","name","chrome_process_type"
-5,"p5","[NULL]"
-"""))
-
-  def test_chrome_histogram_hashes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_histogram_hashes.textproto'),
-        query=Metric('chrome_histogram_hashes'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_histogram_hashes]: {
-  hash: 10
-  hash: 20
-}
-"""))
-
-  def test_chrome_user_event_hashes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_user_event_hashes.textproto'),
-        query=Metric('chrome_user_event_hashes'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_user_event_hashes]: {
-  action_hash: 10
-  action_hash: 20
-}
-
-"""))
-
-  def test_chrome_performance_mark_hashes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_performance_mark_hashes.textproto'),
-        query=Metric('chrome_performance_mark_hashes'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_performance_mark_hashes]: {
-  site_hash: 10
-  site_hash: 20
-  mark_hash: 100
-  mark_hash: 200
-}
-"""))
-
-  def test_chrome_reliable_range(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-12,"First slice for utid=2","[NULL]",2
-"""))
-
-  def test_chrome_reliable_range_cropping(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range_cropping.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-10000,"Range of interest packet","[NULL]",2
-"""))
-
-  def test_chrome_reliable_range_missing_processes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range_missing_processes.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing process data for upid=2",2,1
-"""))
-
-  def test_chrome_reliable_range_missing_browser_main(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range_missing_browser_main.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing main thread for upid=1",1,1
-"""))
-
-  def test_chrome_reliable_range_missing_renderer_main(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range_missing_renderer_main.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing main thread for upid=1",1,1
-"""))
-
-  def test_chrome_reliable_range_non_chrome_process(self):
-    return DiffTestBlueprint(
-        # We need a trace with a large number of non-chrome slices, so that the
-        # reliable range is affected by their filtering.
-        trace=Path('../../data/example_android_trace_30s.pb'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-  "start","reason","debug_limiting_upid","debug_limiting_utid"
-  0,"[NULL]","[NULL]","[NULL]"
-  """))
-
-  def test_chrome_slice_names(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_slice_names.textproto'),
-        query=Metric('chrome_slice_names'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_slice_names]: {
-  chrome_version_code: 123
-  slice_name: "Looper.Dispatch: class1"
-  slice_name: "name2"
-}
-"""))
-
-  def test_chrome_tasks(self):
-    return DiffTestBlueprint(
-        trace=Path(
-            '../../data/chrome_page_load_all_categories_not_extended.pftrace.gz'
-        ),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
-        out=Path('chrome_tasks.out'))
-
-  def test_top_level_java_choreographer_slices_top_level_java_chrome_tasks_test(
-      self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/top_level_java_choreographer_slices'),
-        query="""
-SELECT RUN_METRIC(
-  'chrome/chrome_tasks_template.sql',
-  'slice_table_name', 'slice',
-  'function_prefix', ''
-);
-
-SELECT
-  full_name,
-  task_type
-FROM chrome_tasks
-WHERE category = "toplevel,Java"
-AND ts < 263904000000000
-GROUP BY full_name, task_type;
-""",
-        out=Path(
-            'top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out'
-        ))
-
-  def test_chrome_stack_samples_for_task_test(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_stack_traces_symbolized_trace.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
-    'target_duration_ms', '0.000001',
-    'thread_name', '"CrBrowserMain"',
-    'task_name', '"sendTouchEvent"');
-
-SELECT
-  sample.description,
-  sample.ts,
-  sample.depth
-FROM chrome_stack_samples_for_task sample
-JOIN (
-    SELECT
-      ts,
-      dur
-    FROM slice
-    WHERE ts = 696373965001470
-) test_slice
-ON sample.ts >= test_slice.ts
-  AND sample.ts <= test_slice.ts + test_slice.dur
-ORDER BY sample.ts, sample.depth;
-""",
-        out=Path('chrome_stack_samples_for_task_test.out'))
-
-  def test_unsymbolized_args(self):
-    return DiffTestBlueprint(
-        trace=Path('unsymbolized_args.textproto'),
-        query=Metric('chrome_unsymbolized_args'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_unsymbolized_args]: {
-  args {
-     module: "/liblib.so"
-     build_id: "6275696c642d6964"
-     address: 123
-     google_lookup_id: "6275696c642d6964"
-   }
-   args {
-     module: "/libmonochrome_64.so"
-     build_id: "7f0715c286f8b16c10e4ad349cda3b9b56c7a773"
-     address: 234
-     google_lookup_id: "c215077ff8866cb110e4ad349cda3b9b0"
-   }
-}"""))
-
-  def test_async_trace_1_count_slices(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/async-trace-1.json'),
-        query="""
-SELECT COUNT(1) FROM slice;
-""",
-        out=Csv("""
-"COUNT(1)"
-16
-"""))
-
-  def test_async_trace_2_count_slices(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/async-trace-2.json'),
-        query="""
-SELECT COUNT(1) FROM slice;
-""",
-        out=Csv("""
-"COUNT(1)"
-35
-"""))
-
-  def test_chrome_args_class_names(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_args_class_names.textproto'),
-        query=Metric('chrome_args_class_names'),
-        out=TextProto(r"""
-
-[perfetto.protos.chrome_args_class_names] {
-  class_names_per_version {
-    class_name: "abc"
-    class_name: "def"
-    class_name: "ghi"
-    class_name: "jkl"
-  }
-}
-"""))
-
-  def test_chrome_log_message(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_log_message.textproto'),
-        query="""
-SELECT utid, tag, msg FROM android_logs;
-""",
-        out=Csv("""
-"utid","tag","msg"
-1,"foo.cc:123","log message"
-"""))
-
-  def test_chrome_log_message_args(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_log_message.textproto'),
-        query=Path('chrome_log_message_args_test.sql'),
-        out=Csv("""
-"log_message","function_name","file_name","line_number"
-"log message","func","foo.cc",123
-"""))
-
-  def test_chrome_missing_processes_default_trace(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT upid, pid, reliable_from
-FROM
-  experimental_missing_chrome_processes
-JOIN
-  process
-  USING(upid)
-ORDER BY upid;
-""",
-        out=Csv("""
-"upid","pid","reliable_from"
-"""))
-
-  def test_chrome_missing_processes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_missing_processes.textproto'),
-        query="""
-SELECT upid, pid, reliable_from
-FROM
-  experimental_missing_chrome_processes
-JOIN
-  process
-  USING(upid)
-ORDER BY upid;
-""",
-        out=Csv("""
-"upid","pid","reliable_from"
-2,100,1000000000
-3,1000,"[NULL]"
-"""))
-
-  def test_chrome_missing_processes_args(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_missing_processes.textproto'),
-        query="""
-SELECT arg_set_id, key, int_value
-FROM
-  slice
-JOIN
-  args
-  USING(arg_set_id)
-ORDER BY arg_set_id, key;
-""",
-        out=Csv("""
-"arg_set_id","key","int_value"
-2,"chrome_active_processes.pid[0]",10
-2,"chrome_active_processes.pid[1]",100
-2,"chrome_active_processes.pid[2]",1000
-"""))
-
-  def test_chrome_missing_processes_2(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_missing_processes_extension.textproto'),
-        query="""
-SELECT upid, pid, reliable_from
-FROM
-  experimental_missing_chrome_processes
-JOIN
-  process
-  USING(upid)
-ORDER BY upid;
-""",
-        out=Csv("""
-"upid","pid","reliable_from"
-2,100,1000000000
-3,1000,"[NULL]"
-"""))
-
-  def test_chrome_missing_processes_extension_args(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_missing_processes_extension.textproto'),
-        query="""
-SELECT arg_set_id, key, int_value
-FROM
-  slice
-JOIN
-  args
-  USING(arg_set_id)
-ORDER BY arg_set_id, key;
-""",
-        out=Csv("""
-"arg_set_id","key","int_value"
-2,"active_processes.pid[0]",10
-2,"active_processes.pid[1]",100
-2,"active_processes.pid[2]",1000
-"""))
-
-  def test_chrome_custom_navigation_tasks(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_custom_navigation_trace.gz'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-WHERE full_name GLOB 'FrameHost::BeginNavigation*'
-  OR full_name GLOB 'FrameHost::DidCommitProvisionalLoad*'
-  OR full_name GLOB 'FrameHost::DidCommitSameDocumentNavigation*'
-  OR full_name GLOB 'FrameHost::DidStopLoading*'
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
-        out=Csv("""
-"full_name","task_type","count"
-"FrameHost::BeginNavigation (SUBFRAME)","navigation_task",5
-"FrameHost::DidStopLoading (SUBFRAME)","navigation_task",3
-"FrameHost::BeginNavigation (PRIMARY_MAIN_FRAME)","navigation_task",1
-"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
-"""))
-
-  def test_proto_content(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/chrome_scroll_without_vsync.pftrace'),
-        query="""
-SELECT path, SUM(total_size) as total_size
-FROM experimental_proto_content as content 
-JOIN experimental_proto_path as frame ON content.path_id = frame.id
-GROUP BY path
-ORDER BY total_size DESC, path
-LIMIT 10;
-""",
-        out=Path('proto_content.out'))
-
-  def test_chrome_scroll_jank_v2(self):
-    return DiffTestBlueprint(
-        trace=Path('../../data/event_latency_with_args.perfetto-trace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_scroll_jank_v2.sql');
-
-SELECT
-  scroll_processing_ms,
-  scroll_jank_processing_ms,
-  scroll_jank_percentage
-FROM chrome_scroll_jank_v2_output;
-""",
-        out=Path('chrome_scroll_jank_v2.out'))
diff --git a/test/trace_processor/diff_tests/chrome/tests_general.py b/test/trace_processor/diff_tests/chrome/tests_general.py
deleted file mode 100644
index 99938b8..0000000
--- a/test/trace_processor/diff_tests/chrome/tests_general.py
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class ChromeGeneral(DiffTestModule):
-
-  def test_chrome_histogram_hashes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_histogram_hashes.textproto'),
-        query=Metric('chrome_histogram_hashes'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_histogram_hashes]: {
-  hash: 10
-  hash: 20
-}
-"""))
-
-  def test_chrome_user_event_hashes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_user_event_hashes.textproto'),
-        query=Metric('chrome_user_event_hashes'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_user_event_hashes]: {
-  action_hash: 10
-  action_hash: 20
-}
-
-"""))
-
-  def test_chrome_performance_mark_hashes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_performance_mark_hashes.textproto'),
-        query=Metric('chrome_performance_mark_hashes'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_performance_mark_hashes]: {
-  site_hash: 10
-  site_hash: 20
-  mark_hash: 100
-  mark_hash: 200
-}
-"""))
-
-  def test_chrome_reliable_range(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-12,"First slice for utid=2","[NULL]",2
-"""))
-
-  def test_chrome_reliable_range_cropping(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range_cropping.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-10000,"Range of interest packet","[NULL]",2
-"""))
-
-  def test_chrome_reliable_range_missing_processes(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_reliable_range_missing_processes.textproto'),
-        query=Path('chrome_reliable_range_test.sql'),
-        out=Csv("""
-"start","reason","debug_limiting_upid","debug_limiting_utid"
-1011,"Missing process data for upid=2",2,1
-"""))
-
-  def test_chrome_slice_names(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_slice_names.textproto'),
-        query=Metric('chrome_slice_names'),
-        out=TextProto(r"""
-[perfetto.protos.chrome_slice_names]: {
-  chrome_version_code: 123
-  slice_name: "Looper.Dispatch: class1"
-  slice_name: "name2"
-}
-"""))
-
-  def test_chrome_tasks(self):
-    return DiffTestBlueprint(
-        trace=DataPath(
-            'chrome_page_load_all_categories_not_extended.pftrace.gz'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
-        out=Path('chrome_tasks.out'))
-
-  def test_top_level_java_choreographer_slices_top_level_java_chrome_tasks(
-      self):
-    return DiffTestBlueprint(
-        trace=DataPath('top_level_java_choreographer_slices'),
-        query="""
-SELECT RUN_METRIC(
-  'chrome/chrome_tasks_template.sql',
-  'slice_table_name', 'slice',
-  'function_prefix', ''
-);
-
-SELECT
-  full_name,
-  task_type
-FROM chrome_tasks
-WHERE category = "toplevel,Java"
-AND ts < 263904000000000
-GROUP BY full_name, task_type;
-""",
-        out=Path(
-            'top_level_java_choreographer_slices_top_level_java_chrome_tasks_test.out'
-        ))
-
-  def test_chrome_stack_samples_for_task(self):
-    return DiffTestBlueprint(
-        trace=DataPath('chrome_stack_traces_symbolized_trace.pftrace'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_stack_samples_for_task.sql',
-    'target_duration_ms', '0.000001',
-    'thread_name', '"CrBrowserMain"',
-    'task_name', '"sendTouchEvent"');
-
-SELECT
-  sample.description,
-  sample.ts,
-  sample.depth
-FROM chrome_stack_samples_for_task sample
-JOIN (
-    SELECT
-      ts,
-      dur
-    FROM slice
-    WHERE ts = 696373965001470
-) test_slice
-ON sample.ts >= test_slice.ts
-  AND sample.ts <= test_slice.ts + test_slice.dur
-ORDER BY sample.ts, sample.depth;
-""",
-        out=Path('chrome_stack_samples_for_task_test.out'))
-
-  def test_chrome_log_message(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_log_message.textproto'),
-        query="""
-SELECT utid, tag, msg FROM android_logs;
-""",
-        out=Csv("""
-"utid","tag","msg"
-1,"foo.cc:123","log message"
-"""))
-
-  def test_chrome_log_message_args(self):
-    return DiffTestBlueprint(
-        trace=Path('chrome_log_message.textproto'),
-        query=Path('chrome_log_message_args_test.sql'),
-        out=Csv("""
-"log_message","function_name","file_name","line_number"
-"log message","func","foo.cc",123
-"""))
-
-  def test_chrome_custom_navigation_tasks(self):
-    return DiffTestBlueprint(
-        trace=DataPath('chrome_custom_navigation_trace.gz'),
-        query="""
-SELECT RUN_METRIC('chrome/chrome_tasks.sql');
-
-SELECT full_name, task_type, count() AS count
-FROM chrome_tasks
-WHERE full_name GLOB 'FrameHost::BeginNavigation*'
-  OR full_name GLOB 'FrameHost::DidCommitProvisionalLoad*'
-  OR full_name GLOB 'FrameHost::DidCommitSameDocumentNavigation*'
-  OR full_name GLOB 'FrameHost::DidStopLoading*'
-GROUP BY full_name, task_type
-ORDER BY count DESC
-LIMIT 50;
-""",
-        out=Csv("""
-"full_name","task_type","count"
-"FrameHost::BeginNavigation (SUBFRAME)","navigation_task",5
-"FrameHost::DidStopLoading (SUBFRAME)","navigation_task",3
-"FrameHost::BeginNavigation (PRIMARY_MAIN_FRAME)","navigation_task",1
-"FrameHost::DidCommitProvisionalLoad (SUBFRAME)","navigation_task",1
-"""))
-
-  def test_proto_content(self):
-    return DiffTestBlueprint(
-        trace=DataPath('chrome_scroll_without_vsync.pftrace'),
-        query=Path('proto_content_test.sql'),
-        out=Path('proto_content.out'))
diff --git a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
index 62833c2..c8594d1 100644
--- a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
@@ -540,3 +540,18 @@
         5679,60000000,40000000,60000000,90000000
         5680,120000000,70000000,120000000,-1
         """))
+
+  # TODO(283531332): reenable this test after fixing.
+  # def test_chrome_scroll_jank_v2(self):
+  #   return DiffTestBlueprint(
+  #       trace=Path('../../data/event_latency_with_args.perfetto-trace'),
+  #       query="""
+  #       SELECT RUN_METRIC('chrome/chrome_scroll_jank_v2.sql');
+
+  #       SELECT
+  #         scroll_processing_ms,
+  #         scroll_jank_processing_ms,
+  #         scroll_jank_percentage
+  #       FROM chrome_scroll_jank_v2_output;
+  #       """,
+  #       out=Path('chrome_scroll_jank_v2.out'))
diff --git a/test/trace_processor/diff_tests/cros/tests_general.py b/test/trace_processor/diff_tests/cros/tests_general.py
deleted file mode 100644
index 5f012db..0000000
--- a/test/trace_processor/diff_tests/cros/tests_general.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class CrosGeneral(DiffTestModule):
-
-  def test_cros_ec_sensorhub_data(self):
-    return DiffTestBlueprint(
-        trace=Path('cros_ec_sensorhub_data.textproto'),
-        query="""
-SELECT
-  t.name,
-  c.ts,
-  c.value,
-  EXTRACT_ARG(c.arg_set_id, 'ec_num') AS ec_num,
-  EXTRACT_ARG(c.arg_set_id, 'ec_delta') AS ec_delta,
-  EXTRACT_ARG(c.arg_set_id, 'sample_ts') AS sample_ts
-FROM counter c
-JOIN track t
-  ON c.track_id = t.id
-WHERE t.name = 'cros_ec.cros_ec_sensorhub_data.0';
-""",
-        out=Path('cros_ec_sensorhub_data.out'))
diff --git a/test/trace_processor/diff_tests/span_join/tests.py b/test/trace_processor/diff_tests/span_join/tests.py
deleted file mode 100644
index e675aff..0000000
--- a/test/trace_processor/diff_tests/span_join/tests.py
+++ /dev/null
@@ -1,411 +0,0 @@
-#!/usr/bin/env python3
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License a
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from python.generators.diff_tests.testing import Path, DataPath, Metric
-from python.generators.diff_tests.testing import Csv, Json, TextProto
-from python.generators.diff_tests.testing import DiffTestBlueprint
-from python.generators.diff_tests.testing import DiffTestModule
-
-
-class DiffTestModule_SpanJoin(DiffTestModule):
-
-  def test_span_outer_join(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query=Path('span_outer_join_test.sql'),
-        out=Path('span_outer_join.out'))
-
-  def test_span_outer_join_empty(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES (500, 100, 10);
-
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part,
-                                              t2 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part"
-500,100,10
-"""))
-
-  def test_span_outer_join_unpartitioned_empty(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur"
-"""))
-
-  def test_span_outer_join_unpartitioned_left_empty(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur"
-100,400
-500,50
-600,100
-"""))
-
-  def test_span_outer_join_unpartitioned_right_empty(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1, t2);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur"
-100,400
-500,50
-600,100
-"""))
-
-  def test_span_outer_join_mixed(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query=Path('span_outer_join_mixed_test.sql'),
-        out=Path('span_outer_join_mixed.out'))
-
-  def test_span_outer_join_mixed_empty(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part"
-"""))
-
-  def test_span_outer_join_mixed_left_empty(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part"
-"""))
-
-  def test_span_outer_join_mixed_left_empty_rev(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(100, 400, 0),
-(100, 50, 1),
-(600, 100, 1);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part"
-100,400,0
-100,50,1
-600,100,1
-"""))
-
-  def test_span_outer_join_mixed_right_empty(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  b BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(100, 400, 0),
-(100, 50, 1),
-(600, 100, 1);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t1 PARTITIONED part, t2);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part","b"
-100,400,0,"[NULL]"
-100,50,1,"[NULL]"
-600,100,1,"[NULL]"
-"""))
-
-  def test_span_outer_join_mixed_right_empty_rev(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  b BIGINT,
-  PRIMARY KEY (ts)
-) WITHOUT ROWID;
-
-INSERT INTO t2(ts, dur)
-VALUES
-(100, 400),
-(500, 50),
-(600, 100);
-
-CREATE VIRTUAL TABLE sp USING span_outer_join(t2, t1 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part","b"
-"""))
-
-  def test_span_outer_join_mixed_2(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query=Path('span_outer_join_mixed_test.sql'),
-        out=Path('span_outer_join_mixed.out'))
-
-  def test_span_left_join(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query=Path('span_left_join_test.sql'),
-        out=Path('span_left_join.out'))
-
-  def test_span_left_join_unpartitioned(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query=Path('span_left_join_unpartitioned_test.sql'),
-        out=Path('span_left_join_unpartitioned.out'))
-
-  def test_span_left_join_left_unpartitioned(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query=Path('span_left_join_left_unpartitioned_test.sql'),
-        out=Path('span_left_join_left_unpartitioned.out'))
-
-  def test_span_left_join_left_partitioned(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query=Path('span_left_join_left_partitioned_test.sql'),
-        out=Path('span_left_join_left_partitioned.out'))
-
-  def test_span_left_join_empty_right(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES
-(500, 500, 100);
-
-CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
-                                             t2 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part"
-500,500,100
-"""))
-
-  def test_span_left_join_unordered_android_sched_and_ps(self):
-    return DiffTestBlueprint(
-        trace=Path('../common/synth_1.py'),
-        query="""
-CREATE TABLE t1(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-CREATE TABLE t2(
-  ts BIGINT,
-  dur BIGINT,
-  part BIGINT,
-  PRIMARY KEY (part, ts)
-) WITHOUT ROWID;
-
-INSERT INTO t1(ts, dur, part)
-VALUES (500, 100, 10);
-
-INSERT INTO t2(ts, dur, part)
-VALUES (500, 100, 5);
-
-CREATE VIRTUAL TABLE sp USING span_left_join(t1 PARTITIONED part,
-                                             t2 PARTITIONED part);
-
-SELECT * FROM sp;
-""",
-        out=Csv("""
-"ts","dur","part"
-500,100,10
-"""))
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 5317914..179ac14 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -103,6 +103,7 @@
     '//protos/perfetto/trace:perfetto_trace_protos',
     '//src/trace_processor:demangle',
     '//src/trace_processor:trace_processor_shell',
+    '//src/traced/service:traced',
 ]
 
 target_vendor_available = [
diff --git a/ui/src/controller/track_decider.ts b/ui/src/controller/track_decider.ts
index 4882400..aadb889 100644
--- a/ui/src/controller/track_decider.ts
+++ b/ui/src/controller/track_decider.ts
@@ -198,14 +198,39 @@
     return 'Unknown';
   }
 
+  async guessCpuSizes(): Promise<Map<number, string>> {
+    const cpuToSize = new Map<number, string>();
+    await this.engine.query(`
+      SELECT IMPORT('common.cpus');
+    `);
+    const result = await this.engine.query(`
+      SELECT cpu, GUESS_CPU_SIZE(cpu) as size FROM cpu_counter_track;
+    `);
+
+    const it = result.iter({
+      cpu: NUM,
+      size: STR,
+    });
+
+    for (; it.valid(); it.next()) {
+      cpuToSize.set(it.cpu, it.size);
+    }
+
+    return cpuToSize;
+  }
+
   async addCpuSchedulingTracks(): Promise<void> {
     const cpus = await this.engine.getCpus();
+    const cpuToSize = await this.guessCpuSizes();
+
     for (const cpu of cpus) {
+      const size = cpuToSize.get(cpu);
+      const name = size === undefined ? `Cpu ${cpu}` : `Cpu ${cpu} (${size})`;
       this.tracksToAdd.push({
         engineId: this.engineId,
         kind: CPU_SLICE_TRACK_KIND,
         trackSortKey: PrimaryTrackSortKey.ORDINARY_TRACK,
-        name: `Cpu ${cpu}`,
+        name,
         trackGroup: SCROLLING_TRACK_GROUP,
         config: {
           cpu,
diff --git a/ui/src/tracks/debug/add_debug_track_menu.ts b/ui/src/tracks/debug/add_debug_track_menu.ts
index d14bfd8..32bc871 100644
--- a/ui/src/tracks/debug/add_debug_track_menu.ts
+++ b/ui/src/tracks/debug/add_debug_track_menu.ts
@@ -89,7 +89,7 @@
         m(FormLabel,
           {for: 'track_name',
           },
-          'Name'),
+          'Track name'),
         m(TextInput, {
           id: 'track_name',
           onkeydown: (e: KeyboardEvent) => {