summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2017-08-02 11:55:04 -0700
committerYabin Cui <yabinc@google.com>2017-08-03 14:52:52 -0700
commit58d10880d69339f18faeb387de34619ec747749d (patch)
tree40f9b9283c62f977121e21fddcea1f395f103193
parent76163e6103d6854b89f4916772a838573657e34a (diff)
downloadextras-58d10880d69339f18faeb387de34619ec747749d.tar.gz
simpleperf: support profiling native programs in app_profiler.py.
This is a preparation of using app_profiler.py in inferno. In app_profiler.py: Add -np option to profile native programs like surfaceflinger. Add -cmd option to profile command lines. Add --arch option to set the app arch, because we don't guess the app arch when profiling command lines. Add corresponding tests in test.py. Also improve the way of detecting whether the device supports trace-offcpu. Bug: http://b/63006886 Test: run test.py. Change-Id: Id899063d9a94beec67b22fd7710cda7a438557fb
-rw-r--r--simpleperf/scripts/annotate.py6
-rw-r--r--simpleperf/scripts/app_profiler.py139
-rw-r--r--simpleperf/scripts/binary_cache_builder.py4
-rw-r--r--simpleperf/scripts/report.py2
-rw-r--r--simpleperf/scripts/test.py96
-rw-r--r--simpleperf/scripts/utils.py35
6 files changed, 172 insertions, 110 deletions
diff --git a/simpleperf/scripts/annotate.py b/simpleperf/scripts/annotate.py
index 156af6dc..56b83e1e 100644
--- a/simpleperf/scripts/annotate.py
+++ b/simpleperf/scripts/annotate.py
@@ -124,11 +124,11 @@ class Addr2Line(object):
(file, line) = items
line = line.split()[0] # Remove comments after line number
out_pos += 1
- if file.find('?') != -1:
+ if '?' in file:
file = 0
else:
file = self._get_file_id(file)
- if line.find('?') != -1:
+ if '?' in line:
line = 0
else:
line = int(line)
@@ -573,7 +573,7 @@ class SourceFileAnnotator(object):
path = key
from_path = path
to_path = os.path.join(dest_dir, path[1:])
- elif is_windows() and key.find(':\\') != -1 and os.path.isfile(key):
+ elif is_windows() and ':\\' in key and os.path.isfile(key):
from_path = key
to_path = os.path.join(dest_dir, key.replace(':\\', '\\'))
else:
diff --git a/simpleperf/scripts/app_profiler.py b/simpleperf/scripts/app_profiler.py
index 8108f336..0de735fa 100644
--- a/simpleperf/scripts/app_profiler.py
+++ b/simpleperf/scripts/app_profiler.py
@@ -49,20 +49,30 @@ class AppProfiler(object):
self.is_root_device = False
self.android_version = 0
self.device_arch = None
- self.app_arch = None
+ self.app_arch = self.config['app_arch']
+ self.app_program = self.config['app_package_name'] or self.config['native_program']
self.app_pid = None
self.has_symfs_on_device = False
def check_config(self, config):
- config_names = ['app_package_name', 'native_lib_dir', 'apk_file_path',
- 'recompile_app', 'launch_activity', 'launch_inst_test',
+ config_names = ['app_package_name', 'native_program', 'cmd', 'native_lib_dir',
+ 'apk_file_path', 'recompile_app', 'launch_activity', 'launch_inst_test',
'record_options', 'perf_data_path']
for name in config_names:
if name not in config:
log_exit('config [%s] is missing' % name)
- if not config['app_package_name']:
- log_exit("The package name of the application hasn't been set")
+ if config['app_package_name'] and config['native_program']:
+ log_exit("We can't profile an Android app and a native program at the same time.")
+ elif config['app_package_name'] and config['cmd']:
+ log_exit("We can't profile an Android app and a cmd at the same time.")
+ elif config['native_program'] and config['cmd']:
+ log_exit("We can't profile a native program and a cmd at the same time.")
+ elif not config['app_package_name'] and not config['native_program'] and not config["cmd"]:
+ log_exit("Please set a profiling target: an Android app, a native program or a cmd.")
+ if config['app_package_name']:
+ if config['launch_activity'] and config['launch_inst_test']:
+ log_exit("We can't launch an activity and a test at the same time.")
native_lib_dir = config.get('native_lib_dir')
if native_lib_dir and not os.path.isdir(native_lib_dir):
log_exit('[native_lib_dir] "%s" is not a dir' % native_lib_dir)
@@ -71,7 +81,8 @@ class AppProfiler(object):
log_exit('[apk_file_path] "%s" is not a file' % apk_file_path)
if config['recompile_app']:
if not config['launch_activity'] and not config['launch_inst_test']:
- log_exit('one of launch_activity and launch_inst_test is needed for recompile app')
+ # If recompile app, the app needs to be restarted to take effect.
+ config['launch_activity'] = '.MainActivity'
def profile(self):
@@ -111,18 +122,7 @@ class AppProfiler(object):
if strs:
self.android_version = int(strs[0])
- # Get device architecture.
- output = self.adb.check_run_and_return_output(['shell', 'uname', '-m'])
- if output.find('aarch64') != -1:
- self.device_arch = 'aarch64'
- elif output.find('arm') != -1:
- self.device_arch = 'arm'
- elif output.find('x86_64') != -1:
- self.device_arch = 'x86_64'
- elif output.find('86') != -1:
- self.device_arch = 'x86'
- else:
- log_fatal('unsupported architecture: %s' % output.strip())
+ self.device_arch = self.adb.get_device_arch()
def _enable_profiling(self):
@@ -155,8 +155,14 @@ class AppProfiler(object):
def _restart_app(self):
- if not self.config['launch_activity'] and not self.config['launch_inst_test']:
+ if not self.config['app_package_name']:
return
+ if not self.config['launch_activity'] and not self.config['launch_inst_test']:
+ self.app_pid = self._find_app_process()
+ if self.app_pid is not None:
+ return
+ else:
+ self.config['launch_activity'] = '.MainActivity'
pid = self._find_app_process()
if pid is not None:
@@ -187,31 +193,33 @@ class AppProfiler(object):
def _find_app_process(self):
# On Android >= N, pidof is available. Otherwise, we can use ps.
if self.android_version >= 7:
- result, output = self.adb.run_and_return_output(['shell', 'pidof',
- self.config['app_package_name']])
+ result, output = self.adb.run_and_return_output(['shell', 'pidof', self.app_program])
return int(output) if result else None
result, output = self.adb.run_and_return_output(['shell', 'ps'], log_output=False)
if not result:
return None
for line in output.split('\n'):
strs = line.split()
- if len(strs) > 2 and strs[-1].find(self.config['app_package_name']) != -1:
+ if len(strs) > 2 and self.app_program in strs[-1]:
return int(strs[1])
return None
def _get_app_environment(self):
- self.app_pid = self._find_app_process()
- if self.app_pid is None:
- log_exit("can't find process for app [%s]" % self.config['app_package_name'])
- if self.device_arch in ['aarch64', 'x86_64']:
- output = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid], log_output=False)
- if output.find('linker64') != -1:
- self.app_arch = self.device_arch
+ if not self.config['cmd']:
+ if self.app_pid is None:
+ self.app_pid = self._find_app_process()
+ if self.app_pid is None:
+ log_exit("can't find process for app [%s]" % self.app_program)
+ if not self.app_arch:
+ if not self.config['cmd'] and self.device_arch in ['arm64', 'x86_64']:
+ output = self.run_in_app_dir(['cat', '/proc/%d/maps' % self.app_pid], log_output=False)
+ if 'linker64' in output:
+ self.app_arch = self.device_arch
+ else:
+ self.app_arch = 'arm' if self.device_arch == 'arm64' else 'x86'
else:
- self.app_arch = 'arm' if self.device_arch == 'aarch64' else 'x86'
- else:
- self.app_arch = self.device_arch
+ self.app_arch = self.device_arch
log_info('app_arch: %s' % self.app_arch)
@@ -258,17 +266,17 @@ class AppProfiler(object):
if old_path is None:
return True
if self.app_arch == 'arm':
- result1 = new_path.find('armeabi-v7a/') != -1
- result2 = old_path.find('armeabi-v7a') != -1
+ result1 = 'armeabi-v7a/' in new_path
+ result2 = 'armeabi-v7a' in old_path
if result1 != result2:
return result1
- arch_dir = 'arm64' if self.app_arch == 'aarch64' else self.app_arch + '/'
- result1 = new_path.find(arch_dir) != -1
- result2 = old_path.find(arch_dir) != -1
+ arch_dir = self.app_arch + '/'
+ result1 = arch_dir in new_path
+ result2 = arch_dir in old_path
if result1 != result2:
return result1
- result1 = new_path.find('obj/') != -1
- result2 = old_path.find('obj/') != -1
+ result1 = 'obj/' in new_path
+ result2 = 'obj/' in old_path
if result1 != result2:
return result1
return False
@@ -279,7 +287,13 @@ class AppProfiler(object):
returncode = None
try:
args = ['/data/local/tmp/simpleperf', 'record', self.config['record_options'],
- '--app', self.config['app_package_name'], '-o', '/data/local/tmp/perf.data']
+ '-o', '/data/local/tmp/perf.data']
+ if self.config['app_package_name']:
+ args += ['--app', self.config['app_package_name']]
+ elif self.config['native_program']:
+ args += ['-p', str(self.app_pid)]
+ elif self.config['cmd']:
+ args.append(self.config['cmd'])
if self.has_symfs_on_device:
args += ['--symfs', '/data/local/tmp/native_libs']
adb_args = [self.adb.adb_path, 'shell'] + args
@@ -324,38 +338,46 @@ class AppProfiler(object):
args = self.get_run_in_app_dir_args(args)
if check_result:
return self.adb.check_run_and_return_output(args, stdout_file, log_output=log_output)
- else:
- return self.adb.run_and_return_output(args, stdout_file, log_output=log_output)
+ return self.adb.run_and_return_output(args, stdout_file, log_output=log_output)
def get_run_in_app_dir_args(self, args):
+ if not self.config['app_package_name']:
+ return ['shell'] + args
if self.is_root_device:
return ['shell', 'cd /data/data/' + self.config['app_package_name'] + ' && ' +
(' '.join(args))]
- else:
- return ['shell', 'run-as', self.config['app_package_name']] + args
+ return ['shell', 'run-as', self.config['app_package_name']] + args
def main():
parser = argparse.ArgumentParser(
description=
-"""Profile an android app.""")
+"""Profile an Android app or native program.""")
parser.add_argument('-p', '--app', help=
-"""The package name of the profiled Android app.""")
+"""Profile an Android app, given the package name. Like -p com.example.android.myapp.""")
+ parser.add_argument('-np', '--native_program', help=
+"""Profile a native program. The program should be running on the device.
+Like -np surfaceflinger.""")
+ parser.add_argument('-cmd', help=
+"""Run a cmd and profile it. Like -cmd "pm -l".""")
parser.add_argument('-lib', '--native_lib_dir', help=
"""Path to find debug version of native shared libraries used in the app.""")
parser.add_argument('-nc', '--skip_recompile', action='store_true', help=
-"""By default we recompile java bytecode to native instructions to profile java
-code. It takes some time. You can skip it if the code has been compiled or you
+"""When profiling an Android app, by default we recompile java bytecode to native instructions
+to profile java code. It takes some time. You can skip it if the code has been compiled or you
don't need to profile java code.""")
parser.add_argument('--apk', help=
-"""Apk file of the profiled app, used on Android version <= M, which needs to
-reinstall the app to recompile it.""")
+"""When profiling an Android app, we need the apk file to recompile the app on
+Android version <= M.""")
parser.add_argument('-a', '--activity', help=
-"""Start an activity before profiling. It can be used to profile the startup
-time of an activity. Default is .MainActivity.""")
+"""When profiling an Android app, start an activity before profiling. It can be used to profile
+the startup time of an activity.""")
parser.add_argument('-t', '--test', help=
-"""Start an instrumentation test before profiling. It can be used to profile
-an instrumentation test.""")
+"""When profiling an Android app, start an instrumentation test before profiling.
+It can be used to profile an instrumentation test.""")
+ parser.add_argument('--arch', help=
+"""Select which arch the app is running on, possible values are:
+arm, arm64, x86, x86_64. If not set, the script will try to detect it.""")
parser.add_argument('-r', '--record_options', default="-e cpu-cycles:u -g --duration 10", help=
"""Set options for `simpleperf record` command.""")
parser.add_argument('-o', '--perf_data_path', default="perf.data", help=
@@ -368,17 +390,16 @@ binary_cache directory. It can be used to annotate source code. This option skip
args = parser.parse_args()
config = {}
config['app_package_name'] = args.app
+ config['native_program'] = args.native_program
+ config['cmd'] = args.cmd
config['native_lib_dir'] = args.native_lib_dir
- config['recompile_app'] = not args.skip_recompile
+ config['recompile_app'] = args.app and not args.skip_recompile
config['apk_file_path'] = args.apk
- if args.activity and args.test:
- log_exit("-a and -t can't be used at the same time.")
- if not args.activity and not args.test:
- args.activity = '.MainActivity'
config['launch_activity'] = args.activity
config['launch_inst_test'] = args.test
+ config['app_arch'] = args.arch
config['record_options'] = args.record_options
config['perf_data_path'] = args.perf_data_path
config['collect_binaries'] = not args.skip_collect_binaries
diff --git a/simpleperf/scripts/binary_cache_builder.py b/simpleperf/scripts/binary_cache_builder.py
index 546f76b2..9665c1d7 100644
--- a/simpleperf/scripts/binary_cache_builder.py
+++ b/simpleperf/scripts/binary_cache_builder.py
@@ -200,9 +200,7 @@ class BinaryCacheBuilder(object):
return False
output = subprocess.check_output([self.readelf_path, '-S', file])
output = bytes_to_str(output)
- if output.find('.symtab') != -1:
- return True
- return False
+ return '.symtab' in output
def _pull_file_from_device(self, device_path, host_path):
diff --git a/simpleperf/scripts/report.py b/simpleperf/scripts/report.py
index 590d0f05..1c1197d8 100644
--- a/simpleperf/scripts/report.py
+++ b/simpleperf/scripts/report.py
@@ -149,7 +149,7 @@ def parse_event_reports(lines):
if not line.strip('| \t'):
continue
- if line.find('skipped in brief callgraph mode') != -1:
+ if 'skipped in brief callgraph mode' in line:
has_skipped_callgraph = True
continue
diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py
index 491e897e..c22601ae 100644
--- a/simpleperf/scripts/test.py
+++ b/simpleperf/scripts/test.py
@@ -49,11 +49,21 @@ try:
except:
has_google_protobuf = False
+support_trace_offcpu = None
+
+def is_trace_offcpu_supported():
+ global support_trace_offcpu
+ if support_trace_offcpu is None:
+ adb = AdbHelper()
+ adb.check_run_and_return_output(['push',
+ 'bin/android/%s/simpleperf' % adb.get_device_arch(),
+ "/data/local/tmp"])
+ adb.check_run_and_return_output(['shell', 'chmod', 'a+x', '/data/local/tmp/simpleperf'])
+ output = adb.check_run_and_return_output(['shell', '/data/local/tmp/simpleperf', 'list',
+ '--show-features'])
+ support_trace_offcpu = 'trace-offcpu' in output
+ return support_trace_offcpu
-adb = AdbHelper()
-# TODO: Change to check ro.build.version.sdk >= 26 when OMR1 is prevalent.
-device_version = adb.check_run_and_return_output(['shell', 'getprop', 'ro.build.version.release'])
-support_trace_offcpu = device_version.find('OMR1') != -1
def build_testdata():
""" Collect testdata from ../testdata and ../demo. """
@@ -66,15 +76,13 @@ def build_testdata():
'SimpleperfExampleOfKotlin']
testdata_path = "testdata"
- if os.path.isdir(testdata_path):
- shutil.rmtree(testdata_path)
- os.mkdir(testdata_path)
+ remove(testdata_path)
+ os.mkdir(testdata_path)
for testdata in copy_testdata_list:
shutil.copy(os.path.join(from_testdata_path, testdata), testdata_path)
for demo in copy_demo_list:
shutil.copytree(os.path.join(from_demo_path, demo), os.path.join(testdata_path, demo))
-
class TestExampleBase(unittest.TestCase):
@classmethod
def prepare(cls, example_name, package_name, activity_name, abi=None, adb_root=False):
@@ -90,7 +98,6 @@ class TestExampleBase(unittest.TestCase):
log_fatal("can't find app-profiling.apk under " + cls.example_path)
cls.package_name = package_name
cls.activity_name = activity_name
- cls.python_path = sys.executable
args = ["install", "-r"]
if abi:
args += ["--abi", abi]
@@ -100,23 +107,24 @@ class TestExampleBase(unittest.TestCase):
cls.compiled = False
def setUp(self):
- if self.id().find('TraceOffCpu') != -1 and not support_trace_offcpu:
+ if self.id().find('TraceOffCpu') != -1 and not is_trace_offcpu_supported():
self.skipTest('trace-offcpu is not supported on device')
@classmethod
def tearDownClass(cls):
- cls.adb.check_run(["uninstall", cls.package_name])
+ if hasattr(cls, 'package_name'):
+ cls.adb.check_run(["uninstall", cls.package_name])
@classmethod
def cleanupTestFiles(cls):
- cls.remove("binary_cache")
- cls.remove("annotated_files")
- cls.remove("perf.data")
- cls.remove("report.txt")
- cls.remove("pprof.profile")
+ remove("binary_cache")
+ remove("annotated_files")
+ remove("perf.data")
+ remove("report.txt")
+ remove("pprof.profile")
def run_cmd(self, args, return_output=False):
- args = [self.python_path] + args
+ args = [sys.executable] + args
try:
if not return_output:
returncode = subprocess.call(args)
@@ -152,10 +160,6 @@ class TestExampleBase(unittest.TestCase):
if not skip_compile:
self.__class__.compiled = True
- @classmethod
- def remove(cls, dir):
- shutil.rmtree(dir, ignore_errors=True)
-
def check_exist(self, file=None, dir=None):
if file:
self.assertTrue(os.path.isfile(file), file)
@@ -194,7 +198,7 @@ class TestExampleBase(unittest.TestCase):
for line in summary.split('\n'):
for i in range(len(check_entries)):
(name, need_acc_period, need_period) = check_entries[i]
- if not fulfilled[i] and line.find(name) != -1:
+ if not fulfilled[i] and name in line:
m = self.summary_check_re.search(line)
if m:
acc_period = float(m.group(1))
@@ -205,7 +209,7 @@ class TestExampleBase(unittest.TestCase):
def common_test_app_profiler(self):
self.run_cmd(["app_profiler.py", "-h"])
- self.remove("binary_cache")
+ remove("binary_cache")
self.run_app_profiler(build_binary_cache=False)
self.assertFalse(os.path.isdir("binary_cache"))
args = ["binary_cache_builder.py"]
@@ -213,7 +217,7 @@ class TestExampleBase(unittest.TestCase):
args.append("--disable_adb_root")
self.run_cmd(args)
self.check_exist(dir="binary_cache")
- self.remove("binary_cache")
+ remove("binary_cache")
self.run_app_profiler(build_binary_cache=True)
self.run_app_profiler(skip_compile=True)
self.run_app_profiler(start_activity=False)
@@ -230,13 +234,13 @@ class TestExampleBase(unittest.TestCase):
def common_test_annotate(self):
self.run_cmd(["annotate.py", "-h"])
self.run_app_profiler()
- self.remove("annotated_files")
+ remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path])
self.check_exist(dir="annotated_files")
def common_test_report_sample(self, check_strings):
self.run_cmd(["report_sample.py", "-h"])
- self.remove("binary_cache")
+ remove("binary_cache")
self.run_app_profiler(build_binary_cache=False)
self.run_cmd(["report_sample.py"])
output = self.run_cmd(["report_sample.py", "perf.data"], return_output=True)
@@ -253,7 +257,7 @@ class TestExampleBase(unittest.TestCase):
self.run_cmd(["pprof_proto_generator.py", "-h"])
self.run_app_profiler()
self.run_cmd(["pprof_proto_generator.py"])
- self.remove("pprof.profile")
+ remove("pprof.profile")
self.run_cmd(["pprof_proto_generator.py", "-i", "perf.data", "-o", "pprof.profile"])
self.check_exist(file="pprof.profile")
self.run_cmd(["pprof_proto_generator.py", "--show"])
@@ -261,7 +265,7 @@ class TestExampleBase(unittest.TestCase):
return_output=True)
self.check_strings_in_content(output, check_strings_with_lines +
["has_line_numbers: True"])
- self.remove("binary_cache")
+ remove("binary_cache")
self.run_cmd(["pprof_proto_generator.py"])
output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
return_output=True)
@@ -337,7 +341,7 @@ class TestExamplePureJavaTraceOffCpu(TestExampleBase):
"long com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction()",
"long com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction(long)"
])
- self.remove("annotated_files")
+ remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path])
self.check_exist(dir="annotated_files")
self.check_file_under_dir("annotated_files", "SleepActivity.java")
@@ -360,7 +364,7 @@ class TestExampleWithNative(TestExampleBase):
def test_app_profiler(self):
self.common_test_app_profiler()
- self.remove("binary_cache")
+ remove("binary_cache")
self.run_app_profiler(native_lib_dir=self.example_path)
def test_report(self):
@@ -403,7 +407,7 @@ class TestExampleWithNativeRoot(TestExampleBase):
def test_app_profiler(self):
self.common_test_app_profiler()
- self.remove("binary_cache")
+ remove("binary_cache")
self.run_app_profiler(native_lib_dir=self.example_path)
@@ -421,7 +425,7 @@ class TestExampleWithNativeTraceOffCpu(TestExampleBase):
["SleepThread(void*)",
"RunFunction()",
"SleepFunction(unsigned long long)"])
- self.remove("annotated_files")
+ remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "SleepThread"])
self.check_exist(dir="annotated_files")
self.check_file_under_dir("annotated_files", "native-lib.cpp")
@@ -449,7 +453,7 @@ class TestExampleWithNativeJniCall(TestExampleBase):
["void com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run()",
"int com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction(int)",
"Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"])
- self.remove("annotated_files")
+ remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "BusyThread"])
self.check_exist(dir="annotated_files")
self.check_file_under_dir("annotated_files", "native-lib.cpp")
@@ -560,7 +564,7 @@ class TestExampleOfKotlinTraceOffCpu(TestExampleBase):
"long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction()",
"long com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction(long)"
])
- self.remove("annotated_files")
+ remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path])
self.check_exist(dir="annotated_files")
self.check_file_under_dir("annotated_files", "SleepActivity.kt")
@@ -574,6 +578,30 @@ class TestExampleOfKotlinTraceOffCpu(TestExampleBase):
("line 32", 20, 0)])
+class TestProfilingNativeProgram(TestExampleBase):
+ def test_smoke(self):
+ if not AdbHelper().switch_to_root():
+ log_info('skip TestProfilingNativeProgram on non-rooted devices.')
+ return
+ remove("perf.data")
+ self.run_cmd(["app_profiler.py", "-np", "surfaceflinger",
+ "-r", "-g --duration 3 -e cpu-cycles:u"])
+ self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+
+
+class TestProfilingCmd(TestExampleBase):
+ def test_smoke(self):
+ remove("perf.data")
+ self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--disable_adb_root"])
+ self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+
+ def test_set_arch(self):
+ arch = AdbHelper().get_device_arch()
+ remove("perf.data")
+ self.run_cmd(["app_profiler.py", "-cmd", "pm -l", "--arch", arch])
+ self.run_cmd(["report.py", "-g", "-o", "report.txt"])
+
+
class TestReportLib(unittest.TestCase):
def setUp(self):
self.report_lib = ReportLib()
diff --git a/simpleperf/scripts/utils.py b/simpleperf/scripts/utils.py
index 22dd3998..0570cfdb 100644
--- a/simpleperf/scripts/utils.py
+++ b/simpleperf/scripts/utils.py
@@ -22,6 +22,7 @@ from __future__ import print_function
import logging
import os
import os.path
+import shutil
import subprocess
import sys
import time
@@ -87,7 +88,7 @@ def get_host_binary_path(binary_name):
if is_windows():
if binary_name.endswith('.so'):
binary_name = binary_name[0:-3] + '.dll'
- elif binary_name.find('.') == -1:
+ elif '.' not in binary_name:
binary_name += '.exe'
dir = os.path.join(dir, 'windows')
elif sys.platform == 'darwin': # OSX
@@ -214,7 +215,7 @@ class AdbHelper(object):
result, stdoutdata = self.run_and_return_output(['shell', 'whoami'])
if not result:
return
- if stdoutdata.find('root') == -1:
+ if 'root' not in stdoutdata:
return
log_info('unroot adb')
self.run(['unroot'])
@@ -229,7 +230,7 @@ class AdbHelper(object):
result, stdoutdata = self.run_and_return_output(['shell', 'whoami'])
if not result:
return False
- if stdoutdata.find('root') != -1:
+ if 'root' in stdoutdata:
return True
build_type = self.get_property('ro.build.type')
if build_type == 'user':
@@ -238,21 +239,29 @@ class AdbHelper(object):
time.sleep(1)
self.run(['wait-for-device'])
result, stdoutdata = self.run_and_return_output(['shell', 'whoami'])
- if result and stdoutdata.find('root') != -1:
- return True
- return False
+ return result and 'root' in stdoutdata
def get_property(self, name):
result, stdoutdata = self.run_and_return_output(['shell', 'getprop', name])
- if not result:
- return None
- return stdoutdata
-
+ return stdoutdata if result else None
def set_property(self, name, value):
return self.run(['shell', 'setprop', name, value])
+ def get_device_arch(self):
+ output = self.check_run_and_return_output(['shell', 'uname', '-m'])
+ if 'aarch64' in output:
+ return 'arm64'
+ if 'arm' in output:
+ return 'arm'
+ if 'x86_64' in output:
+ return 'x86_64'
+ if '86' in output:
+ return 'x86'
+ log_fatal('unsupported architecture: %s' % output.strip())
+
+
def flatten_arg_list(arg_list):
res = []
if arg_list:
@@ -261,4 +270,10 @@ def flatten_arg_list(arg_list):
return res
+def remove(dir_or_file):
+ if os.path.isfile(dir_or_file):
+ os.remove(dir_or_file)
+ elif os.path.isdir(dir_or_file):
+ shutil.rmtree(dir_or_file, ignore_errors=True)
+
logging.getLogger().setLevel(logging.DEBUG)