1#!/usr/bin/env python3 2# 3# Copyright (C) 2019 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18""" 19 This script is part of controlling simpleperf recording in user code. It is used to prepare 20 profiling environment (upload simpleperf to device and enable profiling) before recording 21 and collect recording data on host after recording. 22 Controlling simpleperf recording is done in below steps: 23 1. Add simpleperf Java API/C++ API to the app's source code. And call the API in user code. 24 2. Run `api_profiler.py prepare` to prepare profiling environment. 25 3. Run the app one or more times to generate recording data. 26 4. Run `api_profiler.py collect` to collect recording data on host. 27""" 28 29from __future__ import print_function 30import argparse 31import os 32import os.path 33import shutil 34import zipfile 35 36from simpleperf_utils import AdbHelper, get_target_binary_path, log_exit, log_info, remove 37 38 39def prepare_recording(args): 40 adb = AdbHelper() 41 enable_profiling_on_device(adb, args) 42 upload_simpleperf_to_device(adb) 43 run_simpleperf_prepare_cmd(adb) 44 45 46def enable_profiling_on_device(adb, args): 47 android_version = adb.get_android_version() 48 if android_version >= 10: 49 adb.set_property('debug.perf_event_max_sample_rate', str(args.max_sample_rate[0])) 50 adb.set_property('debug.perf_cpu_time_max_percent', str(args.max_cpu_percent[0])) 51 adb.set_property('debug.perf_event_mlock_kb', str(args.max_memory_in_kb[0])) 52 adb.set_property('security.perf_harden', '0') 53 54 55def upload_simpleperf_to_device(adb): 56 device_arch = adb.get_device_arch() 57 simpleperf_binary = get_target_binary_path(device_arch, 'simpleperf') 58 adb.check_run(['push', simpleperf_binary, '/data/local/tmp']) 59 adb.check_run(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf']) 60 61 62def run_simpleperf_prepare_cmd(adb): 63 adb.check_run(['shell', '/data/local/tmp/simpleperf', 'api-prepare']) 64 65 66def collect_data(args): 67 adb = AdbHelper() 68 if not os.path.isdir(args.out_dir): 69 os.makedirs(args.out_dir) 70 download_recording_data(adb, args) 71 unzip_recording_data(args) 72 73 74def download_recording_data(adb, args): 75 """ download recording data to simpleperf_data.zip.""" 76 upload_simpleperf_to_device(adb) 77 adb.check_run(['shell', '/data/local/tmp/simpleperf', 'api-collect', '--app', args.app[0], 78 '-o', '/data/local/tmp/simpleperf_data.zip']) 79 adb.check_run(['pull', '/data/local/tmp/simpleperf_data.zip', args.out_dir]) 80 adb.check_run(['shell', 'rm', '-rf', '/data/local/tmp/simpleperf_data']) 81 82 83def unzip_recording_data(args): 84 zip_file_path = os.path.join(args.out_dir, 'simpleperf_data.zip') 85 with zipfile.ZipFile(zip_file_path, 'r') as zip_fh: 86 names = zip_fh.namelist() 87 log_info('There are %d recording data files.' % len(names)) 88 for name in names: 89 log_info('recording file: %s' % os.path.join(args.out_dir, name)) 90 zip_fh.extract(name, args.out_dir) 91 remove(zip_file_path) 92 93 94class ArgumentHelpFormatter(argparse.ArgumentDefaultsHelpFormatter, 95 argparse.RawDescriptionHelpFormatter): 96 pass 97 98 99def main(): 100 parser = argparse.ArgumentParser(description=__doc__, 101 formatter_class=ArgumentHelpFormatter) 102 subparsers = parser.add_subparsers() 103 prepare_parser = subparsers.add_parser('prepare', help='Prepare recording on device.', 104 formatter_class=ArgumentHelpFormatter) 105 prepare_parser.add_argument('--max-sample-rate', nargs=1, type=int, default=[100000], help=""" 106 Set max sample rate (only on Android >= Q).""") 107 prepare_parser.add_argument('--max-cpu-percent', nargs=1, type=int, default=[25], help=""" 108 Set max cpu percent for recording (only on Android >= Q).""") 109 prepare_parser.add_argument('--max-memory-in-kb', nargs=1, type=int, 110 default=[(1024 + 1) * 4 * 8], help=""" 111 Set max kernel buffer size for recording (only on Android >= Q). 112 """) 113 prepare_parser.set_defaults(func=prepare_recording) 114 collect_parser = subparsers.add_parser('collect', help='Collect recording data.', 115 formatter_class=ArgumentHelpFormatter) 116 collect_parser.add_argument('-p', '--app', nargs=1, required=True, help=""" 117 The app package name of the app profiled.""") 118 collect_parser.add_argument('-o', '--out-dir', default='simpleperf_data', help=""" 119 The directory to store recording data.""") 120 collect_parser.set_defaults(func=collect_data) 121 args = parser.parse_args() 122 args.func(args) 123 124 125if __name__ == '__main__': 126 main() 127