Merge "tp: FilterOp::Regex"
diff --git a/Android.bp b/Android.bp
index 8cd7aff..9e9630c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2103,6 +2103,7 @@
":perfetto_src_trace_processor_util_proto_profiler",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
+ ":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
@@ -10842,6 +10843,11 @@
],
}
+// GN: //src/trace_processor/util:regex
+filegroup {
+ name: "perfetto_src_trace_processor_util_regex",
+}
+
// GN: //src/trace_processor/util:sql_argument
filegroup {
name: "perfetto_src_trace_processor_util_sql_argument",
@@ -12314,6 +12320,7 @@
":perfetto_src_trace_processor_util_proto_profiler",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
+ ":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
@@ -12998,6 +13005,7 @@
":perfetto_src_trace_processor_util_proto_profiler",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
+ ":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
@@ -13226,6 +13234,7 @@
":perfetto_src_trace_processor_util_proto_profiler",
":perfetto_src_trace_processor_util_proto_to_args_parser",
":perfetto_src_trace_processor_util_protozero_to_text",
+ ":perfetto_src_trace_processor_util_regex",
":perfetto_src_trace_processor_util_sql_argument",
":perfetto_src_trace_processor_util_stack_traces_util",
":perfetto_src_trace_processor_util_stdlib",
diff --git a/BUILD b/BUILD
index ff78198..73afdf9 100644
--- a/BUILD
+++ b/BUILD
@@ -129,6 +129,7 @@
":src_trace_processor_util_proto_profiler",
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_text",
+ ":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
@@ -2509,6 +2510,14 @@
],
)
+# GN target: //src/trace_processor/util:regex
+perfetto_filegroup(
+ name = "src_trace_processor_util_regex",
+ srcs = [
+ "src/trace_processor/util/regex.h",
+ ],
+)
+
# GN target: //src/trace_processor/util:sql_argument
perfetto_filegroup(
name = "src_trace_processor_util_sql_argument",
@@ -5187,6 +5196,7 @@
":src_trace_processor_util_proto_profiler",
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_text",
+ ":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
@@ -5350,6 +5360,7 @@
":src_trace_processor_util_proto_profiler",
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_text",
+ ":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
@@ -5568,6 +5579,7 @@
":src_trace_processor_util_proto_profiler",
":src_trace_processor_util_proto_to_args_parser",
":src_trace_processor_util_protozero_to_text",
+ ":src_trace_processor_util_regex",
":src_trace_processor_util_sql_argument",
":src_trace_processor_util_stack_traces_util",
":src_trace_processor_util_stdlib",
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 84eb5c3..221e7b4 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -182,6 +182,7 @@
"util",
"util:gzip",
"util:protozero_to_text",
+ "util:regex",
"util:stdlib",
"views",
]
diff --git a/src/trace_processor/db/BUILD.gn b/src/trace_processor/db/BUILD.gn
index 3117602..7a926f9 100644
--- a/src/trace_processor/db/BUILD.gn
+++ b/src/trace_processor/db/BUILD.gn
@@ -40,6 +40,7 @@
"../../../include/perfetto/trace_processor",
"../containers",
"../util:glob",
+ "../util:regex",
"overlays",
"storage",
]
diff --git a/src/trace_processor/db/column.cc b/src/trace_processor/db/column.cc
index 01c6b5e..fbb6236 100644
--- a/src/trace_processor/db/column.cc
+++ b/src/trace_processor/db/column.cc
@@ -13,12 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#include "src/trace_processor/db/column.h"
+#include "perfetto/base/logging.h"
#include "src/trace_processor/db/compare.h"
+#include "src/trace_processor/db/storage/utils.h"
#include "src/trace_processor/db/table.h"
#include "src/trace_processor/util/glob.h"
+#include "src/trace_processor/util/regex.h"
namespace perfetto {
namespace trace_processor {
@@ -281,6 +283,7 @@
case FilterOp::kGlob:
rm->Clear();
break;
+ case FilterOp::kRegex:
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
PERFETTO_FATAL("Should be handled above");
@@ -359,6 +362,22 @@
});
break;
}
+ case FilterOp::kRegex: {
+ if constexpr (regex::IsRegexSupported()) {
+ auto regex = regex::Regex::Create(str_value.c_str());
+ if (!regex.status().ok()) {
+ rm->Clear();
+ break;
+ }
+ overlay().FilterInto(rm, [this, ®ex](uint32_t idx) {
+ auto v = GetStringPoolStringAtIdx(idx);
+ return v.data() != nullptr && regex->Search(v.c_str());
+ });
+ } else {
+ PERFETTO_FATAL("Regex not supported");
+ }
+ break;
+ }
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
PERFETTO_FATAL("Should be handled above");
@@ -415,6 +434,7 @@
});
break;
case FilterOp::kGlob:
+ case FilterOp::kRegex:
rm->Clear();
break;
case FilterOp::kIsNull:
diff --git a/src/trace_processor/db/column.h b/src/trace_processor/db/column.h
index 7fa0fcc..8ed49f9 100644
--- a/src/trace_processor/db/column.h
+++ b/src/trace_processor/db/column.h
@@ -388,6 +388,10 @@
return Constraint{index_in_table_, FilterOp::kGlob, value};
}
+ Constraint regex_value(SqlValue value) const {
+ return Constraint{index_in_table_, FilterOp::kRegex, value};
+ }
+
// Returns an Order for each Order type for this Column.
Order ascending() const { return Order{index_in_table_, false}; }
Order descending() const { return Order{index_in_table_, true}; }
@@ -541,6 +545,7 @@
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
case FilterOp::kGlob:
+ case FilterOp::kRegex:
break;
}
return false;
diff --git a/src/trace_processor/db/query_executor_benchmark.cc b/src/trace_processor/db/query_executor_benchmark.cc
index 6f325ce..963552f 100644
--- a/src/trace_processor/db/query_executor_benchmark.cc
+++ b/src/trace_processor/db/query_executor_benchmark.cc
@@ -273,6 +273,13 @@
BENCHMARK(BM_QESliceTableNameGlob)->ArgsProduct({{DB::V1, DB::V2}});
+static void BM_QESliceTableNameRegex(benchmark::State& state) {
+ SliceTableForBenchmark table(state);
+ BenchmarkSliceTable(state, table, {table.table_.name().regex(".*Pool.*")});
+}
+
+BENCHMARK(BM_QESliceTableNameRegex)->ArgsProduct({{DB::V1, DB::V2}});
+
static void BM_QESliceTableSorted(benchmark::State& state) {
SliceTableForBenchmark table(state);
BenchmarkSliceTable(state, table, {table.table_.ts().gt(1000)});
diff --git a/src/trace_processor/db/query_executor_unittest.cc b/src/trace_processor/db/query_executor_unittest.cc
index 4841d19..d8b79d7 100644
--- a/src/trace_processor/db/query_executor_unittest.cc
+++ b/src/trace_processor/db/query_executor_unittest.cc
@@ -445,6 +445,64 @@
ASSERT_EQ(res.Get(0), 2u);
}
+TEST(QueryExecutor, StringBinarySearchRegex) {
+ StringPool pool;
+ std::vector<std::string> strings{"cheese", "pasta", "pizza",
+ "pierogi", "onion", "fries"};
+ std::vector<StringPool::Id> ids;
+ for (const auto& string : strings) {
+ ids.push_back(pool.InternString(base::StringView(string)));
+ }
+ ids.insert(ids.begin() + 3, StringPool::Id::Null());
+ StringStorage storage(&pool, ids.data(), 7);
+
+ // Final vec {"cheese", "pasta", "NULL", "pierogi", "fries"}.
+ BitVector selector_bv{1, 1, 0, 1, 1, 0, 1};
+ SelectorOverlay selector_overlay(&selector_bv);
+
+ // Create the column.
+ OverlaysVec overlays_vec;
+ overlays_vec.emplace_back(&selector_overlay);
+ SimpleColumn col{overlays_vec, &storage};
+
+ // Filter.
+ Constraint c{0, FilterOp::kRegex, SqlValue::String("p.*")};
+ QueryExecutor exec({col}, 5);
+ RowMap res = exec.Filter({c});
+
+ ASSERT_EQ(res.size(), 2u);
+ ASSERT_EQ(res.Get(0), 1u);
+ ASSERT_EQ(res.Get(1), 3u);
+}
+
+TEST(QueryExecutor, StringBinarySearchRegexWithNum) {
+ StringPool pool;
+ std::vector<std::string> strings{"cheese", "pasta", "pizza",
+ "pierogi", "onion", "fries"};
+ std::vector<StringPool::Id> ids;
+ for (const auto& string : strings) {
+ ids.push_back(pool.InternString(base::StringView(string)));
+ }
+ ids.insert(ids.begin() + 3, StringPool::Id::Null());
+ StringStorage storage(&pool, ids.data(), 7);
+
+ // Final vec {"cheese", "pasta", "NULL", "pierogi", "fries"}.
+ BitVector selector_bv{1, 1, 0, 1, 1, 0, 1};
+ SelectorOverlay selector_overlay(&selector_bv);
+
+ // Create the column.
+ OverlaysVec overlays_vec;
+ overlays_vec.emplace_back(&selector_overlay);
+ SimpleColumn col{overlays_vec, &storage};
+
+ // Filter.
+ Constraint c{0, FilterOp::kRegex, SqlValue::Long(4)};
+ QueryExecutor exec({col}, 5);
+ RowMap res = exec.Filter({c});
+
+ ASSERT_EQ(res.size(), 0u);
+}
+
} // namespace
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/db/storage/BUILD.gn b/src/trace_processor/db/storage/BUILD.gn
index 69e26c1..f27c9db 100644
--- a/src/trace_processor/db/storage/BUILD.gn
+++ b/src/trace_processor/db/storage/BUILD.gn
@@ -31,8 +31,10 @@
"../..:metatrace",
"../../../../gn:default_deps",
"../../../../include/perfetto/trace_processor:basic_types",
+ "../../../base",
"../../containers",
"../../util:glob",
+ "../../util:regex",
]
}
diff --git a/src/trace_processor/db/storage/id_storage.cc b/src/trace_processor/db/storage/id_storage.cc
index 4bec887..0f08647 100644
--- a/src/trace_processor/db/storage/id_storage.cc
+++ b/src/trace_processor/db/storage/id_storage.cc
@@ -93,7 +93,8 @@
if (op == FilterOp::kIsNotNull)
return RangeOrBitVector(BitVector(indices_size, true));
- if (op == FilterOp::kIsNull || op == FilterOp::kGlob || sql_val.is_null() ||
+ if (op == FilterOp::kIsNull || op == FilterOp::kGlob ||
+ op == FilterOp::kRegex || sql_val.is_null() ||
sql_val.AsLong() > std::numeric_limits<uint32_t>::max() ||
sql_val.AsLong() < std::numeric_limits<uint32_t>::min())
return RangeOrBitVector(BitVector(indices_size, false));
@@ -120,6 +121,7 @@
return IndexSearchWithComparator(val, indices, indices_size,
std::greater_equal<uint32_t>());
case FilterOp::kGlob:
+ case FilterOp::kRegex:
case FilterOp::kIsNotNull:
case FilterOp::kIsNull:
PERFETTO_FATAL("Illegal argument");
@@ -165,6 +167,7 @@
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
case FilterOp::kGlob:
+ case FilterOp::kRegex:
return RowMap::Range();
}
return RowMap::Range();
diff --git a/src/trace_processor/db/storage/numeric_storage.cc b/src/trace_processor/db/storage/numeric_storage.cc
index 4768848..31f1245 100644
--- a/src/trace_processor/db/storage/numeric_storage.cc
+++ b/src/trace_processor/db/storage/numeric_storage.cc
@@ -91,6 +91,7 @@
case FilterOp::kLt:
return FilterOpVariant<T>(std::less<T>());
case FilterOp::kGlob:
+ case FilterOp::kRegex:
case FilterOp::kIsNotNull:
case FilterOp::kIsNull:
PERFETTO_FATAL("Not a valid operation on numeric type.");
@@ -187,6 +188,7 @@
return utils::LinearSearchWithComparator(
typed_val, start, std::greater_equal<T>(), builder);
case FilterOp::kGlob:
+ case FilterOp::kRegex:
case FilterOp::kIsNotNull:
case FilterOp::kIsNull:
PERFETTO_DFATAL("Illegal argument");
@@ -318,6 +320,7 @@
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
case FilterOp::kGlob:
+ case FilterOp::kRegex:
return RowMap::Range();
}
return RowMap::Range();
@@ -357,6 +360,7 @@
case FilterOp::kIsNull:
case FilterOp::kIsNotNull:
case FilterOp::kGlob:
+ case FilterOp::kRegex:
return RowMap::Range();
}
return RowMap::Range();
diff --git a/src/trace_processor/db/storage/string_storage.cc b/src/trace_processor/db/storage/string_storage.cc
index 51676d1..2a31652 100644
--- a/src/trace_processor/db/storage/string_storage.cc
+++ b/src/trace_processor/db/storage/string_storage.cc
@@ -15,6 +15,10 @@
*/
#include "src/trace_processor/db/storage/string_storage.h"
+#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 "perfetto/base/logging.h"
#include "src/trace_processor/containers/bit_vector.h"
@@ -25,6 +29,7 @@
#include "src/trace_processor/db/storage/utils.h"
#include "src/trace_processor/tp_metatrace.h"
#include "src/trace_processor/util/glob.h"
+#include "src/trace_processor/util/regex.h"
namespace perfetto {
namespace trace_processor {
@@ -90,6 +95,35 @@
std::vector<uint8_t> matches_;
};
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+struct Regex {
+ bool operator()(StringPool::Id rhs, regex::Regex& pattern) const {
+ return rhs != StringPool::Id::Null() &&
+ pattern.Search(pool_->Get(rhs).c_str());
+ }
+ const StringPool* pool_;
+};
+
+struct RegexFullStringPool {
+ RegexFullStringPool(StringPool* pool, const regex::Regex& regex)
+ : pool_(pool), matches_(pool->MaxSmallStringId().raw_id()) {
+ PERFETTO_DCHECK(!pool->HasLargeString());
+ for (auto it = pool->CreateIterator(); it; ++it) {
+ auto id = it.StringId();
+ matches_[id.raw_id()] =
+ id != StringPool::Id::Null() && regex.Search(pool_->Get(id).c_str());
+ }
+ }
+ bool operator()(StringPool::Id rhs, StringPool::Id) {
+ return matches_[rhs.raw_id()];
+ }
+ StringPool* pool_;
+ std::vector<uint8_t> matches_;
+};
+
+#endif
+
struct IsNull {
bool operator()(StringPool::Id rhs, StringPool::Id) const {
return rhs == StringPool::Id::Null();
@@ -112,6 +146,11 @@
return RangeOrBitVector(Range());
}
+ if (sql_val.type != SqlValue::kString &&
+ (op == FilterOp::kGlob || op == FilterOp::kRegex)) {
+ return RangeOrBitVector(Range());
+ }
+
StringPool::Id val =
(op == FilterOp::kIsNull || op == FilterOp::kIsNotNull)
? StringPool::Id::Null()
@@ -163,17 +202,10 @@
break;
}
- // For very big string pools (or small ranges) run a standard glob
- // function.
- if (range.size() < string_pool_->size()) {
- utils::LinearSearchWithComparator(std::move(matcher), start,
- Glob{string_pool_}, builder);
- break;
- }
-
- // Optimised glob function works only if there are no large strings in
- // string pool.
- if (string_pool_->HasLargeString()) {
+ // For very big string pools (or small ranges) or pools with large strings
+ // run a standard glob function.
+ if (range.size() < string_pool_->size() ||
+ string_pool_->HasLargeString()) {
utils::LinearSearchWithComparator(std::move(matcher), start,
Glob{string_pool_}, builder);
break;
@@ -184,6 +216,31 @@
GlobFullStringPool{string_pool_, matcher}, builder);
break;
}
+ case FilterOp::kRegex:
+ if constexpr (regex::IsRegexSupported()) {
+ base::StatusOr<regex::Regex> regex =
+ regex::Regex::Create(sql_val.AsString());
+ if (!regex.status().ok()) {
+ break;
+ }
+
+ // For very big string pools (or small ranges) or pools with large
+ // strings run a standard regex function.
+ if (range.size() < string_pool_->size() ||
+ string_pool_->HasLargeString()) {
+ utils::LinearSearchWithComparator(std::move(regex.value()), start,
+ Regex{string_pool_}, builder);
+ break;
+ }
+
+ utils::LinearSearchWithComparator(
+ StringPool::Id::Null(), start,
+ RegexFullStringPool{string_pool_, regex.value()}, builder);
+ break;
+ } else {
+ PERFETTO_DFATAL("Regex not supported");
+ }
+
case FilterOp::kIsNull:
utils::LinearSearchWithComparator(val, start, IsNull(), builder);
break;
@@ -255,7 +312,15 @@
Glob{string_pool_}, builder);
break;
}
-
+ case FilterOp::kRegex:
+ PERFETTO_CHECK(regex::IsRegexSupported());
+ if constexpr (regex::IsRegexSupported()) {
+ base::StatusOr<regex::Regex> regex =
+ regex::Regex::Create(sql_val.AsString());
+ utils::IndexSearchWithComparator(std::move(regex.value()), start,
+ indices, Regex{string_pool_}, builder);
+ }
+ break;
case FilterOp::kIsNull:
utils::IndexSearchWithComparator(val, start, indices, IsNull(), builder);
break;
diff --git a/src/trace_processor/db/storage/string_storage_unittest.cc b/src/trace_processor/db/storage/string_storage_unittest.cc
index 86be7b9..ab3d7d8 100644
--- a/src/trace_processor/db/storage/string_storage_unittest.cc
+++ b/src/trace_processor/db/storage/string_storage_unittest.cc
@@ -194,6 +194,42 @@
ASSERT_EQ(bv.CountSetBits(), 3u);
}
+TEST(StringStorageUnittest, LinearSearchRegex) {
+ std::vector<std::string> strings{"cheese", "pasta", "pizza",
+ "pierogi", "onion", "fries"};
+ std::vector<StringPool::Id> ids;
+ StringPool pool;
+ for (const auto& string : strings) {
+ ids.push_back(pool.InternString(base::StringView(string)));
+ }
+ ids.insert(ids.begin() + 3, StringPool::Id::Null());
+
+ StringStorage storage(&pool, ids.data(), 6);
+ BitVector bv =
+ storage.Search(FilterOp::kRegex, SqlValue::String(".*zz.*"), Range(0, 7))
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 1u);
+}
+
+TEST(StringStorageUnittest, LinearSearchRegexMalformed) {
+ std::vector<std::string> strings{"cheese", "pasta", "pizza",
+ "pierogi", "onion", "fries"};
+ std::vector<StringPool::Id> ids;
+ StringPool pool;
+ for (const auto& string : strings) {
+ ids.push_back(pool.InternString(base::StringView(string)));
+ }
+ ids.insert(ids.begin() + 3, StringPool::Id::Null());
+
+ StringStorage storage(&pool, ids.data(), 6);
+ BitVector bv =
+ storage.Search(FilterOp::kRegex, SqlValue::String("*"), Range(0, 7))
+ .TakeIfBitVector();
+
+ ASSERT_EQ(bv.CountSetBits(), 0u);
+}
+
TEST(StringStorageUnittest, IndexSearchEq) {
std::vector<std::string> strings{"cheese", "pasta", "pizza",
"pierogi", "onion", "fries"};
diff --git a/src/trace_processor/db/storage/types.h b/src/trace_processor/db/storage/types.h
index 6b12f3d..ff3fc57 100644
--- a/src/trace_processor/db/storage/types.h
+++ b/src/trace_processor/db/storage/types.h
@@ -57,6 +57,7 @@
kIsNull,
kIsNotNull,
kGlob,
+ kRegex,
};
// Represents a constraint on a column.
diff --git a/src/trace_processor/db/storage/utils.h b/src/trace_processor/db/storage/utils.h
index f4e2a0a..55e18f8 100644
--- a/src/trace_processor/db/storage/utils.h
+++ b/src/trace_processor/db/storage/utils.h
@@ -82,6 +82,7 @@
builder.Append(comparator(*(data_ptr + *cur_idx), val));
}
}
+
} // namespace utils
} // namespace storage
diff --git a/src/trace_processor/db/typed_column.h b/src/trace_processor/db/typed_column.h
index a08aeed..d0bc19b 100644
--- a/src/trace_processor/db/typed_column.h
+++ b/src/trace_processor/db/typed_column.h
@@ -110,6 +110,9 @@
Constraint ge(sql_value_type v) const { return ge_value(ToSqlValue(v)); }
Constraint le(sql_value_type v) const { return le_value(ToSqlValue(v)); }
Constraint glob(sql_value_type v) const { return glob_value(ToSqlValue(v)); }
+ Constraint regex(sql_value_type v) const {
+ return regex_value(ToSqlValue(v));
+ }
// Implements equality between two items of type |T|.
static constexpr bool Equals(T a, T b) { return TH::Equals(a, b); }
diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn b/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn
index fb15c53..99488e2 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn
+++ b/src/trace_processor/perfetto_sql/intrinsics/functions/BUILD.gn
@@ -54,6 +54,7 @@
"../../../../base",
"../../../containers",
"../../../db",
+ "../../../db/storage",
"../../../importers/common",
"../../../importers/ftrace:ftrace_descriptors",
"../../../perfetto_sql/intrinsics/table_functions",
@@ -62,6 +63,7 @@
"../../../types",
"../../../util",
"../../../util:profile_builder",
+ "../../../util:regex",
"../../../util:sql_argument",
"../../../util:stdlib",
"../../engine",
diff --git a/src/trace_processor/perfetto_sql/intrinsics/functions/utils.h b/src/trace_processor/perfetto_sql/intrinsics/functions/utils.h
index 8154c72..36f1afc 100644
--- a/src/trace_processor/perfetto_sql/intrinsics/functions/utils.h
+++ b/src/trace_processor/perfetto_sql/intrinsics/functions/utils.h
@@ -20,14 +20,18 @@
#include <sqlite3.h>
#include <unordered_map>
+#include "perfetto/base/compiler.h"
#include "perfetto/ext/base/base64.h"
#include "perfetto/ext/base/file_utils.h"
+#include "perfetto/ext/base/string_utils.h"
#include "perfetto/ext/trace_processor/demangle.h"
#include "protos/perfetto/common/builtin_clock.pbzero.h"
+#include "src/trace_processor/db/storage/utils.h"
#include "src/trace_processor/export_json.h"
#include "src/trace_processor/importers/common/clock_tracker.h"
#include "src/trace_processor/perfetto_sql/intrinsics/functions/sql_function.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
+#include "src/trace_processor/util/regex.h"
#include "src/trace_processor/util/status_macros.h"
namespace perfetto {
@@ -110,10 +114,10 @@
};
base::Status Reverse::Run(void*,
- size_t argc,
- sqlite3_value** argv,
- SqlValue& out,
- Destructors& destructors) {
+ size_t argc,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors& destructors) {
if (argc != 1) {
return base::ErrStatus("REVERSE: expected one arg but got %zu", argc);
}
@@ -360,6 +364,30 @@
}
};
+struct Regex : public SqlFunction {
+ static base::Status Run(void*,
+ size_t,
+ sqlite3_value** argv,
+ SqlValue& out,
+ Destructors&) {
+ if constexpr (regex::IsRegexSupported()) {
+ const char* pattern_str =
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[0]));
+ const char* text =
+ reinterpret_cast<const char*>(sqlite3_value_text(argv[1]));
+ if (pattern_str && text) {
+ auto regex = regex::Regex::Create(pattern_str);
+ if (!regex.status().ok()) {
+ return regex.status();
+ }
+ out = SqlValue::Long(regex->Search(text));
+ }
+ return base::OkStatus();
+ }
+ PERFETTO_FATAL("Regex not supported");
+ }
+};
+
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/sqlite/BUILD.gn b/src/trace_processor/sqlite/BUILD.gn
index ddb52f9..b93a55a 100644
--- a/src/trace_processor/sqlite/BUILD.gn
+++ b/src/trace_processor/sqlite/BUILD.gn
@@ -55,6 +55,7 @@
"../types",
"../util",
"../util:profile_builder",
+ "../util:regex",
]
}
diff --git a/src/trace_processor/sqlite/db_sqlite_table.cc b/src/trace_processor/sqlite/db_sqlite_table.cc
index dab30d1..9f88cd9 100644
--- a/src/trace_processor/sqlite/db_sqlite_table.cc
+++ b/src/trace_processor/sqlite/db_sqlite_table.cc
@@ -15,6 +15,7 @@
*/
#include "src/trace_processor/sqlite/db_sqlite_table.h"
+#include <optional>
#include "perfetto/base/status.h"
#include "perfetto/ext/base/small_vector.h"
@@ -23,6 +24,7 @@
#include "src/trace_processor/sqlite/query_cache.h"
#include "src/trace_processor/sqlite/sqlite_utils.h"
#include "src/trace_processor/tp_metatrace.h"
+#include "src/trace_processor/util/regex.h"
namespace perfetto {
namespace trace_processor {
@@ -49,6 +51,11 @@
return FilterOp::kIsNotNull;
case SQLITE_INDEX_CONSTRAINT_GLOB:
return FilterOp::kGlob;
+ case SQLITE_INDEX_CONSTRAINT_REGEXP:
+ if constexpr (regex::IsRegexSupported()) {
+ return FilterOp::kRegex;
+ }
+ return std::nullopt;
case SQLITE_INDEX_CONSTRAINT_LIKE:
// TODO(lalitm): start supporting these constraints.
case SQLITE_INDEX_CONSTRAINT_LIMIT:
@@ -452,6 +459,16 @@
continue;
SqlValue value = SqliteValueToSqlValue(argv[i]);
+ if constexpr (regex::IsRegexSupported()) {
+ if (*opt_op == FilterOp::kRegex) {
+ if (value.type != SqlValue::kString)
+ return base::ErrStatus("Value has to be a string");
+
+ if (auto regex_status = regex::Regex::Create(value.AsString());
+ !regex_status.ok())
+ return regex_status.status();
+ }
+ }
constraints_[constraints_pos++] = Constraint{col, *opt_op, value};
}
constraints_.resize(constraints_pos);
@@ -537,6 +554,9 @@
case FilterOp::kGlob:
writer.AppendString("GLOB");
break;
+ case FilterOp::kRegex:
+ writer.AppendString("REGEXP");
+ break;
}
writer.AppendString(" ");
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 9bf46d4..b7ae178 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -87,6 +87,7 @@
#include "src/trace_processor/tp_metatrace.h"
#include "src/trace_processor/types/variadic.h"
#include "src/trace_processor/util/protozero_to_text.h"
+#include "src/trace_processor/util/regex.h"
#include "src/trace_processor/util/sql_modules.h"
#include "src/trace_processor/util/status_macros.h"
@@ -432,6 +433,9 @@
std::unique_ptr<ToFtrace::Context>(new ToFtrace::Context{
context_.storage.get(), SystraceSerializer(&context_)}));
+ if constexpr (regex::IsRegexSupported()) {
+ RegisterFunction<Regex>(&engine_, "regexp", 2);
+ }
// Old style function registration.
// TODO(lalitm): migrate this over to using RegisterFunction once aggregate
// functions are supported.
diff --git a/src/trace_processor/util/BUILD.gn b/src/trace_processor/util/BUILD.gn
index d22329b..7aa64b3 100644
--- a/src/trace_processor/util/BUILD.gn
+++ b/src/trace_processor/util/BUILD.gn
@@ -167,6 +167,14 @@
]
}
+source_set("regex") {
+ sources = [ "regex.h" ]
+ deps = [
+ "../../../gn:default_deps",
+ "../../base",
+ ]
+}
+
source_set("sql_argument") {
sources = [
"sql_argument.cc",
diff --git a/src/trace_processor/util/regex.h b/src/trace_processor/util/regex.h
new file mode 100644
index 0000000..d999c48
--- /dev/null
+++ b/src/trace_processor/util/regex.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SRC_TRACE_PROCESSOR_UTIL_REGEX_H_
+#define SRC_TRACE_PROCESSOR_UTIL_REGEX_H_
+
+#include <optional>
+#include "perfetto/ext/base/scoped_file.h"
+#include "perfetto/ext/base/status_or.h"
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+#include <regex.h>
+#endif
+
+namespace perfetto {
+namespace trace_processor {
+namespace regex {
+
+constexpr bool IsRegexSupported() {
+#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+ return false;
+#else
+ return true;
+#endif
+}
+
+#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
+
+// Implements regex parsing and regex search based on C library `regex.h`.
+// Doesn't work on Windows.
+class Regex {
+ public:
+ ~Regex() {
+ if (regex_) {
+ regfree(®ex_.value());
+ }
+ }
+ Regex(Regex&) = delete;
+ Regex(Regex&& other) {
+ regex_ = std::move(other.regex_);
+ other.regex_ = std::nullopt;
+ }
+ Regex& operator=(Regex&& other) {
+ this->~Regex();
+ new (this) Regex(std::move(other));
+ return *this;
+ }
+ Regex& operator=(const Regex&) = delete;
+
+ // Parse regex pattern. Returns error if regex pattern is invalid.
+ static base::StatusOr<Regex> Create(const char* pattern) {
+ regex_t regex;
+ if (regcomp(®ex, pattern, 0)) {
+ return base::ErrStatus("Regex pattern '%s' is malformed.", pattern);
+ }
+ return Regex(std::move(regex));
+ }
+
+ // Returns true if string matches the regex.
+ bool Search(const char* s) const {
+ PERFETTO_CHECK(regex_);
+ return regexec(®ex_.value(), s, 0, nullptr, 0) == 0;
+ }
+
+ private:
+ explicit Regex(regex_t regex) : regex_(std::move(regex)) {}
+
+ std::optional<regex_t> regex_;
+};
+
+#endif
+} // namespace regex
+
+} // namespace trace_processor
+} // namespace perfetto
+
+#endif // SRC_TRACE_PROCESSOR_UTIL_REGEX_H_