| #!/usr/bin/env fuchsia-vendored-python | |
| # Copyright 2026 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| """ | |
| Extracts ODPM power metrics from a trace file (FXT or JSON) into fuchsiaperf.json format. | |
| """ | |
| import argparse | |
| import logging | |
| import pathlib | |
| from reporting import metrics | |
| from trace_processing import trace_importing, trace_model | |
| from trace_processing.metrics import odpm_power | |
| logging.basicConfig(level=logging.INFO) | |
| def main() -> None: | |
| """Takes in a trace file (FXT or JSON) and writes ODPM power metrics in fuchsiaperf.json format.""" | |
| parser = argparse.ArgumentParser( | |
| description="Extract ODPM power metrics from a Fuchsia trace file." | |
| ) | |
| parser.add_argument( | |
| "path_to_trace", | |
| type=str, | |
| help="Path to input trace file (.fxt or .json).", | |
| ) | |
| parser.add_argument( | |
| "output_path", | |
| type=str, | |
| nargs="?", | |
| help="Path to output file (must end with .fuchsiaperf.json unless --list-rails is used).", | |
| ) | |
| rail_group = parser.add_mutually_exclusive_group(required=False) | |
| rail_group.add_argument( | |
| "--rails", | |
| nargs="+", | |
| type=str, | |
| help="List of ODPM rail names to extract metrics for (e.g. cpu_big gpu).", | |
| ) | |
| rail_group.add_argument( | |
| "--all-rails", | |
| action="store_true", | |
| help="Extract metrics for all ODPM rails present in the trace.", | |
| ) | |
| rail_group.add_argument( | |
| "--list-rails", | |
| action="store_true", | |
| help="List all ODPM rails available in the trace and exit.", | |
| ) | |
| parser.add_argument( | |
| "--sum-rails", | |
| action="append", | |
| metavar="NAME=RAIL1,RAIL2,...", | |
| help=( | |
| "Compute summed power metrics across a specified list of rails " | |
| "(format: NAME=RAIL1,RAIL2,...; produces *_<NAME> metrics). " | |
| "Can be specified multiple times." | |
| ), | |
| ) | |
| parser.add_argument( | |
| "--test-suite", | |
| type=str, | |
| default="Manual", | |
| help="Test suite name to embed in fuchsiaperf.json (default: Manual).", | |
| ) | |
| args = parser.parse_args() | |
| if not (args.rails or args.all_rails or args.list_rails or args.sum_rails): | |
| parser.error( | |
| "one of the arguments --rails --all-rails --list-rails --sum-rails is required" | |
| ) | |
| if args.list_rails and args.sum_rails: | |
| parser.error( | |
| "argument --sum-rails: not allowed with argument --list-rails" | |
| ) | |
| if not args.list_rails and not args.output_path: | |
| parser.error( | |
| "output_path is required unless --list-rails is specified." | |
| ) | |
| if ( | |
| args.output_path | |
| and not args.list_rails | |
| and not args.output_path.endswith(".fuchsiaperf.json") | |
| ): | |
| parser.error("output_path must end with .fuchsiaperf.json") | |
| sum_rails: dict[str, list[str]] = {} | |
| for spec in args.sum_rails or []: | |
| if "=" not in spec: | |
| parser.error( | |
| f"invalid --sum-rails value '{spec}': expected format NAME=RAIL1,RAIL2,..." | |
| ) | |
| name, rails_csv = spec.split("=", 1) | |
| rail_list = [r.strip() for r in rails_csv.split(",") if r.strip()] | |
| if not name or not rail_list: | |
| parser.error( | |
| f"invalid --sum-rails value '{spec}': expected non-empty NAME and at least one rail" | |
| ) | |
| sum_rails[name] = rail_list | |
| processor = odpm_power.OdpmPowerMetricsProcessor( | |
| rails=args.rails or (), | |
| sum_rails=sum_rails, | |
| all_rails=args.all_rails or args.list_rails, | |
| ) | |
| if args.path_to_trace.endswith(".json"): | |
| path_to_trace_json = args.path_to_trace | |
| elif args.path_to_trace.endswith(".fxt"): | |
| path_to_trace_json = trace_importing.convert_trace_file_to_json( | |
| trace_path=args.path_to_trace, | |
| patterns=processor.event_patterns, | |
| categories=processor.category_names, | |
| ) | |
| else: | |
| raise ValueError("Trace file must be in either .fxt or .json format") | |
| model: trace_model.Model = trace_importing.create_model_from_file_path( | |
| path_to_trace_json | |
| ) | |
| if args.list_rails: | |
| available = odpm_power.OdpmPowerMetricsProcessor.list_rails(model) | |
| print(f"Available ODPM rails ({len(available)}):") | |
| for rail in available: | |
| print(f" {rail}") | |
| return | |
| trace_results = processor.process_metrics(model) | |
| metrics.TestCaseResult.write_fuchsiaperf_json( | |
| results=trace_results, | |
| test_suite=args.test_suite, | |
| output_path=pathlib.Path(args.output_path), | |
| ) | |
| if __name__ == "__main__": | |
| main() |