Skip to content

Commit afb17b3

Browse files
kalutesV8 LUCI CQ
authored andcommitted
Add v2 metrics support to trace_processor
Add support for perfetto v2 metric definitions and outputting trace summaries to the trace_processor probe. Bug: 413156992 Change-Id: I3fe5dc93de2e47a80cee638161117b553128a4bd Reviewed-on: https://chromium-review.googlesource.com/c/crossbench/+/6567346 Reviewed-by: Camillo Bruni <[email protected]> Commit-Queue: Kameron Lutes <[email protected]>
1 parent 89c4875 commit afb17b3

File tree

4 files changed

+167
-53
lines changed

4 files changed

+167
-53
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
metric_spec: {
2+
id: "file_metric_def_textproto"
3+
value: "metric_column"
4+
query {
5+
id: "query_id"
6+
table {
7+
table_name: "metric_table"
8+
module_name: "module_name"
9+
column_names: "metric_column"
10+
}
11+
}
12+
}

config/doc/probe/trace_processor.config.hjson

Lines changed: 76 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,69 +2,97 @@
22
{
33
probes: {
44
trace_processor: {
5+
// If necessary, a custom path for trace_processor_shell can be passed
6+
// trace_processor_bin: "/optional/path/to/trace_processor_shell"
57
queries: [
68
// Queries can be specified as a reference to a file in
79
// crossbench/probes/perfetto/trace_processor/queries:
8-
"speedometer_cpu_time",
10+
speedometer_cpu_time
911
// Or specified inline:
1012
{
11-
name: "my_query",
12-
sql: "select dur from slice where slice.name = 'my_slice'",
13-
},
14-
],
15-
metrics: ["trace_stats"],
13+
name: my_query
14+
sql: select dur from slice where slice.name = 'my_slice'
15+
}
16+
]
17+
metrics: [
18+
trace_stats
19+
]
1620
// Additional directories containing trace processor modules
1721
// can be specified:
1822
module_paths: [
19-
"/my_project/modules/ext",
23+
/my_project/modules/ext
2024
]
21-
// If necessary, a custom path for trace_processor_shell
22-
// can be passed
23-
// trace_processor_bin: "/optional/path/to/trace_processor_shell"
24-
},
25+
// Perfetto v2 metric definitions can be specified either inline
26+
// or as a path:
27+
metric_definitions: [
28+
./metric_def.textproto
29+
'''
30+
metric_spec: {
31+
id: "inline_textproto_metric"
32+
value: "metric_column"
33+
query {
34+
id: "query_id"
35+
table {
36+
table_name: "metric_table"
37+
module_name: "module_name"
38+
column_names: "metric_column"
39+
}
40+
}
41+
}
42+
'''
43+
]
44+
// Metrics to include only in the trace summary can be specified.
45+
// summary_metrics will include everything in the 'metrics' list as well
46+
// as additional metrics (such as v2 metrics) specified here:
47+
summary_metrics: [
48+
file_metric_def_textproto
49+
inline_textproto_metric
50+
]
51+
}
2552
perfetto: {
26-
textproto: '''
27-
buffers {
28-
size_kb: 300000
29-
fill_policy: DISCARD
30-
}
31-
data_sources {
32-
config {
33-
name: "org.chromium.trace_metadata"
53+
textproto:
54+
'''
55+
buffers {
56+
size_kb: 300000
57+
fill_policy: DISCARD
3458
}
35-
}
36-
data_sources {
37-
config {
38-
name: "track_event"
39-
track_event_config {
40-
disabled_categories: "*"
41-
enabled_categories: "blink.user_timing"
42-
enabled_categories: "toplevel"
43-
enabled_categories: "__metadata"
44-
timestamp_unit_multiplier: 1000
45-
enable_thread_time_sampling: true
46-
filter_debug_annotations: false
47-
filter_dynamic_event_names: false
59+
data_sources {
60+
config {
61+
name: "org.chromium.trace_metadata"
4862
}
4963
}
50-
}
51-
data_sources: {
52-
config {
53-
name: "linux.ftrace"
54-
ftrace_config {
55-
ftrace_events: "sched/sched_switch"
56-
ftrace_events: "power/suspend_resume"
57-
ftrace_events: "sched/sched_wakeup"
58-
ftrace_events: "sched/sched_wakeup_new"
59-
ftrace_events: "sched/sched_waking"
60-
ftrace_events: "sched/sched_process_exit"
61-
ftrace_events: "sched/sched_process_free"
62-
ftrace_events: "task/task_newtask"
63-
ftrace_events: "task/task_rename"
64+
data_sources {
65+
config {
66+
name: "track_event"
67+
track_event_config {
68+
disabled_categories: "*"
69+
enabled_categories: "blink.user_timing"
70+
enabled_categories: "toplevel"
71+
enabled_categories: "__metadata"
72+
timestamp_unit_multiplier: 1000
73+
enable_thread_time_sampling: true
74+
filter_debug_annotations: false
75+
filter_dynamic_event_names: false
76+
}
6477
}
6578
}
66-
}
67-
'''
79+
data_sources: {
80+
config {
81+
name: "linux.ftrace"
82+
ftrace_config {
83+
ftrace_events: "sched/sched_switch"
84+
ftrace_events: "power/suspend_resume"
85+
ftrace_events: "sched/sched_wakeup"
86+
ftrace_events: "sched/sched_wakeup_new"
87+
ftrace_events: "sched/sched_waking"
88+
ftrace_events: "sched/sched_process_exit"
89+
ftrace_events: "sched/sched_process_free"
90+
ftrace_events: "task/task_newtask"
91+
ftrace_events: "task/task_rename"
92+
}
93+
}
94+
}
95+
'''
6896
}
6997
}
7098
}

crossbench/probes/perfetto/trace_processor/trace_processor.py

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
from crossbench.parse import ObjectParser, PathParser
2727
from crossbench.probes.metric import MetricsMerger
2828
from crossbench.probes.probe import Probe, ProbeConfigParser, ProbeContext
29-
from crossbench.probes.results import LocalProbeResult, ProbeResult
29+
from crossbench.probes.results import EmptyProbeResult, LocalProbeResult, ProbeResult
3030
from crossbench.replacements import Replacements
3131

3232
if TYPE_CHECKING:
@@ -135,6 +135,21 @@ def config_parser(cls) -> ProbeConfigParser[Self]:
135135
is_list=True,
136136
default=tuple(),
137137
help="Name of metric to be run (can be any metric from Perfetto)")
138+
parser.add_argument(
139+
"metric_definitions",
140+
type=ObjectParser.str_or_file_contents,
141+
is_list=True,
142+
default=tuple(),
143+
help=("Textproto for perfetto metrics v2 definition files. "
144+
"Can be inline textproto or a path to a .textproto file."))
145+
parser.add_argument(
146+
"summary_metrics",
147+
type=str,
148+
is_list=True,
149+
default=tuple(),
150+
help=("Additional metrics to only include in the trace summary. "
151+
"Includes all of <metrics>. These can be v2 metrics if the "
152+
"corresponding metric definition is supplied."))
138153
parser.add_argument(
139154
"queries",
140155
type=TraceProcessorQueryConfig,
@@ -156,13 +171,18 @@ def config_parser(cls) -> ProbeConfigParser[Self]:
156171

157172
def __init__(self,
158173
batch: bool,
174+
metric_definitions: Iterable[str],
175+
summary_metrics: Iterable[str],
159176
metrics: Iterable[str],
160177
queries: Iterable[TraceProcessorQueryConfig],
161178
module_paths: Iterable[pth.LocalPath],
162179
trace_processor_bin: Optional[pth.LocalPath] = None) -> None:
163180
super().__init__()
164181
self._batch = batch
165182
self._metrics = tuple(metrics)
183+
self._metric_definitions: tuple[str, ...] = tuple(metric_definitions)
184+
self._summary_metrics: tuple[str,
185+
...] = tuple(metrics) + tuple(summary_metrics)
166186
ObjectParser.unique_sequence([query.name for query in queries],
167187
name="query names")
168188
self._queries = tuple(queries)
@@ -184,13 +204,22 @@ def metrics(self) -> tuple[str, ...]:
184204
def queries(self) -> tuple[TraceProcessorQueryConfig, ...]:
185205
return self._queries
186206

207+
@property
208+
def metric_definitions(self) -> tuple[str, ...]:
209+
return self._metric_definitions
210+
211+
@property
212+
def summary_metrics(self) -> tuple[str, ...]:
213+
return self._summary_metrics
214+
187215
@property
188216
def module_paths(self) -> tuple[pth.LocalPath, ...]:
189217
return self._module_paths
190218

191219
@property
192220
def has_work(self) -> bool:
193-
return len(self._queries) != 0 or len(self._metrics) != 0
221+
return len(self._queries) != 0 or len(self._metrics) != 0 or len(
222+
self._summary_metrics) != 0 or len(self._metric_definitions) != 0
194223

195224
@property
196225
def needs_tp_run(self) -> bool:
@@ -209,7 +238,7 @@ def tp_config(self) -> TraceProcessorConfig:
209238
extra_flags = []
210239

211240
for module_path in self.module_paths:
212-
extra_flags.append("--override-sql-module")
241+
extra_flags.append("--add-sql-module")
213242
extra_flags.append(str(module_path))
214243

215244
return TraceProcessorConfig(
@@ -240,6 +269,13 @@ def _check_sql(self) -> None:
240269
for query in self.queries:
241270
tp.query(query.sql)
242271

272+
metric_ids: Optional[list[str]] = None
273+
if len(self.summary_metrics):
274+
metric_ids = list(self.summary_metrics)
275+
276+
tp.trace_summary(
277+
specs=list(self.metric_definitions), metric_ids=metric_ids)
278+
243279
def _add_cb_columns(self, df: pd.DataFrame, run: Run) -> pd.DataFrame:
244280
df["cb_browser"] = run.browser.unique_name
245281
df["cb_story"] = run.story.name
@@ -373,12 +409,13 @@ def _merge_trace_files(self) -> LocalProbeResult:
373409

374410
def _maybe_run_tp(self) -> ProbeResult:
375411
if not self.probe.needs_tp_run:
376-
return LocalProbeResult()
412+
return EmptyProbeResult()
377413

378414
with TraceProcessor(
379415
trace=CrossbenchTraceUriResolver(self),
380416
config=self.probe.tp_config) as tp:
381-
return self._run_queries(tp).merge(self._run_metrics(tp))
417+
return self._run_queries(tp).merge(self._run_metrics(tp)).merge(
418+
self._summarize_trace(tp))
382419

383420
def _run_queries(self, tp: TraceProcessor) -> LocalProbeResult:
384421

@@ -406,6 +443,32 @@ def run_metric(metric: str):
406443
files = tuple(map(run_metric, self.probe.metrics))
407444
return LocalProbeResult(json=files)
408445

446+
def _summarize_trace(self, tp: TraceProcessor) -> ProbeResult:
447+
if not self.probe.summary_metrics and not self.probe.metric_definitions:
448+
return EmptyProbeResult()
449+
450+
with self.run.actions(
451+
"TRACE_PROCESSOR: Running trace summary", verbose=True):
452+
453+
# Trace processor interprets an empty list as 'emit no metrics' and
454+
# 'None' as emit all metrics specified in the metric definitions.
455+
# When no metric IDs are explicitly given, default to the more
456+
# sensible option of emitting every metric.
457+
metric_ids: Optional[list[str]] = None
458+
if len(self.probe.summary_metrics):
459+
metric_ids = list(self.probe.summary_metrics)
460+
461+
proto_result = tp.trace_summary(
462+
specs=list(self.probe.metric_definitions), metric_ids=metric_ids)
463+
464+
proto_file = self.local_result_path / "v2_metrics.proto"
465+
466+
with proto_file.open("wb") as f:
467+
f.write(proto_result.SerializeToString())
468+
469+
return LocalProbeResult(file=[proto_file])
470+
471+
409472
@property
410473
def merged_trace_path(self) -> pth.LocalPath:
411474
return self.local_result_path / "merged_trace.zip"

tests/crossbench/probes/test_trace_processor.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ def test_parse_example_config(self):
5555
r".*\/crossbench\/probes\/perfetto\/trace_processor\/modules\/ext")
5656
self.assertEqual(str(probe.module_paths[1]), "/my_project/modules/ext")
5757

58+
metric_definitions = probe.metric_definitions
59+
self.assertEqual(len(metric_definitions), 2)
60+
self.assertTrue("file_textproto_metric" in metric_definitions[0])
61+
self.assertTrue("inline_textproto_metric" in metric_definitions[1])
62+
63+
summary_metrics = probe.summary_metrics
64+
# Should contain everything in 'metrics' plus 'summary_metrics'
65+
self.assertListEqual(
66+
list(summary_metrics),
67+
["trace_stats", "file_textproto_metric", "inline_textproto_metric"])
68+
5869
def test_query_config_duplicate_name_raises(self):
5970
with self.assertRaisesRegex(ArgumentTypeError,
6071
"Unexpected duplicates in query names"):

0 commit comments

Comments
 (0)