| #!/usr/bin/env python3 |
| # |
| # Copyright (C) 2018 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. |
| # |
| |
| """ |
| Support profiling without usb connection in below steps: |
| 1. With usb connection, start simpleperf recording. |
| 2. Unplug the usb cable and play the app you want to profile, while the process of |
| simpleperf keeps running and collecting samples. |
| 3. Replug the usb cable, stop simpleperf recording and pull recording file on host. |
| |
| Note that recording is stopped once the app is killed. So if you restart the app |
| during profiling time, simpleperf only records the first running. |
| """ |
| |
| import logging |
| import subprocess |
| import sys |
| import time |
| |
| from simpleperf_utils import AdbHelper, BaseArgumentParser, get_target_binary_path |
| |
| |
| def start_recording(args): |
| adb = AdbHelper() |
| device_arch = adb.get_device_arch() |
| simpleperf_binary = get_target_binary_path(device_arch, 'simpleperf') |
| adb.check_run(['push', simpleperf_binary, '/data/local/tmp']) |
| adb.check_run(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf']) |
| adb.check_run(['shell', 'rm', '-rf', '/data/local/tmp/perf.data', |
| '/data/local/tmp/simpleperf_output']) |
| shell_cmd = 'cd /data/local/tmp && nohup ./simpleperf record ' + args.record_options |
| if args.app: |
| shell_cmd += ' --app ' + args.app |
| if args.pid: |
| shell_cmd += ' -p ' + args.pid |
| if args.size_limit: |
| shell_cmd += ' --size-limit ' + args.size_limit |
| shell_cmd += ' >/data/local/tmp/simpleperf_output 2>&1' |
| print('shell_cmd: %s' % shell_cmd) |
| subproc = subprocess.Popen([adb.adb_path, 'shell', shell_cmd]) |
| # Wait 2 seconds to see if the simpleperf command fails to start. |
| time.sleep(2) |
| if subproc.poll() is None: |
| print('Simpleperf recording has started. Please unplug the usb cable and run the app.') |
| print('After that, run `%s stop` to get recording result.' % sys.argv[0]) |
| else: |
| adb.run(['shell', 'cat', '/data/local/tmp/simpleperf_output']) |
| sys.exit(subproc.returncode) |
| |
| |
| def stop_recording(args): |
| adb = AdbHelper() |
| result = adb.run(['shell', 'pidof', 'simpleperf']) |
| if not result: |
| logging.warning('No simpleperf process on device. The recording has ended.') |
| else: |
| adb.run(['shell', 'pkill', '-l', '2', 'simpleperf']) |
| print('Waiting for simpleperf process to finish...') |
| while adb.run(['shell', 'pidof', 'simpleperf']): |
| time.sleep(1) |
| adb.run(['shell', 'cat', '/data/local/tmp/simpleperf_output']) |
| adb.check_run(['pull', '/data/local/tmp/perf.data', args.perf_data_path]) |
| print('The recording data has been collected in %s.' % args.perf_data_path) |
| |
| |
| def main(): |
| parser = BaseArgumentParser(description=__doc__) |
| subparsers = parser.add_subparsers() |
| start_parser = subparsers.add_parser('start', help='Start recording.') |
| start_parser.add_argument('-r', '--record_options', |
| default='-e task-clock:u -g', |
| help="""Set options for `simpleperf record` command. |
| Default is `-e task-clock:u -g`.""") |
| start_parser.add_argument('-p', '--app', help="""Profile an Android app, given the package |
| name. Like `-p com.example.android.myapp`.""") |
| start_parser.add_argument('--pid', help="""Profile an Android app, given the process id. |
| Like `--pid 918`.""") |
| start_parser.add_argument('--size_limit', type=str, |
| help="""Stop profiling when recording data reaches |
| [size_limit][K|M|G] bytes. Like `--size_limit 1M`.""") |
| start_parser.set_defaults(func=start_recording) |
| stop_parser = subparsers.add_parser('stop', help='Stop recording.') |
| stop_parser.add_argument('-o', '--perf_data_path', default='perf.data', help="""The path to |
| store profiling data on host. Default is perf.data.""") |
| stop_parser.set_defaults(func=stop_recording) |
| args = parser.parse_args() |
| args.func(args) |
| |
| |
| if __name__ == '__main__': |
| main() |