Merge "Show correct Android CUJ boundaries" into main
diff --git a/Android.bp b/Android.bp
index e435f99..94b84ff 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5323,6 +5323,7 @@
 genrule {
     name: "perfetto_protos_perfetto_metrics_webview_descriptor",
     srcs: [
+        ":libprotobuf-internal-descriptor-proto",
         "protos/perfetto/metrics/android/ad_services_metric.proto",
         "protos/perfetto/metrics/android/android_blocking_call.proto",
         "protos/perfetto/metrics/android/android_blocking_calls_cuj_metric.proto",
@@ -5388,7 +5389,7 @@
     tools: [
         "aprotoc",
     ],
-    cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --descriptor_set_out=$(out) $(in)",
+    cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --proto_path=external/protobuf/src --descriptor_set_out=$(out) $(in)",
     out: [
         "perfetto_protos_perfetto_metrics_webview_descriptor.bin",
     ],
@@ -11347,6 +11348,7 @@
     srcs: [
         "src/trace_processor/db/column/arrangement_overlay_unittest.cc",
         "src/trace_processor/db/column/dense_null_overlay_unittest.cc",
+        "src/trace_processor/db/column/fake_storage_unittest.cc",
         "src/trace_processor/db/column/id_storage_unittest.cc",
         "src/trace_processor/db/column/null_overlay_unittest.cc",
         "src/trace_processor/db/column/numeric_storage_unittest.cc",
diff --git a/CHANGELOG b/CHANGELOG
index b600600..99ff035 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -4,7 +4,18 @@
   Trace Processor:
     *
   UI:
-    *
+    * Add tracks to the list of searchable items.
+    * Use mipmaps to improve track query performance on large traces.
+    * Fix slow scrolling bug in ftrace explorer tab on low DPI machines.
+    * Overhaul track decider queries to improve trace load times.
+    * Add track
+    * Tidy up command names & remove some example ones.
+    * Remove arg auto-completion in pivot table.
+    * Show dominator tree views by default.
+    * Fix counter event selection off-by-one error.
+    * Add viewport control to the plugin API.
+    * Sticky track titles to improve track button accessibility in tall tracks.
+    * A handful of small bugfixes.
   SDK:
     * The TRACE_EVENT macro used to reject `const char *` event names: either
       `StaticString` or `DynamicString` needed to be specified. In the last year
diff --git a/gn/proto_library.gni b/gn/proto_library.gni
index f69d3ce..a7a77c6 100644
--- a/gn/proto_library.gni
+++ b/gn/proto_library.gni
@@ -371,7 +371,7 @@
 
         metadata = {
           proto_library_sources = invoker.sources
-          import_dirs = import_dirs_
+          proto_import_dirs = import_dirs_
           exports = get_path_info(public_deps_, "abspath")
         }
         forward_variables_from(invoker, vars_to_forward)
diff --git a/gn/standalone/proto_library.gni b/gn/standalone/proto_library.gni
index 07b8140..1a23a97 100644
--- a/gn/standalone/proto_library.gni
+++ b/gn/standalone/proto_library.gni
@@ -170,6 +170,10 @@
       ]
     }
 
+    metadata = {
+      proto_import_dirs = import_dirs
+    }
+
     if (generate_cc) {
       cc_generator_options_ = ""
       if (defined(invoker.cc_generator_options)) {
diff --git a/src/trace_processor/db/column/BUILD.gn b/src/trace_processor/db/column/BUILD.gn
index 74d1611..d53e7ae 100644
--- a/src/trace_processor/db/column/BUILD.gn
+++ b/src/trace_processor/db/column/BUILD.gn
@@ -75,6 +75,7 @@
   sources = [
     "arrangement_overlay_unittest.cc",
     "dense_null_overlay_unittest.cc",
+    "fake_storage_unittest.cc",
     "id_storage_unittest.cc",
     "null_overlay_unittest.cc",
     "numeric_storage_unittest.cc",
diff --git a/src/trace_processor/db/column/fake_storage.cc b/src/trace_processor/db/column/fake_storage.cc
index f587c77..babfb7c 100644
--- a/src/trace_processor/db/column/fake_storage.cc
+++ b/src/trace_processor/db/column/fake_storage.cc
@@ -41,6 +41,7 @@
 SingleSearchResult FakeStorageChain::SingleSearch(FilterOp,
                                                   SqlValue,
                                                   uint32_t i) const {
+  PERFETTO_CHECK(i < size_);
   switch (strategy_) {
     case kAll:
       return SingleSearchResult::kMatch;
@@ -115,37 +116,37 @@
     FilterOp,
     SqlValue,
     const OrderedIndices& indices) const {
-  if (strategy_ == kAll) {
-    return {0, indices.size};
-  }
-
-  if (strategy_ == kNone) {
-    return {};
-  }
-
-  if (strategy_ == kRange) {
-    // We are looking at intersection of |range_| and |indices_|.
-    const uint32_t* first_in_range = std::partition_point(
-        indices.data, indices.data + indices.size,
-        [this](uint32_t i) { return !range_.Contains(i); });
-    const uint32_t* first_outside_range =
-        std::partition_point(first_in_range, indices.data + indices.size,
-                             [this](uint32_t i) { return range_.Contains(i); });
-    return {static_cast<uint32_t>(std::distance(indices.data, first_in_range)),
-            static_cast<uint32_t>(
-                std::distance(indices.data, first_outside_range))};
-  }
-
-  PERFETTO_DCHECK(strategy_ == kBitVector);
-  // We are looking at intersection of |range_| and |bit_vector_|.
-  const uint32_t* first_set = std::partition_point(
-      indices.data, indices.data + indices.size,
-      [this](uint32_t i) { return !bit_vector_.IsSet(i); });
-  const uint32_t* first_non_set =
-      std::partition_point(first_set, indices.data + indices.size,
-                           [this](uint32_t i) { return bit_vector_.IsSet(i); });
-  return {static_cast<uint32_t>(std::distance(indices.data, first_set)),
+  switch (strategy_) {
+    case kAll:
+      return {0, indices.size};
+    case kNone:
+      return {};
+    case kRange: {
+      // We are looking at intersection of |range_| and |indices_|.
+      const uint32_t* first_in_range = std::partition_point(
+          indices.data, indices.data + indices.size,
+          [this](uint32_t i) { return !range_.Contains(i); });
+      const uint32_t* first_outside_range = std::partition_point(
+          first_in_range, indices.data + indices.size,
+          [this](uint32_t i) { return range_.Contains(i); });
+      return {
+          static_cast<uint32_t>(std::distance(indices.data, first_in_range)),
+          static_cast<uint32_t>(
+              std::distance(indices.data, first_outside_range))};
+    }
+    case kBitVector:
+      // We are looking at intersection of |range_| and |bit_vector_|.
+      const uint32_t* first_set = std::partition_point(
+          indices.data, indices.data + indices.size,
+          [this](uint32_t i) { return !bit_vector_.IsSet(i); });
+      const uint32_t* first_non_set = std::partition_point(
+          first_set, indices.data + indices.size,
+          [this](uint32_t i) { return bit_vector_.IsSet(i); });
+      return {
+          static_cast<uint32_t>(std::distance(indices.data, first_set)),
           static_cast<uint32_t>(std::distance(indices.data, first_non_set))};
+  }
+  PERFETTO_FATAL("For GCC");
 }
 
 void FakeStorageChain::StableSort(SortToken*, SortToken*, SortDirection) const {
diff --git a/src/trace_processor/db/column/fake_storage_unittest.cc b/src/trace_processor/db/column/fake_storage_unittest.cc
new file mode 100644
index 0000000..0bc0211
--- /dev/null
+++ b/src/trace_processor/db/column/fake_storage_unittest.cc
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "src/trace_processor/db/column/fake_storage.h"
+
+#include <cstdint>
+#include <limits>
+#include <vector>
+
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/containers/bit_vector.h"
+#include "src/trace_processor/db/column/data_layer.h"
+#include "src/trace_processor/db/column/types.h"
+#include "src/trace_processor/db/column/utils.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto::trace_processor {
+
+inline bool operator==(const Range& a, const Range& b) {
+  return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
+inline bool operator==(const BitVector& a, const BitVector& b) {
+  return a.size() == b.size() && a.CountSetBits() == b.CountSetBits();
+}
+
+namespace column {
+namespace {
+
+using testing::ElementsAre;
+using testing::IsEmpty;
+
+using Indices = DataLayerChain::Indices;
+using OrderedIndices = DataLayerChain::OrderedIndices;
+
+TEST(FakeStorage, ValidateSearchConstraints) {
+  {
+    // All passes
+    auto fake = FakeStorageChain::SearchAll(10);
+    EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+              SearchValidationResult::kOk);
+  }
+  {
+    // None passes
+    auto fake = FakeStorageChain::SearchNone(10);
+    EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+              SearchValidationResult::kOk);
+  }
+  {
+    // Index vector
+    auto fake =
+        FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3, 4, 5});
+    EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+              SearchValidationResult::kOk);
+  }
+  {
+    // BitVector
+    auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 1, 0, 1, 0});
+    EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+              SearchValidationResult::kOk);
+  }
+  {
+    // Range
+    auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+    EXPECT_EQ(fake->ValidateSearchConstraints(FilterOp::kEq, SqlValue()),
+              SearchValidationResult::kOk);
+  }
+}
+
+TEST(FakeStorage, SingleSearch) {
+  {
+    // All passes
+    auto fake = FakeStorageChain::SearchAll(10);
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 5u),
+              SingleSearchResult::kMatch);
+  }
+  {
+    // None passes
+    auto fake = FakeStorageChain::SearchNone(10);
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 5u),
+              SingleSearchResult::kNoMatch);
+  }
+  {
+    // Index vector
+    auto fake =
+        FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3, 4, 5});
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 0u),
+              SingleSearchResult::kNoMatch);
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 1u),
+              SingleSearchResult::kMatch);
+  }
+  {
+    // BitVector
+    auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 1, 0, 1, 0});
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 0),
+              SingleSearchResult::kNoMatch);
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 1u),
+              SingleSearchResult::kMatch);
+  }
+  {
+    // Range
+    auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 0),
+              SingleSearchResult::kNoMatch);
+    EXPECT_EQ(fake->SingleSearch(FilterOp::kEq, SqlValue(), 1u),
+              SingleSearchResult::kMatch);
+  }
+}
+
+TEST(FakeStorage, IndexSearchValidated) {
+  {
+    // All passes
+    Indices indices = Indices::CreateWithIndexPayloadForTesting(
+        {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+    auto fake = FakeStorageChain::SearchAll(5);
+    fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 1, 2));
+  }
+  {
+    // None passes
+    Indices indices = Indices::CreateWithIndexPayloadForTesting(
+        {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+    auto fake = FakeStorageChain::SearchNone(5);
+    fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    EXPECT_TRUE(utils::ExtractPayloadForTesting(indices).empty());
+  }
+  {
+    // BitVector
+    Indices indices = Indices::CreateWithIndexPayloadForTesting(
+        {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+    auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 1, 0, 1, 0});
+    fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
+  }
+  {
+    // Index vector
+    Indices indices = Indices::CreateWithIndexPayloadForTesting(
+        {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+    auto fake =
+        FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3});
+    fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
+  }
+  {
+    // Range
+    Indices indices = Indices::CreateWithIndexPayloadForTesting(
+        {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+    auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+    fake->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
+  }
+}
+
+TEST(FakeStorage, OrderedIndexSearchValidated) {
+  std::vector<uint32_t> table_idx{4, 3, 2, 1};
+  OrderedIndices indices{table_idx.data(), uint32_t(table_idx.size()),
+                         Indices::State::kNonmonotonic};
+  {
+    // All passes
+    auto fake = FakeStorageChain::SearchAll(5);
+    Range ret =
+        fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    EXPECT_EQ(ret, Range(0, 4));
+  }
+  {
+    // None passes
+    auto fake = FakeStorageChain::SearchNone(5);
+    Range ret =
+        fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    EXPECT_EQ(ret, Range(0, 0));
+  }
+  {
+    // BitVector
+    auto fake = FakeStorageChain::SearchSubset(5, BitVector{0, 0, 1, 1, 1});
+    Range ret =
+        fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    EXPECT_EQ(ret, Range(0, 3));
+  }
+  {
+    // Index vector
+    auto fake =
+        FakeStorageChain::SearchSubset(5, std::vector<uint32_t>{1, 2, 3});
+    Range ret =
+        fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    EXPECT_EQ(ret, Range(1, 4));
+  }
+  {
+    // Range
+    auto fake = FakeStorageChain::SearchSubset(5, Range(1, 4));
+    Range ret =
+        fake->OrderedIndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
+    EXPECT_EQ(ret, Range(1, 4));
+  }
+}
+
+}  // namespace
+}  // namespace column
+}  // namespace perfetto::trace_processor
diff --git a/src/trace_processor/db/column/range_overlay_unittest.cc b/src/trace_processor/db/column/range_overlay_unittest.cc
index a965be3..240e998 100644
--- a/src/trace_processor/db/column/range_overlay_unittest.cc
+++ b/src/trace_processor/db/column/range_overlay_unittest.cc
@@ -95,14 +95,17 @@
 TEST(RangeOverlay, IndexSearch) {
   auto fake =
       FakeStorageChain::SearchSubset(8, BitVector({0, 1, 0, 1, 0, 1, 0, 0}));
+
+  // {true, false}
   Range range(3, 5);
   RangeOverlay storage(&range);
   auto chain = storage.MakeChain(std::move(fake));
 
+  // {true, false, true}
   Indices indices = Indices::CreateWithIndexPayloadForTesting(
-      {1u, 0u, 3u}, Indices::State::kNonmonotonic);
+      {0, 1, 0}, Indices::State::kNonmonotonic);
   chain->IndexSearch(FilterOp::kGe, SqlValue::Long(0u), indices);
-  ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(1u));
+  ASSERT_THAT(utils::ExtractPayloadForTesting(indices), ElementsAre(0, 2));
 }
 
 TEST(RangeOverlay, StableSort) {
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index d71b0dc..c8a2b1a 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -768,7 +768,6 @@
   # The .proto filegroup will be added to `tool_files` of rdeps so that the
   # genrules can be sandboxed.
 
-  tool_files = set()
   for proto_dep in target.proto_deps().union(target.transitive_proto_deps()):
     tool_files.add(":" + label_to_module_name(proto_dep.name))
 
diff --git a/tools/gen_tp_table_headers.py b/tools/gen_tp_table_headers.py
index f15245e..91e4cdb 100755
--- a/tools/gen_tp_table_headers.py
+++ b/tools/gen_tp_table_headers.py
@@ -72,7 +72,8 @@
   ]
   headers: Dict[str, Header] = {}
   for table in parse_tables_from_modules(modules):
