Merge "tp: Parse OEM atoms generically where possible" into main
diff --git a/Android.bp b/Android.bp
index 9ceeed2..328fc3d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5944,6 +5944,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6362,6 +6363,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6443,6 +6445,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/oom.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/panel.gen.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/power.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/printk.gen.cc",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.cc",
@@ -6524,6 +6527,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/oom.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/panel.gen.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/power.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/printk.gen.h",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.gen.h",
@@ -6601,6 +6605,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6681,6 +6686,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pb.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/power.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pb.cc",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.cc",
@@ -6761,6 +6767,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pb.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/power.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pb.h",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pb.h",
@@ -6838,6 +6845,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
@@ -6919,6 +6927,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pbzero.cc",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/power.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.cc",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.cc",
@@ -7000,6 +7009,7 @@
"external/perfetto/protos/perfetto/trace/ftrace/net.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/oom.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/panel.pbzero.h",
+ "external/perfetto/protos/perfetto/trace/ftrace/perf_trace_counters.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/power.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/printk.pbzero.h",
"external/perfetto/protos/perfetto/trace/ftrace/raw_syscalls.pbzero.h",
@@ -13367,6 +13377,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
diff --git a/BUILD b/BUILD
index eada055..cddab6b 100644
--- a/BUILD
+++ b/BUILD
@@ -4460,6 +4460,7 @@
"protos/perfetto/trace/ftrace/net.proto",
"protos/perfetto/trace/ftrace/oom.proto",
"protos/perfetto/trace/ftrace/panel.proto",
+ "protos/perfetto/trace/ftrace/perf_trace_counters.proto",
"protos/perfetto/trace/ftrace/power.proto",
"protos/perfetto/trace/ftrace/printk.proto",
"protos/perfetto/trace/ftrace/raw_syscalls.proto",
diff --git a/include/perfetto/ext/base/thread_annotations.h b/include/perfetto/ext/base/thread_annotations.h
index aaf291f..6aee16a 100644
--- a/include/perfetto/ext/base/thread_annotations.h
+++ b/include/perfetto/ext/base/thread_annotations.h
@@ -29,10 +29,8 @@
const char* description);
}
-#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
- AnnotateBenignRaceSized(__FILE__, __LINE__, \
- reinterpret_cast<unsigned long>(pointer), size, \
- description);
+#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description) \
+ AnnotateBenignRaceSized(__FILE__, __LINE__, pointer, size, description);
#else // defined(ADDRESS_SANITIZER)
#define PERFETTO_ANNOTATE_BENIGN_RACE_SIZED(pointer, size, description)
#endif // defined(ADDRESS_SANITIZER)
diff --git a/protos/perfetto/trace/ftrace/all_protos.gni b/protos/perfetto/trace/ftrace/all_protos.gni
index bd055ab..2237aef 100644
--- a/protos/perfetto/trace/ftrace/all_protos.gni
+++ b/protos/perfetto/trace/ftrace/all_protos.gni
@@ -57,6 +57,7 @@
"net.proto",
"oom.proto",
"panel.proto",
+ "perf_trace_counters.proto",
"power.proto",
"printk.proto",
"raw_syscalls.proto",
diff --git a/protos/perfetto/trace/ftrace/ftrace_event.proto b/protos/perfetto/trace/ftrace/ftrace_event.proto
index a068c9d..f7e314b 100644
--- a/protos/perfetto/trace/ftrace/ftrace_event.proto
+++ b/protos/perfetto/trace/ftrace/ftrace_event.proto
@@ -57,6 +57,7 @@
import "protos/perfetto/trace/ftrace/net.proto";
import "protos/perfetto/trace/ftrace/oom.proto";
import "protos/perfetto/trace/ftrace/panel.proto";
+import "protos/perfetto/trace/ftrace/perf_trace_counters.proto";
import "protos/perfetto/trace/ftrace/power.proto";
import "protos/perfetto/trace/ftrace/printk.proto";
import "protos/perfetto/trace/ftrace/raw_syscalls.proto";
@@ -601,5 +602,6 @@
SamsungTracingMarkWriteFtraceEvent samsung_tracing_mark_write = 484;
BinderCommandFtraceEvent binder_command = 485;
BinderReturnFtraceEvent binder_return = 486;
+ SchedSwitchWithCtrsFtraceEvent sched_switch_with_ctrs = 487;
}
}
diff --git a/protos/perfetto/trace/ftrace/perf_trace_counters.proto b/protos/perfetto/trace/ftrace/perf_trace_counters.proto
new file mode 100644
index 0000000..0e3531c
--- /dev/null
+++ b/protos/perfetto/trace/ftrace/perf_trace_counters.proto
@@ -0,0 +1,26 @@
+// Autogenerated by:
+// ../../src/tools/ftrace_proto_gen/ftrace_proto_gen.cc
+// Do not edit.
+
+syntax = "proto2";
+package perfetto.protos;
+
+message SchedSwitchWithCtrsFtraceEvent {
+ optional int32 old_pid = 1;
+ optional int32 new_pid = 2;
+ optional uint32 cctr = 3;
+ optional uint32 ctr0 = 4;
+ optional uint32 ctr1 = 5;
+ optional uint32 ctr2 = 6;
+ optional uint32 ctr3 = 7;
+ optional uint32 lctr0 = 8;
+ optional uint32 lctr1 = 9;
+ optional uint32 ctr4 = 10;
+ optional uint32 ctr5 = 11;
+ optional string prev_comm = 12;
+ optional int32 prev_pid = 13;
+ optional uint32 cyc = 14;
+ optional uint32 inst = 15;
+ optional uint32 stallbm = 16;
+ optional uint32 l3dm = 17;
+}
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index fb3bd6a..8cfa67e 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -8486,6 +8486,30 @@
// End of protos/perfetto/trace/ftrace/panel.proto
+// Begin of protos/perfetto/trace/ftrace/perf_trace_counters.proto
+
+message SchedSwitchWithCtrsFtraceEvent {
+ optional int32 old_pid = 1;
+ optional int32 new_pid = 2;
+ optional uint32 cctr = 3;
+ optional uint32 ctr0 = 4;
+ optional uint32 ctr1 = 5;
+ optional uint32 ctr2 = 6;
+ optional uint32 ctr3 = 7;
+ optional uint32 lctr0 = 8;
+ optional uint32 lctr1 = 9;
+ optional uint32 ctr4 = 10;
+ optional uint32 ctr5 = 11;
+ optional string prev_comm = 12;
+ optional int32 prev_pid = 13;
+ optional uint32 cyc = 14;
+ optional uint32 inst = 15;
+ optional uint32 stallbm = 16;
+ optional uint32 l3dm = 17;
+}
+
+// End of protos/perfetto/trace/ftrace/perf_trace_counters.proto
+
// Begin of protos/perfetto/trace/ftrace/power.proto
message CpuFrequencyFtraceEvent {
@@ -9768,6 +9792,7 @@
SamsungTracingMarkWriteFtraceEvent samsung_tracing_mark_write = 484;
BinderCommandFtraceEvent binder_command = 485;
BinderReturnFtraceEvent binder_return = 486;
+ SchedSwitchWithCtrsFtraceEvent sched_switch_with_ctrs = 487;
}
}
diff --git a/src/tools/ftrace_proto_gen/event_list b/src/tools/ftrace_proto_gen/event_list
index f2dcbb3..68bb2ba 100644
--- a/src/tools/ftrace_proto_gen/event_list
+++ b/src/tools/ftrace_proto_gen/event_list
@@ -481,3 +481,4 @@
samsung/tracing_mark_write
binder/binder_command
binder/binder_return
+perf_trace_counters/sched_switch_with_ctrs
diff --git a/src/trace_processor/db/query_executor.cc b/src/trace_processor/db/query_executor.cc
index bd04fa0..1151079 100644
--- a/src/trace_processor/db/query_executor.cc
+++ b/src/trace_processor/db/query_executor.cc
@@ -50,9 +50,18 @@
void QueryExecutor::FilterColumn(const Constraint& c,
const storage::Storage& storage,
RowMap* rm) {
+ // Shortcut of empty row map.
if (rm->empty())
return;
+ // Comparison of NULL with any operation apart from |IS_NULL| and
+ // |IS_NOT_NULL| should return no rows.
+ if (c.value.is_null() && c.op != FilterOp::kIsNull &&
+ c.op != FilterOp::kIsNotNull) {
+ rm->Clear();
+ return;
+ }
+
uint32_t rm_size = rm->size();
uint32_t rm_first = rm->Get(0);
uint32_t rm_last = rm->Get(rm_size - 1);
@@ -64,8 +73,9 @@
bool disallows_index_search = rm->IsRange();
bool prefers_index_search =
rm->IsIndexVector() || rm_size < 1024 || rm_size * 10 < range_size;
+
if (!disallows_index_search && prefers_index_search) {
- *rm = IndexSearch(c, storage, rm);
+ IndexSearch(c, storage, rm);
return;
}
LinearSearch(c, storage, rm);
@@ -99,9 +109,9 @@
rm->Intersect(RowMap(std::move(res).TakeIfBitVector()));
}
-RowMap QueryExecutor::IndexSearch(const Constraint& c,
- const storage::Storage& storage,
- RowMap* rm) {
+void QueryExecutor::IndexSearch(const Constraint& c,
+ const storage::Storage& storage,
+ RowMap* rm) {
// Create outmost TableIndexVector.
std::vector<uint32_t> table_indices = std::move(*rm).TakeAsIndexVector();
@@ -109,16 +119,27 @@
c.op, c.value, table_indices.data(),
static_cast<uint32_t>(table_indices.size()), false /* sorted */);
- // TODO(b/283763282): Remove after implementing extrinsic binary search.
- PERFETTO_CHECK(matched.IsBitVector());
+ if (matched.IsBitVector()) {
+ BitVector res = std::move(matched).TakeIfBitVector();
+ uint32_t i = 0;
+ table_indices.erase(
+ std::remove_if(table_indices.begin(), table_indices.end(),
+ [&i, &res](uint32_t) { return !res.IsSet(i++); }),
+ table_indices.end());
+ *rm = RowMap(std::move(table_indices));
+ return;
+ }
- BitVector res = std::move(matched).TakeIfBitVector();
- uint32_t i = 0;
- table_indices.erase(
- std::remove_if(table_indices.begin(), table_indices.end(),
- [&i, &res](uint32_t) { return !res.IsSet(i++); }),
- table_indices.end());
- return RowMap(std::move(table_indices));
+ Range res = std::move(matched).TakeIfRange();
+ if (res.size() == 0) {
+ rm->Clear();
+ return;
+ }
+ if (res.size() == table_indices.size()) {
+ return;
+ }
+ // TODO(b/283763282): Remove after implementing extrinsic binary search.
+ PERFETTO_FATAL("Extrinsic binary search is not implemented.");
}
RowMap QueryExecutor::FilterLegacy(const Table* table,
@@ -142,7 +163,7 @@
// Mismatched types.
use_legacy = use_legacy ||
(c.op != FilterOp::kIsNull && c.op != FilterOp::kIsNotNull &&
- col.type() != c.value.type);
+ col.type() != c.value.type && !c.value.is_null());
// Dense columns.
use_legacy = use_legacy || col.IsDense();
@@ -226,8 +247,8 @@
}
}
if (col.IsNullable()) {
- // String columns are inherently nullable: null values are signified with
- // Id::Null().
+ // String columns are inherently nullable: null values are signified
+ // with Id::Null().
PERFETTO_CHECK(col.col_type() != ColumnType::kString);
storage = std::make_unique<storage::NullStorage>(std::move(storage),
col.storage_base().bv());
diff --git a/src/trace_processor/db/query_executor.h b/src/trace_processor/db/query_executor.h
index 07281f7..013dbf2 100644
--- a/src/trace_processor/db/query_executor.h
+++ b/src/trace_processor/db/query_executor.h
@@ -70,10 +70,10 @@
}
// Used only in unittests. Exposes private function.
- static RowMap IndexedColumnFilterForTesting(const Constraint& c,
- const storage::Storage& col,
- RowMap* rm) {
- return IndexSearch(c, col, rm);
+ static void IndexedColumnFilterForTesting(const Constraint& c,
+ const storage::Storage& col,
+ RowMap* rm) {
+ IndexSearch(c, col, rm);
}
private:
@@ -86,9 +86,7 @@
// Filters the column using Index algorithm - finds the indices to filter the
// storage with.
- static RowMap IndexSearch(const Constraint&,
- const storage::Storage&,
- RowMap*);
+ static void IndexSearch(const Constraint&, const storage::Storage&, RowMap*);
std::vector<storage::Storage*> columns_;
diff --git a/src/trace_processor/db/query_executor_unittest.cc b/src/trace_processor/db/query_executor_unittest.cc
index e5022c0..95ce5ef 100644
--- a/src/trace_processor/db/query_executor_unittest.cc
+++ b/src/trace_processor/db/query_executor_unittest.cc
@@ -56,7 +56,7 @@
std::vector<int64_t> storage_data{1, 2, 3, 4, 5};
storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 5);
QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
@@ -73,24 +73,24 @@
Constraint c{0, FilterOp::kLt, SqlValue::Long(2)};
RowMap rm(0, 10);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 4u);
- ASSERT_EQ(res.Get(0), 0u);
- ASSERT_EQ(res.Get(1), 1u);
- ASSERT_EQ(res.Get(2), 5u);
- ASSERT_EQ(res.Get(3), 6u);
+ ASSERT_EQ(rm.size(), 4u);
+ ASSERT_EQ(rm.Get(0), 0u);
+ ASSERT_EQ(rm.Get(1), 1u);
+ ASSERT_EQ(rm.Get(2), 5u);
+ ASSERT_EQ(rm.Get(3), 6u);
}
TEST(QueryExecutor, OnlyStorageIndexIsNull) {
std::vector<int64_t> storage_data{1, 2, 3, 4, 5};
storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 5);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 0u);
+ ASSERT_EQ(rm.size(), 0u);
}
TEST(QueryExecutor, NullBounds) {
@@ -118,7 +118,7 @@
BitVector bv{1, 1, 0, 1, 1, 0, 0, 0, 1, 0};
storage::NullStorage storage(std::move(numeric), &bv);
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 10);
QueryExecutor::BoundedColumnFilterForTesting(c, storage, &rm);
@@ -143,13 +143,13 @@
Constraint c{0, FilterOp::kGe, SqlValue::Long(1)};
RowMap rm(0, 10);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 4u);
- ASSERT_EQ(res.Get(0), 1u);
- ASSERT_EQ(res.Get(1), 3u);
- ASSERT_EQ(res.Get(2), 6u);
- ASSERT_EQ(res.Get(3), 9u);
+ ASSERT_EQ(rm.size(), 4u);
+ ASSERT_EQ(rm.Get(0), 1u);
+ ASSERT_EQ(rm.Get(1), 3u);
+ ASSERT_EQ(rm.Get(2), 6u);
+ ASSERT_EQ(rm.Get(3), 9u);
}
TEST(QueryExecutor, NullIndexIsNull) {
@@ -161,16 +161,16 @@
BitVector bv{1, 1, 0, 1, 1, 0, 0, 0, 1, 0};
storage::NullStorage storage(std::move(numeric), &bv);
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(3)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
RowMap rm(0, 10);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_EQ(res.size(), 5u);
- ASSERT_EQ(res.Get(0), 2u);
- ASSERT_EQ(res.Get(1), 5u);
- ASSERT_EQ(res.Get(2), 6u);
- ASSERT_EQ(res.Get(3), 7u);
- ASSERT_EQ(res.Get(4), 9u);
+ ASSERT_EQ(rm.size(), 5u);
+ ASSERT_EQ(rm.Get(0), 2u);
+ ASSERT_EQ(rm.Get(1), 5u);
+ ASSERT_EQ(rm.Get(2), 6u);
+ ASSERT_EQ(rm.Get(3), 7u);
+ ASSERT_EQ(rm.Get(4), 9u);
}
TEST(QueryExecutor, SelectorStorageBounds) {
@@ -202,9 +202,9 @@
Constraint c{0, FilterOp::kGe, SqlValue::Long(2)};
RowMap rm(0, 6);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_THAT(res.GetAllIndices(), ElementsAre(2u, 3u, 5u));
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(2u, 3u, 5u));
}
TEST(QueryExecutor, ArrangementStorageBounds) {
@@ -223,7 +223,7 @@
ASSERT_THAT(rm.GetAllIndices(), ElementsAre(0u, 4u));
}
-TEST(QueryExecutor, ArrangementOverlaySubsetInputRange) {
+TEST(QueryExecutor, ArrangementStorageSubsetInputRange) {
std::unique_ptr<storage::Storage> fake =
storage::FakeStorage::SearchSubset(5u, RowMap::Range(2u, 4u));
@@ -237,7 +237,7 @@
ASSERT_THAT(rm.GetAllIndices(), ElementsAre(2u));
}
-TEST(QueryExecutor, ArrangementOverlaySubsetInputBitvector) {
+TEST(QueryExecutor, ArrangementStorageSubsetInputBitvector) {
std::unique_ptr<storage::Storage> fake =
storage::FakeStorage::SearchSubset(5u, BitVector({0, 0, 1, 1, 0}));
@@ -262,9 +262,21 @@
Constraint c{0, FilterOp::kGe, SqlValue::Long(3)};
RowMap rm(0, 5);
- RowMap res = QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
+ QueryExecutor::IndexedColumnFilterForTesting(c, storage, &rm);
- ASSERT_THAT(res.GetAllIndices(), ElementsAre(0u, 4u));
+ ASSERT_THAT(rm.GetAllIndices(), ElementsAre(0u, 4u));
+}
+
+TEST(QueryExecutor, MismatchedTypeNullWithOtherOperations) {
+ std::vector<int64_t> storage_data{0, 1, 2, 3, 0, 1, 2, 3};
+ storage::NumericStorage<int64_t> storage(&storage_data, ColumnType::kInt64);
+
+ // Filter.
+ Constraint c{0, FilterOp::kGe, SqlValue()};
+ QueryExecutor exec({&storage}, 6);
+ RowMap res = exec.Filter({c});
+
+ ASSERT_TRUE(res.empty());
}
TEST(QueryExecutor, SingleConstraintWithNullAndSelector) {
@@ -336,7 +348,7 @@
SelectorStorage storage(std::move(null), &selector_bv);
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
QueryExecutor exec({&storage}, 6);
RowMap res = exec.Filter({c});
@@ -436,7 +448,7 @@
IdStorage storage(5);
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
@@ -447,7 +459,7 @@
IdStorage storage(5);
// Filter.
- Constraint c{0, FilterOp::kIsNotNull, SqlValue::Long(0)};
+ Constraint c{0, FilterOp::kIsNotNull, SqlValue()};
QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
@@ -481,7 +493,7 @@
SelectorStorage storage(std::move(string), &selector_bv);
// Filter.
- Constraint c{0, FilterOp::kIsNull, SqlValue::Long(0)};
+ Constraint c{0, FilterOp::kIsNull, SqlValue()};
QueryExecutor exec({&storage}, 5);
RowMap res = exec.Filter({c});
diff --git a/src/trace_processor/db/storage/arrangement_storage.cc b/src/trace_processor/db/storage/arrangement_storage.cc
index ee777f4..fac7f68 100644
--- a/src/trace_processor/db/storage/arrangement_storage.cc
+++ b/src/trace_processor/db/storage/arrangement_storage.cc
@@ -40,6 +40,12 @@
inner_->size());
}
+Storage::SearchValidationResult ArrangementStorage::ValidateSearchConstraints(
+ SqlValue sql_val,
+ FilterOp op) const {
+ return inner_->ValidateSearchConstraints(sql_val, op);
+}
+
RangeOrBitVector ArrangementStorage::Search(FilterOp op,
SqlValue sql_val,
Range in) const {
@@ -60,6 +66,7 @@
}
} else {
BitVector storage_bitvector = std::move(storage_result).TakeIfBitVector();
+ PERFETTO_DCHECK(storage_bitvector.size() == *max_i + 1);
// After benchmarking, it turns out this complexity *is* actually worthwhile
// and has a noticable impact on the performance of this function in real
@@ -67,13 +74,13 @@
// Fast path: we compare as many groups of 64 elements as we can.
// This should be very easy for the compiler to auto-vectorize.
+ const uint32_t* arrangement_idx = arrangement.data() + in.start;
uint32_t fast_path_elements = builder.BitsInCompleteWordsUntilFull();
- uint32_t cur_idx = 0;
for (uint32_t i = 0; i < fast_path_elements; i += BitVector::kBitsInWord) {
uint64_t word = 0;
// This part should be optimised by SIMD and is expected to be fast.
- for (uint32_t k = 0; k < BitVector::kBitsInWord; ++k, ++cur_idx) {
- bool comp_result = storage_bitvector.IsSet((*arrangement_)[cur_idx]);
+ for (uint32_t k = 0; k < BitVector::kBitsInWord; ++k, ++arrangement_idx) {
+ bool comp_result = storage_bitvector.IsSet(*arrangement_idx);
word |= static_cast<uint64_t>(comp_result) << k;
}
builder.AppendWord(word);
@@ -81,8 +88,8 @@
// Slow path: we compare <64 elements and append to fill the Builder.
uint32_t back_elements = builder.BitsUntilFull();
- for (uint32_t i = 0; i < back_elements; ++i, ++cur_idx) {
- builder.Append(storage_bitvector.IsSet((*arrangement_)[cur_idx]));
+ for (uint32_t i = 0; i < back_elements; ++i, ++arrangement_idx) {
+ builder.Append(storage_bitvector.IsSet(*arrangement_idx));
}
}
return RangeOrBitVector(std::move(builder).Build());
diff --git a/src/trace_processor/db/storage/arrangement_storage.h b/src/trace_processor/db/storage/arrangement_storage.h
index cfc10c9..97a944e 100644
--- a/src/trace_processor/db/storage/arrangement_storage.h
+++ b/src/trace_processor/db/storage/arrangement_storage.h
@@ -32,6 +32,9 @@
explicit ArrangementStorage(std::unique_ptr<Storage> inner,
const std::vector<uint32_t>* arrangement);
+ Storage::SearchValidationResult ValidateSearchConstraints(SqlValue, FilterOp)
+ const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
diff --git a/src/trace_processor/db/storage/dummy_storage.cc b/src/trace_processor/db/storage/dummy_storage.cc
index 4c6eb05..b4f0be5 100644
--- a/src/trace_processor/db/storage/dummy_storage.cc
+++ b/src/trace_processor/db/storage/dummy_storage.cc
@@ -21,6 +21,12 @@
namespace trace_processor {
namespace storage {
+DummyStorage::SearchValidationResult DummyStorage::ValidateSearchConstraints(
+ SqlValue,
+ FilterOp) const {
+ PERFETTO_FATAL("Shouldn't be called");
+}
+
RangeOrBitVector DummyStorage::Search(FilterOp, SqlValue, RowMap::Range) const {
PERFETTO_FATAL("Shouldn't be called");
}
diff --git a/src/trace_processor/db/storage/dummy_storage.h b/src/trace_processor/db/storage/dummy_storage.h
index 8d76d2f..7fd3a40 100644
--- a/src/trace_processor/db/storage/dummy_storage.h
+++ b/src/trace_processor/db/storage/dummy_storage.h
@@ -36,6 +36,9 @@
RangeOrBitVector Search(FilterOp, SqlValue, RowMap::Range) const override;
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector IndexSearch(FilterOp,
SqlValue,
uint32_t*,
diff --git a/src/trace_processor/db/storage/fake_storage.cc b/src/trace_processor/db/storage/fake_storage.cc
index 8d2550c..fd9ac2c 100644
--- a/src/trace_processor/db/storage/fake_storage.cc
+++ b/src/trace_processor/db/storage/fake_storage.cc
@@ -17,6 +17,7 @@
#include "src/trace_processor/db/storage/fake_storage.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
+#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
namespace perfetto {
@@ -26,6 +27,12 @@
FakeStorage::FakeStorage(uint32_t size, SearchStrategy strategy)
: size_(size), strategy_(strategy) {}
+FakeStorage::SearchValidationResult FakeStorage::ValidateSearchConstraints(
+ SqlValue,
+ FilterOp) const {
+ return SearchValidationResult::kOk;
+}
+
RangeOrBitVector FakeStorage::Search(FilterOp,
SqlValue,
RowMap::Range in) const {
diff --git a/src/trace_processor/db/storage/fake_storage.h b/src/trace_processor/db/storage/fake_storage.h
index 942be1a..2320269 100644
--- a/src/trace_processor/db/storage/fake_storage.h
+++ b/src/trace_processor/db/storage/fake_storage.h
@@ -20,6 +20,7 @@
#include <memory>
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/storage/storage.h"
+#include "src/trace_processor/db/storage/types.h"
namespace perfetto {
namespace trace_processor {
@@ -28,6 +29,9 @@
// Fake implementation of Storage for use in tests.
class FakeStorage final : public Storage {
public:
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
diff --git a/src/trace_processor/db/storage/id_storage.cc b/src/trace_processor/db/storage/id_storage.cc
index 0493f97..bec9b041 100644
--- a/src/trace_processor/db/storage/id_storage.cc
+++ b/src/trace_processor/db/storage/id_storage.cc
@@ -16,12 +16,15 @@
#include "src/trace_processor/db/storage/id_storage.h"
+#include <optional>
+
#include "perfetto/base/logging.h"
-#include "perfetto/trace_processor/basic_types.h"
+#include "perfetto/public/compiler.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/storage/types.h"
+#include "src/trace_processor/db/storage/utils.h"
#include "src/trace_processor/tp_metatrace.h"
namespace perfetto {
@@ -68,34 +71,112 @@
}
return RangeOrBitVector(std::move(builder).Build());
}
+
} // namespace
+IdStorage::SearchValidationResult IdStorage::ValidateSearchConstraints(
+ SqlValue val,
+ FilterOp op) const {
+ // NULL checks.
+ if (PERFETTO_UNLIKELY(val.is_null())) {
+ if (op == FilterOp::kIsNotNull) {
+ return SearchValidationResult::kAllData;
+ }
+ if (op == FilterOp::kIsNull) {
+ return SearchValidationResult::kNoData;
+ }
+ PERFETTO_DFATAL(
+ "Invalid filter operation. NULL should only be compared with 'IS NULL' "
+ "and 'IS NOT NULL'");
+ return SearchValidationResult::kNoData;
+ }
+
+ // FilterOp checks. Switch so that we get a warning if new FilterOp is not
+ // handled.
+ switch (op) {
+ case FilterOp::kEq:
+ case FilterOp::kNe:
+ case FilterOp::kLt:
+ case FilterOp::kLe:
+ case FilterOp::kGt:
+ case FilterOp::kGe:
+ break;
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ PERFETTO_FATAL("Invalid constraint");
+ case FilterOp::kGlob:
+ case FilterOp::kRegex:
+ return SearchValidationResult::kNoData;
+ }
+
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ break;
+ case SqlValue::kString:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kLt || op == FilterOp::kLe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ // TODO(b/307482437): Remove after adding support for double
+ PERFETTO_CHECK(val.type != SqlValue::kDouble);
+
+ // Bounds of the value.
+ if (PERFETTO_UNLIKELY(val.AsLong() > std::numeric_limits<uint32_t>::max())) {
+ if (op == FilterOp::kLe || op == FilterOp::kLt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+ if (PERFETTO_UNLIKELY(val.AsLong() < std::numeric_limits<uint32_t>::min())) {
+ if (op == FilterOp::kGe || op == FilterOp::kGt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+
+ return SearchValidationResult::kOk;
+}
+
RangeOrBitVector IdStorage::Search(FilterOp op,
SqlValue sql_val,
- RowMap::Range range) const {
+ RowMap::Range search_range) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "IdStorage::Search",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+ PERFETTO_DCHECK(search_range.end <= size_);
+
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(search_range);
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
if (op == FilterOp::kNe) {
- if (sql_val.AsLong() > std::numeric_limits<uint32_t>::max() ||
- sql_val.AsLong() < std::numeric_limits<uint32_t>::min()) {
- return RangeOrBitVector(range);
- }
-
- uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
- BitVector ret(range.start, false);
- ret.Resize(range.end, true);
+ BitVector ret(search_range.start, false);
+ ret.Resize(search_range.end, true);
ret.Resize(size_, false);
-
ret.Clear(val);
return RangeOrBitVector(std::move(ret));
}
- return RangeOrBitVector(BinarySearchIntrinsic(op, sql_val, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, val, search_range));
}
RangeOrBitVector IdStorage::IndexSearch(FilterOp op,
@@ -109,29 +190,17 @@
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- // Validate sql_val
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return RangeOrBitVector(Range(indices_size, true));
- }
- return RangeOrBitVector(Range());
+
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
}
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return RangeOrBitVector(Range(indices_size, true));
- }
- return RangeOrBitVector(Range());
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return RangeOrBitVector(Range(indices_size, true));
- }
- return RangeOrBitVector(Range());
- }
uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
switch (op) {
@@ -154,46 +223,15 @@
return IndexSearchWithComparator(val, indices, indices_size,
std::greater_equal<uint32_t>());
case FilterOp::kIsNotNull:
- return RangeOrBitVector(Range(indices_size, true));
case FilterOp::kIsNull:
case FilterOp::kGlob:
case FilterOp::kRegex:
- return RangeOrBitVector(Range());
+ PERFETTO_FATAL("Invalid filter operation");
}
PERFETTO_FATAL("FilterOp not matched");
}
-Range IdStorage::BinarySearchIntrinsic(FilterOp op,
- SqlValue sql_val,
- Range range) const {
- PERFETTO_DCHECK(range.end <= size_);
-
- // Validate sql_value
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return range;
- }
- return Range();
- }
-
- uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
-
+Range IdStorage::BinarySearchIntrinsic(FilterOp op, Id val, Range range) const {
switch (op) {
case FilterOp::kEq:
return Range(val, val + (range.start <= val && val < range.end));
@@ -206,13 +244,11 @@
case FilterOp::kGt:
return RowMap::Range(std::max(val + 1, range.start), range.end);
case FilterOp::kIsNotNull:
- return range;
case FilterOp::kNe:
- PERFETTO_FATAL("Shouldn't be called");
case FilterOp::kIsNull:
case FilterOp::kGlob:
case FilterOp::kRegex:
- return RowMap::Range();
+ PERFETTO_FATAL("Invalid filter operation");
}
PERFETTO_FATAL("FilterOp not matched");
}
diff --git a/src/trace_processor/db/storage/id_storage.h b/src/trace_processor/db/storage/id_storage.h
index e797028..475c29b 100644
--- a/src/trace_processor/db/storage/id_storage.h
+++ b/src/trace_processor/db/storage/id_storage.h
@@ -16,6 +16,10 @@
#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_ID_STORAGE_H_
#define SRC_TRACE_PROCESSOR_DB_STORAGE_ID_STORAGE_H_
+#include "perfetto/base/status.h"
+#include "perfetto/ext/base/status_or.h"
+#include "perfetto/trace_processor/basic_types.h"
+#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
@@ -34,6 +38,9 @@
public:
explicit IdStorage(uint32_t size) : size_(size) {}
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -53,9 +60,11 @@
uint32_t size() const override { return size_; }
private:
- BitVector IndexSearch(FilterOp, SqlValue, uint32_t*, uint32_t) const;
+ using Id = uint32_t;
+
+ BitVector IndexSearch(FilterOp, Id, uint32_t*, uint32_t) const;
RowMap::Range BinarySearchIntrinsic(FilterOp op,
- SqlValue val,
+ Id,
RowMap::Range search_range) const;
const uint32_t size_ = 0;
diff --git a/src/trace_processor/db/storage/id_storage_unittest.cc b/src/trace_processor/db/storage/id_storage_unittest.cc
index 57c148d..2262c43 100644
--- a/src/trace_processor/db/storage/id_storage_unittest.cc
+++ b/src/trace_processor/db/storage/id_storage_unittest.cc
@@ -14,17 +14,100 @@
* limitations under the License.
*/
#include "src/trace_processor/db/storage/id_storage.h"
+#include <limits>
#include "src/trace_processor/db/storage/types.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
namespace trace_processor {
+
+inline bool operator==(const RowMap::Range& a, const RowMap::Range& b) {
+ return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
namespace storage {
namespace {
using Range = RowMap::Range;
+TEST(IdStorageUnittest, InvalidSearchConstraints) {
+ IdStorage storage(100);
+ Range test_range(10, 20);
+ Range empty_range;
+
+ // NULL checks
+ SqlValue val;
+ val.type = SqlValue::kNull;
+ Range search_result =
+ storage.Search(FilterOp::kIsNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kIsNotNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+
+ // FilterOp checks
+ search_result =
+ storage.Search(FilterOp::kGlob, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kRegex, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Type checks
+ search_result =
+ storage.Search(FilterOp::kGe, SqlValue::String("cheese"), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Value bounds
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 10);
+ search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, test_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
TEST(IdStorageUnittest, BinarySearchIntrinsicEqSimple) {
IdStorage storage(100);
Range range = storage.Search(FilterOp::kEq, SqlValue::Long(15), Range(10, 20))
diff --git a/src/trace_processor/db/storage/null_storage.cc b/src/trace_processor/db/storage/null_storage.cc
index cac62d6..6de2759 100644
--- a/src/trace_processor/db/storage/null_storage.cc
+++ b/src/trace_processor/db/storage/null_storage.cc
@@ -73,6 +73,12 @@
} // namespace
+Storage::SearchValidationResult NullStorage::ValidateSearchConstraints(
+ SqlValue sql_val,
+ FilterOp op) const {
+ return storage_->ValidateSearchConstraints(sql_val, op);
+}
+
NullStorage::NullStorage(std::unique_ptr<Storage> storage,
const BitVector* non_null)
: storage_(std::move(storage)), non_null_(non_null) {
diff --git a/src/trace_processor/db/storage/null_storage.h b/src/trace_processor/db/storage/null_storage.h
index c1ea44b..3087749 100644
--- a/src/trace_processor/db/storage/null_storage.h
+++ b/src/trace_processor/db/storage/null_storage.h
@@ -34,6 +34,9 @@
public:
NullStorage(std::unique_ptr<Storage> storage, const BitVector* non_null);
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
diff --git a/src/trace_processor/db/storage/numeric_storage.cc b/src/trace_processor/db/storage/numeric_storage.cc
index b7ecfa9..0b792de 100644
--- a/src/trace_processor/db/storage/numeric_storage.cc
+++ b/src/trace_processor/db/storage/numeric_storage.cc
@@ -17,12 +17,17 @@
#include "src/trace_processor/db/storage/numeric_storage.h"
+#include <cmath>
#include <cstddef>
#include <string>
+#include "perfetto/base/compiler.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/public/compiler.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
+#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
#include "src/trace_processor/db/storage/utils.h"
#include "src/trace_processor/tp_metatrace.h"
@@ -32,7 +37,7 @@
namespace storage {
namespace {
-// All viable numeric values for ColumnTypes.
+using Range = RowMap::Range;
using NumericValue = std::variant<uint32_t, int32_t, int64_t, double_t>;
// Using the fact that binary operators in std are operators() of classes, we
@@ -46,33 +51,22 @@
std::equal_to<T>,
std::not_equal_to<T>>;
-// Based on SqlValue and ColumnType, casts SqlValue to proper type, returns
-// std::nullopt if SqlValue can't be cast and should be considered invalid for
-// comparison.
-inline std::optional<NumericValue> GetNumericTypeVariant(ColumnType type,
- SqlValue val) {
- if (val.is_null())
- return std::nullopt;
-
+// Based on SqlValue and ColumnType, casts SqlValue to proper type. Assumes the
+// |val| and |type| are correct.
+inline NumericValue GetNumericTypeVariant(ColumnType type, SqlValue val) {
switch (type) {
case ColumnType::kDouble:
return val.AsDouble();
case ColumnType::kInt64:
return val.AsLong();
case ColumnType::kInt32:
- if (val.AsLong() > std::numeric_limits<int32_t>::max() ||
- val.AsLong() < std::numeric_limits<int32_t>::min())
- return std::nullopt;
return static_cast<int32_t>(val.AsLong());
case ColumnType::kUint32:
- if (val.AsLong() > std::numeric_limits<uint32_t>::max() ||
- val.AsLong() < std::numeric_limits<uint32_t>::min())
- return std::nullopt;
return static_cast<uint32_t>(val.AsLong());
case ColumnType::kString:
case ColumnType::kDummy:
case ColumnType::kId:
- return std::nullopt;
+ PERFETTO_FATAL("Invalid type");
}
PERFETTO_FATAL("For GCC");
}
@@ -201,72 +195,198 @@
} // namespace
+NumericStorageBase::SearchValidationResult
+NumericStorageBase::ValidateSearchConstraints(SqlValue val, FilterOp op) const {
+ // NULL checks.
+ if (PERFETTO_UNLIKELY(val.is_null())) {
+ if (op == FilterOp::kIsNotNull) {
+ return SearchValidationResult::kAllData;
+ }
+ if (op == FilterOp::kIsNull) {
+ return SearchValidationResult::kNoData;
+ }
+ PERFETTO_FATAL(
+ "Invalid path. NULL should only be compared with 'IS NULL' and 'IS NOT "
+ "NULL'");
+ }
+
+ // FilterOp checks. Switch so that we get a warning if new FilterOp is not
+ // handled.
+ switch (op) {
+ case FilterOp::kEq:
+ case FilterOp::kNe:
+ case FilterOp::kLt:
+ case FilterOp::kLe:
+ case FilterOp::kGt:
+ case FilterOp::kGe:
+ break;
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ PERFETTO_FATAL("Invalid constraint");
+ case FilterOp::kGlob:
+ case FilterOp::kRegex:
+ return SearchValidationResult::kNoData;
+ }
+
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ break;
+ case SqlValue::kString:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kLt || op == FilterOp::kLe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ // TODO(b/307482437): There is currently no support for comparison with double
+ // and it is prevented on QueryExecutor level.
+ if (type_ != ColumnType::kDouble) {
+ PERFETTO_CHECK(val.type != SqlValue::kDouble);
+ }
+
+ // Bounds of the value.
+ enum ExtremeVal { kTooBig, kTooSmall, kOk };
+ ExtremeVal extreme_validator = kOk;
+
+ switch (type_) {
+ case ColumnType::kDouble:
+ // Any value would make a sensible comparison with a double.
+ case ColumnType::kInt64:
+ // TODO(b/307482437): As long as the type is not double there is nothing
+ // to verify here, as all values are going to be in the int64_t limits.
+ break;
+ case ColumnType::kInt32:
+ if (val.AsLong() > std::numeric_limits<int32_t>::max()) {
+ extreme_validator = kTooBig;
+ break;
+ }
+ if (val.AsLong() < std::numeric_limits<int32_t>::min()) {
+ extreme_validator = kTooSmall;
+ break;
+ }
+ break;
+ case ColumnType::kUint32:
+ if (val.AsLong() > std::numeric_limits<uint32_t>::max()) {
+ extreme_validator = kTooBig;
+ break;
+ }
+ if (val.AsLong() < std::numeric_limits<uint32_t>::min()) {
+ extreme_validator = kTooSmall;
+ break;
+ }
+ break;
+ case ColumnType::kString:
+ case ColumnType::kDummy:
+ case ColumnType::kId:
+ break;
+ }
+
+ switch (extreme_validator) {
+ case kOk:
+ return Storage::SearchValidationResult::kOk;
+ case kTooBig:
+ if (op == FilterOp::kLt || op == FilterOp::kLe || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ case kTooSmall:
+ if (op == FilterOp::kGt || op == FilterOp::kGe || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+
+ PERFETTO_FATAL("For GCC");
+}
+
RangeOrBitVector NumericStorageBase::Search(FilterOp op,
- SqlValue value,
- RowMap::Range range) const {
+ SqlValue sql_val,
+ RowMap::Range search_range) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "NumericStorage::Search",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, search_range.end));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ NumericValue val = GetNumericTypeVariant(type_, sql_val);
+
if (is_sorted_) {
if (op != FilterOp::kNe) {
- return RangeOrBitVector(BinarySearchIntrinsic(op, value, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, val, search_range));
}
// Not equal is a special operation on binary search, as it doesn't define a
// range, and rather just `not` range returned with `equal` operation.
- RowMap::Range r = BinarySearchIntrinsic(FilterOp::kEq, value, range);
+ RowMap::Range r = BinarySearchIntrinsic(FilterOp::kEq, val, search_range);
BitVector bv(r.start, true);
bv.Resize(r.end, false);
- bv.Resize(range.end, true);
+ bv.Resize(search_range.end, true);
return RangeOrBitVector(std::move(bv));
}
- return RangeOrBitVector(LinearSearchInternal(op, value, range));
+
+ return RangeOrBitVector(LinearSearchInternal(op, val, search_range));
}
RangeOrBitVector NumericStorageBase::IndexSearch(FilterOp op,
- SqlValue value,
+ SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_count,
+ uint32_t indices_size,
bool sorted) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "NumericStorage::IndexSearch",
- [indices_count, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices_count));
+ [indices_size, op](metatrace::Record* r) {
+ r->AddArg("Count", std::to_string(indices_size));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+ NumericValue val = GetNumericTypeVariant(type_, sql_val);
if (sorted) {
return RangeOrBitVector(
- BinarySearchExtrinsic(op, value, indices, indices_count));
+ BinarySearchExtrinsic(op, val, indices, indices_size));
}
- return RangeOrBitVector(
- IndexSearchInternal(op, value, indices, indices_count));
+ return RangeOrBitVector(IndexSearchInternal(op, val, indices, indices_size));
}
BitVector NumericStorageBase::LinearSearchInternal(FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
RowMap::Range range) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
- if (op == FilterOp::kIsNotNull)
- return BitVector(range.end, true);
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return BitVector(range.end, false);
-
BitVector::Builder builder(range.end, range.start);
- if (const auto* u32 = std::get_if<uint32_t>(&*val)) {
+ if (const auto* u32 = std::get_if<uint32_t>(&val)) {
auto* start = static_cast<const uint32_t*>(data_) + range.start;
TypedLinearSearch(*u32, start, op, builder);
- } else if (const auto* i64 = std::get_if<int64_t>(&*val)) {
+ } else if (const auto* i64 = std::get_if<int64_t>(&val)) {
auto* start = static_cast<const int64_t*>(data_) + range.start;
TypedLinearSearch(*i64, start, op, builder);
- } else if (const auto* i32 = std::get_if<int32_t>(&*val)) {
+ } else if (const auto* i32 = std::get_if<int32_t>(&val)) {
auto* start = static_cast<const int32_t*>(data_) + range.start;
TypedLinearSearch(*i32, start, op, builder);
- } else if (const auto* db = std::get_if<double>(&*val)) {
+ } else if (const auto* db = std::get_if<double>(&val)) {
auto* start = static_cast<const double*>(data_) + range.start;
TypedLinearSearch(*db, start, op, builder);
} else {
@@ -277,16 +397,9 @@
BitVector NumericStorageBase::IndexSearchInternal(
FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
uint32_t* indices,
uint32_t indices_count) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
- if (op == FilterOp::kIsNotNull)
- return BitVector(indices_count, true);
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return BitVector(indices_count, false);
-
BitVector::Builder builder(indices_count);
std::visit(
[this, indices, op, &builder](auto val) {
@@ -299,37 +412,30 @@
},
GetFilterOpVariant<T>(op));
},
- *val);
+ val);
return std::move(builder).Build();
}
RowMap::Range NumericStorageBase::BinarySearchIntrinsic(
FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
RowMap::Range search_range) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
- if (op == FilterOp::kIsNotNull)
- return search_range;
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return RowMap::Range();
-
switch (op) {
case FilterOp::kEq:
- return RowMap::Range(LowerBoundIntrinsic(data_, *val, search_range),
- UpperBoundIntrinsic(data_, *val, search_range));
+ return RowMap::Range(LowerBoundIntrinsic(data_, val, search_range),
+ UpperBoundIntrinsic(data_, val, search_range));
case FilterOp::kLe: {
return RowMap::Range(search_range.start,
- UpperBoundIntrinsic(data_, *val, search_range));
+ UpperBoundIntrinsic(data_, val, search_range));
}
case FilterOp::kLt:
return RowMap::Range(search_range.start,
- LowerBoundIntrinsic(data_, *val, search_range));
+ LowerBoundIntrinsic(data_, val, search_range));
case FilterOp::kGe:
- return RowMap::Range(LowerBoundIntrinsic(data_, *val, search_range),
+ return RowMap::Range(LowerBoundIntrinsic(data_, val, search_range),
search_range.end);
case FilterOp::kGt:
- return RowMap::Range(UpperBoundIntrinsic(data_, *val, search_range),
+ return RowMap::Range(UpperBoundIntrinsic(data_, val, search_range),
search_range.end);
case FilterOp::kNe:
case FilterOp::kIsNull:
@@ -343,34 +449,26 @@
RowMap::Range NumericStorageBase::BinarySearchExtrinsic(
FilterOp op,
- SqlValue sql_val,
+ NumericValue val,
uint32_t* indices,
uint32_t indices_count) const {
- std::optional<NumericValue> val = GetNumericTypeVariant(type_, sql_val);
-
- if (op == FilterOp::kIsNotNull)
- return RowMap::Range(0, size());
-
- if (!val.has_value() || op == FilterOp::kIsNull || op == FilterOp::kGlob)
- return RowMap::Range();
-
switch (op) {
case FilterOp::kEq:
return RowMap::Range(
- LowerBoundExtrinsic(data_, *val, indices, indices_count),
- UpperBoundExtrinsic(data_, *val, indices, indices_count));
+ LowerBoundExtrinsic(data_, val, indices, indices_count),
+ UpperBoundExtrinsic(data_, val, indices, indices_count));
case FilterOp::kLe:
return RowMap::Range(
- 0, UpperBoundExtrinsic(data_, *val, indices, indices_count));
+ 0, UpperBoundExtrinsic(data_, val, indices, indices_count));
case FilterOp::kLt:
return RowMap::Range(
- 0, LowerBoundExtrinsic(data_, *val, indices, indices_count));
+ 0, LowerBoundExtrinsic(data_, val, indices, indices_count));
case FilterOp::kGe:
return RowMap::Range(
- LowerBoundExtrinsic(data_, *val, indices, indices_count), size_);
+ LowerBoundExtrinsic(data_, val, indices, indices_count), size_);
case FilterOp::kGt:
return RowMap::Range(
- UpperBoundExtrinsic(data_, *val, indices, indices_count), size_);
+ UpperBoundExtrinsic(data_, val, indices, indices_count), size_);
case FilterOp::kNe:
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
@@ -382,7 +480,6 @@
}
void NumericStorageBase::StableSort(uint32_t* rows, uint32_t rows_size) const {
- NumericValue val = *GetNumericTypeVariant(type_, SqlValue::Long(0));
std::visit(
[this, &rows, rows_size](auto val_data) {
using T = decltype(val_data);
@@ -394,7 +491,7 @@
return first_val < second_val;
});
},
- val);
+ GetNumericTypeVariant(type_, SqlValue::Long(0)));
}
void NumericStorageBase::Sort(uint32_t*, uint32_t) const {
diff --git a/src/trace_processor/db/storage/numeric_storage.h b/src/trace_processor/db/storage/numeric_storage.h
index a91e289..741a8e0 100644
--- a/src/trace_processor/db/storage/numeric_storage.h
+++ b/src/trace_processor/db/storage/numeric_storage.h
@@ -18,6 +18,7 @@
#include <variant>
+#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
@@ -33,6 +34,9 @@
// Storage for all numeric type data (i.e. doubles, int32, int64, uint32).
class NumericStorageBase : public Storage {
public:
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -59,21 +63,24 @@
: size_(size), data_(data), type_(type), is_sorted_(is_sorted) {}
private:
+ // All viable numeric values for ColumnTypes.
+ using NumericValue = std::variant<uint32_t, int32_t, int64_t, double>;
+
BitVector LinearSearchInternal(FilterOp op,
- SqlValue val,
+ NumericValue val,
RowMap::Range) const;
BitVector IndexSearchInternal(FilterOp op,
- SqlValue value,
+ NumericValue value,
uint32_t* indices,
uint32_t indices_count) const;
RowMap::Range BinarySearchIntrinsic(FilterOp op,
- SqlValue val,
+ NumericValue val,
RowMap::Range search_range) const;
RowMap::Range BinarySearchExtrinsic(FilterOp op,
- SqlValue val,
+ NumericValue val,
uint32_t* indices,
uint32_t indices_count) const;
diff --git a/src/trace_processor/db/storage/numeric_storage_unittest.cc b/src/trace_processor/db/storage/numeric_storage_unittest.cc
index b6ffb59..e82883f 100644
--- a/src/trace_processor/db/storage/numeric_storage_unittest.cc
+++ b/src/trace_processor/db/storage/numeric_storage_unittest.cc
@@ -20,11 +20,160 @@
namespace perfetto {
namespace trace_processor {
+
+inline bool operator==(const RowMap::Range& a, const RowMap::Range& b) {
+ return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
namespace storage {
namespace {
using Range = RowMap::Range;
+TEST(IdStorageUnittest, InvalidSearchConstraintsGeneralChecks) {
+ std::vector<uint32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage<uint32_t> storage(&data_vec, ColumnType::kUint32);
+
+ Range test_range(20, 100);
+ Range full_range(0, 100);
+ Range empty_range;
+
+ // NULL checks
+ SqlValue val;
+ val.type = SqlValue::kNull;
+ Range search_result =
+ storage.Search(FilterOp::kIsNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kIsNotNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ // FilterOp checks
+ search_result =
+ storage.Search(FilterOp::kGlob, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kRegex, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Type checks
+ search_result =
+ storage.Search(FilterOp::kGe, SqlValue::String("cheese"), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
+TEST(IdStorageUnittest, InvalidValueBoundsUint32) {
+ std::vector<uint32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage<uint32_t> storage(&data_vec, ColumnType::kUint32);
+
+ Range test_range(20, 100);
+ Range full_range(0, 100);
+ Range empty_range;
+
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 10);
+ Range search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
+TEST(IdStorageUnittest, InvalidValueBoundsInt32) {
+ std::vector<int32_t> data_vec(128);
+ std::iota(data_vec.begin(), data_vec.end(), 0);
+ NumericStorage<int32_t> storage(&data_vec, ColumnType::kInt32);
+
+ Range test_range(20, 100);
+ Range full_range(0, 100);
+ Range empty_range;
+
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<int32_t>::max()) + 10);
+ Range search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<int32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
TEST(NumericStorageUnittest, StableSortTrivial) {
std::vector<uint32_t> data_vec{0, 1, 2, 0, 1, 2, 0, 1, 2};
std::vector<uint32_t> out = {0, 1, 2, 3, 4, 5, 6, 7, 8};
diff --git a/src/trace_processor/db/storage/selector_storage.cc b/src/trace_processor/db/storage/selector_storage.cc
index 8a4e93a..934b900 100644
--- a/src/trace_processor/db/storage/selector_storage.cc
+++ b/src/trace_processor/db/storage/selector_storage.cc
@@ -31,6 +31,12 @@
const BitVector* selector)
: inner_(std::move(inner)), selector_(selector) {}
+Storage::SearchValidationResult SelectorStorage::ValidateSearchConstraints(
+ SqlValue sql_val,
+ FilterOp op) const {
+ return inner_->ValidateSearchConstraints(sql_val, op);
+}
+
RangeOrBitVector SelectorStorage::Search(FilterOp op,
SqlValue sql_val,
RowMap::Range in) const {
diff --git a/src/trace_processor/db/storage/selector_storage.h b/src/trace_processor/db/storage/selector_storage.h
index bc8de30..9d368e2 100644
--- a/src/trace_processor/db/storage/selector_storage.h
+++ b/src/trace_processor/db/storage/selector_storage.h
@@ -31,6 +31,9 @@
public:
SelectorStorage(std::unique_ptr<Storage> storage, const BitVector* non_null);
+ Storage::SearchValidationResult ValidateSearchConstraints(SqlValue, FilterOp)
+ const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
diff --git a/src/trace_processor/db/storage/set_id_storage.cc b/src/trace_processor/db/storage/set_id_storage.cc
index 8244ae0..570afb7 100644
--- a/src/trace_processor/db/storage/set_id_storage.cc
+++ b/src/trace_processor/db/storage/set_id_storage.cc
@@ -58,74 +58,139 @@
} // namespace
+SetIdStorage::SearchValidationResult SetIdStorage::ValidateSearchConstraints(
+ SqlValue val,
+ FilterOp op) const {
+ // NULL checks.
+ if (PERFETTO_UNLIKELY(val.is_null())) {
+ if (op == FilterOp::kIsNotNull) {
+ return SearchValidationResult::kAllData;
+ }
+ if (op == FilterOp::kIsNull) {
+ return SearchValidationResult::kNoData;
+ }
+ PERFETTO_FATAL(
+ "Invalid filter operation. NULL should only be compared with 'IS NULL' "
+ "and 'IS NOT NULL'");
+ }
+
+ // FilterOp checks. Switch so that we get a warning if new FilterOp is not
+ // handled.
+ switch (op) {
+ case FilterOp::kEq:
+ case FilterOp::kNe:
+ case FilterOp::kLt:
+ case FilterOp::kLe:
+ case FilterOp::kGt:
+ case FilterOp::kGe:
+ break;
+ case FilterOp::kIsNull:
+ case FilterOp::kIsNotNull:
+ PERFETTO_FATAL("Invalid constraints.");
+ case FilterOp::kGlob:
+ case FilterOp::kRegex:
+ return SearchValidationResult::kNoData;
+ }
+
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ break;
+ case SqlValue::kString:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kLt || op == FilterOp::kLe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ // TODO(b/307482437): Remove after adding support for double
+ PERFETTO_CHECK(val.type != SqlValue::kDouble);
+
+ // Bounds of the value.
+ if (PERFETTO_UNLIKELY(val.AsLong() > std::numeric_limits<uint32_t>::max())) {
+ if (op == FilterOp::kLe || op == FilterOp::kLt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+ if (PERFETTO_UNLIKELY(val.AsLong() < std::numeric_limits<uint32_t>::min())) {
+ if (op == FilterOp::kGe || op == FilterOp::kGt || op == FilterOp::kNe) {
+ return SearchValidationResult::kAllData;
+ }
+ return SearchValidationResult::kNoData;
+ }
+
+ return SearchValidationResult::kOk;
+}
+
RangeOrBitVector SetIdStorage::Search(FilterOp op,
SqlValue sql_val,
- RowMap::Range range) const {
+ RowMap::Range search_range) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "SetIdStorage::Search",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- PERFETTO_DCHECK(range.end <= size());
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, search_range.end));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ PERFETTO_DCHECK(search_range.end <= size());
+ uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
if (op == FilterOp::kNe) {
- if (sql_val.is_null()) {
- return RangeOrBitVector(Range());
- }
// Not equal is a special operation on binary search, as it doesn't define a
// range, and rather just `not` range returned with `equal` operation.
RowMap::Range eq_range =
- BinarySearchIntrinsic(FilterOp::kEq, sql_val, range);
- BitVector bv(range.start, false);
+ BinarySearchIntrinsic(FilterOp::kEq, val, search_range);
+ BitVector bv(search_range.start, false);
bv.Resize(eq_range.start, true);
bv.Resize(eq_range.end, false);
- bv.Resize(range.end, true);
+ bv.Resize(search_range.end, true);
return RangeOrBitVector(std::move(bv));
}
- return RangeOrBitVector(BinarySearchIntrinsic(op, sql_val, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, val, search_range));
}
RangeOrBitVector SetIdStorage::IndexSearch(FilterOp op,
SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_count,
+ uint32_t indices_size,
bool) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "SetIdStorage::IndexSearch",
- [indices_count, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices_count));
+ [indices_size, op](metatrace::Record* r) {
+ r->AddArg("Count", std::to_string(indices_size));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- // Validate sql_val
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return RangeOrBitVector(Range(indices_count, true));
- }
- return RangeOrBitVector(Range());
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
}
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return RangeOrBitVector(Range(indices_count, true));
- }
- return RangeOrBitVector(Range());
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return RangeOrBitVector(Range(indices_count, true));
- }
- return RangeOrBitVector(Range());
- }
uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
- BitVector::Builder builder(indices_count);
+ BitVector::Builder builder(indices_size);
// TODO(mayzner): Instead of utils::IndexSearchWithComparator, use the
// property of SetId data - that for each index i, data[i] <= i.
@@ -155,7 +220,7 @@
std::greater_equal<uint32_t>(), builder);
break;
case FilterOp::kIsNotNull:
- return RangeOrBitVector(Range(0, indices_count));
+ return RangeOrBitVector(Range(0, indices_size));
case FilterOp::kIsNull:
return RangeOrBitVector(Range());
case FilterOp::kGlob:
@@ -166,34 +231,8 @@
}
Range SetIdStorage::BinarySearchIntrinsic(FilterOp op,
- SqlValue sql_val,
+ SetId val,
Range range) const {
- // Validate sql_value
- if (PERFETTO_UNLIKELY(sql_val.is_null())) {
- if (op == FilterOp::kIsNotNull) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() >
- std::numeric_limits<uint32_t>::max())) {
- if (op == FilterOp::kLe || op == FilterOp::kLt) {
- return range;
- }
- return Range();
- }
-
- if (PERFETTO_UNLIKELY(sql_val.AsLong() <
- std::numeric_limits<uint32_t>::min())) {
- if (op == FilterOp::kGe || op == FilterOp::kGt) {
- return range;
- }
- return Range();
- }
-
- uint32_t val = static_cast<uint32_t>(sql_val.AsLong());
-
switch (op) {
case FilterOp::kEq:
return Range(LowerBoundIntrinsic(values_->data(), val, range),
diff --git a/src/trace_processor/db/storage/set_id_storage.h b/src/trace_processor/db/storage/set_id_storage.h
index ad9fd1a..6886bef 100644
--- a/src/trace_processor/db/storage/set_id_storage.h
+++ b/src/trace_processor/db/storage/set_id_storage.h
@@ -36,6 +36,9 @@
explicit SetIdStorage(const std::vector<uint32_t>* data) : values_(data) {}
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -57,9 +60,9 @@
}
private:
- BitVector IndexSearch(FilterOp, SqlValue, uint32_t*, uint32_t) const;
- RowMap::Range BinarySearchIntrinsic(FilterOp op,
- SqlValue val,
+ BitVector IndexSearch(FilterOp, SetId, uint32_t*, uint32_t) const;
+ RowMap::Range BinarySearchIntrinsic(FilterOp,
+ SetId,
RowMap::Range search_range) const;
// TODO(b/307482437): After the migration vectors should be owned by storage,
diff --git a/src/trace_processor/db/storage/set_id_storage_unittest.cc b/src/trace_processor/db/storage/set_id_storage_unittest.cc
index f487dfa..658b731 100644
--- a/src/trace_processor/db/storage/set_id_storage_unittest.cc
+++ b/src/trace_processor/db/storage/set_id_storage_unittest.cc
@@ -19,11 +19,96 @@
namespace perfetto {
namespace trace_processor {
+
+inline bool operator==(const RowMap::Range& a, const RowMap::Range& b) {
+ return std::tie(a.start, a.end) == std::tie(b.start, b.end);
+}
+
namespace storage {
namespace {
using Range = RowMap::Range;
+TEST(IdStorageUnittest, InvalidSearchConstraints) {
+ std::vector<uint32_t> storage_data{0, 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9};
+ SetIdStorage storage(&storage_data);
+
+ Range test_range(3, 9);
+ Range full_range(0, 9);
+ Range empty_range;
+
+ // NULL checks
+ SqlValue val;
+ val.type = SqlValue::kNull;
+ Range search_result =
+ storage.Search(FilterOp::kIsNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kIsNotNull, val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ // FilterOp checks
+ search_result =
+ storage.Search(FilterOp::kGlob, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kRegex, SqlValue::Long(15), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Type checks
+ search_result =
+ storage.Search(FilterOp::kGe, SqlValue::String("cheese"), test_range)
+ .TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ // Value bounds
+ SqlValue max_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::max()) + 10);
+ search_result =
+ storage.Search(FilterOp::kGe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kGt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kLt, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, max_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ SqlValue min_val = SqlValue::Long(
+ static_cast<int64_t>(std::numeric_limits<uint32_t>::min()) - 1);
+ search_result =
+ storage.Search(FilterOp::kGe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kGt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+ search_result =
+ storage.Search(FilterOp::kNe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, full_range);
+
+ search_result =
+ storage.Search(FilterOp::kLe, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kLt, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+ search_result =
+ storage.Search(FilterOp::kEq, min_val, test_range).TakeIfRange();
+ ASSERT_EQ(search_result, empty_range);
+}
+
TEST(SetIdStorageUnittest, SearchEqSimple) {
std::vector<uint32_t> storage_data{0, 0, 0, 3, 3, 3, 6, 6, 6, 9, 9, 9};
diff --git a/src/trace_processor/db/storage/storage.h b/src/trace_processor/db/storage/storage.h
index 605329b..2a9c9fb 100644
--- a/src/trace_processor/db/storage/storage.h
+++ b/src/trace_processor/db/storage/storage.h
@@ -16,7 +16,6 @@
#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_STORAGE_H_
#define SRC_TRACE_PROCESSOR_DB_STORAGE_STORAGE_H_
-#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/containers/bit_vector.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/db/storage/types.h"
@@ -34,8 +33,24 @@
public:
using StorageProto = protos::pbzero::SerializedColumn_Storage;
+ enum class SearchValidationResult { kOk = 0, kAllData = 1, kNoData = 2 };
+
virtual ~Storage();
+ // Verifies whether any further filtering is needed and if not, whether the
+ // search would return all values or none of them. This allows for skipping
+ // the |Search| and |IndexSearch| in special cases.
+ //
+ // Notes for callers:
+ // * This function is being called by Search and IndexSearch and there is no
+ // need to call it before searches.
+ // * The SqlValue and FilterOp have to be valid in Sqlite: it will crash if
+ // either: value is NULL and operation is different than "IS NULL" and "IS
+ // NOT NULL" or the operation is "IS NULL" and "IS NOT NULL" and value is
+ // different than NULL.
+ virtual SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const = 0;
+
// Searches for elements which match |op| and |value| between |range.start|
// and |range.end|.
//
@@ -49,9 +64,7 @@
// optimize based on this.
// * Implementations should ensure that, if they return a BitVector, it is
// precisely of size |range.end|.
- virtual RangeOrBitVector Search(FilterOp op,
- SqlValue value,
- RowMap::Range range) const = 0;
+ virtual RangeOrBitVector Search(FilterOp, SqlValue, RowMap::Range) const = 0;
// Searches for elements which match |op| and |value| at the positions given
// by |indices| array. The |sorted| flag allows the caller to specify if the
@@ -69,8 +82,8 @@
// Notes for implementors:
// * Implementations should ensure that, if they return a BitVector, it is
// precisely of size |indices_count|.
- virtual RangeOrBitVector IndexSearch(FilterOp op,
- SqlValue value,
+ virtual RangeOrBitVector IndexSearch(FilterOp,
+ SqlValue,
uint32_t* indices,
uint32_t indices_count,
bool sorted) const = 0;
diff --git a/src/trace_processor/db/storage/string_storage.cc b/src/trace_processor/db/storage/string_storage.cc
index d1d88b7..d9c9b98 100644
--- a/src/trace_processor/db/storage/string_storage.cc
+++ b/src/trace_processor/db/storage/string_storage.cc
@@ -19,7 +19,6 @@
#include "perfetto/ext/base/scoped_file.h"
#include "perfetto/ext/base/status_or.h"
#include "perfetto/ext/base/string_utils.h"
-#include "perfetto/trace_processor/basic_types.h"
#include "protos/perfetto/trace_processor/serialization.pbzero.h"
#include "perfetto/base/logging.h"
@@ -27,6 +26,7 @@
#include "src/trace_processor/containers/null_term_string_view.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/containers/string_pool.h"
+#include "src/trace_processor/db/storage/storage.h"
#include "src/trace_processor/db/storage/types.h"
#include "src/trace_processor/db/storage/utils.h"
@@ -163,55 +163,97 @@
} // namespace
+StringStorage::SearchValidationResult StringStorage::ValidateSearchConstraints(
+ SqlValue val,
+ FilterOp op) const {
+ // Type checks.
+ switch (val.type) {
+ case SqlValue::kNull:
+ case SqlValue::kString:
+ break;
+ case SqlValue::kLong:
+ case SqlValue::kDouble:
+ // Any string is always more than any numeric.
+ if (op == FilterOp::kGt || op == FilterOp::kGe) {
+ return Storage::SearchValidationResult::kAllData;
+ }
+ return Storage::SearchValidationResult::kNoData;
+ case SqlValue::kBytes:
+ return Storage::SearchValidationResult::kNoData;
+ }
+
+ return SearchValidationResult::kOk;
+}
+
RangeOrBitVector StringStorage::Search(FilterOp op,
- SqlValue value,
- RowMap::Range range) const {
- PERFETTO_TP_TRACE(metatrace::Category::DB, "StringStorage::LinearSearch",
- [&range, op](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
+ SqlValue sql_val,
+ Range search_range) const {
+ PERFETTO_TP_TRACE(metatrace::Category::DB, "StringStorage::Search",
+ [&search_range, op](metatrace::Record* r) {
+ r->AddArg("Start", std::to_string(search_range.start));
+ r->AddArg("End", std::to_string(search_range.end));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, search_range.end));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
if (is_sorted_) {
if (op != FilterOp::kNe) {
- return RangeOrBitVector(BinarySearchIntrinsic(op, value, range));
+ return RangeOrBitVector(BinarySearchIntrinsic(op, sql_val, search_range));
}
// Not equal is a special operation on binary search, as it doesn't define
// a range, and rather just `not` range returned with `equal` operation.
- RowMap::Range r = BinarySearchIntrinsic(FilterOp::kEq, value, range);
+ Range r = BinarySearchIntrinsic(FilterOp::kEq, sql_val, search_range);
BitVector bv(r.start, true);
- bv.Resize(r.end, false);
- bv.Resize(range.end, true);
+ bv.Resize(r.end);
+ bv.Resize(search_range.end, true);
return RangeOrBitVector(std::move(bv));
}
- return RangeOrBitVector(LinearSearchInternal(op, value, range));
+ return RangeOrBitVector(LinearSearch(op, sql_val, search_range));
}
RangeOrBitVector StringStorage::IndexSearch(FilterOp op,
- SqlValue value,
+ SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_count,
- bool sorted) const {
+ uint32_t indices_size,
+ bool indices_sorted) const {
PERFETTO_TP_TRACE(metatrace::Category::DB, "StringStorage::IndexSearch",
- [indices_count, op](metatrace::Record* r) {
- r->AddArg("Count", std::to_string(indices_count));
+ [indices_size, op](metatrace::Record* r) {
+ r->AddArg("Count", std::to_string(indices_size));
r->AddArg("Op",
std::to_string(static_cast<uint32_t>(op)));
});
- if (sorted) {
+ // After this switch we assume the search is valid.
+ switch (ValidateSearchConstraints(sql_val, op)) {
+ case SearchValidationResult::kOk:
+ break;
+ case SearchValidationResult::kAllData:
+ return RangeOrBitVector(Range(0, indices_size));
+ case SearchValidationResult::kNoData:
+ return RangeOrBitVector(Range());
+ }
+
+ if (indices_sorted) {
return RangeOrBitVector(
- BinarySearchExtrinsic(op, value, indices, indices_count));
+ BinarySearchExtrinsic(op, sql_val, indices, indices_size));
}
return RangeOrBitVector(
- IndexSearchInternal(op, value, indices, indices_count, sorted));
+ IndexSearchInternal(op, sql_val, indices, indices_size));
}
-BitVector StringStorage::LinearSearchInternal(FilterOp op,
- SqlValue sql_val,
- RowMap::Range range) const {
+BitVector StringStorage::LinearSearch(FilterOp op,
+ SqlValue sql_val,
+ RowMap::Range range) const {
if (sql_val.is_null() &&
(op != FilterOp::kIsNotNull && op != FilterOp::kIsNull)) {
return BitVector(range.end, false);
@@ -227,16 +269,6 @@
? StringPool::Id::Null()
: string_pool_->InternString(base::StringView(sql_val.AsString()));
const StringPool::Id* start = values_->data() + range.start;
- PERFETTO_TP_TRACE(
- metatrace::Category::DB, "StringStorage::Search",
- [range, op, &sql_val](metatrace::Record* r) {
- r->AddArg("Start", std::to_string(range.start));
- r->AddArg("End", std::to_string(range.end));
- r->AddArg("Op", std::to_string(static_cast<uint32_t>(op)));
- r->AddArg("String", sql_val.type == SqlValue::Type::kString
- ? sql_val.AsString()
- : "NULL");
- });
BitVector::Builder builder(range.end, range.start);
switch (op) {
@@ -318,11 +350,11 @@
return std::move(builder).Build();
}
-RangeOrBitVector StringStorage::IndexSearchInternal(FilterOp op,
- SqlValue sql_val,
- uint32_t* indices,
- uint32_t indices_size,
- bool) const {
+RangeOrBitVector StringStorage::IndexSearchInternal(
+ FilterOp op,
+ SqlValue sql_val,
+ uint32_t* indices,
+ uint32_t indices_size) const {
if (sql_val.is_null() &&
(op != FilterOp::kIsNotNull && op != FilterOp::kIsNull)) {
return RangeOrBitVector(Range());
diff --git a/src/trace_processor/db/storage/string_storage.h b/src/trace_processor/db/storage/string_storage.h
index 9f14917..1e89f24 100644
--- a/src/trace_processor/db/storage/string_storage.h
+++ b/src/trace_processor/db/storage/string_storage.h
@@ -16,6 +16,7 @@
#ifndef SRC_TRACE_PROCESSOR_DB_STORAGE_STRING_STORAGE_H_
#define SRC_TRACE_PROCESSOR_DB_STORAGE_STRING_STORAGE_H_
+#include "perfetto/trace_processor/basic_types.h"
#include "src/trace_processor/containers/row_map.h"
#include "src/trace_processor/containers/string_pool.h"
#include "src/trace_processor/db/storage/storage.h"
@@ -38,6 +39,9 @@
bool is_sorted = false)
: values_(data), string_pool_(string_pool), is_sorted_(is_sorted) {}
+ SearchValidationResult ValidateSearchConstraints(SqlValue,
+ FilterOp) const override;
+
RangeOrBitVector Search(FilterOp op,
SqlValue value,
RowMap::Range range) const override;
@@ -58,13 +62,12 @@
}
private:
- BitVector LinearSearchInternal(FilterOp, SqlValue, RowMap::Range) const;
+ BitVector LinearSearch(FilterOp, SqlValue, RowMap::Range) const;
RangeOrBitVector IndexSearchInternal(FilterOp op,
SqlValue sql_val,
uint32_t* indices,
- uint32_t indices_size,
- bool) const;
+ uint32_t indices_size) const;
RowMap::Range BinarySearchExtrinsic(FilterOp,
SqlValue,
diff --git a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
index 557512c..225f951 100644
--- a/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
+++ b/src/trace_processor/importers/ftrace/ftrace_descriptors.cc
@@ -24,7 +24,7 @@
namespace trace_processor {
namespace {
-std::array<FtraceMessageDescriptor, 487> descriptors{{
+std::array<FtraceMessageDescriptor, 488> descriptors{{
{nullptr, 0, {}},
{nullptr, 0, {}},
{nullptr, 0, {}},
@@ -5351,6 +5351,30 @@
{"cmd", ProtoSchemaType::kUint32},
},
},
+ {
+ "sched_switch_with_ctrs",
+ 17,
+ {
+ {},
+ {"old_pid", ProtoSchemaType::kInt32},
+ {"new_pid", ProtoSchemaType::kInt32},
+ {"cctr", ProtoSchemaType::kUint32},
+ {"ctr0", ProtoSchemaType::kUint32},
+ {"ctr1", ProtoSchemaType::kUint32},
+ {"ctr2", ProtoSchemaType::kUint32},
+ {"ctr3", ProtoSchemaType::kUint32},
+ {"lctr0", ProtoSchemaType::kUint32},
+ {"lctr1", ProtoSchemaType::kUint32},
+ {"ctr4", ProtoSchemaType::kUint32},
+ {"ctr5", ProtoSchemaType::kUint32},
+ {"prev_comm", ProtoSchemaType::kString},
+ {"prev_pid", ProtoSchemaType::kInt32},
+ {"cyc", ProtoSchemaType::kUint32},
+ {"inst", ProtoSchemaType::kUint32},
+ {"stallbm", ProtoSchemaType::kUint32},
+ {"l3dm", ProtoSchemaType::kUint32},
+ },
+ },
}};
} // namespace
diff --git a/src/traced/probes/ftrace/event_info.cc b/src/traced/probes/ftrace/event_info.cc
index 7ad51cf..62bd4b9 100644
--- a/src/traced/probes/ftrace/event_info.cc
+++ b/src/traced/probes/ftrace/event_info.cc
@@ -7361,6 +7361,64 @@
kUnsetFtraceId,
430,
kUnsetSize},
+ {"sched_switch_with_ctrs",
+ "perf_trace_counters",
+ {
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "old_pid", 1, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "new_pid", 2, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "cctr", 3, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr0", 4, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr1", 5, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr2", 6, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr3", 7, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "lctr0", 8, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "lctr1", 9, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr4", 10, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "ctr5", 11, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "prev_comm", 12, ProtoSchemaType::kString,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "prev_pid", 13, ProtoSchemaType::kInt32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "cyc", 14, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "inst", 15, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "stallbm", 16, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ {kUnsetOffset, kUnsetSize, FtraceFieldType::kInvalidFtraceFieldType,
+ "l3dm", 17, ProtoSchemaType::kUint32,
+ TranslationStrategy::kInvalidTranslationStrategy},
+ },
+ kUnsetFtraceId,
+ 487,
+ kUnsetSize},
{"cpu_frequency",
"power",
{
diff --git a/src/traced/probes/ftrace/test/data/synthetic/events/perf_trace_counters/sched_switch_with_ctrs/format b/src/traced/probes/ftrace/test/data/synthetic/events/perf_trace_counters/sched_switch_with_ctrs/format
new file mode 100644
index 0000000..e357f11
--- /dev/null
+++ b/src/traced/probes/ftrace/test/data/synthetic/events/perf_trace_counters/sched_switch_with_ctrs/format
@@ -0,0 +1,16 @@
+name: sched_switch_with_ctrs
+ID: 1237
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:char prev_comm[16]; offset:8; size:16; signed:0;
+ field:pid_t prev_pid; offset:24; size:4; signed:1;
+ field:u32 cyc; offset:28; size:4; signed:0;
+ field:u32 inst; offset:32; size:4; signed:0;
+ field:u32 stallbm; offset:36; size:4; signed:0;
+ field:u32 l3dm; offset:40; size:4; signed:0;
+
+print fmt: "prev_comm=%s, prev_pid=%d, CYC=%u, INST=%u, STALLBM=%u, L3DM=%u", REC->prev_comm, REC->prev_pid, REC->cyc, REC->inst, REC->stallbm, REC->l3dm
diff --git a/ui/src/frontend/base_slice_track.ts b/ui/src/frontend/base_slice_track.ts
index 0995ff2..2dbe0f2 100644
--- a/ui/src/frontend/base_slice_track.ts
+++ b/ui/src/frontend/base_slice_track.ts
@@ -446,8 +446,10 @@
if (slice.flags & SLICE_FLAGS_INSTANT) {
this.drawChevron(ctx, slice.x, y, sliceHeight);
} else if (slice.flags & SLICE_FLAGS_INCOMPLETE) {
- const w = CROP_INCOMPLETE_SLICE_FLAG.get() ? slice.w : Math.max(slice.w - 2, 2);
- drawIncompleteSlice(ctx, slice.x, y, w, sliceHeight, !CROP_INCOMPLETE_SLICE_FLAG.get());
+ const w = CROP_INCOMPLETE_SLICE_FLAG.get() ? slice.w :
+ Math.max(slice.w - 2, 2);
+ drawIncompleteSlice(
+ ctx, slice.x, y, w, sliceHeight, !CROP_INCOMPLETE_SLICE_FLAG.get());
} else {
const w = Math.max(slice.w, SLICE_MIN_WIDTH_PX);
ctx.fillRect(slice.x, y, w, sliceHeight);
@@ -815,12 +817,15 @@
}
for (const slice of this.incomplete) {
+ const visibleTimeScale = globals.frontendLocalState.visibleTimeScale;
const startPx = CROP_INCOMPLETE_SLICE_FLAG.get() ?
- globals.frontendLocalState.visibleTimeScale.timeToPx(slice.startNsQ) : slice.x;
+ visibleTimeScale.timeToPx(slice.startNsQ) :
+ slice.x;
const cropUnfinishedSlicesCondition = CROP_INCOMPLETE_SLICE_FLAG.get() ?
startPx + INCOMPLETE_SLICE_WIDTH_PX >= x : true;
- if (slice.depth === depth && startPx <= x && cropUnfinishedSlicesCondition) {
+ if (slice.depth === depth && startPx <= x &&
+ cropUnfinishedSlicesCondition) {
return slice;
}
}
diff --git a/ui/src/tracks/chrome_critical_user_interactions/index.ts b/ui/src/tracks/chrome_critical_user_interactions/index.ts
index 73e7428..2e0a381 100644
--- a/ui/src/tracks/chrome_critical_user_interactions/index.ts
+++ b/ui/src/tracks/chrome_critical_user_interactions/index.ts
@@ -23,7 +23,6 @@
NAMED_ROW,
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
import {
Plugin,
PluginContext,
@@ -80,10 +79,6 @@
CustomSqlTableSliceTrack<CriticalUserInteractionSliceTrackTypes> {
static readonly kind = CRITICAL_USER_INTERACTIONS_KIND;
- static create(args: NewTrackArgs): TrackBase {
- return new CriticalUserInteractionTrack(args);
- }
-
getSqlDataSource(): CustomSqlTableDefConfig {
return {
columns: ['scoped_id AS id', 'name', 'ts', 'dur', 'type'],
diff --git a/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts b/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts
index 95d1924..5b87694 100644
--- a/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/chrome_tasks_scroll_jank_track.ts
@@ -17,7 +17,7 @@
NamedSliceTrack,
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
+import {NewTrackArgs} from '../../frontend/track';
import {Engine} from '../../trace_processor/engine';
import {NUM} from '../../trace_processor/query_result';
@@ -35,9 +35,6 @@
export class ChromeTasksScrollJankTrack extends
NamedSliceTrack<ChromeTasksScrollJankTrackTypes> {
static readonly kind = 'org.chromium.ScrollJank.BrowserUIThreadLongTasks';
- static create(args: NewTrackArgs): TrackBase {
- return new ChromeTasksScrollJankTrack(args);
- }
constructor(args: NewTrackArgs) {
super(args);
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
index a5bf0aa..14d296a 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_jank_v3_track.ts
@@ -14,7 +14,7 @@
import {globals} from '../../frontend/globals';
import {NamedRow, NamedSliceTrackTypes} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
+import {NewTrackArgs} from '../../frontend/track';
import {PrimaryTrackSortKey, Slice} from '../../public';
import {
CustomSqlDetailsPanelConfig,
@@ -38,10 +38,6 @@
CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
static readonly kind = 'org.chromium.ScrollJank.scroll_jank_v3_track';
- static create(args: NewTrackArgs): TrackBase {
- return new ScrollJankV3Track(args);
- }
-
constructor(args: NewTrackArgs) {
super(args);
ScrollJankPluginState.getInstance().registerTrack({
diff --git a/ui/src/tracks/chrome_scroll_jank/scroll_track.ts b/ui/src/tracks/chrome_scroll_jank/scroll_track.ts
index 7eaba65..a888844 100644
--- a/ui/src/tracks/chrome_scroll_jank/scroll_track.ts
+++ b/ui/src/tracks/chrome_scroll_jank/scroll_track.ts
@@ -13,13 +13,14 @@
// limitations under the License.
import {NamedSliceTrackTypes} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
+import {NewTrackArgs} from '../../frontend/track';
import {PrimaryTrackSortKey} from '../../public';
import {
CustomSqlDetailsPanelConfig,
CustomSqlTableDefConfig,
CustomSqlTableSliceTrack,
} from '../custom_sql_table_slices';
+
import {
SCROLL_JANK_GROUP_ID,
ScrollJankPluginState,
@@ -33,9 +34,6 @@
export class TopLevelScrollTrack extends
CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
public static kind = CHROME_TOPLEVEL_SCROLLS_KIND;
- static create(args: NewTrackArgs): TrackBase {
- return new TopLevelScrollTrack(args);
- }
getSqlDataSource(): CustomSqlTableDefConfig {
return {
diff --git a/ui/src/tracks/cpu_freq/index.ts b/ui/src/tracks/cpu_freq/index.ts
index c42b480..f07f263 100644
--- a/ui/src/tracks/cpu_freq/index.ts
+++ b/ui/src/tracks/cpu_freq/index.ts
@@ -274,10 +274,6 @@
const RECT_HEIGHT = 20;
class CpuFreqTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): CpuFreqTrack {
- return new CpuFreqTrack(args);
- }
-
private mousePos = {x: 0, y: 0};
private hoveredValue: number|undefined = undefined;
private hoveredTs: time|undefined = undefined;
diff --git a/ui/src/tracks/cpu_profile/index.ts b/ui/src/tracks/cpu_profile/index.ts
index 143e057..59ad373 100644
--- a/ui/src/tracks/cpu_profile/index.ts
+++ b/ui/src/tracks/cpu_profile/index.ts
@@ -90,10 +90,6 @@
}
class CpuProfileTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): CpuProfileTrack {
- return new CpuProfileTrack(args);
- }
-
private centerY = this.getHeight() / 2 + BAR_HEIGHT;
private markerWidth = (this.getHeight() - MARGIN_TOP - BAR_HEIGHT) / 2;
private hoveredTs: time|undefined = undefined;
diff --git a/ui/src/tracks/cpu_slices/index.ts b/ui/src/tracks/cpu_slices/index.ts
index 28497a2..0e9c470 100644
--- a/ui/src/tracks/cpu_slices/index.ts
+++ b/ui/src/tracks/cpu_slices/index.ts
@@ -206,10 +206,6 @@
const TRACK_HEIGHT = MARGIN_TOP * 2 + RECT_HEIGHT;
class CpuSliceTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): CpuSliceTrack {
- return new CpuSliceTrack(args);
- }
-
private mousePos?: {x: number, y: number};
private utidHoveredInThisTrack = -1;
diff --git a/ui/src/tracks/null_track/index.ts b/ui/src/tracks/null_track/index.ts
index 1b2bb24..7bc77b5 100644
--- a/ui/src/tracks/null_track/index.ts
+++ b/ui/src/tracks/null_track/index.ts
@@ -28,10 +28,6 @@
super(args);
}
- static create(args: NewTrackArgs): NullTrack {
- return new NullTrack(args);
- }
-
getHeight(): number {
return 30;
}
@@ -50,7 +46,7 @@
uri: NULL_TRACK_URI,
displayName: 'Null Track',
kind: NULL_TRACK_KIND,
- track: ({trackKey}) => NullTrack.create({
+ track: ({trackKey}) => new NullTrack({
engine: ctx.engine,
trackKey,
}),
diff --git a/ui/src/tracks/perf_samples_profile/index.ts b/ui/src/tracks/perf_samples_profile/index.ts
index 5da1f71..dd30045 100644
--- a/ui/src/tracks/perf_samples_profile/index.ts
+++ b/ui/src/tracks/perf_samples_profile/index.ts
@@ -87,10 +87,6 @@
const RECT_HEIGHT = 30.5;
class PerfSamplesProfileTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): PerfSamplesProfileTrack {
- return new PerfSamplesProfileTrack(args);
- }
-
private centerY = this.getHeight() / 2;
private markerWidth = (this.getHeight() - MARGIN_TOP) / 2;
private hoveredTs: time|undefined = undefined;
diff --git a/ui/src/tracks/process_summary/process_scheduling_track.ts b/ui/src/tracks/process_summary/process_scheduling_track.ts
index f7ad4c1..a955278 100644
--- a/ui/src/tracks/process_summary/process_scheduling_track.ts
+++ b/ui/src/tracks/process_summary/process_scheduling_track.ts
@@ -189,10 +189,6 @@
}
export class ProcessSchedulingTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): ProcessSchedulingTrack {
- return new ProcessSchedulingTrack(args);
- }
-
private mousePos?: {x: number, y: number};
private utidHoveredInThisTrack = -1;
diff --git a/ui/src/tracks/process_summary/process_summary_track.ts b/ui/src/tracks/process_summary/process_summary_track.ts
index a1b32c7..ee0bcc8 100644
--- a/ui/src/tracks/process_summary/process_summary_track.ts
+++ b/ui/src/tracks/process_summary/process_summary_track.ts
@@ -142,10 +142,6 @@
const SUMMARY_HEIGHT = TRACK_HEIGHT - MARGIN_TOP;
export class ProcessSummaryTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): ProcessSummaryTrack {
- return new ProcessSummaryTrack(args);
- }
-
constructor(args: NewTrackArgs) {
super(args);
}
diff --git a/ui/src/tracks/screenshots/index.ts b/ui/src/tracks/screenshots/index.ts
index 32df756..4af3c94 100644
--- a/ui/src/tracks/screenshots/index.ts
+++ b/ui/src/tracks/screenshots/index.ts
@@ -16,7 +16,6 @@
import {
NamedSliceTrackTypes,
} from '../../frontend/named_slice_track';
-import {NewTrackArgs, TrackBase} from '../../frontend/track';
import {
Plugin,
PluginContext,
@@ -36,9 +35,6 @@
class ScreenshotsTrack extends CustomSqlTableSliceTrack<NamedSliceTrackTypes> {
static readonly kind = 'dev.perfetto.ScreenshotsTrack';
- static create(args: NewTrackArgs): TrackBase {
- return new ScreenshotsTrack(args);
- }
getSqlDataSource(): CustomSqlTableDefConfig {
return {
diff --git a/ui/src/tracks/thread_state/index.ts b/ui/src/tracks/thread_state/index.ts
index 5882283..7544ddb 100644
--- a/ui/src/tracks/thread_state/index.ts
+++ b/ui/src/tracks/thread_state/index.ts
@@ -179,10 +179,6 @@
const EXCESS_WIDTH = 10;
class ThreadStateTrack extends TrackAdapter<Config, Data> {
- static create(args: NewTrackArgs): ThreadStateTrack {
- return new ThreadStateTrack(args);
- }
-
constructor(args: NewTrackArgs) {
super(args);
}