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