Merge "Revert "[metrics] Add thread creation spam stats to android_task_names""
diff --git a/Android.bp b/Android.bp
index 63c30e1..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",
@@ -4437,6 +4438,7 @@
"protos/perfetto/metrics/chrome/reported_by_page.proto",
"protos/perfetto/metrics/chrome/scroll_jank.proto",
"protos/perfetto/metrics/chrome/scroll_jank_v2.proto",
+ "protos/perfetto/metrics/chrome/scroll_jank_v3.proto",
"protos/perfetto/metrics/chrome/slice_names.proto",
"protos/perfetto/metrics/chrome/test_chrome_metric.proto",
"protos/perfetto/metrics/chrome/touch_jank.proto",
@@ -10264,6 +10266,7 @@
"src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql",
"src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql",
"src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql",
+ "src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v3.sql",
"src/trace_processor/metrics/sql/chrome/chrome_slice_names.sql",
"src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql",
"src/trace_processor/metrics/sql/chrome/chrome_tasks.sql",
@@ -10294,7 +10297,6 @@
"src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql",
"src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql",
"src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql",
- "src/trace_processor/metrics/sql/chrome/scroll_jank_v3.sql",
"src/trace_processor/metrics/sql/chrome/sufficient_chrome_processes.sql",
"src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql",
"src/trace_processor/metrics/sql/chrome/touch_flow_event.sql",
@@ -10841,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",
@@ -12313,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",
@@ -12997,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",
@@ -13225,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 a7e0557..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",
@@ -1927,6 +1928,7 @@
"src/trace_processor/metrics/sql/chrome/chrome_scroll_inputs_per_frame.sql",
"src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_caused_by_scheduling.sql",
"src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v2.sql",
+ "src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v3.sql",
"src/trace_processor/metrics/sql/chrome/chrome_slice_names.sql",
"src/trace_processor/metrics/sql/chrome/chrome_stack_samples_for_task.sql",
"src/trace_processor/metrics/sql/chrome/chrome_tasks.sql",
@@ -1957,7 +1959,6 @@
"src/trace_processor/metrics/sql/chrome/scroll_jank_cause_blocking_touch_move.sql",
"src/trace_processor/metrics/sql/chrome/scroll_jank_cause_get_bitmap.sql",
"src/trace_processor/metrics/sql/chrome/scroll_jank_cause_queuing_delay.sql",
- "src/trace_processor/metrics/sql/chrome/scroll_jank_v3.sql",
"src/trace_processor/metrics/sql/chrome/sufficient_chrome_processes.sql",
"src/trace_processor/metrics/sql/chrome/test_chrome_metric.sql",
"src/trace_processor/metrics/sql/chrome/touch_flow_event.sql",
@@ -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",
@@ -4070,6 +4079,7 @@
"protos/perfetto/metrics/chrome/reported_by_page.proto",
"protos/perfetto/metrics/chrome/scroll_jank.proto",
"protos/perfetto/metrics/chrome/scroll_jank_v2.proto",
+ "protos/perfetto/metrics/chrome/scroll_jank_v3.proto",
"protos/perfetto/metrics/chrome/slice_names.proto",
"protos/perfetto/metrics/chrome/test_chrome_metric.proto",
"protos/perfetto/metrics/chrome/touch_jank.proto",
@@ -5186,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",
@@ -5349,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",
@@ -5567,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/gn/BUILD.gn b/gn/BUILD.gn
index 749c2ee..a639b4d 100644
--- a/gn/BUILD.gn
+++ b/gn/BUILD.gn
@@ -56,6 +56,7 @@
} else {
perfetto_watchdog = "0"
}
+
if (enable_perfetto_tools) {
perfetto_local_symbolizer =
"PERFETTO_BUILDFLAG_DEFINE_PERFETTO_OS_LINUX() || " +
diff --git a/gn/perfetto.gni b/gn/perfetto.gni
index 579fc0f..eebfc41 100644
--- a/gn/perfetto.gni
+++ b/gn/perfetto.gni
@@ -300,8 +300,7 @@
# service and by the "perfetto" cmdline client) and to decompress traces (by
# trace_processor).
enable_perfetto_zlib =
- (enable_perfetto_trace_processor && !build_with_chromium) ||
- enable_perfetto_platform_services
+ enable_perfetto_trace_processor || enable_perfetto_platform_services
# Enables function name demangling using sources from llvm. Otherwise
# trace_processor falls back onto using the c++ runtime demangler, which
diff --git a/protos/perfetto/metrics/chrome/BUILD.gn b/protos/perfetto/metrics/chrome/BUILD.gn
index 1a7ae98..f82d634 100644
--- a/protos/perfetto/metrics/chrome/BUILD.gn
+++ b/protos/perfetto/metrics/chrome/BUILD.gn
@@ -32,6 +32,7 @@
"reported_by_page.proto",
"scroll_jank.proto",
"scroll_jank_v2.proto",
+ "scroll_jank_v3.proto",
"slice_names.proto",
"test_chrome_metric.proto",
"touch_jank.proto",
diff --git a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
index a82d617..908af94 100644
--- a/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
+++ b/protos/perfetto/metrics/chrome/all_chrome_metrics.proto
@@ -30,6 +30,7 @@
import "protos/perfetto/metrics/chrome/slice_names.proto";
import "protos/perfetto/metrics/chrome/scroll_jank.proto";
import "protos/perfetto/metrics/chrome/scroll_jank_v2.proto";
+import "protos/perfetto/metrics/chrome/scroll_jank_v3.proto";
import "protos/perfetto/metrics/chrome/test_chrome_metric.proto";
import "protos/perfetto/metrics/chrome/touch_jank.proto";
import "protos/perfetto/metrics/chrome/user_event_hashes.proto";
@@ -53,4 +54,5 @@
optional ChromeUnsymbolizedArgs chrome_unsymbolized_args = 1014;
optional ChromeArgsClassNames chrome_args_class_names = 1015;
optional ChromeScrollJankV2 chrome_scroll_jank_v2 = 1016;
+ optional ChromeScrollJankV3 chrome_scroll_jank_v3 = 1017;
}
diff --git a/protos/perfetto/metrics/chrome/scroll_jank_v3.proto b/protos/perfetto/metrics/chrome/scroll_jank_v3.proto
new file mode 100644
index 0000000..6f677a6
--- /dev/null
+++ b/protos/perfetto/metrics/chrome/scroll_jank_v3.proto
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+
+package perfetto.protos;
+
+import "protos/perfetto/metrics/custom_options.proto";
+
+message ChromeScrollJankV3 {
+ // Total count of unique frames in the trace.
+ optional int64 trace_num_frames = 1 [(unit) = "count_biggerIsBetter"];
+ // Number of frames in the trace that had missed vsyncs.
+ optional int64 trace_num_janky_frames = 2 [(unit) = "count_smallerIsBetter"];
+ // Computed as: `100 * trace_num_janky_frames /
+ // trace_num_frames`.
+ optional double trace_scroll_jank_percentage = 3
+ [(unit) = "n%_smallerIsBetter"];
+ // Vsync interval at the time of recording the trace.
+ optional double vsync_interval_ms = 4 [(unit) = "ms_biggerIsBetter"];
+ // Per-scroll metrics.
+ message Scroll {
+ // Total count of unique frames during this scroll.
+ optional int64 num_frames = 1 [(unit) = "count_biggerIsBetter"];
+ // Number of frames during this scroll that had missed vsyncs.
+ optional int64 num_janky_frames = 2 [(unit) = "count_smallerIsBetter"];
+ // Computed as: `100 * num_janky_frames / num_frames`.
+ optional double scroll_jank_percentage = 3 [(unit) = "n%_smallerIsBetter"];
+ // Maximum `delay_since_last_frame` for janky frames during this scroll
+ // (i.e. maximum `delay_since_last_frame` from the `scroll_jank_causes`
+ // repeated field below).
+ // This is measured in units of vsync interval. For example, a value of 2
+ // means the delay was 2x the vsync interval.
+ optional double max_delay_since_last_frame = 4
+ [(unit) = "unitless_smallerIsBetter"];
+ // The primary cause and sub-cause for scroll jank, one for each jank.
+ // There are exactly `num_janky_frames` items in this field.
+ message ScrollJankCause {
+ // May be empty, if a cause is not available.
+ optional string cause = 1;
+ // May be empty, if a sub-cause is not available.
+ optional string sub_cause = 2;
+ // Number of vsyncs elapsed since the previous frame was presented: will
+ // be > 1.0 since this was a "janky" frame. For example, a value of 2
+ // means the delay was 2x the vsync interval.
+ optional double delay_since_last_frame = 3
+ [(unit) = "unitless_smallerIsBetter"];
+ }
+ repeated ScrollJankCause scroll_jank_causes = 5;
+ }
+ repeated Scroll scrolls = 5;
+}
diff --git a/python/generators/diff_tests/runner.py b/python/generators/diff_tests/runner.py
index 4aa0286..b3c9fe9 100644
--- a/python/generators/diff_tests/runner.py
+++ b/python/generators/diff_tests/runner.py
@@ -149,6 +149,7 @@
trace_processor_path: str
trace_descriptor_path: str
colors: ColorFormatter
+ override_sql_module_paths: List[str]
def __output_to_text_proto(self, actual: str, out: BinaryProto) -> str:
"""Deserializes a binary proto and returns its text representation.
@@ -207,6 +208,8 @@
tmp_perf_file.name,
trace_path,
]
+ for sql_module_path in self.override_sql_module_paths:
+ cmd += ['--override-sql-module', sql_module_path]
tp = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
@@ -267,6 +270,8 @@
tmp_perf_file.name,
trace_path,
]
+ for sql_module_path in self.override_sql_module_paths:
+ cmd += ['--override-sql-module', sql_module_path]
tp = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
@@ -381,11 +386,9 @@
# Run a TestCase.
def execute(self, extension_descriptor_paths: List[str],
- metrics_descriptor: str, keep_input: bool,
+ metrics_descriptor_paths: List[str], keep_input: bool,
rebase: bool) -> Tuple[str, str, TestResult]:
- if metrics_descriptor:
- metrics_descriptor_paths = [metrics_descriptor]
- else:
+ if not metrics_descriptor_paths:
out_path = os.path.dirname(self.trace_processor_path)
metrics_protos_path = os.path.join(out_path, 'gen', 'protos', 'perfetto',
'metrics')
@@ -416,8 +419,9 @@
test_runners: List[TestCaseRunner]
def __init__(self, name_filter: str, trace_processor_path: str,
- trace_descriptor: str, no_colors: bool):
- self.tests = read_all_tests(name_filter, ROOT_DIR)
+ trace_descriptor: str, no_colors: bool,
+ override_sql_module_paths: List[str], test_dir: str):
+ self.tests = read_all_tests(name_filter, test_dir)
self.trace_processor_path = trace_processor_path
out_path = os.path.dirname(self.trace_processor_path)
@@ -428,26 +432,21 @@
for test in self.tests:
self.test_runners.append(
TestCaseRunner(test, self.trace_processor_path,
- self.trace_descriptor_path, color_formatter))
+ self.trace_descriptor_path, color_formatter,
+ override_sql_module_paths))
- def run_all_tests(self, metrics_descriptor: str, keep_input: bool,
- rebase: bool) -> TestResults:
+ def run_all_tests(self, metrics_descriptor_paths: List[str],
+ chrome_extensions: str, test_extensions: str,
+ keep_input: bool, rebase: bool) -> TestResults:
perf_results = []
failures = []
rebased = []
test_run_start = datetime.datetime.now()
- out_path = os.path.dirname(self.trace_processor_path)
- chrome_extensions = os.path.join(out_path, 'gen', 'protos', 'third_party',
- 'chromium',
- 'chrome_track_event.descriptor')
- test_extensions = os.path.join(out_path, 'gen', 'protos', 'perfetto',
- 'trace', 'test_extensions.descriptor')
-
with concurrent.futures.ProcessPoolExecutor() as e:
fut = [
e.submit(test.execute, [chrome_extensions, test_extensions],
- metrics_descriptor, keep_input, rebase)
+ metrics_descriptor_paths, keep_input, rebase)
for test in self.test_runners
]
for res in concurrent.futures.as_completed(fut):
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 6c27685..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",
]
@@ -217,7 +218,8 @@
"util/proto_to_json.cc",
"util/proto_to_json.h",
]
- if (perfetto_build_standalone && !is_perfetto_build_generator) {
+ if ((perfetto_build_standalone || build_with_chromium) &&
+ !is_perfetto_build_generator) {
data_deps = [
# The diff testing framework depends on these descriptors.
"../../protos/perfetto/metrics:descriptor",
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/metrics/sql/chrome/BUILD.gn b/src/trace_processor/metrics/sql/chrome/BUILD.gn
index 1f4cff8..0a5fb6c 100644
--- a/src/trace_processor/metrics/sql/chrome/BUILD.gn
+++ b/src/trace_processor/metrics/sql/chrome/BUILD.gn
@@ -37,6 +37,7 @@
"chrome_scroll_inputs_per_frame.sql",
"chrome_scroll_jank_caused_by_scheduling.sql",
"chrome_scroll_jank_v2.sql",
+ "chrome_scroll_jank_v3.sql",
"chrome_slice_names.sql",
"chrome_stack_samples_for_task.sql",
"chrome_tasks.sql",
@@ -67,7 +68,6 @@
"scroll_jank_cause_blocking_touch_move.sql",
"scroll_jank_cause_get_bitmap.sql",
"scroll_jank_cause_queuing_delay.sql",
- "scroll_jank_v3.sql",
"sufficient_chrome_processes.sql",
"test_chrome_metric.sql",
"touch_flow_event.sql",
diff --git a/src/trace_processor/metrics/sql/chrome/scroll_jank_v3.sql b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v3.sql
similarity index 77%
rename from src/trace_processor/metrics/sql/chrome/scroll_jank_v3.sql
rename to src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v3.sql
index 72211a3..720ac0d 100644
--- a/src/trace_processor/metrics/sql/chrome/scroll_jank_v3.sql
+++ b/src/trace_processor/metrics/sql/chrome/chrome_scroll_jank_v3.sql
@@ -212,6 +212,7 @@
-- @column event_latency_id Event latency id of the presented frame.
-- @column vsync_interval Vsync interval at the time of recording the trace.
-- @column hardware_class Device brand and model.
+-- @column scroll_id The scroll corresponding to this frame.
DROP VIEW IF EXISTS chrome_janky_frames;
CREATE VIEW chrome_janky_frames AS
SELECT
@@ -220,7 +221,8 @@
delay_since_last_frame,
event_latency_id,
(SELECT vsync_interval FROM chrome_vsyncs) AS vsync_interval,
- CHROME_HARDWARE_CLASS() AS hardware_class
+ CHROME_HARDWARE_CLASS() AS hardware_class,
+ scroll_id
FROM chrome_janky_frame_info_with_delay
WHERE delay_since_last_frame > (select vsync_interval + vsync_interval / 2 from chrome_vsyncs)
AND delay_since_last_input < (select vsync_interval + vsync_interval / 2 from chrome_vsyncs);
@@ -243,4 +245,100 @@
FROM chrome_janky_frames) * 1.0
/ (SELECT
COUNT()
- FROM chrome_unique_frame_presentation_ts) * 100 AS delayed_frame_percentage;
\ No newline at end of file
+ FROM chrome_unique_frame_presentation_ts) * 100 AS delayed_frame_percentage;
+
+-- Number of frames and janky frames per scroll.
+DROP VIEW IF EXISTS frames_per_scroll;
+CREATE VIEW frames_per_scroll AS
+WITH
+ frames AS (
+ SELECT scroll_id, COUNT(*) AS num_frames
+ FROM
+ chrome_janky_frame_info_with_delay
+ GROUP BY scroll_id
+ ),
+ janky_frames AS (
+ SELECT scroll_id, COUNT(*) AS num_janky_frames
+ FROM
+ chrome_janky_frames
+ GROUP BY scroll_id
+ )
+SELECT
+ frames.scroll_id AS scroll_id,
+ frames.num_frames AS num_frames,
+ janky_frames.num_janky_frames AS num_janky_frames,
+ 100.0 * janky_frames.num_janky_frames / frames.num_frames
+ AS scroll_jank_percentage
+FROM frames
+INNER JOIN janky_frames
+ ON frames.scroll_id = janky_frames.scroll_id;
+
+-- Scroll jank causes per scroll.
+DROP VIEW IF EXISTS causes_per_scroll;
+CREATE VIEW causes_per_scroll AS
+SELECT
+ scroll_id,
+ MAX(1.0 * delay_since_last_frame / vsync_interval)
+ AS max_delay_since_last_frame,
+ -- MAX does not matter, since `vsync_interval` is the computed as the
+ -- same value for a single trace.
+ MAX(vsync_interval) AS vsync_interval,
+ RepeatedField(
+ ChromeScrollJankV3_Scroll_ScrollJankCause(
+ 'cause',
+ cause_of_jank,
+ 'sub_cause',
+ sub_cause_of_jank,
+ 'delay_since_last_frame',
+ 1.0 * delay_since_last_frame / vsync_interval))
+ AS scroll_jank_causes
+FROM
+ chrome_janky_frames
+GROUP BY scroll_id;
+
+-- An "intermediate" view for computing `chrome_scroll_jank_v3_output` below.
+DROP VIEW IF EXISTS chrome_scroll_jank_v3_intermediate;
+CREATE VIEW chrome_scroll_jank_v3_intermediate AS
+SELECT
+ -- MAX does not matter for these aggregations, since the values are the
+ -- same across rows.
+ (SELECT COUNT(*) FROM chrome_janky_frame_info_with_delay)
+ AS trace_num_frames,
+ (SELECT COUNT(*) FROM chrome_janky_frames)
+ AS trace_num_janky_frames,
+ causes.vsync_interval,
+ RepeatedField(
+ ChromeScrollJankV3_Scroll(
+ 'num_frames',
+ frames.num_frames,
+ 'num_janky_frames',
+ frames.num_janky_frames,
+ 'scroll_jank_percentage',
+ frames.scroll_jank_percentage,
+ 'max_delay_since_last_frame',
+ causes.max_delay_since_last_frame,
+ 'scroll_jank_causes',
+ causes.scroll_jank_causes))
+ AS scrolls
+FROM
+ frames_per_scroll AS frames
+INNER JOIN causes_per_scroll AS causes
+ ON frames.scroll_id = causes.scroll_id;
+
+-- For producing a "native" Perfetto UI metric.
+DROP VIEW IF EXISTS chrome_scroll_jank_v3_output;
+CREATE VIEW chrome_scroll_jank_v3_output AS
+SELECT
+ ChromeScrollJankV3(
+ 'trace_num_frames',
+ trace_num_frames,
+ 'trace_num_janky_frames',
+ trace_num_janky_frames,
+ 'trace_scroll_jank_percentage',
+ 100.0 * trace_num_janky_frames / trace_num_frames,
+ 'vsync_interval_ms',
+ vsync_interval,
+ 'scrolls',
+ scrolls)
+FROM
+ chrome_scroll_jank_v3_intermediate;
diff --git a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
index 295a385..3ed0449 100644
--- a/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
+++ b/src/trace_processor/perfetto_sql/engine/perfetto_sql_parser.cc
@@ -202,7 +202,8 @@
for (; !TokenIsTerminal(token); token = tokenizer_.Next()) {
}
uint32_t offset = static_cast<uint32_t>(first.str.data() - sql_.sql().data());
- uint32_t len = static_cast<uint32_t>(token.str.end() - sql_.sql().data());
+ uint32_t len = static_cast<uint32_t>((token.str.data() + token.str.size()) -
+ sql_.sql().data());
statement_ = CreateFunction{std::move(prototype), std::string(ret_token.str),
sql_.Substr(offset, len)};
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/perfetto_sql/prelude/views.sql b/src/trace_processor/perfetto_sql/prelude/views.sql
index 3e5b8af..6962bae 100644
--- a/src/trace_processor/perfetto_sql/prelude/views.sql
+++ b/src/trace_processor/perfetto_sql/prelude/views.sql
@@ -1,45 +1,45 @@
-CREATE VIEW counters AS
+CREATE VIEW counters AS
SELECT *
-FROM counter v
-JOIN counter_track t ON v.track_id = t.id
+FROM counter v
+JOIN counter_track t ON v.track_id = t.id
ORDER BY ts;
-CREATE VIEW slice AS
+CREATE VIEW slice AS
SELECT
- *,
- category AS cat,
- id AS slice_id
+ *,
+ category AS cat,
+ id AS slice_id
FROM internal_slice;
-CREATE VIEW instant AS
-SELECT ts, track_id, name, arg_set_id
-FROM slice
+CREATE VIEW instant AS
+SELECT ts, track_id, name, arg_set_id
+FROM slice
WHERE dur = 0;
-CREATE VIEW sched AS
-SELECT
+CREATE VIEW sched AS
+SELECT
*,
ts + dur as ts_end
FROM sched_slice;
-CREATE VIEW slices AS
+CREATE VIEW slices AS
SELECT * FROM slice;
-CREATE VIEW thread AS
-SELECT
+CREATE VIEW thread AS
+SELECT
id as utid,
*
FROM internal_thread;
-CREATE VIEW process AS
-SELECT
+CREATE VIEW process AS
+SELECT
id as upid,
- *
+ *
FROM internal_process;
-- This should be kept in sync with GlobalArgsTracker::AddArgSet.
-CREATE VIEW args AS
-SELECT
+CREATE VIEW args AS
+SELECT
*,
CASE value_type
WHEN 'int' THEN CAST(int_value AS text)
@@ -51,5 +51,5 @@
CASE WHEN int_value <> 0 THEN 'true'
ELSE 'false' END)
WHEN 'json' THEN string_value
- ELSE 'NULL' END AS display_value
+ ELSE NULL END AS display_value
FROM internal_args;
diff --git a/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scroll_janks.sql b/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scroll_janks.sql
index b12e6a7..fd870f5 100644
--- a/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scroll_janks.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/chrome/chrome_scroll_janks.sql
@@ -13,7 +13,7 @@
-- limitations under the License.
-- TODO(b/286187288): Move this dependency to stdlib.
-SELECT RUN_METRIC('chrome/scroll_jank_v3.sql');
+SELECT RUN_METRIC('chrome/chrome_scroll_jank_v3.sql');
SELECT IMPORT('common.slices');
-- Selects EventLatency slices that correspond with janks in a scroll. This is
diff --git a/src/trace_processor/perfetto_sql/stdlib/common/args.sql b/src/trace_processor/perfetto_sql/stdlib/common/args.sql
index 90a5ced..4835bf8 100644
--- a/src/trace_processor/perfetto_sql/stdlib/common/args.sql
+++ b/src/trace_processor/perfetto_sql/stdlib/common/args.sql
@@ -1,3 +1,18 @@
+--
+-- Copyright 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
+--
+-- https://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.
+--
-- Copyright 2023 The Android Open Source Project
--
-- Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,4 +42,4 @@
SELECT display_value
FROM args
WHERE arg_set_id = $arg_set_id AND key = $key
-');
+');
\ No newline at end of file
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/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 2aa130d..ba49625 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -691,7 +691,7 @@
std::string trace_file_path;
std::string port_number;
std::string override_stdlib_path;
- std::string override_sql_module_path;
+ std::vector<std::string> override_sql_module_paths;
std::vector<std::string> raw_metric_extensions;
bool launch_shell = false;
bool enable_httpd = false;
@@ -766,8 +766,7 @@
module name.
--override-sql-module MODULE_PATH Will override trace processor module with
passed contents. The outer directory will
- specify the module name. Only allowed when
- --dev is specified.
+ specify the module name.
--override-stdlib=[path_to_stdlib] Will override trace_processor/stdlib with
passed contents. The outer directory will
be ignored. Only allowed when --dev is
@@ -963,7 +962,7 @@
}
if (option == OPT_OVERRIDE_SQL_MODULE) {
- command_line_options.override_sql_module_path = optarg;
+ command_line_options.override_sql_module_paths.push_back(optarg);
continue;
}
@@ -1543,14 +1542,14 @@
status.c_message());
}
- if (!options.override_sql_module_path.empty()) {
- if (!options.dev)
- return base::ErrStatus("Overriding stdlib modules requires --dev flag");
-
- auto status = IncludeSqlModule(options.override_sql_module_path, true);
- if (!status.ok())
- return base::ErrStatus("Couldn't override stdlib module: %s",
- status.c_message());
+ if (!options.override_sql_module_paths.empty()) {
+ for (const auto& override_sql_module_path :
+ options.override_sql_module_paths) {
+ auto status = IncludeSqlModule(override_sql_module_path, true);
+ if (!status.ok())
+ return base::ErrStatus("Couldn't override stdlib module: %s",
+ status.c_message());
+ }
}
if (!options.sql_module_path.empty()) {
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_
diff --git a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
index 3e21de5..ce2066a 100644
--- a/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
+++ b/test/trace_processor/diff_tests/chrome/tests_scroll_jank.py
@@ -69,7 +69,7 @@
return DiffTestBlueprint(
trace=DataPath('chrome_input_with_frame_view.pftrace'),
query="""
- SELECT RUN_METRIC('chrome/scroll_jank_v3.sql');
+ SELECT RUN_METRIC('chrome/chrome_scroll_jank_v3.sql');
SELECT
cause_of_jank,
@@ -84,7 +84,7 @@
return DiffTestBlueprint(
trace=DataPath('chrome_input_with_frame_view.pftrace'),
query="""
- SELECT RUN_METRIC('chrome/scroll_jank_v3.sql');
+ SELECT RUN_METRIC('chrome/chrome_scroll_jank_v3.sql');
SELECT
delayed_frame_percentage
@@ -658,3 +658,39 @@
}
}
"""))
+
+ def test_chrome_scroll_jank_v3(self):
+ return DiffTestBlueprint(
+ trace=DataPath('chrome_input_with_frame_view.pftrace'),
+ query=Metric('chrome_scroll_jank_v3'),
+ out=TextProto(r"""
+ [perfetto.protos.chrome_scroll_jank_v3] {
+ trace_num_frames: 291
+ trace_num_janky_frames: 3
+ trace_scroll_jank_percentage: 1.0309278350515463
+ vsync_interval_ms: 16.368
+ scrolls {
+ num_frames: 105
+ num_janky_frames: 2
+ scroll_jank_percentage: 1.9047619047619047
+ max_delay_since_last_frame: 6.126221896383187
+ scroll_jank_causes {
+ delay_since_last_frame: 2.044354838709678
+ }
+ scroll_jank_causes {
+ cause: "RendererCompositorFinishedToBeginImplFrame"
+ delay_since_last_frame: 6.126221896383187
+ }
+ }
+ scrolls {
+ num_frames: 84
+ num_janky_frames: 1
+ scroll_jank_percentage: 1.1904761904761905
+ max_delay_since_last_frame: 2.040811339198436
+ scroll_jank_causes {
+ cause: "RendererCompositorQueueingDelay"
+ delay_since_last_frame: 2.040811339198436
+ }
+ }
+ }
+ """))
diff --git a/tools/diff_test_trace_processor.py b/tools/diff_test_trace_processor.py
index e505258..5316763 100755
--- a/tools/diff_test_trace_processor.py
+++ b/tools/diff_test_trace_processor.py
@@ -38,9 +38,14 @@
parser = argparse.ArgumentParser()
parser.add_argument('--test-type', type=str, default='all')
parser.add_argument('--trace-descriptor', type=str)
- parser.add_argument('--metrics-descriptor', type=str)
+ parser.add_argument('--metrics-descriptor', nargs='+', type=str)
+ parser.add_argument('--chrome-track-event-descriptor', type=str, default=None)
+ parser.add_argument('--test-extensions', type=str, default=None)
parser.add_argument('--perf-file', type=str)
parser.add_argument(
+ '--override-sql-module', type=str, action='append', default=[])
+ parser.add_argument('--test-dir', type=str, default=ROOT_DIR)
+ parser.add_argument(
'--name-filter',
default='.*',
type=str,
@@ -59,11 +64,23 @@
'trace_processor', type=str, help='location of trace processor binary')
args = parser.parse_args()
+ out_path = os.path.dirname(args.trace_processor)
+ if args.chrome_track_event_descriptor is None:
+ args.chrome_track_event_descriptor = os.path.join(
+ out_path, 'gen', 'protos', 'third_party', 'chromium',
+ 'chrome_track_event.descriptor')
+ if args.test_extensions is None:
+ args.test_extensions = os.path.join(out_path, 'gen', 'protos', 'perfetto',
+ 'trace', 'test_extensions.descriptor')
+
test_runner = DiffTestsRunner(args.name_filter, args.trace_processor,
- args.trace_descriptor, args.no_colors)
+ args.trace_descriptor, args.no_colors,
+ args.override_sql_module, args.test_dir)
sys.stderr.write(f"[==========] Running {len(test_runner.tests)} tests.\n")
- results = test_runner.run_all_tests(args.metrics_descriptor, args.keep_input,
+ results = test_runner.run_all_tests(args.metrics_descriptor,
+ args.chrome_track_event_descriptor,
+ args.test_extensions, args.keep_input,
args.rebase)
sys.stderr.write(results.str(args.no_colors, len(test_runner.tests)))
diff --git a/ui/release/channels.json b/ui/release/channels.json
index 0dcf5cb..704f94b 100644
--- a/ui/release/channels.json
+++ b/ui/release/channels.json
@@ -2,11 +2,11 @@
"channels": [
{
"name": "stable",
- "rev": "1b8de44522f33d371def110325bf31b847c1f5c4"
+ "rev": "ad24a64421b255cccc7f80dd0532dcf8ff8902a5"
},
{
"name": "canary",
- "rev": "5f456dbc00731d4dff20ae4e395cf06f96374a2d"
+ "rev": "e6805137e51cde470d227d63ba06f99be1ed8639"
},
{
"name": "autopush",
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index 19954e4..9f791b6 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -56,6 +56,7 @@
NewEngineMode,
OmniboxState,
Pagination,
+ PendingDeeplinkState,
PivotTableResult,
PrimaryTrackSortKey,
ProfileType,
@@ -479,8 +480,7 @@
}
},
- maybeSetPendingDeeplink(
- state: StateDraft, args: {ts?: string, dur?: string, tid?: string}) {
+ maybeSetPendingDeeplink(state: StateDraft, args: PendingDeeplinkState) {
state.pendingDeeplink = args;
},
diff --git a/ui/src/common/commands.ts b/ui/src/common/commands.ts
index 3b9446b..b3973ed 100644
--- a/ui/src/common/commands.ts
+++ b/ui/src/common/commands.ts
@@ -12,11 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-export interface Command {
- id: string;
- name: string;
- callback: (...args: any[]) => void;
-}
+import {Command} from '../public';
export interface CommandSource {
commands(): Command[];
diff --git a/ui/src/common/plugins.ts b/ui/src/common/plugins.ts
index ede9e57..df93cc0 100644
--- a/ui/src/common/plugins.ts
+++ b/ui/src/common/plugins.ts
@@ -12,51 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Disposable} from 'src/base/disposable';
import {
TrackControllerFactory,
trackControllerRegistry,
} from '../controller/track_controller';
-import {Store} from '../frontend/store';
import {TrackCreator} from '../frontend/track';
import {trackRegistry} from '../frontend/track_registry';
import {
+ Command,
EngineProxy,
PluginContext,
PluginInfo,
+ Store,
+ TracePlugin,
+ TracePluginFactory,
TrackInfo,
TrackProvider,
} from '../public';
-import {Command} from './commands';
import {Engine} from './engine';
import {Registry} from './registry';
import {State} from './state';
-// All trace plugins must implement this interface.
-export interface TracePlugin extends Disposable {
- commands(): Command[];
-}
-
-// This interface defines what a plugin factory should look like.
-// This can be defined in the plugin class definition by defining a constructor
-// and the relevant static methods:
-// E.g.
-// class MyPlugin implements TracePlugin<MyState> {
-// static migrate(initialState: unknown): MyState {...}
-// constructor(store: Store<MyState>, engine: EngineProxy) {...}
-// ... methods from the TracePlugin interface go here ...
-// }
-// ... which can then be passed around by class i.e. MyPlugin
-export interface TracePluginFactory<StateT> {
- // Function to migrate the persistent state. Called before new().
- migrate(initialState: unknown): StateT;
-
- // Instantiate the plugin.
- new(store: Store<StateT>, engine: EngineProxy): TracePlugin;
-}
-
interface TracePluginContext {
plugin: TracePlugin;
store: Store<unknown>;
@@ -106,7 +84,7 @@
const TracePluginClass = this.tracePluginFactory;
if (TracePluginClass) {
// Make an engine proxy for this plugin.
- const engineProxy = engine.getProxy(this.pluginId);
+ const engineProxy: EngineProxy = engine.getProxy(this.pluginId);
// Extract the initial state and pass to the plugin factory for migration.
const initialState = store.state.plugins[this.pluginId];
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index 7cdc177..9237dd4 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -108,7 +108,8 @@
// 31. Convert all timestamps to bigints.
// 32. Add pendingDeeplink.
// 33. Add plugins state.
-export const STATE_VERSION = 33;
+// 34. Add additional pendingDeeplink fields (query, pid).
+export const STATE_VERSION = 34;
export const SCROLLING_TRACK_GROUP = 'ScrollingTracks';
@@ -525,6 +526,8 @@
ts?: string;
dur?: string;
tid?: string;
+ pid?: string;
+ query?: string;
}
export interface State {
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index f78d656..59cf7b4 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -63,6 +63,7 @@
publishOverviewData,
publishThreads,
} from '../frontend/publish';
+import {runQueryInNewTab} from '../frontend/query_result_tab';
import {Router} from '../frontend/router';
import {
@@ -531,6 +532,9 @@
if (pendingDeeplink !== undefined) {
globals.dispatch(Actions.clearPendingDeeplink({}));
await this.selectPendingDeeplink(pendingDeeplink);
+ if (pendingDeeplink.query !== undefined) {
+ runQueryInNewTab(pendingDeeplink.query, 'Deeplink Query');
+ }
}
// If the trace was shared via a permalink, it might already have a
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index 3b8f9bd..64980da 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -367,6 +367,8 @@
ts: route.args.ts,
tid: route.args.tid,
dur: route.args.dur,
+ pid: route.args.dur,
+ query: route.args.query,
}));
if (!globals.embeddedMode) {
diff --git a/ui/src/frontend/router.ts b/ui/src/frontend/router.ts
index 1057d80..54e0144 100644
--- a/ui/src/frontend/router.ts
+++ b/ui/src/frontend/router.ts
@@ -77,6 +77,8 @@
ts: optStr,
dur: optStr,
tid: optStr,
+ pid: optStr,
+ query: optStr,
});
type RouteArgs = ValidatedType<typeof routeArgs>;
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index 11bd3dc..c7336f4 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -15,9 +15,9 @@
import m from 'mithril';
import {Actions} from '../common/actions';
-import {Command} from '../common/commands';
import {raf} from '../core/raf_scheduler';
import {VERSION} from '../gen/perfetto_version';
+import {Command} from '../public';
import {classNames} from './classnames';
import {globals} from './globals';
diff --git a/ui/src/plugins/dev.perfetto.ExamplePlugin/index.ts b/ui/src/plugins/dev.perfetto.ExamplePlugin/index.ts
index 1ce82f3..261ad39 100644
--- a/ui/src/plugins/dev.perfetto.ExamplePlugin/index.ts
+++ b/ui/src/plugins/dev.perfetto.ExamplePlugin/index.ts
@@ -12,10 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {Command} from '../../common/commands';
-import {TracePlugin} from '../../common/plugins';
-import {Store} from '../../frontend/store';
-import {EngineProxy, PluginContext} from '../../public';
+import {
+ Command,
+ EngineProxy,
+ PluginContext,
+ Store,
+ TracePlugin,
+} from '../../public';
interface ExampleState {
counter: number;
diff --git a/ui/src/public/index.ts b/ui/src/public/index.ts
index fc10405..63a2657 100644
--- a/ui/src/public/index.ts
+++ b/ui/src/public/index.ts
@@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import {Disposable} from '../base/disposable';
import {EngineProxy} from '../common/engine';
-import {TracePluginFactory} from '../common/plugins';
import {TrackControllerFactory} from '../controller/track_controller';
+import {Store} from '../frontend/store';
import {TrackCreator} from '../frontend/track';
export {EngineProxy} from '../common/engine';
@@ -26,6 +27,39 @@
STR,
STR_NULL,
} from '../common/query_result';
+export {Store} from '../frontend/store';
+
+export interface Command {
+ // A unique id for this command.
+ id: string;
+ // A friendly human name for the command.
+ name: string;
+ // Callback is called when the command is invoked.
+ callback: (...args: any[]) => void;
+}
+
+// All trace plugins must implement this interface.
+export interface TracePlugin extends Disposable {
+ commands(): Command[];
+}
+
+// This interface defines what a plugin factory should look like.
+// This can be defined in the plugin class definition by defining a constructor
+// and the relevant static methods:
+// E.g.
+// class MyPlugin implements TracePlugin<MyState> {
+// static migrate(initialState: unknown): MyState {...}
+// constructor(store: Store<MyState>, engine: EngineProxy) {...}
+// ... methods from the TracePlugin interface go here ...
+// }
+// ... which can then be passed around by class i.e. MyPlugin
+export interface TracePluginFactory<StateT> {
+ // Function to migrate the persistent state. Called before new().
+ migrate(initialState: unknown): StateT;
+
+ // Instantiate the plugin.
+ new(store: Store<StateT>, engine: EngineProxy): TracePlugin;
+}
export interface TrackInfo {
// The id of this 'type' of track. This id is used to select the