perf_trace.py (3719B)
1 # Copyright 2025 The Chromium Authors 2 # Use of this source code is governed by a BSD-style license that can be 3 # found in the LICENSE file. 4 """ Retrieves the system metrics and logs them into the monitors system. """ 5 6 import json 7 import os 8 import subprocess 9 10 from numbers import Number 11 from typing import Optional 12 13 import monitors 14 15 from common import run_ffx_command, get_host_tool_path 16 17 # Copy to avoid cycle dependency. 18 TEMP_DIR = os.environ.get('TMPDIR', '/tmp') 19 20 FXT_FILE = os.path.join(TEMP_DIR, 'perf_trace.fxt') 21 JSON_FILE = os.path.join(TEMP_DIR, 'perf_trace.json') 22 23 METRIC_FILTERS = [ 24 'gfx/ContiguousPooledMemoryAllocator::Allocate/size_bytes', 25 'gfx/SysmemProtectedPool/size', 26 'gfx/WindowedFramePredictor::GetPrediction/Predicted frame duration(ms)', 27 'gfx/WindowedFramePredictor::GetPrediction/Render time(ms)', 28 'gfx/WindowedFramePredictor::GetPrediction/Update time(ms)', 29 'memory_monitor/bandwidth_free/value', 30 'memory_monitor/free/free$', 31 'system_metrics/cpu_usage/average_cpu_percentage', 32 ] 33 34 35 def start() -> None: 36 """ Starts the system tracing. """ 37 # TODO(crbug.com/40935291): May include kernel:meta, kernel:sched, magma, 38 # oemcrypto, media, kernel:syscall 39 run_ffx_command(cmd=('trace', 'start', '--background', '--categories', 40 'gfx,memory_monitor,system_metrics', '--output', 41 FXT_FILE)) 42 43 44 def stop(prefix: Optional[str] = None) -> None: 45 """ Stops the system tracing and logs the metrics into the monitors system 46 with an optional prefix as part of the metric names. """ 47 run_ffx_command(cmd=('trace', 'stop')) 48 _parse_trace(prefix) 49 50 51 # pylint: disable=too-many-nested-blocks 52 def _parse_trace(prefix: Optional[str] = None) -> None: 53 subprocess.run([ 54 get_host_tool_path('trace2json'), f'--input-file={FXT_FILE}', 55 f'--output-file={JSON_FILE}' 56 ], 57 check=True) 58 with open(JSON_FILE, 'r') as file: 59 recorders = {} 60 for event in json.load(file)['traceEvents']: 61 if not 'args' in event: 62 # Support only the events with args now. 63 continue 64 cat_name = [event['cat'], event['name']] 65 if prefix: 66 cat_name.insert(0, prefix) 67 args = event['args'] 68 # Support only the events with str or numeric args now. 69 for arg in args: 70 if isinstance(args[arg], str): 71 cat_name.append(arg) 72 cat_name.append(args[arg]) 73 for arg in args: 74 # Allows all number types. 75 if isinstance(args[arg], Number): 76 name = cat_name.copy() 77 name.append(arg) 78 for f in METRIC_FILTERS: 79 if f in '/'.join(name) + '$': 80 if tuple(name) not in recorders: 81 recorders[tuple(name)] = monitors.average( 82 *name) 83 recorders[tuple(name)].record(args[arg]) 84 85 86 # For tests only. To run this test, create a perf_trace.fxt in the /tmp/, run 87 # this script and inspect /tmp/test_script_metrics.jsonpb. 88 # 89 # If nothing needs to be customized, the commands look like, 90 # $ ffx trace start --background \ 91 # --categories 'gfx,memory_monitor,system_metrics' \ 92 # --output /tmp/perf_trace.fxt 93 # -- do something on the fuchsia device -- 94 # $ ffx trace stop 95 # $ vpython3 build/fuchsia/test/perf_trace.py 96 # 97 # Note, reuse the perf_trace.fxt is OK, i.e. running perf_trace.py multiple 98 # times works. 99 if __name__ == '__main__': 100 _parse_trace() 101 monitors.dump(TEMP_DIR)