1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*288bf522SAndroid Build Coastguard Worker# 3*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2019 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""" 19*288bf522SAndroid Build Coastguard Worker This script is part of controlling simpleperf recording in user code. It is used to prepare 20*288bf522SAndroid Build Coastguard Worker profiling environment (upload simpleperf to device and enable profiling) before recording 21*288bf522SAndroid Build Coastguard Worker and collect recording data on host after recording. 22*288bf522SAndroid Build Coastguard Worker Controlling simpleperf recording is done in below steps: 23*288bf522SAndroid Build Coastguard Worker 1. Add simpleperf Java API/C++ API to the app's source code. And call the API in user code. 24*288bf522SAndroid Build Coastguard Worker 2. Run `api_profiler.py prepare` to prepare profiling environment. 25*288bf522SAndroid Build Coastguard Worker 3. Run the app one or more times to generate recording data. 26*288bf522SAndroid Build Coastguard Worker 4. Run `api_profiler.py collect` to collect recording data on host. 27*288bf522SAndroid Build Coastguard Worker""" 28*288bf522SAndroid Build Coastguard Worker 29*288bf522SAndroid Build Coastguard Workerfrom argparse import Namespace 30*288bf522SAndroid Build Coastguard Workerimport logging 31*288bf522SAndroid Build Coastguard Workerimport os 32*288bf522SAndroid Build Coastguard Workerimport os.path 33*288bf522SAndroid Build Coastguard Workerimport shutil 34*288bf522SAndroid Build Coastguard Workerimport zipfile 35*288bf522SAndroid Build Coastguard Worker 36*288bf522SAndroid Build Coastguard Workerfrom simpleperf_utils import (AdbHelper, BaseArgumentParser, 37*288bf522SAndroid Build Coastguard Worker get_target_binary_path, log_exit, remove) 38*288bf522SAndroid Build Coastguard Worker 39*288bf522SAndroid Build Coastguard Worker 40*288bf522SAndroid Build Coastguard Workerclass ApiProfiler: 41*288bf522SAndroid Build Coastguard Worker def __init__(self, args: Namespace): 42*288bf522SAndroid Build Coastguard Worker self.args = args 43*288bf522SAndroid Build Coastguard Worker self.adb = AdbHelper() 44*288bf522SAndroid Build Coastguard Worker 45*288bf522SAndroid Build Coastguard Worker def prepare_recording(self): 46*288bf522SAndroid Build Coastguard Worker self.enable_profiling_on_device() 47*288bf522SAndroid Build Coastguard Worker self.upload_simpleperf_to_device() 48*288bf522SAndroid Build Coastguard Worker self.run_simpleperf_prepare_cmd() 49*288bf522SAndroid Build Coastguard Worker 50*288bf522SAndroid Build Coastguard Worker def enable_profiling_on_device(self): 51*288bf522SAndroid Build Coastguard Worker android_version = self.adb.get_android_version() 52*288bf522SAndroid Build Coastguard Worker if android_version >= 10: 53*288bf522SAndroid Build Coastguard Worker self.adb.set_property('debug.perf_event_max_sample_rate', 54*288bf522SAndroid Build Coastguard Worker str(self.args.max_sample_rate)) 55*288bf522SAndroid Build Coastguard Worker self.adb.set_property('debug.perf_cpu_time_max_percent', str(self.args.max_cpu_percent)) 56*288bf522SAndroid Build Coastguard Worker self.adb.set_property('debug.perf_event_mlock_kb', str(self.args.max_memory_in_kb)) 57*288bf522SAndroid Build Coastguard Worker self.adb.set_property('security.perf_harden', '0') 58*288bf522SAndroid Build Coastguard Worker 59*288bf522SAndroid Build Coastguard Worker def upload_simpleperf_to_device(self): 60*288bf522SAndroid Build Coastguard Worker device_arch = self.adb.get_device_arch() 61*288bf522SAndroid Build Coastguard Worker simpleperf_binary = get_target_binary_path(device_arch, 'simpleperf') 62*288bf522SAndroid Build Coastguard Worker self.adb.check_run(['push', simpleperf_binary, '/data/local/tmp']) 63*288bf522SAndroid Build Coastguard Worker self.adb.check_run(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf']) 64*288bf522SAndroid Build Coastguard Worker 65*288bf522SAndroid Build Coastguard Worker def run_simpleperf_prepare_cmd(self): 66*288bf522SAndroid Build Coastguard Worker cmd_args = ['shell', '/data/local/tmp/simpleperf', 'api-prepare', '--app', self.args.app] 67*288bf522SAndroid Build Coastguard Worker if self.args.days: 68*288bf522SAndroid Build Coastguard Worker cmd_args += ['--days', str(self.args.days)] 69*288bf522SAndroid Build Coastguard Worker self.adb.check_run(cmd_args) 70*288bf522SAndroid Build Coastguard Worker 71*288bf522SAndroid Build Coastguard Worker def collect_data(self): 72*288bf522SAndroid Build Coastguard Worker if not os.path.isdir(self.args.out_dir): 73*288bf522SAndroid Build Coastguard Worker os.makedirs(self.args.out_dir) 74*288bf522SAndroid Build Coastguard Worker self.download_recording_data() 75*288bf522SAndroid Build Coastguard Worker self.unzip_recording_data() 76*288bf522SAndroid Build Coastguard Worker 77*288bf522SAndroid Build Coastguard Worker def download_recording_data(self): 78*288bf522SAndroid Build Coastguard Worker """ download recording data to simpleperf_data.zip.""" 79*288bf522SAndroid Build Coastguard Worker self.upload_simpleperf_to_device() 80*288bf522SAndroid Build Coastguard Worker self.adb.check_run(['shell', '/data/local/tmp/simpleperf', 'api-collect', 81*288bf522SAndroid Build Coastguard Worker '--app', self.args.app, '-o', '/data/local/tmp/simpleperf_data.zip']) 82*288bf522SAndroid Build Coastguard Worker self.adb.check_run(['pull', '/data/local/tmp/simpleperf_data.zip', self.args.out_dir]) 83*288bf522SAndroid Build Coastguard Worker self.adb.check_run(['shell', 'rm', '-rf', '/data/local/tmp/simpleperf_data']) 84*288bf522SAndroid Build Coastguard Worker 85*288bf522SAndroid Build Coastguard Worker def unzip_recording_data(self): 86*288bf522SAndroid Build Coastguard Worker zip_file_path = os.path.join(self.args.out_dir, 'simpleperf_data.zip') 87*288bf522SAndroid Build Coastguard Worker with zipfile.ZipFile(zip_file_path, 'r') as zip_fh: 88*288bf522SAndroid Build Coastguard Worker names = zip_fh.namelist() 89*288bf522SAndroid Build Coastguard Worker logging.info('There are %d recording data files.' % len(names)) 90*288bf522SAndroid Build Coastguard Worker for name in names: 91*288bf522SAndroid Build Coastguard Worker logging.info('recording file: %s' % os.path.join(self.args.out_dir, name)) 92*288bf522SAndroid Build Coastguard Worker zip_fh.extract(name, self.args.out_dir) 93*288bf522SAndroid Build Coastguard Worker remove(zip_file_path) 94*288bf522SAndroid Build Coastguard Worker 95*288bf522SAndroid Build Coastguard Worker 96*288bf522SAndroid Build Coastguard Workerdef main(): 97*288bf522SAndroid Build Coastguard Worker parser = BaseArgumentParser(description=__doc__) 98*288bf522SAndroid Build Coastguard Worker subparsers = parser.add_subparsers(title='actions', dest='command') 99*288bf522SAndroid Build Coastguard Worker 100*288bf522SAndroid Build Coastguard Worker prepare_parser = subparsers.add_parser('prepare', help='Prepare recording on device.') 101*288bf522SAndroid Build Coastguard Worker prepare_parser.add_argument('-p', '--app', required=True, help=""" 102*288bf522SAndroid Build Coastguard Worker The app package name of the app profiled.""") 103*288bf522SAndroid Build Coastguard Worker prepare_parser.add_argument('-d', '--days', type=int, help=""" 104*288bf522SAndroid Build Coastguard Worker By default, the recording permission is reset after device reboot. 105*288bf522SAndroid Build Coastguard Worker But on Android >= 13, we can use --days to set how long we want the 106*288bf522SAndroid Build Coastguard Worker permission to persist. It can last after device reboot. 107*288bf522SAndroid Build Coastguard Worker """) 108*288bf522SAndroid Build Coastguard Worker prepare_parser.add_argument('--max-sample-rate', type=int, default=100000, help=""" 109*288bf522SAndroid Build Coastguard Worker Set max sample rate (only on Android >= Q).""") 110*288bf522SAndroid Build Coastguard Worker prepare_parser.add_argument('--max-cpu-percent', type=int, default=25, help=""" 111*288bf522SAndroid Build Coastguard Worker Set max cpu percent for recording (only on Android >= Q).""") 112*288bf522SAndroid Build Coastguard Worker prepare_parser.add_argument('--max-memory-in-kb', type=int, 113*288bf522SAndroid Build Coastguard Worker default=(1024 + 1) * 4 * 8, help=""" 114*288bf522SAndroid Build Coastguard Worker Set max kernel buffer size for recording (only on Android >= Q). 115*288bf522SAndroid Build Coastguard Worker """) 116*288bf522SAndroid Build Coastguard Worker 117*288bf522SAndroid Build Coastguard Worker collect_parser = subparsers.add_parser('collect', help='Collect recording data.') 118*288bf522SAndroid Build Coastguard Worker collect_parser.add_argument('-p', '--app', required=True, help=""" 119*288bf522SAndroid Build Coastguard Worker The app package name of the app profiled.""") 120*288bf522SAndroid Build Coastguard Worker collect_parser.add_argument('-o', '--out-dir', default='simpleperf_data', help=""" 121*288bf522SAndroid Build Coastguard Worker The directory to store recording data.""") 122*288bf522SAndroid Build Coastguard Worker args = parser.parse_args() 123*288bf522SAndroid Build Coastguard Worker 124*288bf522SAndroid Build Coastguard Worker if args.command == 'prepare': 125*288bf522SAndroid Build Coastguard Worker ApiProfiler(args).prepare_recording() 126*288bf522SAndroid Build Coastguard Worker elif args.command == 'collect': 127*288bf522SAndroid Build Coastguard Worker ApiProfiler(args).collect_data() 128*288bf522SAndroid Build Coastguard Worker 129*288bf522SAndroid Build Coastguard Worker 130*288bf522SAndroid Build Coastguard Workerif __name__ == '__main__': 131*288bf522SAndroid Build Coastguard Worker main() 132