xref: /aosp_15_r20/system/extras/simpleperf/scripts/api_profiler.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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