2626from crossbench .parse import ObjectParser , PathParser
2727from crossbench .probes .metric import MetricsMerger
2828from crossbench .probes .probe import Probe , ProbeConfigParser , ProbeContext
29- from crossbench .probes .results import LocalProbeResult , ProbeResult
29+ from crossbench .probes .results import EmptyProbeResult , LocalProbeResult , ProbeResult
3030from crossbench .replacements import Replacements
3131
3232if 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"
0 commit comments