Merge "tp: introduce PerfettoSqlEngine intermediary"
diff --git a/Android.bp b/Android.bp
index bfd01ac..dcabf2e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -9410,6 +9410,18 @@
 // GN: //src/trace_processor/db/overlays:overlays
 filegroup {
     name: "perfetto_src_trace_processor_db_overlays_overlays",
+    srcs: [
+        "src/trace_processor/db/overlays/null_overlay.cc",
+        "src/trace_processor/db/overlays/storage_overlay.cc",
+    ],
+}
+
+// GN: //src/trace_processor/db/overlays:unittests
+filegroup {
+    name: "perfetto_src_trace_processor_db_overlays_unittests",
+    srcs: [
+        "src/trace_processor/db/overlays/null_overlay_unittest.cc",
+    ],
 }
 
 // GN: //src/trace_processor/db/storage:storage
@@ -12064,6 +12076,7 @@
         ":perfetto_src_trace_processor_containers_unittests",
         ":perfetto_src_trace_processor_db_db",
         ":perfetto_src_trace_processor_db_overlays_overlays",
+        ":perfetto_src_trace_processor_db_overlays_unittests",
         ":perfetto_src_trace_processor_db_storage_storage",
         ":perfetto_src_trace_processor_db_storage_unittests",
         ":perfetto_src_trace_processor_db_unittests",
