Init
This commit is contained in:
137
Tools/Platform/Android/android-ndk-r27d/simpleperf/ipc.py
Normal file
137
Tools/Platform/Android/android-ndk-r27d/simpleperf/ipc.py
Normal file
@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (C) 2023 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
"""ipc.py: Capture the Instructions per Cycle (IPC) of the system during a
|
||||
specified duration.
|
||||
|
||||
Example:
|
||||
./ipc.py
|
||||
./ipc.py 2 20 # Set interval to 2 secs and total duration to 20 secs
|
||||
./ipc.py -p 284 -C 4 # Only profile the PID 284 while running on core 4
|
||||
./ipc.py -c 'sleep 5' # Only profile the command to run
|
||||
|
||||
Result looks like:
|
||||
K_CYCLES K_INSTR IPC
|
||||
36840 14138 0.38
|
||||
70701 27743 0.39
|
||||
104562 41350 0.40
|
||||
138264 54916 0.40
|
||||
"""
|
||||
|
||||
import io
|
||||
import logging
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
from simpleperf_utils import (
|
||||
AdbHelper, BaseArgumentParser, get_target_binary_path, log_exit)
|
||||
|
||||
def start_profiling(adb, args, target_args):
|
||||
"""Start simpleperf process on device."""
|
||||
shell_args = ['simpleperf', 'stat', '-e', 'cpu-cycles',
|
||||
'-e', 'instructions', '--interval', str(args.interval * 1000),
|
||||
'--duration', str(args.duration)]
|
||||
shell_args += target_args
|
||||
adb_args = [adb.adb_path, 'shell'] + shell_args
|
||||
logging.info('run adb cmd: %s' % adb_args)
|
||||
return subprocess.Popen(adb_args, stdout=subprocess.PIPE)
|
||||
|
||||
def capture_stats(adb, args, stat_subproc):
|
||||
"""Capture IPC profiling stats or stop profiling when user presses Ctrl-C."""
|
||||
try:
|
||||
print("%-10s %-10s %5s" % ("K_CYCLES", "K_INSTR", "IPC"))
|
||||
cpu_cycles = 0
|
||||
for line in io.TextIOWrapper(stat_subproc.stdout, encoding="utf-8"):
|
||||
if 'cpu-cycles' in line:
|
||||
if args.cpu == None:
|
||||
cpu_cycles = int(line.split()[0].replace(",", ""))
|
||||
continue
|
||||
columns = line.split()
|
||||
if args.cpu == int(columns[0]):
|
||||
cpu_cycles = int(columns[1].replace(",", ""))
|
||||
elif 'instructions' in line:
|
||||
if cpu_cycles == 0: cpu_cycles = 1 # PMCs are broken, or no events
|
||||
ins = -1
|
||||
columns = line.split()
|
||||
if args.cpu == None:
|
||||
ins = int(columns[0].replace(",", ""))
|
||||
elif args.cpu == int(columns[0]):
|
||||
ins = int(columns[1].replace(",", ""))
|
||||
if ins >= 0:
|
||||
print("%-10d %-10d %5.2f" %
|
||||
(cpu_cycles / 1000, ins / 1000, ins / cpu_cycles))
|
||||
|
||||
except KeyboardInterrupt:
|
||||
stop_profiling(adb)
|
||||
stat_subproc = None
|
||||
|
||||
def stop_profiling(adb):
|
||||
"""Stop profiling by sending SIGINT to simpleperf and wait until it exits."""
|
||||
has_killed = False
|
||||
while True:
|
||||
(result, _) = adb.run_and_return_output(['shell', 'pidof', 'simpleperf'])
|
||||
if not result:
|
||||
break
|
||||
if not has_killed:
|
||||
has_killed = True
|
||||
adb.run_and_return_output(['shell', 'pkill', '-l', '2', 'simpleperf'])
|
||||
time.sleep(1)
|
||||
|
||||
def capture_ipc(args):
|
||||
# Initialize adb and verify device
|
||||
adb = AdbHelper(enable_switch_to_root=True)
|
||||
if not adb.is_device_available():
|
||||
log_exit('No Android device is connected via ADB.')
|
||||
is_root_device = adb.switch_to_root()
|
||||
device_arch = adb.get_device_arch()
|
||||
|
||||
if args.pid:
|
||||
(result, _) = adb.run_and_return_output(['shell', 'ls', '/proc/%s' % args.pid])
|
||||
if not result:
|
||||
log_exit("Pid '%s' does not exist" % args.pid)
|
||||
|
||||
target_args = []
|
||||
if args.cpu is not None:
|
||||
target_args += ['--per-core']
|
||||
if args.pid:
|
||||
target_args += ['-p', args.pid]
|
||||
elif args.command:
|
||||
target_args += [args.command]
|
||||
else:
|
||||
target_args += ['-a']
|
||||
|
||||
stat_subproc = start_profiling(adb, args, target_args)
|
||||
capture_stats(adb, args, stat_subproc)
|
||||
|
||||
def main():
|
||||
parser = BaseArgumentParser(description=__doc__)
|
||||
parser.add_argument('-C', '--cpu', type=int, help='Capture IPC only for this CPU core')
|
||||
process_group = parser.add_mutually_exclusive_group()
|
||||
process_group.add_argument('-p', '--pid', help='Capture IPC only for this PID')
|
||||
process_group.add_argument('-c', '--command', help='Capture IPC only for this command')
|
||||
parser.add_argument('interval', nargs='?', default=1, type=int, help='sampling interval in seconds')
|
||||
parser.add_argument('duration', nargs='?', default=10, type=int, help='sampling duration in seconds')
|
||||
|
||||
args = parser.parse_args()
|
||||
if args.interval > args.duration:
|
||||
log_exit("interval cannot be greater than duration")
|
||||
|
||||
capture_ipc(args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user