-    input_path = os.path.relpath(table.table.python_module, ROOT_DIR)
+    raw_path = table.table.python_module
+    input_path = raw_path[raw_path.rfind('/src') + 1:]
     header = headers.get(input_path, Header([]))
     header.tables.append(table)
     headers[input_path] = header
diff --git a/tools/gn_utils.py b/tools/gn_utils.py
index d0417d7..904760e 100644
--- a/tools/gn_utils.py
+++ b/tools/gn_utils.py
@@ -529,9 +529,8 @@
     return metadata.get('exports', [])
 
   def get_proto_paths(self, proto_desc):
-    # import_dirs in metadata will be available for source_set targets.
     metadata = proto_desc.get('metadata', {})
-    return metadata.get('import_dirs', [])
+    return metadata.get('proto_import_dirs', [])
 
   def get_proto_target_type(self, target: Target
                            ) -> Tuple[Optional[str], Optional[Dict]]:
diff --git a/ui/src/common/flamegraph_util.ts b/ui/src/common/flamegraph_util.ts
index 0817ebf..acf2ee8 100644
--- a/ui/src/common/flamegraph_util.ts
+++ b/ui/src/common/flamegraph_util.ts
@@ -24,7 +24,7 @@
   id: 'showHeapGraphDominatorTree',
   name: 'Show heap graph dominator tree',
   description: 'Show dominated size and objects tabs in Java heap graph view.',
-  defaultValue: false,
+  defaultValue: true,
 });
 
 export function viewingOptions(profileType: ProfileType): Array<ViewingOption> {