diff --git a/BUILD b/BUILD
index 5e43586..f9a1035 100644
--- a/BUILD
+++ b/BUILD
@@ -1277,6 +1277,9 @@
 perfetto_filegroup(
     name = "src_trace_processor_db_overlays_overlays",
     srcs = [
+        "src/trace_processor/db/overlays/null_overlay.cc",
+        "src/trace_processor/db/overlays/null_overlay.h",
+        "src/trace_processor/db/overlays/storage_overlay.cc",
         "src/trace_processor/db/overlays/storage_overlay.h",
         "src/trace_processor/db/overlays/types.h",
     ],
diff --git a/protos/perfetto/common/trace_stats.proto b/protos/perfetto/common/trace_stats.proto
index 4e6d455..bbee8f9 100644
--- a/protos/perfetto/common/trace_stats.proto
+++ b/protos/perfetto/common/trace_stats.proto
@@ -20,7 +20,7 @@
 
 // Statistics for the internals of the tracing service.
 //
-// Next id: 17.
+// Next id: 19.
 message TraceStats {
   // From TraceBuffer::Stats.
   //
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index e886311..1681cd9 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -3240,7 +3240,7 @@
 
 // Statistics for the internals of the tracing service.
 //
-// Next id: 17.
+// Next id: 19.
 message TraceStats {
   // From TraceBuffer::Stats.
   //
diff --git a/src/tools/proto_merger/proto_file_serializer.cc b/src/tools/proto_merger/proto_file_serializer.cc
index dca5a07..c94a101 100644
--- a/src/tools/proto_merger/proto_file_serializer.cc
+++ b/src/tools/proto_merger/proto_file_serializer.cc
@@ -70,8 +70,11 @@
 
   std::string output;
   output += " [";
-  for (const auto& option : options) {
-    output += option.key + " = " + option.value;
+  size_t n = options.size();
+  for (size_t i = 0; i < n; i++) {
+    output += options[i].key + " = " + options[i].value;
+    if (i != n - 1)
+      output += ", ";
   }
   output += "]";
   return output;
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6ff25a9..04814a6 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -268,6 +268,7 @@
     ":top_level_unittests",
     "containers:unittests",
     "db:unittests",
+    "db/overlays:unittests",
     "db/storage:unittests",
     "importers/android_bugreport:unittests",
     "importers/common:unittests",
diff --git a/src/trace_processor/db/overlays/BUILD.gn b/src/trace_processor/db/overlays/BUILD.gn
index c1d50d8..f0b3e72 100644
--- a/src/trace_processor/db/overlays/BUILD.gn
+++ b/src/trace_processor/db/overlays/BUILD.gn
@@ -12,13 +12,30 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import("../../../../gn/perfetto_tp_tables.gni")
+import("../../../../gn/test.gni")
+
 source_set("overlays") {
   sources = [
+    "null_overlay.cc",
+    "null_overlay.h",
+    "storage_overlay.cc",
     "storage_overlay.h",
     "types.h",
   ]
   deps = [
     "../../../../gn:default_deps",
+    "../../../base",
     "../../containers",
   ]
 }
+
+perfetto_unittest_source_set("unittests") {
+  testonly = true
+  sources = [ "null_overlay_unittest.cc" ]
+  deps = [
+    ":overlays",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_and_gmock",
+  ]
+}
diff --git a/src/trace_processor/db/overlays/null_overlay.cc b/src/trace_processor/db/overlays/null_overlay.cc
new file mode 100644
index 0000000..c5f5f66
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay.cc
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/overlays/null_overlay.h"
+#include "perfetto/ext/base/flat_hash_map.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+using Range = RowMap::Range;
+
+StorageRange NullOverlay::MapToStorageRange(TableRange t_range) const {
+  uint32_t start = non_null_->CountSetBits(t_range.range.start);
+  uint32_t end = non_null_->CountSetBits(t_range.range.end);
+
+  return StorageRange({Range(start, end)});
+}
+
+TableBitVector NullOverlay::MapToTableBitVector(StorageBitVector s_bv) const {
+  BitVector res = non_null_->Copy();
+  res.UpdateSetBits(s_bv.bv);
+  return {std::move(res)};
+}
+
+BitVector NullOverlay::IsStorageLookupRequired(
+    OverlayOp op,
+    const TableIndexVector& t_iv) const {
+  PERFETTO_DCHECK(t_iv.indices.size() <= non_null_->size());
+
+  if (op != OverlayOp::kOther)
+    return BitVector();
+
+  BitVector in_storage(static_cast<uint32_t>(t_iv.indices.size()), false);
+
+  // For each index in TableIndexVector check whether this index is in storage.
+  for (uint32_t i = 0; i < t_iv.indices.size(); ++i) {
+    if (non_null_->IsSet(t_iv.indices[i]))
+      in_storage.Set(i);
+  }
+
+  return in_storage;
+}
+
+StorageIndexVector NullOverlay::MapToStorageIndexVector(
+    TableIndexVector t_iv_with_idx_in_storage) const {
+  PERFETTO_DCHECK(t_iv_with_idx_in_storage.indices.size() <=
+                  non_null_->CountSetBits());
+
+  std::vector<uint32_t> storage_index_vector;
+  storage_index_vector.reserve(t_iv_with_idx_in_storage.indices.size());
+  for (auto t_idx : t_iv_with_idx_in_storage.indices) {
+    storage_index_vector.push_back(non_null_->CountSetBits(t_idx));
+  }
+
+  return StorageIndexVector({std::move(storage_index_vector)});
+}
+
+BitVector NullOverlay::IndexSearch(
+    OverlayOp op,
+    const TableIndexVector& t_iv_overlay_idx) const {
+  if (op == OverlayOp::kOther)
+    return BitVector();
+
+  BitVector res(static_cast<uint32_t>(t_iv_overlay_idx.indices.size()), false);
+  if (op == OverlayOp::kIsNull) {
+    for (uint32_t i = 0; i < res.size(); ++i) {
+      if (!non_null_->IsSet(t_iv_overlay_idx.indices[i]))
+        res.Set(i);
+    }
+    return res;
+  }
+
+  PERFETTO_DCHECK(op == OverlayOp::kIsNotNull);
+  for (uint32_t i = 0; i < res.size(); ++i) {
+    if (non_null_->IsSet(t_iv_overlay_idx.indices[i]))
+      res.Set(i);
+  }
+  return res;
+}
+
+CostEstimatePerRow NullOverlay::EstimateCostPerRow(OverlayOp op) const {
+  // TODO(b/283763282): Replace with benchmarked data.
+  CostEstimatePerRow res;
+
+  // Two |BitVector::CountSetBits| calls.
+  res.to_storage_range = 100;
+
+  // Cost of |BitVector::UpdateSetBits|
+  res.to_table_bit_vector = 100;
+
+  if (op == OverlayOp::kOther) {
+    // Cost of |BitVector::IsSet| and |BitVector::Set|
+    res.is_storage_search_required = 10;
+
+    // Cost of iterating all set bits and looping the index vector divided by
+    // number of indices.
+    res.map_to_storage_index_vector = 100;
+
+    // Won't be called.
+    res.index_search = 0;
+  } else {
+    // Cost of creating trivial BitVector.
+    res.is_storage_search_required = 0;
+
+    // Won't be called
+    res.map_to_storage_index_vector = 0;
+
+    // Cost of calling |BitVector::IsSet|
+    res.index_search = 10;
+  }
+
+  return res;
+}
+
+}  // namespace overlays
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/db/overlays/null_overlay.h b/src/trace_processor/db/overlays/null_overlay.h
new file mode 100644
index 0000000..93da2c0
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay.h
@@ -0,0 +1,54 @@
+/*
+ * 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_NULL_OVERLAY_H_
+#define SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
+
+#include "src/trace_processor/db/overlays/storage_overlay.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+// Introduces the layer of nullability - spreads out the storage with nulls
+// using BitVector.
+class NullOverlay : public StorageOverlay {
+ public:
+  explicit NullOverlay(BitVector* null) : non_null_(std::move(null)) {}
+
+  StorageRange MapToStorageRange(TableRange) const override;
+
+  TableBitVector MapToTableBitVector(StorageBitVector) const override;
+
+  BitVector IsStorageLookupRequired(OverlayOp,
+                                    const TableIndexVector&) const override;
+
+  StorageIndexVector MapToStorageIndexVector(TableIndexVector) const override;
+
+  BitVector IndexSearch(OverlayOp, const TableIndexVector&) const override;
+
+  CostEstimatePerRow EstimateCostPerRow(OverlayOp) const override;
+
+ private:
+  // Non null data in the overlay.
+  BitVector* non_null_;
+};
+
+}  // namespace overlays
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_DB_OVERLAYS_NULL_OVERLAY_H_
diff --git a/src/trace_processor/db/overlays/null_overlay_unittest.cc b/src/trace_processor/db/overlays/null_overlay_unittest.cc
new file mode 100644
index 0000000..6f80eba
--- /dev/null
+++ b/src/trace_processor/db/overlays/null_overlay_unittest.cc
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/overlays/null_overlay.h"
+#include "test/gtest_and_gmock.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+namespace {
+
+TEST(NullOverlay, MapToStorageRangeOutsideBoundary) {
+  BitVector bv{0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+  NullOverlay overlay(&bv);
+  StorageRange r = overlay.MapToStorageRange({RowMap::Range(1, 6)});
+
+  ASSERT_EQ(r.range.start, 0u);
+  ASSERT_EQ(r.range.end, 2u);
+}
+
+TEST(NullOverlay, MapToStorageRangeOnBoundary) {
+  BitVector bv{0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0};
+  NullOverlay overlay(&bv);
+  StorageRange r = overlay.MapToStorageRange({RowMap::Range(3, 8)});
+
+  ASSERT_EQ(r.range.start, 1u);
+  ASSERT_EQ(r.range.end, 4u);
+}
+
+TEST(NullOverlay, MapToTableBitVector) {
+  BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+  NullOverlay overlay(&bv);
+
+  BitVector storage_bv{0, 1, 0, 1};
+  TableBitVector table_bv =
+      overlay.MapToTableBitVector({std::move(storage_bv)});
+
+  ASSERT_EQ(table_bv.bv.CountSetBits(), 2u);
+  ASSERT_TRUE(table_bv.bv.IsSet(2));
+  ASSERT_TRUE(table_bv.bv.IsSet(6));
+}
+
+TEST(NullOverlay, IsStorageLookupRequiredNullOp) {
+  BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+  NullOverlay overlay(&bv);
+
+  std::vector<uint32_t> table_idx{0, 2, 4, 6};
+  BitVector lookup_bv =
+      overlay.IsStorageLookupRequired(OverlayOp::kIsNull, {table_idx});
+
+  ASSERT_EQ(lookup_bv.size(), 0u);
+}
+
+TEST(NullOverlay, IsStorageLookupRequiredOtherOp) {
+  BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+  NullOverlay overlay(&bv);
+
+  std::vector<uint32_t> table_idx{0, 2, 4, 6};
+  BitVector lookup_bv =
+      overlay.IsStorageLookupRequired(OverlayOp::kOther, {table_idx});
+
+  ASSERT_EQ(lookup_bv.size(), 4u);
+  ASSERT_EQ(lookup_bv.CountSetBits(), 2u);
+  ASSERT_TRUE(lookup_bv.IsSet(1));
+  ASSERT_TRUE(lookup_bv.IsSet(3));
+}
+
+TEST(NullOverlay, MapToStorageIndexVector) {
+  BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+  NullOverlay overlay(&bv);
+
+  std::vector<uint32_t> table_idx{1, 5, 2};
+  StorageIndexVector storage_iv = overlay.MapToStorageIndexVector({table_idx});
+
+  std::vector<uint32_t> res{0, 2, 1};
+  ASSERT_EQ(storage_iv.indices, res);
+}
+
+TEST(NullOverlay, IndexSearchOtherOp) {
+  BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+  NullOverlay overlay(&bv);
+
+  std::vector<uint32_t> table_idx{0, 3, 4};
+  BitVector idx_search_bv = overlay.IndexSearch(OverlayOp::kOther, {table_idx});
+
+  ASSERT_EQ(idx_search_bv.size(), 0u);
+}
+
+TEST(NullOverlay, IndexSearchIsNullOp) {
+  BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+  NullOverlay overlay(&bv);
+
+  std::vector<uint32_t> table_idx{0, 3, 4};
+  BitVector idx_search_bv =
+      overlay.IndexSearch(OverlayOp::kIsNull, {table_idx});
+
+  ASSERT_EQ(idx_search_bv.size(), 3u);
+  ASSERT_EQ(idx_search_bv.CountSetBits(), 3u);
+}
+
+TEST(NullOverlay, IndexSearchIsNotNullOp) {
+  BitVector bv{0, 1, 1, 0, 0, 1, 1, 0};
+  NullOverlay overlay(&bv);
+
+  std::vector<uint32_t> table_idx{0, 3, 4};
+  BitVector idx_search_bv =
+      overlay.IndexSearch(OverlayOp::kIsNotNull, {table_idx});
+
+  ASSERT_EQ(idx_search_bv.size(), 3u);
+  ASSERT_EQ(idx_search_bv.CountSetBits(), 0u);
+}
+
+}  // namespace
+}  // namespace overlays
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/db/overlays/storage_overlay.cc b/src/trace_processor/db/overlays/storage_overlay.cc
new file mode 100644
index 0000000..56897f2
--- /dev/null
+++ b/src/trace_processor/db/overlays/storage_overlay.cc
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "src/trace_processor/db/overlays/storage_overlay.h"
+
+namespace perfetto {
+namespace trace_processor {
+namespace overlays {
+
+StorageOverlay::~StorageOverlay() = default;
+
+}  // namespace overlays
+}  // namespace trace_processor
+}  // namespace perfetto