17c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui#!/usr/bin/env python 27c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# 37c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# Copyright (C) 2016 The Android Open Source Project 47c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# 57c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# Licensed under the Apache License, Version 2.0 (the "License"); 67c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# you may not use this file except in compliance with the License. 77c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# You may obtain a copy of the License at 87c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# 97c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# http://www.apache.org/licenses/LICENSE-2.0 107c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# 117c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# Unless required by applicable law or agreed to in writing, software 127c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# distributed under the License is distributed on an "AS IS" BASIS, 137c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 147c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# See the License for the specific language governing permissions and 157c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# limitations under the License. 167c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui# 177c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 187c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui"""app_profiler.py: manage the process of profiling an android app. 197c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui It downloads simpleperf on device, uses it to collect samples from 207c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui user's app, and pulls perf.data and needed binaries on host. 217c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui""" 227c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 237c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuifrom __future__ import print_function 247c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport argparse 257c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport copy 267c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport os 277c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport os.path 287c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport shutil 297c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport subprocess 307c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport sys 317c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiimport time 327c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 337c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuifrom binary_cache_builder import BinaryCacheBuilder 347c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuifrom simpleperf_report_lib import * 357c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuifrom utils import * 367c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 377c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiclass AppProfiler(object): 387c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui """Used to manage the process of profiling an android app. 397c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 407c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui There are three steps: 417c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1. Prepare profiling. 427c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2. Profile the app. 437c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 3. Collect profiling data. 447c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui """ 457c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def __init__(self, config): 467c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui # check config variables 477c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui config_names = ['app_package_name', 'native_lib_dir', 'apk_file_path', 4835c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik 'recompile_app', 'launch_activity', 'launch_inst_test', 497c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 'record_options', 'perf_data_path', 'adb_path', 'readelf_path', 507c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 'binary_cache_dir'] 517c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui for name in config_names: 527c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if not config.has_key(name): 537c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_fatal('config [%s] is missing' % name) 547c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui native_lib_dir = config.get('native_lib_dir') 557c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if native_lib_dir and not os.path.isdir(native_lib_dir): 567c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_fatal('[native_lib_dir] "%s" is not a dir' % native_lib_dir) 577c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui apk_file_path = config.get('apk_file_path') 587c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if apk_file_path and not os.path.isfile(apk_file_path): 597c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_fatal('[apk_file_path] "%s" is not a file' % apk_file_path) 607c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.config = config 617c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb = AdbHelper(self.config['adb_path']) 627c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.is_root_device = False 637c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.android_version = 0 647c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.device_arch = None 657c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.app_arch = None 667c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.app_pid = None 677c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 687c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 697c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def profile(self): 707c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('prepare profiling') 717c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.prepare_profiling() 727c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('start profiling') 737c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.start_and_wait_profiling() 747c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('collect profiling data') 757c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.collect_profiling_data() 767c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('profiling is finished.') 777c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 787c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 797c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def prepare_profiling(self): 807c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self._get_device_environment() 817c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self._enable_profiling() 827c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self._recompile_app() 837c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self._restart_app() 847c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self._get_app_environment() 857c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self._download_simpleperf() 867c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self._download_native_libs() 877c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 887c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 897c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _get_device_environment(self): 907c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.is_root_device = self.adb.switch_to_root() 917c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 927c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui # Get android version. 937c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui build_version = self.adb.get_property('ro.build.version.release') 947c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if build_version: 957c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if not build_version[0].isdigit(): 967c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui c = build_version[0].upper() 977c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if c < 'L': 987c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.android_version = 0 997c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 1007c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.android_version = ord(c) - ord('L') + 5 1017c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 1027c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui strs = build_version.split('.') 1037c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if strs: 1047c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.android_version = int(strs[0]) 1057c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1067c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui # Get device architecture. 1077c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui output = self.adb.check_run_and_return_output(['shell', 'uname', '-m']) 1087c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if output.find('aarch64') != -1: 1097c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.device_arch = 'aarch64' 1107c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui elif output.find('arm') != -1: 1117c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.device_arch = 'arm' 1127c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui elif output.find('x86_64') != -1: 1137c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.device_arch = 'x86_64' 1147c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui elif output.find('86') != -1: 1157c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.device_arch = 'x86' 1167c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 1177c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_fatal('unsupported architecture: %s' % output.strip()) 1187c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1197c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1207c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _enable_profiling(self): 1217c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.set_property('security.perf_harden', '0') 1227c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if self.is_root_device: 1237c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui # We can enable kernel symbols 1247c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.run(['shell', 'echo', '0', '>/proc/sys/kernel/kptr_restrict']) 1257c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1267c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1277c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _recompile_app(self): 1287c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if not self.config['recompile_app']: 1297c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return 1307c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if self.android_version == 0: 1317c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_warning("Can't fully compile an app on android version < L.") 1327c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui elif self.android_version == 5 or self.android_version == 6: 1337c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if not self.is_root_device: 1347c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_warning("Can't fully compile an app on android version < N on non-root devices.") 1357c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui elif not self.config['apk_file_path']: 1367c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_warning("apk file is needed to reinstall the app on android version < N.") 1377c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 1387c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui flag = '-g' if self.android_version == 6 else '--include-debug-symbols' 1397c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.set_property('dalvik.vm.dex2oat-flags', flag) 1407c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.check_run(['install', '-r', self.config['apk_file_path']]) 1417c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui elif self.android_version >= 7: 1427c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.set_property('debug.generate-debug-info', 'true') 1437c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.check_run(['shell', 'cmd', 'package', 'compile', '-f', '-m', 'speed', 1447c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.config['app_package_name']]) 1457c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 1467c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_fatal('unreachable') 1477c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1487c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1497c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _restart_app(self): 15035c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik if not self.config['launch_activity'] and not self.config['launch_inst_test']: 1517c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return 15235c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik 1537c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui pid = self._find_app_process() 1547c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if pid is not None: 1558f3d1485e1b915d500579aedae40555a78d74744Yabin Cui self.run_in_app_dir(['kill', '-9', str(pid)]) 1567c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui time.sleep(1) 15735c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik 15835c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik if self.config['launch_activity']: 15935c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik activity = self.config['app_package_name'] + '/' + self.config['launch_activity'] 16035c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik result = self.adb.run(['shell', 'am', 'start', '-n', activity]) 16135c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik if not result: 16235c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik log_fatal("Can't start activity %s" % activity) 16335c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik else: 16435c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik runner = self.config['app_package_name'] + '/android.support.test.runner.AndroidJUnitRunner' 16535c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik result = self.adb.run(['shell', 'am', 'instrument', '-e', 'class', 16635c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik self.config['launch_inst_test'], runner]) 16735c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik if not result: 16835c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik log_fatal("Can't start instrumentation test %s" % self.config['launch_inst_test']) 16935c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik 1707c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui for i in range(10): 1717c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui pid = self._find_app_process() 1727c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if pid is not None: 17335c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik return 1747c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui time.sleep(1) 1757c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('Wait for the app process for %d seconds' % (i + 1)) 1767c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_fatal("Can't find the app process") 1777c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1787c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1797c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _find_app_process(self): 1808f3d1485e1b915d500579aedae40555a78d74744Yabin Cui result, output = self.adb.run_and_return_output(['shell', 'ps']) 1817c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if not result: 1827c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return None 1837c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui output = output.split('\n') 1847c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui for line in output: 1857c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui strs = line.split() 1867c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if len(strs) > 2 and strs[-1].find(self.config['app_package_name']) != -1: 1877c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return int(strs[1]) 1887c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return None 1897c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1907c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 1917c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _get_app_environment(self): 1927c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.app_pid = self._find_app_process() 1937c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if self.app_pid is None: 1947c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_fatal("can't find process for app [%s]" % self.config['app_package_name']) 1957c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if self.device_arch in ['aarch64', 'x86_64']: 1968f3d1485e1b915d500579aedae40555a78d74744Yabin Cui output = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid]) 1977c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if output.find('linker64') != -1: 1987c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.app_arch = self.device_arch 1997c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 2007c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.app_arch = 'arm' if self.device_arch == 'aarch64' else 'x86' 2017c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 2027c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.app_arch = self.device_arch 2037c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('app_arch: %s' % self.app_arch) 2047c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2057c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2067c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _download_simpleperf(self): 2077c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui simpleperf_binary = get_target_binary_path(self.app_arch, 'simpleperf') 2087c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.check_run(['push', simpleperf_binary, '/data/local/tmp']) 2098f3d1485e1b915d500579aedae40555a78d74744Yabin Cui self.run_in_app_dir(['cp', '/data/local/tmp/simpleperf', '.']) 2108f3d1485e1b915d500579aedae40555a78d74744Yabin Cui self.run_in_app_dir(['chmod', 'a+x', 'simpleperf']) 2117c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2127c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2137c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _download_native_libs(self): 2147c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if not self.config['native_lib_dir']: 2157c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return 2167c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui filename_dict = dict() 2177c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui for root, _, files in os.walk(self.config['native_lib_dir']): 2187c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui for file in files: 2197c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if not file.endswith('.so'): 2207c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui continue 2217c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui path = os.path.join(root, file) 2227c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui old_path = filename_dict.get(file) 2237c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('app_arch = %s' % self.app_arch) 2247c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if self._is_lib_better(path, old_path): 2257c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('%s is better than %s' % (path, old_path)) 2267c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui filename_dict[file] = path 2277c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui else: 2287c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui log_info('%s is worse than %s' % (path, old_path)) 2298f3d1485e1b915d500579aedae40555a78d74744Yabin Cui maps = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid]) 2307c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui searched_lib = dict() 2317c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui for item in maps.split(): 2327c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if item.endswith('.so') and searched_lib.get(item) is None: 2337c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui searched_lib[item] = True 2347c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui # Use '/' as path separator as item comes from android environment. 2357c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui filename = item[item.rfind('/') + 1:] 2367c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui dirname = item[1:item.rfind('/')] 2377c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui path = filename_dict.get(filename) 2387c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if path is None: 2397c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui continue 2407c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.check_run(['push', path, '/data/local/tmp']) 2418f3d1485e1b915d500579aedae40555a78d74744Yabin Cui self.run_in_app_dir(['mkdir', '-p', dirname]) 2428f3d1485e1b915d500579aedae40555a78d74744Yabin Cui self.run_in_app_dir(['cp', '/data/local/tmp/' + filename, dirname]) 2437c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2447c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2457c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def _is_lib_better(self, new_path, old_path): 2467c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui """ Return true if new_path is more likely to be used on device. """ 2477c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if old_path is None: 2487c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return True 2497c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if self.app_arch == 'arm': 2507c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui result1 = new_path.find('armeabi-v7a/') != -1 2517c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui result2 = old_path.find('armeabi-v7a') != -1 2527c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if result1 != result2: 2537c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return result1 2547c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui arch_dir = 'arm64' if self.app_arch == 'aarch64' else self.app_arch + '/' 2557c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui result1 = new_path.find(arch_dir) != -1 2567c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui result2 = old_path.find(arch_dir) != -1 2577c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if result1 != result2: 2587c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return result1 2597c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui result1 = new_path.find('obj/') != -1 2607c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui result2 = old_path.find('obj/') != -1 2617c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if result1 != result2: 2627c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return result1 2637c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui return False 2647c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2657c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2667c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def start_and_wait_profiling(self): 2678f3d1485e1b915d500579aedae40555a78d74744Yabin Cui self.run_in_app_dir([ 2687c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui './simpleperf', 'record', self.config['record_options'], '-p', 2697c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui str(self.app_pid), '--symfs', '.']) 2707c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2717c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2727c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui def collect_profiling_data(self): 2738f3d1485e1b915d500579aedae40555a78d74744Yabin Cui self.run_in_app_dir(['chmod', 'a+rw', 'perf.data']) 2747c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.check_run(['shell', 'cp', 2757c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui '/data/data/%s/perf.data' % self.config['app_package_name'], '/data/local/tmp']) 2767c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui self.adb.check_run(['pull', '/data/local/tmp/perf.data', self.config['perf_data_path']]) 2777c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui config = copy.copy(self.config) 2787c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui config['symfs_dirs'] = [] 2797c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui if self.config['native_lib_dir']: 2807c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui config['symfs_dirs'].append(self.config['native_lib_dir']) 2817c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui binary_cache_builder = BinaryCacheBuilder(config) 2827c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui binary_cache_builder.build_binary_cache() 2837c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2847c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui 2858f3d1485e1b915d500579aedae40555a78d74744Yabin Cui def run_in_app_dir(self, args): 2868f3d1485e1b915d500579aedae40555a78d74744Yabin Cui if self.is_root_device: 2878f3d1485e1b915d500579aedae40555a78d74744Yabin Cui cmd = 'cd /data/data/' + self.config['app_package_name'] + ' && ' + (' '.join(args)) 2888f3d1485e1b915d500579aedae40555a78d74744Yabin Cui return self.adb.check_run_and_return_output(['shell', cmd]) 2898f3d1485e1b915d500579aedae40555a78d74744Yabin Cui else: 2908f3d1485e1b915d500579aedae40555a78d74744Yabin Cui return self.adb.check_run_and_return_output( 2918f3d1485e1b915d500579aedae40555a78d74744Yabin Cui ['shell', 'run-as', self.config['app_package_name']] + args) 2928f3d1485e1b915d500579aedae40555a78d74744Yabin Cui 2938f3d1485e1b915d500579aedae40555a78d74744Yabin Cui 2947c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cuiif __name__ == '__main__': 2957c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui parser = argparse.ArgumentParser( 2967c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui description='Profile an android app. See configurations in app_profiler.config.') 2977c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui parser.add_argument('--config', default='app_profiler.config', 2987c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui help='Set configuration file. Default is app_profiler.config.') 2997c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui args = parser.parse_args() 3007c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui config = load_config(args.config) 3017c83a6129e024993e5611eb2e1dc21cd574b6ab2Yabin Cui profiler = AppProfiler(config) 30235c99d9ae1d0a602d35a52a76a28fba1964f6dc7Chris Craik profiler.profile() 303