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, &regex](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(&regex_.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(&regex, 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(&regex_.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_