1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*288bf522SAndroid Build Coastguard Worker# 3*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 4*288bf522SAndroid Build Coastguard Worker# 5*288bf522SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*288bf522SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*288bf522SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*288bf522SAndroid Build Coastguard Worker# 9*288bf522SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*288bf522SAndroid Build Coastguard Worker# 11*288bf522SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*288bf522SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*288bf522SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*288bf522SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*288bf522SAndroid Build Coastguard Worker# limitations under the License. 16*288bf522SAndroid Build Coastguard Worker# 17*288bf522SAndroid Build Coastguard Worker 18*288bf522SAndroid Build Coastguard Worker"""ipc.py: Capture the Instructions per Cycle (IPC) of the system during a 19*288bf522SAndroid Build Coastguard Worker specified duration. 20*288bf522SAndroid Build Coastguard Worker 21*288bf522SAndroid Build Coastguard Worker Example: 22*288bf522SAndroid Build Coastguard Worker ./ipc.py 23*288bf522SAndroid Build Coastguard Worker ./ipc.py 2 20 # Set interval to 2 secs and total duration to 20 secs 24*288bf522SAndroid Build Coastguard Worker ./ipc.py -p 284 -C 4 # Only profile the PID 284 while running on core 4 25*288bf522SAndroid Build Coastguard Worker ./ipc.py -c 'sleep 5' # Only profile the command to run 26*288bf522SAndroid Build Coastguard Worker 27*288bf522SAndroid Build Coastguard Worker Result looks like: 28*288bf522SAndroid Build Coastguard Worker K_CYCLES K_INSTR IPC 29*288bf522SAndroid Build Coastguard Worker 36840 14138 0.38 30*288bf522SAndroid Build Coastguard Worker 70701 27743 0.39 31*288bf522SAndroid Build Coastguard Worker 104562 41350 0.40 32*288bf522SAndroid Build Coastguard Worker 138264 54916 0.40 33*288bf522SAndroid Build Coastguard Worker""" 34*288bf522SAndroid Build Coastguard Worker 35*288bf522SAndroid Build Coastguard Workerimport io 36*288bf522SAndroid Build Coastguard Workerimport logging 37*288bf522SAndroid Build Coastguard Workerimport subprocess 38*288bf522SAndroid Build Coastguard Workerimport sys 39*288bf522SAndroid Build Coastguard Workerimport time 40*288bf522SAndroid Build Coastguard Worker 41*288bf522SAndroid Build Coastguard Workerfrom simpleperf_utils import ( 42*288bf522SAndroid Build Coastguard Worker AdbHelper, BaseArgumentParser, get_target_binary_path, log_exit) 43*288bf522SAndroid Build Coastguard Worker 44*288bf522SAndroid Build Coastguard Workerdef start_profiling(adb, args, target_args): 45*288bf522SAndroid Build Coastguard Worker """Start simpleperf process on device.""" 46*288bf522SAndroid Build Coastguard Worker shell_args = ['simpleperf', 'stat', '-e', 'cpu-cycles', 47*288bf522SAndroid Build Coastguard Worker '-e', 'instructions', '--interval', str(args.interval * 1000), 48*288bf522SAndroid Build Coastguard Worker '--duration', str(args.duration)] 49*288bf522SAndroid Build Coastguard Worker shell_args += target_args 50*288bf522SAndroid Build Coastguard Worker adb_args = [adb.adb_path, 'shell'] + shell_args 51*288bf522SAndroid Build Coastguard Worker logging.info('run adb cmd: %s' % adb_args) 52*288bf522SAndroid Build Coastguard Worker return subprocess.Popen(adb_args, stdout=subprocess.PIPE) 53*288bf522SAndroid Build Coastguard Worker 54*288bf522SAndroid Build Coastguard Workerdef capture_stats(adb, args, stat_subproc): 55*288bf522SAndroid Build Coastguard Worker """Capture IPC profiling stats or stop profiling when user presses Ctrl-C.""" 56*288bf522SAndroid Build Coastguard Worker try: 57*288bf522SAndroid Build Coastguard Worker print("%-10s %-10s %5s" % ("K_CYCLES", "K_INSTR", "IPC")) 58*288bf522SAndroid Build Coastguard Worker cpu_cycles = 0 59*288bf522SAndroid Build Coastguard Worker for line in io.TextIOWrapper(stat_subproc.stdout, encoding="utf-8"): 60*288bf522SAndroid Build Coastguard Worker if 'cpu-cycles' in line: 61*288bf522SAndroid Build Coastguard Worker if args.cpu == None: 62*288bf522SAndroid Build Coastguard Worker cpu_cycles = int(line.split()[0].replace(",", "")) 63*288bf522SAndroid Build Coastguard Worker continue 64*288bf522SAndroid Build Coastguard Worker columns = line.split() 65*288bf522SAndroid Build Coastguard Worker if args.cpu == int(columns[0]): 66*288bf522SAndroid Build Coastguard Worker cpu_cycles = int(columns[1].replace(",", "")) 67*288bf522SAndroid Build Coastguard Worker elif 'instructions' in line: 68*288bf522SAndroid Build Coastguard Worker if cpu_cycles == 0: cpu_cycles = 1 # PMCs are broken, or no events 69*288bf522SAndroid Build Coastguard Worker ins = -1 70*288bf522SAndroid Build Coastguard Worker columns = line.split() 71*288bf522SAndroid Build Coastguard Worker if args.cpu == None: 72*288bf522SAndroid Build Coastguard Worker ins = int(columns[0].replace(",", "")) 73*288bf522SAndroid Build Coastguard Worker elif args.cpu == int(columns[0]): 74*288bf522SAndroid Build Coastguard Worker ins = int(columns[1].replace(",", "")) 75*288bf522SAndroid Build Coastguard Worker if ins >= 0: 76*288bf522SAndroid Build Coastguard Worker print("%-10d %-10d %5.2f" % 77*288bf522SAndroid Build Coastguard Worker (cpu_cycles / 1000, ins / 1000, ins / cpu_cycles)) 78*288bf522SAndroid Build Coastguard Worker 79*288bf522SAndroid Build Coastguard Worker except KeyboardInterrupt: 80*288bf522SAndroid Build Coastguard Worker stop_profiling(adb) 81*288bf522SAndroid Build Coastguard Worker stat_subproc = None 82*288bf522SAndroid Build Coastguard Worker 83*288bf522SAndroid Build Coastguard Workerdef stop_profiling(adb): 84*288bf522SAndroid Build Coastguard Worker """Stop profiling by sending SIGINT to simpleperf and wait until it exits.""" 85*288bf522SAndroid Build Coastguard Worker has_killed = False 86*288bf522SAndroid Build Coastguard Worker while True: 87*288bf522SAndroid Build Coastguard Worker (result, _) = adb.run_and_return_output(['shell', 'pidof', 'simpleperf']) 88*288bf522SAndroid Build Coastguard Worker if not result: 89*288bf522SAndroid Build Coastguard Worker break 90*288bf522SAndroid Build Coastguard Worker if not has_killed: 91*288bf522SAndroid Build Coastguard Worker has_killed = True 92*288bf522SAndroid Build Coastguard Worker adb.run_and_return_output(['shell', 'pkill', '-l', '2', 'simpleperf']) 93*288bf522SAndroid Build Coastguard Worker time.sleep(1) 94*288bf522SAndroid Build Coastguard Worker 95*288bf522SAndroid Build Coastguard Workerdef capture_ipc(args): 96*288bf522SAndroid Build Coastguard Worker # Initialize adb and verify device 97*288bf522SAndroid Build Coastguard Worker adb = AdbHelper(enable_switch_to_root=True) 98*288bf522SAndroid Build Coastguard Worker if not adb.is_device_available(): 99*288bf522SAndroid Build Coastguard Worker log_exit('No Android device is connected via ADB.') 100*288bf522SAndroid Build Coastguard Worker is_root_device = adb.switch_to_root() 101*288bf522SAndroid Build Coastguard Worker device_arch = adb.get_device_arch() 102*288bf522SAndroid Build Coastguard Worker 103*288bf522SAndroid Build Coastguard Worker if args.pid: 104*288bf522SAndroid Build Coastguard Worker (result, _) = adb.run_and_return_output(['shell', 'ls', '/proc/%s' % args.pid]) 105*288bf522SAndroid Build Coastguard Worker if not result: 106*288bf522SAndroid Build Coastguard Worker log_exit("Pid '%s' does not exist" % args.pid) 107*288bf522SAndroid Build Coastguard Worker 108*288bf522SAndroid Build Coastguard Worker target_args = [] 109*288bf522SAndroid Build Coastguard Worker if args.cpu is not None: 110*288bf522SAndroid Build Coastguard Worker target_args += ['--per-core'] 111*288bf522SAndroid Build Coastguard Worker if args.pid: 112*288bf522SAndroid Build Coastguard Worker target_args += ['-p', args.pid] 113*288bf522SAndroid Build Coastguard Worker elif args.command: 114*288bf522SAndroid Build Coastguard Worker target_args += [args.command] 115*288bf522SAndroid Build Coastguard Worker else: 116*288bf522SAndroid Build Coastguard Worker target_args += ['-a'] 117*288bf522SAndroid Build Coastguard Worker 118*288bf522SAndroid Build Coastguard Worker stat_subproc = start_profiling(adb, args, target_args) 119*288bf522SAndroid Build Coastguard Worker capture_stats(adb, args, stat_subproc) 120*288bf522SAndroid Build Coastguard Worker 121*288bf522SAndroid Build Coastguard Workerdef main(): 122*288bf522SAndroid Build Coastguard Worker parser = BaseArgumentParser(description=__doc__) 123*288bf522SAndroid Build Coastguard Worker parser.add_argument('-C', '--cpu', type=int, help='Capture IPC only for this CPU core') 124*288bf522SAndroid Build Coastguard Worker process_group = parser.add_mutually_exclusive_group() 125*288bf522SAndroid Build Coastguard Worker process_group.add_argument('-p', '--pid', help='Capture IPC only for this PID') 126*288bf522SAndroid Build Coastguard Worker process_group.add_argument('-c', '--command', help='Capture IPC only for this command') 127*288bf522SAndroid Build Coastguard Worker parser.add_argument('interval', nargs='?', default=1, type=int, help='sampling interval in seconds') 128*288bf522SAndroid Build Coastguard Worker parser.add_argument('duration', nargs='?', default=10, type=int, help='sampling duration in seconds') 129*288bf522SAndroid Build Coastguard Worker 130*288bf522SAndroid Build Coastguard Worker args = parser.parse_args() 131*288bf522SAndroid Build Coastguard Worker if args.interval > args.duration: 132*288bf522SAndroid Build Coastguard Worker log_exit("interval cannot be greater than duration") 133*288bf522SAndroid Build Coastguard Worker 134*288bf522SAndroid Build Coastguard Worker capture_ipc(args) 135*288bf522SAndroid Build Coastguard Worker 136*288bf522SAndroid Build Coastguard Workerif __name__ == '__main__': 137*288bf522SAndroid Build Coastguard Worker main() 138