simpleperf: fix pylint warnings in python scripts.
Also add option to list all tests.
Bug: none.
Test: run test.py.
Change-Id: I8d7ea4855bd9bfde9517cd4b563378dc9b91582b
diff --git a/perfprofd/scripts/perf_proto_stack_sqlite_flame.py b/perfprofd/scripts/perf_proto_stack_sqlite_flame.py
index 82555af..b619b8b 100644
--- a/perfprofd/scripts/perf_proto_stack_sqlite_flame.py
+++ b/perfprofd/scripts/perf_proto_stack_sqlite_flame.py
@@ -204,7 +204,7 @@
def print_svg(self, filename, depth):
- from svg_renderer import renderSVG
+ from svg_renderer import render_svg
self.root.svgrenderer_compat(self.dsos, self.syms)
self.root.generate_offset(0)
f = open(filename, 'w')
@@ -220,7 +220,7 @@
def __init__(self):
self.props = { 'trace_offcpu': False }
fake_process = FakeProcess()
- renderSVG(fake_process, self.root, f, 'hot')
+ render_svg(fake_process, self.root, f, 'hot')
f.write('''
</div>
diff --git a/simpleperf/scripts/annotate.py b/simpleperf/scripts/annotate.py
index 066e14e..30cfce1 100644
--- a/simpleperf/scripts/annotate.py
+++ b/simpleperf/scripts/annotate.py
@@ -24,14 +24,15 @@
import os.path
import shutil
import subprocess
-import sys
-from simpleperf_report_lib import *
-from utils import *
+from simpleperf_report_lib import ReportLib
+from utils import log_info, log_warning, log_exit, log_fatal
+from utils import bytes_to_str, extant_dir, find_tool_path, flatten_arg_list, is_windows
+from utils import ReadElf, str_to_bytes
class SourceLine(object):
- def __init__(self, file, function, line):
- self.file = file
+ def __init__(self, file_id, function, line):
+ self.file = file_id
self.function = function
self.line = line
@@ -56,31 +57,30 @@
"""collect information of how to map [dso_name,vaddr] to [source_file:line].
"""
def __init__(self, ndk_path, symfs_dir=None):
- self.dso_dict = dict()
+ self.dso_dict = {}
self.addr2line_path = find_tool_path('addr2line', ndk_path)
if self.addr2line_path is None:
log_exit("Can't find addr2line. Please set ndk path with --ndk-path option.")
self.readelf = ReadElf(ndk_path)
self.symfs_dir = symfs_dir
+ # store a list of source files
+ self.file_list = []
+ # map from file to id with file_list[id] == file
+ self.file_dict = {}
def add_addr(self, dso_name, addr):
dso = self.dso_dict.get(dso_name)
if dso is None:
- self.dso_dict[dso_name] = dso = dict()
+ self.dso_dict[dso_name] = dso = {}
if addr not in dso:
dso[addr] = None
-
def convert_addrs_to_lines(self):
- # store a list of source files
- self.file_list = []
- # map from file to id with file_list[id] == file
- self.file_dict = {}
self.file_list.append('')
self.file_dict[''] = 0
- for dso_name in self.dso_dict.keys():
+ for dso_name in self.dso_dict:
self._convert_addrs_to_lines(dso_name, self.dso_dict[dso_name])
self._combine_source_files()
@@ -121,30 +121,30 @@
items = stdoutdata[out_pos].rsplit(':', 1)
if len(items) != 2:
continue
- (file, line) = items
+ (file_path, line) = items
line = line.split()[0] # Remove comments after line number
out_pos += 1
- if '?' in file:
- file = 0
+ if '?' in file_path:
+ file_id = 0
else:
- file = self._get_file_id(file)
+ file_id = self._get_file_id(file_path)
if '?' in line:
line = 0
else:
line = int(line)
- source_lines.append(SourceLine(file, function, line))
+ source_lines.append(SourceLine(file_id, function, line))
dso[addrs[addr_pos]] = source_lines
addr_pos += 1
assert addr_pos == len(addrs)
- def _get_file_id(self, file):
- id = self.file_dict.get(file)
- if id is None:
- id = len(self.file_list)
- self.file_list.append(file)
- self.file_dict[file] = id
- return id
+ def _get_file_id(self, file_path):
+ file_id = self.file_dict.get(file_path)
+ if file_id is None:
+ file_id = len(self.file_list)
+ self.file_list.append(file_path)
+ self.file_dict[file_path] = file_id
+ return file_id
def _combine_source_files(self):
"""It is possible that addr2line gives us different names for the same
@@ -155,29 +155,29 @@
source files with no conflicts in path.
"""
# Collect files having the same filename.
- filename_dict = dict()
- for file in self.file_list:
- index = max(file.rfind('/'), file.rfind(os.sep))
- filename = file[index+1:]
+ filename_dict = {}
+ for file_path in self.file_list:
+ index = max(file_path.rfind('/'), file_path.rfind(os.sep))
+ filename = file_path[index+1:]
entry = filename_dict.get(filename)
if entry is None:
filename_dict[filename] = entry = []
- entry.append(file)
+ entry.append(file_path)
# Combine files having the same filename and having no conflicts in path.
- for filename in filename_dict.keys():
+ for filename in filename_dict:
files = filename_dict[filename]
if len(files) == 1:
continue
- for file in files:
- to_file = file
+ for file_path in files:
+ to_file = file_path
# Test if we can merge files[i] with another file having longer
# path.
for f in files:
- if len(f) > len(to_file) and f.find(file) != -1:
+ if len(f) > len(to_file) and f.find(file_path) != -1:
to_file = f
- if to_file != file:
- from_id = self.file_dict[file]
+ if to_file != file_path:
+ from_id = self.file_dict[file_path]
to_id = self.file_dict[to_file]
self.file_list[from_id] = self.file_list[to_id]
@@ -239,8 +239,8 @@
class FilePeriod(object):
"""Period for each source file"""
- def __init__(self, file):
- self.file = file
+ def __init__(self, file_id):
+ self.file = file_id
self.period = Period()
# Period for each line in the file.
self.line_dict = {}
@@ -283,12 +283,6 @@
kallsyms = 'binary_cache/kallsyms'
if not os.path.isfile(kallsyms):
kallsyms = None
- source_dirs = config['source_dirs']
- for dir in source_dirs:
- if not os.path.isdir(dir):
- log_exit('[source_dirs] "%s" is not a dir' % dir)
- if not config['source_dirs']:
- log_exit('Please set source directories.')
# init member variables
self.config = config
@@ -312,6 +306,10 @@
os.makedirs(output_dir)
self.addr2line = Addr2Line(self.config['ndk_path'], symfs_dir)
+ self.period = 0
+ self.dso_periods = {}
+ self.file_periods = {}
+ self.source_file_dict = {}
def annotate(self):
@@ -380,9 +378,6 @@
"""read perf.data, collect Period for all types:
binaries, source files, functions, lines.
"""
- self.period = 0
- self.dso_periods = dict()
- self.file_periods = dict()
for perf_data in self.config['perf_data_list']:
lib = ReportLib()
lib.SetRecordFile(perf_data)
@@ -397,49 +392,52 @@
break
if not self._filter_sample(sample):
continue
- symbols = []
- symbols.append(lib.GetSymbolOfCurrentSample())
- callchain = lib.GetCallChainOfCurrentSample()
- for i in range(callchain.nr):
- symbols.append(callchain.entries[i].symbol)
- # Each sample has a callchain, but its period is only used once
- # to add period for each function/source_line/source_file/binary.
- # For example, if more than one entry in the callchain hits a
- # function, the event count of that function is only increased once.
- # Otherwise, we may get periods > 100%.
- is_sample_used = False
- used_dso_dict = dict()
- used_file_dict = dict()
- used_function_dict = dict()
- used_line_dict = dict()
- period = Period(sample.period, sample.period)
- for i in range(len(symbols)):
- symbol = symbols[i]
- if i == 1:
- period = Period(0, sample.period)
- if not self._filter_symbol(symbol):
- continue
- is_sample_used = True
- # Add period to dso.
- self._add_dso_period(symbol.dso_name, period, used_dso_dict)
- # Add period to source file.
- sources = self.addr2line.get_sources(symbol.dso_name, symbol.vaddr_in_file)
- for source in sources:
- if source.file:
- self._add_file_period(source, period, used_file_dict)
- # Add period to line.
- if source.line:
- self._add_line_period(source, period, used_line_dict)
- # Add period to function.
- sources = self.addr2line.get_sources(symbol.dso_name, symbol.symbol_addr)
- for source in sources:
- if source.file:
- self._add_file_period(source, period, used_file_dict)
- if source.function:
- self._add_function_period(source, period, used_function_dict)
+ self._generate_periods_for_sample(lib, sample)
- if is_sample_used:
- self.period += sample.period
+
+ def _generate_periods_for_sample(self, lib, sample):
+ symbols = []
+ symbols.append(lib.GetSymbolOfCurrentSample())
+ callchain = lib.GetCallChainOfCurrentSample()
+ for i in range(callchain.nr):
+ symbols.append(callchain.entries[i].symbol)
+ # Each sample has a callchain, but its period is only used once
+ # to add period for each function/source_line/source_file/binary.
+ # For example, if more than one entry in the callchain hits a
+ # function, the event count of that function is only increased once.
+ # Otherwise, we may get periods > 100%.
+ is_sample_used = False
+ used_dso_dict = {}
+ used_file_dict = {}
+ used_function_dict = {}
+ used_line_dict = {}
+ period = Period(sample.period, sample.period)
+ for j, symbol in enumerate(symbols):
+ if j == 1:
+ period = Period(0, sample.period)
+ if not self._filter_symbol(symbol):
+ continue
+ is_sample_used = True
+ # Add period to dso.
+ self._add_dso_period(symbol.dso_name, period, used_dso_dict)
+ # Add period to source file.
+ sources = self.addr2line.get_sources(symbol.dso_name, symbol.vaddr_in_file)
+ for source in sources:
+ if source.file:
+ self._add_file_period(source, period, used_file_dict)
+ # Add period to line.
+ if source.line:
+ self._add_line_period(source, period, used_line_dict)
+ # Add period to function.
+ sources = self.addr2line.get_sources(symbol.dso_name, symbol.symbol_addr)
+ for source in sources:
+ if source.file:
+ self._add_file_period(source, period, used_file_dict)
+ if source.function:
+ self._add_function_period(source, period, used_function_dict)
+
+ if is_sample_used:
+ self.period += sample.period
def _add_dso_period(self, dso_name, period, used_dso_dict):
@@ -521,20 +519,19 @@
def _collect_source_files(self):
- self.source_file_dict = dict()
source_file_suffix = ['h', 'c', 'cpp', 'cc', 'java', 'kt']
for source_dir in self.config['source_dirs']:
for root, _, files in os.walk(source_dir):
- for file in files:
- if file[file.rfind('.')+1:] in source_file_suffix:
- entry = self.source_file_dict.get(file)
+ for filename in files:
+ if filename[filename.rfind('.')+1:] in source_file_suffix:
+ entry = self.source_file_dict.get(filename)
if entry is None:
- entry = self.source_file_dict[file] = []
- entry.append(os.path.join(root, file))
+ entry = self.source_file_dict[filename] = []
+ entry.append(os.path.join(root, filename))
- def _find_source_file(self, file):
- filename = file[file.rfind(os.sep)+1:]
+ def _find_source_file(self, file_path):
+ filename = file_path[file_path.rfind(os.sep)+1:]
source_files = self.source_file_dict.get(filename)
if source_files is None:
return None
@@ -542,7 +539,7 @@
best_path = None
best_suffix_len = 0
for path in source_files:
- suffix_len = len(os.path.commonprefix((path[::-1], file[::-1])))
+ suffix_len = len(os.path.commonprefix((path[::-1], file_path[::-1])))
if suffix_len > best_suffix_len:
best_suffix_len = suffix_len
best_path = path
@@ -550,7 +547,7 @@
elif suffix_len == best_suffix_len:
best_path_count += 1
if best_path_count > 1:
- log_warning('multiple source for %s, select %s' % (file, best_path))
+ log_warning('multiple source for %s, select %s' % (file_path, best_path))
return best_path
@@ -560,7 +557,7 @@
2. Annotate c++ source files.
"""
dest_dir = self.config['annotate_dest_dir']
- for key in self.file_periods.keys():
+ for key in self.file_periods:
is_java = False
if key.startswith('$JAVA_SRC_ROOT/'):
path = key[len('$JAVA_SRC_ROOT/'):]
@@ -601,7 +598,7 @@
with open(from_path, 'r') as rf:
lines = rf.readlines()
- annotates = dict()
+ annotates = {}
for line in file_period.line_dict.keys():
annotates[line] = self._get_percentage_str(file_period.line_dict[line], True)
for func_name in file_period.function_dict.keys():
@@ -613,7 +610,7 @@
annotates[1] = '[file] ' + self._get_percentage_str(file_period.period, True)
max_annotate_cols = 0
- for key in annotates.keys():
+ for key in annotates:
max_annotate_cols = max(max_annotate_cols, len(annotates[key]))
empty_annotate = ' ' * (max_annotate_cols + 6)
@@ -636,22 +633,22 @@
wf.write(lines[line-1])
def main():
- parser = argparse.ArgumentParser(description=
-"""Annotate source files based on profiling data. It reads line information from
-binary_cache generated by app_profiler.py or binary_cache_builder.py, and
-generate annotated source files in annotated_files directory.""")
- parser.add_argument('-i', '--perf_data_list', nargs='+', action='append', help=
-"""The paths of profiling data. Default is perf.data.""")
- parser.add_argument('-s', '--source_dirs', nargs='+', action='append', help=
-"""Directories to find source files.""")
- parser.add_argument('--comm', nargs='+', action='append', help=
-"""Use samples only in threads with selected names.""")
- parser.add_argument('--pid', nargs='+', action='append', help=
-"""Use samples only in processes with selected process ids.""")
- parser.add_argument('--tid', nargs='+', action='append', help=
-"""Use samples only in threads with selected thread ids.""")
- parser.add_argument('--dso', nargs='+', action='append', help=
-"""Use samples only in selected binaries.""")
+ parser = argparse.ArgumentParser(description="""
+ Annotate source files based on profiling data. It reads line information from binary_cache
+ generated by app_profiler.py or binary_cache_builder.py, and generate annotated source
+ files in annotated_files directory.""")
+ parser.add_argument('-i', '--perf_data_list', nargs='+', action='append', help="""
+ The paths of profiling data. Default is perf.data.""")
+ parser.add_argument('-s', '--source_dirs', type=extant_dir, nargs='+', action='append', help="""
+ Directories to find source files.""")
+ parser.add_argument('--comm', nargs='+', action='append', help="""
+ Use samples only in threads with selected names.""")
+ parser.add_argument('--pid', nargs='+', action='append', help="""
+ Use samples only in processes with selected process ids.""")
+ parser.add_argument('--tid', nargs='+', action='append', help="""
+ Use samples only in threads with selected thread ids.""")
+ parser.add_argument('--dso', nargs='+', action='append', help="""
+ Use samples only in selected binaries.""")
parser.add_argument('--ndk_path', type=extant_dir, help='Set the path of a ndk release.')
args = parser.parse_args()
diff --git a/simpleperf/scripts/binary_cache_builder.py b/simpleperf/scripts/binary_cache_builder.py
index deb312a..b5a53da 100644
--- a/simpleperf/scripts/binary_cache_builder.py
+++ b/simpleperf/scripts/binary_cache_builder.py
@@ -23,14 +23,10 @@
import argparse
import os
import os.path
-import re
import shutil
-import subprocess
-import sys
-import time
-from simpleperf_report_lib import *
-from utils import *
+from simpleperf_report_lib import ReportLib
+from utils import AdbHelper, flatten_arg_list, log_info, log_warning, log_exit, ReadElf
class BinaryCacheBuilder(object):
@@ -53,6 +49,7 @@
self.binary_cache_dir = 'binary_cache'
if not os.path.isdir(self.binary_cache_dir):
os.makedirs(self.binary_cache_dir)
+ self.binaries = {}
def build_binary_cache(self):
@@ -65,7 +62,7 @@
def _collect_used_binaries(self):
"""read perf.data, collect all used binaries and their build id (if available)."""
# A dict mapping from binary name to build_id
- binaries = dict()
+ binaries = {}
lib = ReportLib()
lib.SetRecordFile(self.perf_data_path)
lib.SetLogSeverity('error')
@@ -99,7 +96,7 @@
# and same build_id.
# Map from filename to binary paths.
- filename_dict = dict()
+ filename_dict = {}
for binary in self.binaries:
index = binary.rfind('/')
filename = binary[index+1:]
@@ -111,17 +108,19 @@
# Walk through all files in symfs_dirs, and copy matching files to build_cache.
for symfs_dir in self.symfs_dirs:
for root, _, files in os.walk(symfs_dir):
- for file in files:
- paths = filename_dict.get(file)
- if paths is not None:
- build_id = self._read_build_id(os.path.join(root, file))
- if not build_id:
- continue
- for binary in paths:
- expected_build_id = self.binaries.get(binary)
- if expected_build_id == build_id:
- self._copy_to_binary_cache(os.path.join(root, file),
- expected_build_id, binary)
+ for filename in files:
+ paths = filename_dict.get(filename)
+ if not paths:
+ continue
+ build_id = self._read_build_id(os.path.join(root, filename))
+ if not build_id:
+ continue
+ for binary in paths:
+ expected_build_id = self.binaries.get(binary)
+ if expected_build_id == build_id:
+ self._copy_to_binary_cache(os.path.join(root, filename),
+ expected_build_id, binary)
+ break
def _copy_to_binary_cache(self, from_path, expected_build_id, target_file):
@@ -129,10 +128,8 @@
target_file = target_file[1:]
target_file = target_file.replace('/', os.sep)
target_file = os.path.join(self.binary_cache_dir, target_file)
- if (os.path.isfile(target_file) and self._read_build_id(target_file) == expected_build_id
- and self._file_has_symbol_table(target_file)):
- # The existing file in binary_cache can provide more information, so no
- # need to copy.
+ if not self._need_to_copy(target_file, expected_build_id):
+ # The existing file in binary_cache can provide more information, so no need to copy.
return
target_dir = os.path.dirname(target_file)
if not os.path.isdir(target_dir):
@@ -141,6 +138,16 @@
shutil.copy(from_path, target_file)
+ def _need_to_copy(self, target_file, expected_build_id):
+ if not os.path.isfile(target_file):
+ return True
+ if self._read_build_id(target_file) != expected_build_id:
+ return True
+ if not self._file_has_symbol_table(target_file):
+ return True
+ return False
+
+
def _pull_binaries_from_device(self):
"""pull binaries needed in perf.data to binary_cache."""
for binary in self.binaries:
@@ -176,14 +183,14 @@
log_info('use current file in binary_cache: %s' % binary_cache_file)
- def _read_build_id(self, file):
+ def _read_build_id(self, file_path):
"""read build id of a binary on host."""
- return self.readelf.get_build_id(file)
+ return self.readelf.get_build_id(file_path)
- def _file_has_symbol_table(self, file):
+ def _file_has_symbol_table(self, file_path):
"""Test if an elf file has symbol table section."""
- return '.symtab' in self.readelf.get_sections(file)
+ return '.symtab' in self.readelf.get_sections(file_path)
def _pull_file_from_device(self, device_path, host_path):
@@ -193,7 +200,7 @@
# Instead, we can first copy the file to /data/local/tmp, then pull it.
filename = device_path[device_path.rfind('/')+1:]
if (self.adb.run(['shell', 'cp', device_path, '/data/local/tmp']) and
- self.adb.run(['pull', '/data/local/tmp/' + filename, host_path])):
+ self.adb.run(['pull', '/data/local/tmp/' + filename, host_path])):
self.adb.run(['shell', 'rm', '/data/local/tmp/' + filename])
return True
log_warning('failed to pull %s from device' % device_path)
@@ -201,24 +208,23 @@
def _pull_kernel_symbols(self):
- file = os.path.join(self.binary_cache_dir, 'kallsyms')
- if os.path.isfile(file):
- os.remove(file)
+ file_path = os.path.join(self.binary_cache_dir, 'kallsyms')
+ if os.path.isfile(file_path):
+ os.remove(file_path)
if self.adb.switch_to_root():
self.adb.run(['shell', '"echo 0 >/proc/sys/kernel/kptr_restrict"'])
- self.adb.run(['pull', '/proc/kallsyms', file])
+ self.adb.run(['pull', '/proc/kallsyms', file_path])
def main():
- parser = argparse.ArgumentParser(description=
-"""Pull binaries needed by perf.data from device to binary_cache directory.""")
- parser.add_argument('-i', '--perf_data_path', default='perf.data', help=
-"""The path of profiling data.""")
- parser.add_argument('-lib', '--native_lib_dir', nargs='+', help=
-"""Path to find debug version of native shared libraries used in the app.""",
- action='append')
- parser.add_argument('--disable_adb_root', action='store_true', help=
-"""Force adb to run in non root mode.""")
+ parser = argparse.ArgumentParser(description="""
+ Pull binaries needed by perf.data from device to binary_cache directory.""")
+ parser.add_argument('-i', '--perf_data_path', default='perf.data', help="""
+ The path of profiling data.""")
+ parser.add_argument('-lib', '--native_lib_dir', nargs='+', help="""
+ Path to find debug version of native shared libraries used in the app.""", action='append')
+ parser.add_argument('--disable_adb_root', action='store_true', help="""
+ Force adb to run in non root mode.""")
parser.add_argument('--ndk_path', nargs=1, help='Find tools in the ndk path.')
args = parser.parse_args()
config = {}
@@ -232,4 +238,4 @@
if __name__ == '__main__':
- main()
\ No newline at end of file
+ main()
diff --git a/simpleperf/scripts/debug_unwind_reporter.py b/simpleperf/scripts/debug_unwind_reporter.py
index 56b5176..83faa6c 100644
--- a/simpleperf/scripts/debug_unwind_reporter.py
+++ b/simpleperf/scripts/debug_unwind_reporter.py
@@ -44,7 +44,8 @@
import re
import subprocess
-from utils import *
+from utils import log_exit, log_fatal
+from utils import get_host_binary_path
class MapEntry(object):
@@ -122,7 +123,7 @@
class UnwindingMemConsumption(object):
- def __init___(self):
+ def __init__(self):
self.before_unwinding = None
self.after_unwinding = None
@@ -341,13 +342,14 @@
elif items[0] == 'callchain:':
in_callchain = True
elif in_callchain:
- # "dalvik-jit-code-cache (deleted)[+346c] (/dev/ashmem/dalvik-jit-code-cache (deleted)[+346c])"
+ # "dalvik-jit-code-cache (deleted)[+346c] (/dev/ashmem/dalvik-jit-code-cache
+ # (deleted)[+346c])"
if re.search(r'\)\[\+\w+\]\)$', line):
break_pos = line.rfind('(', 0, line.rfind('('))
else:
break_pos = line.rfind('(')
if break_pos > 0:
- m = re.match('(.+)\[\+(\w+)\]\)', line[break_pos + 1:])
+ m = re.match(r'(.+)\[\+(\w+)\]\)', line[break_pos + 1:])
if m:
function_names.append(line[:break_pos].strip())
filenames.append(m.group(1))
diff --git a/simpleperf/scripts/inferno/data_types.py b/simpleperf/scripts/inferno/data_types.py
index 17af700..deb9f51 100644
--- a/simpleperf/scripts/inferno/data_types.py
+++ b/simpleperf/scripts/inferno/data_types.py
@@ -15,14 +15,14 @@
#
-class CallSite:
+class CallSite(object):
def __init__(self, method, dso):
self.method = method
self.dso = dso
-class Thread:
+class Thread(object):
def __init__(self, tid, pid):
self.tid = tid
@@ -48,7 +48,7 @@
self.flamegraph.add_callchain(chain, sample.period)
-class Process:
+class Process(object):
def __init__(self, name, pid):
self.name = name
@@ -77,7 +77,7 @@
self.num_events += sample.period
-class FlameGraphCallSite:
+class FlameGraphCallSite(object):
callsite_counter = 0
@classmethod
@@ -85,7 +85,7 @@
cls.callsite_counter += 1
return cls.callsite_counter
- def __init__(self, method, dso, id):
+ def __init__(self, method, dso, callsite_id):
# map from (dso, method) to FlameGraphCallSite. Used to speed up add_callchain().
self.child_dict = {}
self.children = []
@@ -93,7 +93,7 @@
self.dso = dso
self.num_events = 0
self.offset = 0 # Offset allows position nodes in different branches.
- self.id = id
+ self.id = callsite_id
def weight(self):
return float(self.num_events)
@@ -102,15 +102,15 @@
self.num_events += num_events
current = self
for callsite in chain:
- current = current._get_child(callsite)
+ current = current.get_child(callsite)
current.num_events += num_events
- def _get_child(self, callsite):
+ def get_child(self, callsite):
key = (callsite.dso, callsite.method)
child = self.child_dict.get(key)
if child is None:
child = self.child_dict[key] = FlameGraphCallSite(callsite.method, callsite.dso,
- self._get_next_callsite_id())
+ self._get_next_callsite_id())
return child
def trim_callchain(self, min_num_events):
diff --git a/simpleperf/scripts/inferno/inferno.py b/simpleperf/scripts/inferno/inferno.py
index e0035d8..517b742 100644
--- a/simpleperf/scripts/inferno/inferno.py
+++ b/simpleperf/scripts/inferno/inferno.py
@@ -35,18 +35,19 @@
import subprocess
import sys
-scripts_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
-sys.path.append(scripts_path)
+# pylint: disable=wrong-import-position
+SCRIPTS_PATH = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+sys.path.append(SCRIPTS_PATH)
from simpleperf_report_lib import ReportLib
from utils import log_exit, log_info, AdbHelper, open_report_in_browser
-from data_types import *
-from svg_renderer import *
+from data_types import Process
+from svg_renderer import get_proper_scaled_time_string, render_svg
def collect_data(args):
""" Run app_profiler.py to generate record file. """
- app_profiler_args = [sys.executable, os.path.join(scripts_path, "app_profiler.py"), "-nb"]
+ app_profiler_args = [sys.executable, os.path.join(SCRIPTS_PATH, "app_profiler.py"), "-nb"]
if args.app:
app_profiler_args += ["-p", args.app]
elif args.native_program:
@@ -106,10 +107,10 @@
process.cmd = lib.GetRecordCmd()
product_props = lib.MetaInfo().get("product_props")
if product_props:
- tuple = product_props.split(':')
- process.props['ro.product.manufacturer'] = tuple[0]
- process.props['ro.product.model'] = tuple[1]
- process.props['ro.product.name'] = tuple[2]
+ manufacturer, model, name = product_props.split(':')
+ process.props['ro.product.manufacturer'] = manufacturer
+ process.props['ro.product.model'] = model
+ process.props['ro.product.name'] = name
if lib.MetaInfo().get('trace_offcpu') == 'true':
process.props['trace_offcpu'] = True
if args.one_flamegraph:
@@ -163,7 +164,7 @@
if not args.embedded_flamegraph:
f.write("<html><body>")
f.write("<div id='flamegraph_id' style='font-family: Monospace; %s'>" % (
- "display: none;" if args.embedded_flamegraph else ""))
+ "display: none;" if args.embedded_flamegraph else ""))
f.write("""<style type="text/css"> .s { stroke:black; stroke-width:0.5; cursor:pointer;}
</style>""")
f.write('<style type="text/css"> .t:hover { cursor:pointer; } </style>')
@@ -177,7 +178,7 @@
event_entry = 'Event count: %s<br/>' % ("{:,}".format(process.num_events))
# TODO: collect capture duration info from perf.data.
duration_entry = ("Duration: %s seconds<br/>" % args.capture_duration
- ) if args.capture_duration else ""
+ ) if args.capture_duration else ""
f.write("""<div style='display:inline-block;'>
<font size='8'>
Inferno Flamegraph Report%s</font><br/><br/>
@@ -186,14 +187,13 @@
Threads : %d <br/>
Samples : %d<br/>
%s
- %s""" % (
- (': ' + args.title) if args.title else '',
- process_entry,
- datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
- len(process.threads),
- process.num_samples,
- event_entry,
- duration_entry))
+ %s""" % ((': ' + args.title) if args.title else '',
+ process_entry,
+ datetime.datetime.now().strftime("%Y-%m-%d (%A) %H:%M:%S"),
+ len(process.threads),
+ process.num_samples,
+ event_entry,
+ duration_entry))
if 'ro.product.model' in process.props:
f.write(
"Machine : %s (%s) by %s<br/>" %
@@ -212,8 +212,8 @@
# Sort threads by the event count in a thread.
for thread in sorted(process.threads.values(), key=lambda x: x.num_events, reverse=True):
f.write("<br/><br/><b>Thread %d (%s) (%d samples):</b><br/>\n\n\n\n" % (
- thread.tid, thread.name, thread.num_samples))
- renderSVG(process, thread.flamegraph, f, args.color)
+ thread.tid, thread.name, thread.num_samples))
+ render_svg(process, thread.flamegraph, f, args.color)
f.write("</div>")
if not args.embedded_flamegraph:
@@ -224,7 +224,7 @@
def generate_threads_offsets(process):
for thread in process.threads.values():
- thread.flamegraph.generate_offset(0)
+ thread.flamegraph.generate_offset(0)
def collect_machine_info(process):
@@ -305,7 +305,7 @@
if result:
try:
process.pid = int(output)
- except:
+ except ValueError:
process.pid = 0
collect_machine_info(process)
else:
@@ -313,7 +313,7 @@
sample_filter_fn = None
if args.one_flamegraph:
- def filter_fn(sample, symbol, callchain):
+ def filter_fn(sample, _symbol, _callchain):
sample.pid = sample.tid = process.pid
return True
sample_filter_fn = filter_fn
diff --git a/simpleperf/scripts/inferno/svg_renderer.py b/simpleperf/scripts/inferno/svg_renderer.py
index fd0096d..0627bc6 100644
--- a/simpleperf/scripts/inferno/svg_renderer.py
+++ b/simpleperf/scripts/inferno/svg_renderer.py
@@ -34,21 +34,21 @@
return hash(string) / float(sys.maxsize)
-def getLegacyColor(method):
+def get_legacy_color(method):
r = 175 + int(50 * hash_to_float(reversed(method)))
g = 60 + int(180 * hash_to_float(method))
b = 60 + int(55 * hash_to_float(reversed(method)))
return (r, g, b)
-def getDSOColor(method):
+def get_dso_color(method):
r = 170 + int(80 * hash_to_float(reversed(method)))
g = 180 + int(70 * hash_to_float((method)))
b = 170 + int(80 * hash_to_float(reversed(method)))
return (r, g, b)
-def getHeatColor(callsite, total_weight):
+def get_heat_color(callsite, total_weight):
r = 245 + 10 * (1 - callsite.weight() / total_weight)
g = 110 + 105 * (1 - callsite.weight() / total_weight)
b = 100
@@ -63,7 +63,7 @@
return '%.3f us' % (value / 1e3)
return '%.0f ns' % value
-def createSVGNode(process, callsite, depth, f, total_weight, height, color_scheme, nav):
+def create_svg_node(process, callsite, depth, f, total_weight, height, color_scheme, nav):
x = float(callsite.offset) / total_weight * 100
y = height - (depth + 1) * SVG_NODE_HEIGHT
width = callsite.weight() / total_weight * 100
@@ -73,11 +73,11 @@
return
if color_scheme == "dso":
- r, g, b = getDSOColor(callsite.dso)
+ r, g, b = get_dso_color(callsite.dso)
elif color_scheme == "legacy":
- r, g, b = getLegacyColor(method)
+ r, g, b = get_legacy_color(method)
else:
- r, g, b = getHeatColor(callsite, total_weight)
+ r, g, b = get_heat_color(callsite, total_weight)
r_border, g_border, b_border = [max(0, color - 50) for color in [r, g, b]]
@@ -119,7 +119,7 @@
FONT_SIZE))
-def renderSVGNodes(process, flamegraph, depth, f, total_weight, height, color_scheme):
+def render_svg_nodes(process, flamegraph, depth, f, total_weight, height, color_scheme):
for i, child in enumerate(flamegraph.children):
# Prebuild navigation target for wasd
@@ -138,12 +138,12 @@
# up, left, down, right
nav = [up_index, left_index, flamegraph.id, right_index]
- createSVGNode(process, child, depth, f, total_weight, height, color_scheme, nav)
+ create_svg_node(process, child, depth, f, total_weight, height, color_scheme, nav)
# Recurse down
- renderSVGNodes(process, child, depth + 1, f, total_weight, height, color_scheme)
+ render_svg_nodes(process, child, depth + 1, f, total_weight, height, color_scheme)
-def renderSearchNode(f):
+def render_search_node(f):
f.write(
"""<rect id="search_rect" style="stroke:rgb(0,0,0);" onclick="search(this);" class="t"
rx="10" ry="10" x="%d" y="10" width="%d" height="30" fill="rgb(255,255,255)""/>
@@ -151,7 +151,7 @@
""" % (SEARCH_NODE_ORIGIN_X, SEARCH_NODE_WIDTH, SEARCH_NODE_ORIGIN_X + RECT_TEXT_PADDING))
-def renderUnzoomNode(f):
+def render_unzoom_node(f):
f.write(
"""<rect id="zoom_rect" style="display:none;stroke:rgb(0,0,0);" class="t"
onclick="unzoom(this);" rx="10" ry="10" x="%d" y="10" width="%d" height="30"
@@ -161,7 +161,7 @@
""" % (UNZOOM_NODE_ORIGIN_X, UNZOOM_NODE_WIDTH, UNZOOM_NODE_ORIGIN_X + RECT_TEXT_PADDING))
-def renderInfoNode(f):
+def render_info_node(f):
f.write(
"""<clipPath id="info_clip_path"> <rect id="info_rect" style="stroke:rgb(0,0,0);"
rx="10" ry="10" x="%d" y="10" width="%d" height="30" fill="rgb(255,255,255)"/>
@@ -173,7 +173,7 @@
INFO_NODE_ORIGIN_X + RECT_TEXT_PADDING))
-def renderPercentNode(f):
+def render_percent_node(f):
f.write(
"""<rect id="percent_rect" style="stroke:rgb(0,0,0);"
rx="10" ry="10" x="%d" y="10" width="%d" height="30" fill="rgb(255,255,255)"/>
@@ -182,7 +182,7 @@
PERCENT_NODE_ORIGIN_X + PERCENT_NODE_WIDTH - RECT_TEXT_PADDING))
-def renderSVG(process, flamegraph, f, color_scheme):
+def render_svg(process, flamegraph, f, color_scheme):
height = (flamegraph.get_max_depth() + 2) * SVG_NODE_HEIGHT
f.write("""<div class="flamegraph_block" style="width:100%%; height:%dpx;">
""" % height)
@@ -196,9 +196,9 @@
</linearGradient> </defs>""")
f.write("""<rect x="0.0" y="0" width="100%" height="100%" fill="url(#background_gradiant)" />
""")
- renderSVGNodes(process, flamegraph, 0, f, flamegraph.weight(), height, color_scheme)
- renderSearchNode(f)
- renderUnzoomNode(f)
- renderInfoNode(f)
- renderPercentNode(f)
+ render_svg_nodes(process, flamegraph, 0, f, flamegraph.weight(), height, color_scheme)
+ render_search_node(f)
+ render_unzoom_node(f)
+ render_info_node(f)
+ render_percent_node(f)
f.write("</svg></div><br/>\n\n")
diff --git a/simpleperf/scripts/pprof_proto_generator.py b/simpleperf/scripts/pprof_proto_generator.py
index fcca090..19f5257 100644
--- a/simpleperf/scripts/pprof_proto_generator.py
+++ b/simpleperf/scripts/pprof_proto_generator.py
@@ -28,22 +28,16 @@
import argparse
import os
import os.path
-import re
-import shutil
-import sys
-import time
from annotate import Addr2Line
-from simpleperf_report_lib import *
-from utils import *
-
+from simpleperf_report_lib import ReportLib
+from utils import log_info, log_exit
+from utils import extant_dir, find_tool_path, flatten_arg_list
try:
- import google.protobuf
-except:
+ import profile_pb2
+except ImportError:
log_exit('google.protobuf module is missing. Please install it first.')
-import profile_pb2
-
def load_pprof_profile(filename):
profile = profile_pb2.Profile()
with open(filename, "rb") as f:
@@ -165,15 +159,8 @@
print('%sfilename: %s' % (space, self.string(function.filename)))
print('%sstart_line: %d' % (space, function.start_line))
- def show_label(self, label, space=''):
- print('%sLabel(%s =', space, self.string(label.key), end='')
- if label.HasField('str'):
- print('%s)' % self.get_string(label.str))
- else:
- print('%d)' % label.num)
-
- def string(self, id):
- return self.string_table[id]
+ def string(self, string_id):
+ return self.string_table[string_id]
class Sample(object):
@@ -185,13 +172,12 @@
def add_location_id(self, location_id):
self.location_ids.append(location_id)
- def add_value(self, id, value):
- self.values[id] = self.values.get(id, 0) + value
+ def add_value(self, sample_type_id, value):
+ self.values[sample_type_id] = self.values.get(sample_type_id, 0) + value
def add_values(self, values):
- for id in values.keys():
- value = values[id]
- self.add_value(id, value)
+ for sample_type_id, value in values.items():
+ self.add_value(sample_type_id, value)
@property
def key(self):
@@ -254,6 +240,7 @@
return (self.name_id, self.dso_name_id)
+# pylint: disable=no-member
class PprofProfileGenerator(object):
def __init__(self, config):
@@ -280,8 +267,6 @@
else:
self.tid_filter = None
self.dso_filter = set(config['dso_filters']) if config.get('dso_filters') else None
-
- def gen(self):
self.profile = profile_pb2.Profile()
self.profile.string_table.append('')
self.string_table = {}
@@ -295,6 +280,7 @@
self.function_map = {}
self.function_list = []
+ def gen(self):
# 1. Process all samples in perf.data, aggregate samples.
while True:
report_sample = self.lib.GetNextSample()
@@ -356,33 +342,33 @@
return True
return False
- def get_string_id(self, str):
- if len(str) == 0:
+ def get_string_id(self, str_value):
+ if not str_value:
return 0
- id = self.string_table.get(str)
- if id is not None:
- return id
- id = len(self.string_table) + 1
- self.string_table[str] = id
- self.profile.string_table.append(str)
- return id
+ str_id = self.string_table.get(str_value)
+ if str_id is not None:
+ return str_id
+ str_id = len(self.string_table) + 1
+ self.string_table[str_value] = str_id
+ self.profile.string_table.append(str_value)
+ return str_id
- def get_string(self, string_id):
- return self.profile.string_table[string_id]
+ def get_string(self, str_id):
+ return self.profile.string_table[str_id]
def get_sample_type_id(self, name):
- id = self.sample_types.get(name)
- if id is not None:
- return id
- id = len(self.profile.sample_type)
+ sample_type_id = self.sample_types.get(name)
+ if sample_type_id is not None:
+ return sample_type_id
+ sample_type_id = len(self.profile.sample_type)
sample_type = self.profile.sample_type.add()
sample_type.type = self.get_string_id('event_' + name + '_samples')
sample_type.unit = self.get_string_id('count')
sample_type = self.profile.sample_type.add()
sample_type.type = self.get_string_id('event_' + name + '_count')
sample_type.unit = self.get_string_id('count')
- self.sample_types[name] = id
- return id
+ self.sample_types[name] = sample_type_id
+ return sample_type_id
def get_location_id(self, ip, symbol):
mapping_id = self.get_mapping_id(symbol.mapping[0], symbol.dso_name)
@@ -485,7 +471,7 @@
if source_id == 0:
# Clear default line info
location.lines = []
- location.lines.append(self.add_line(source, dso_name, function_id))
+ location.lines.append(self.add_line(source, function_id))
source_id += 1
for function in self.function_list:
@@ -498,7 +484,7 @@
if source.line:
function.start_line = source.line
- def add_line(self, source, dso_name, function_id):
+ def add_line(self, source, function_id):
line = Line()
function = self.get_function(function_id)
function.source_filename_id = self.get_string_id(source.file)
@@ -511,8 +497,8 @@
profile_sample.location_id.extend(sample.location_ids)
sample_type_count = len(self.sample_types) * 2
values = [0] * sample_type_count
- for id in sample.values.keys():
- values[id] = sample.values[id]
+ for sample_type_id in sample.values:
+ values[sample_type_id] = sample.values[sample_type_id]
profile_sample.value.extend(values)
def gen_profile_mapping(self, mapping):
@@ -554,18 +540,18 @@
def main():
parser = argparse.ArgumentParser(description='Generate pprof profile data in pprof.profile.')
parser.add_argument('--show', nargs='?', action='append', help='print existing pprof.profile.')
- parser.add_argument('-i', '--perf_data_path', default='perf.data', help=
-"""The path of profiling data.""")
- parser.add_argument('-o', '--output_file', default='pprof.profile', help=
-"""The path of generated pprof profile data.""")
- parser.add_argument('--comm', nargs='+', action='append', help=
-"""Use samples only in threads with selected names.""")
- parser.add_argument('--pid', nargs='+', action='append', help=
-"""Use samples only in processes with selected process ids.""")
- parser.add_argument('--tid', nargs='+', action='append', help=
-"""Use samples only in threads with selected thread ids.""")
- parser.add_argument('--dso', nargs='+', action='append', help=
-"""Use samples only in selected binaries.""")
+ parser.add_argument('-i', '--perf_data_path', default='perf.data', help="""
+ The path of profiling data.""")
+ parser.add_argument('-o', '--output_file', default='pprof.profile', help="""
+ The path of generated pprof profile data.""")
+ parser.add_argument('--comm', nargs='+', action='append', help="""
+ Use samples only in threads with selected names.""")
+ parser.add_argument('--pid', nargs='+', action='append', help="""
+ Use samples only in processes with selected process ids.""")
+ parser.add_argument('--tid', nargs='+', action='append', help="""
+ Use samples only in threads with selected thread ids.""")
+ parser.add_argument('--dso', nargs='+', action='append', help="""
+ Use samples only in selected binaries.""")
parser.add_argument('--ndk_path', type=extant_dir, help='Set the path of a ndk release.')
args = parser.parse_args()
diff --git a/simpleperf/scripts/pylintrc b/simpleperf/scripts/pylintrc
index aeb022e..2801acb 100644
--- a/simpleperf/scripts/pylintrc
+++ b/simpleperf/scripts/pylintrc
@@ -7,3 +7,6 @@
function-rgx=[a-z_][a-z0-9_]{2,50}$
method-rgx=[a-z_][a-z0-9_]{2,50}$
variable-rgx=[a-z_][a-z0-9_]{0,30}$
+attr-rgx=[a-z_][a-z0-9_]{0,30}$
+argument-rgx=[a-z_][a-z0-9_]{0,30}$
+max-module-lines=5000
\ No newline at end of file
diff --git a/simpleperf/scripts/report_html.py b/simpleperf/scripts/report_html.py
index b52a8d1..73ea166 100644
--- a/simpleperf/scripts/report_html.py
+++ b/simpleperf/scripts/report_html.py
@@ -24,7 +24,8 @@
import tempfile
from simpleperf_report_lib import ReportLib
-from utils import *
+from utils import log_info, log_warning, log_exit
+from utils import Addr2Nearestline, get_script_dir, Objdump, open_report_in_browser, remove
class HtmlWriter(object):
@@ -81,9 +82,16 @@
result['eventName'] = self.name
result['eventCount'] = self.event_count
result['processes'] = [process.get_sample_info(gen_addr_hit_map)
- for process in self.processes.values()]
+ for process in self.processes.values()]
return result
+ @property
+ def libraries(self):
+ for process in self.processes.values():
+ for thread in process.threads.values():
+ for lib in thread.libs.values():
+ yield lib
+
class ProcessScope(object):
@@ -107,7 +115,7 @@
result['pid'] = self.pid
result['eventCount'] = self.event_count
result['threads'] = [thread.get_sample_info(gen_addr_hit_map)
- for thread in self.threads.values()]
+ for thread in self.threads.values()]
return result
@@ -123,8 +131,7 @@
""" callstack is a list of tuple (lib_id, func_id, addr).
For each i > 0, callstack[i] calls callstack[i-1]."""
hit_func_ids = set()
- for i in range(len(callstack)):
- lib_id, func_id, addr = callstack[i]
+ for i, (lib_id, func_id, addr) in enumerate(callstack):
# When a callstack contains recursive function, only add for each function once.
if func_id in hit_func_ids:
continue
@@ -157,7 +164,7 @@
result['tid'] = self.tid
result['eventCount'] = self.event_count
result['libs'] = [lib.gen_sample_info(gen_addr_hit_map)
- for lib in self.libs.values()]
+ for lib in self.libs.values()]
return result
@@ -179,7 +186,7 @@
result['libId'] = self.lib_id
result['eventCount'] = self.event_count
result['functions'] = [func.gen_sample_info(gen_addr_hit_map)
- for func in self.functions.values()]
+ for func in self.functions.values()]
return result
@@ -399,17 +406,7 @@
class SourceFileSearcher(object):
-
- SOURCE_FILE_EXTS = {'.h', '.hh', '.H', '.hxx', '.hpp', '.h++',
- '.c', '.cc', '.C', '.cxx', '.cpp', '.c++',
- '.java', '.kt'}
-
- @classmethod
- def is_source_filename(cls, filename):
- ext = os.path.splitext(filename)[1]
- return ext in cls.SOURCE_FILE_EXTS
-
- """" Find source file paths in the file system.
+ """ Find source file paths in the file system.
The file paths reported by addr2line are the paths stored in debug sections
of shared libraries. And we need to convert them to file paths in the file
system. It is done in below steps:
@@ -424,6 +421,16 @@
2.1 Find all real paths with the same file name as the abstract path.
2.2 Select the real path having the longest common suffix with the abstract path.
"""
+
+ SOURCE_FILE_EXTS = {'.h', '.hh', '.H', '.hxx', '.hpp', '.h++',
+ '.c', '.cc', '.C', '.cxx', '.cpp', '.c++',
+ '.java', '.kt'}
+
+ @classmethod
+ def is_source_filename(cls, filename):
+ ext = os.path.splitext(filename)[1]
+ return ext in cls.SOURCE_FILE_EXTS
+
def __init__(self, source_dirs):
# Map from filename to a list of reversed directory path containing filename.
self.filename_to_rparents = {}
@@ -594,30 +601,25 @@
thread.add_callstack(raw_sample.period, callstack, self.build_addr_hit_map)
for event in self.events.values():
- for process in event.processes.values():
- for thread in process.threads.values():
- for lib in thread.libs.values():
- for func_id in lib.functions:
- function = lib.functions[func_id]
- function.update_subtree_event_count()
+ for lib in event.libraries:
+ for func_id in lib.functions:
+ function = lib.functions[func_id]
+ function.update_subtree_event_count()
def limit_percents(self, min_func_percent, min_callchain_percent):
hit_func_ids = set()
for event in self.events.values():
min_limit = event.event_count * min_func_percent * 0.01
- for process in event.processes.values():
- for thread in process.threads.values():
- for lib in thread.libs.values():
- to_del_func_ids = []
- for func_id in lib.functions:
- function = lib.functions[func_id]
- if function.call_graph.subtree_event_count < min_limit:
- to_del_func_ids.append(func_id)
- else:
- function.limit_callchain_percent(min_callchain_percent,
- hit_func_ids)
- for func_id in to_del_func_ids:
- del lib.functions[func_id]
+ for lib in event.libraries:
+ to_del_func_ids = []
+ for func_id in lib.functions:
+ function = lib.functions[func_id]
+ if function.call_graph.subtree_event_count < min_limit:
+ to_del_func_ids.append(func_id)
+ else:
+ function.limit_callchain_percent(min_callchain_percent, hit_func_ids)
+ for func_id in to_del_func_ids:
+ del lib.functions[func_id]
self.functions.trim_functions(hit_func_ids)
def _get_event(self, event_name):
@@ -642,15 +644,12 @@
function.start_addr + function.addr_len - 1)
# Request line for each addr in FunctionScope.addr_hit_map.
for event in self.events.values():
- for process in event.processes.values():
- for thread in process.threads.values():
- for lib in thread.libs.values():
- lib_name = self.libs.get_lib_name(lib.lib_id)
- for function in lib.functions.values():
- func_addr = self.functions.id_to_func[
- function.call_graph.func_id].start_addr
- for addr in function.addr_hit_map:
- addr2line.add_addr(lib_name, func_addr, addr)
+ for lib in event.libraries:
+ lib_name = self.libs.get_lib_name(lib.lib_id)
+ for function in lib.functions.values():
+ func_addr = self.functions.id_to_func[function.call_graph.func_id].start_addr
+ for addr in function.addr_hit_map:
+ addr2line.add_addr(lib_name, func_addr, addr)
addr2line.convert_addrs_to_lines()
# Set line range for each function.
@@ -659,8 +658,7 @@
continue
dso = addr2line.get_dso(self.libs.get_lib_name(function.lib_id))
start_source = addr2line.get_addr_source(dso, function.start_addr)
- end_source = addr2line.get_addr_source(dso,
- function.start_addr + function.addr_len - 1)
+ end_source = addr2line.get_addr_source(dso, function.start_addr + function.addr_len - 1)
if not start_source or not end_source:
continue
start_file_path, start_line = start_source[-1]
@@ -673,22 +671,20 @@
# Build FunctionScope.line_hit_map.
for event in self.events.values():
- for process in event.processes.values():
- for thread in process.threads.values():
- for lib in thread.libs.values():
- dso = addr2line.get_dso(self.libs.get_lib_name(lib.lib_id))
- for function in lib.functions.values():
- for addr in function.addr_hit_map:
- source = addr2line.get_addr_source(dso, addr)
- if not source:
- continue
- for file_path, line in source:
- source_file = self.source_files.get_source_file(file_path)
- # Show [line - 5, line + 5] of the line hit by a sample.
- source_file.request_lines(line - 5, line + 5)
- count_info = function.addr_hit_map[addr]
- function.build_line_hit_map(source_file.file_id, line,
- count_info[0], count_info[1])
+ for lib in event.libraries:
+ dso = addr2line.get_dso(self.libs.get_lib_name(lib.lib_id))
+ for function in lib.functions.values():
+ for addr in function.addr_hit_map:
+ source = addr2line.get_addr_source(dso, addr)
+ if not source:
+ continue
+ for file_path, line in source:
+ source_file = self.source_files.get_source_file(file_path)
+ # Show [line - 5, line + 5] of the line hit by a sample.
+ source_file.request_lines(line - 5, line + 5)
+ count_info = function.addr_hit_map[addr]
+ function.build_line_hit_map(source_file.file_id, line, count_info[0],
+ count_info[1])
# Collect needed source code in SourceFileSet.
self.source_files.load_source_code(source_dirs)
@@ -771,7 +767,7 @@
def _gen_sample_info(self):
return [event.get_sample_info(self.gen_addr_hit_map_in_record_info)
- for event in self.events.values()]
+ for event in self.events.values()]
def _gen_source_files(self):
source_files = sorted(self.source_files.path_to_source_files.values(),
@@ -799,22 +795,23 @@
self.hw.open_tag('html')
self.hw.open_tag('head')
self.hw.open_tag('link', rel='stylesheet', type='text/css',
- href='https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css'
- ).close_tag()
+ href='https://code.jquery.com/ui/1.12.0/themes/smoothness/jquery-ui.css'
+ ).close_tag()
self.hw.open_tag('link', rel='stylesheet', type='text/css',
- href='https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css'
- ).close_tag()
+ href='https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css'
+ ).close_tag()
self.hw.open_tag('script', src='https://www.gstatic.com/charts/loader.js').close_tag()
self.hw.open_tag('script').add(
"google.charts.load('current', {'packages': ['corechart', 'table']});").close_tag()
self.hw.open_tag('script', src='https://code.jquery.com/jquery-3.2.1.js').close_tag()
- self.hw.open_tag('script', src='https://code.jquery.com/ui/1.12.1/jquery-ui.js'
- ).close_tag()
+ self.hw.open_tag('script', src='https://code.jquery.com/ui/1.12.1/jquery-ui.js').close_tag()
self.hw.open_tag('script',
- src='https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js').close_tag()
+ src='https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js'
+ ).close_tag()
self.hw.open_tag('script',
- src='https://cdn.datatables.net/1.10.16/js/dataTables.jqueryui.min.js').close_tag()
+ src='https://cdn.datatables.net/1.10.16/js/dataTables.jqueryui.min.js'
+ ).close_tag()
self.hw.open_tag('style', type='text/css').add("""
.colForLine { width: 50px; }
.colForCount { width: 100px; }
diff --git a/simpleperf/scripts/report_sample.py b/simpleperf/scripts/report_sample.py
index 45e884a..32a5621 100644
--- a/simpleperf/scripts/report_sample.py
+++ b/simpleperf/scripts/report_sample.py
@@ -20,8 +20,7 @@
from __future__ import print_function
import argparse
-import sys
-from simpleperf_report_lib import *
+from simpleperf_report_lib import ReportLib
def report_sample(record_file, symfs_dir, kallsyms_file=None):
@@ -57,7 +56,7 @@
print('')
-if __name__ == '__main__':
+def main():
parser = argparse.ArgumentParser(description='Report samples in perf.data.')
parser.add_argument('--symfs',
help='Set the path to find binaries with symbols and debug info.')
@@ -66,3 +65,7 @@
help='Default is perf.data.')
args = parser.parse_args()
report_sample(args.record_file, args.symfs, args.kallsyms)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/simpleperf/scripts/run_simpleperf_on_device.py b/simpleperf/scripts/run_simpleperf_on_device.py
index 37155bc..9732d6d 100644
--- a/simpleperf/scripts/run_simpleperf_on_device.py
+++ b/simpleperf/scripts/run_simpleperf_on_device.py
@@ -21,7 +21,7 @@
"""
import subprocess
import sys
-from utils import *
+from utils import AdbHelper, disable_debug_log, get_target_binary_path
def main():
disable_debug_log()
@@ -34,4 +34,4 @@
sys.exit(subprocess.call([adb.adb_path, 'shell', shell_cmd]))
if __name__ == '__main__':
- main()
\ No newline at end of file
+ main()
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
index 2eff83e..f31a4e9 100644
--- a/simpleperf/scripts/simpleperf_report_lib.py
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -21,11 +21,8 @@
"""
import ctypes as ct
-import os
-import subprocess
import sys
-import unittest
-from utils import *
+from utils import bytes_to_str, get_host_binary_path, is_windows, str_to_bytes
def _get_native_lib():
@@ -45,6 +42,9 @@
def _char_pt_to_str(char_pt):
return bytes_to_str(char_pt)
+def _check(cond, failmsg):
+ if not cond:
+ raise RuntimeError(failmsg)
class SampleStruct(ct.Structure):
""" Instance of a sample in perf.data.
@@ -179,6 +179,7 @@
_fields_ = []
+# pylint: disable=invalid-name
class ReportLib(object):
def __init__(self, native_lib_path=None):
@@ -231,17 +232,17 @@
def SetLogSeverity(self, log_level='info'):
""" Set log severity of native lib, can be verbose,debug,info,error,fatal."""
cond = self._SetLogSeverityFunc(self.getInstance(), _char_pt(log_level))
- self._check(cond, 'Failed to set log level')
+ _check(cond, 'Failed to set log level')
def SetSymfs(self, symfs_dir):
""" Set directory used to find symbols."""
cond = self._SetSymfsFunc(self.getInstance(), _char_pt(symfs_dir))
- self._check(cond, 'Failed to set symbols directory')
+ _check(cond, 'Failed to set symbols directory')
def SetRecordFile(self, record_file):
""" Set the path of record file, like perf.data."""
cond = self._SetRecordFileFunc(self.getInstance(), _char_pt(record_file))
- self._check(cond, 'Failed to set record file')
+ _check(cond, 'Failed to set record file')
def ShowIpForUnknownSymbol(self):
self._ShowIpForUnknownSymbolFunc(self.getInstance())
@@ -253,7 +254,7 @@
def SetKallsymsFile(self, kallsym_file):
""" Set the file path to a copy of the /proc/kallsyms file (for off device decoding) """
cond = self._SetKallsymsFileFunc(self.getInstance(), _char_pt(kallsym_file))
- self._check(cond, 'Failed to set kallsyms file')
+ _check(cond, 'Failed to set kallsyms file')
def GetNextSample(self):
psample = self._GetNextSampleFunc(self.getInstance())
@@ -364,7 +365,3 @@
if self._instance is None:
raise Exception('Instance is Closed')
return self._instance
-
- def _check(self, cond, failmsg):
- if not cond:
- raise Exception(failmsg)
diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py
index 1b80964..7642b2e 100644
--- a/simpleperf/scripts/test.py
+++ b/simpleperf/scripts/test.py
@@ -34,42 +34,41 @@
Test using both `adb root` and `adb unroot`.
"""
-
+from __future__ import print_function
import os
import re
import shutil
import signal
+import subprocess
import sys
-import tempfile
import time
+import types
import unittest
from app_profiler import NativeLibDownloader
from simpleperf_report_lib import ReportLib
-from utils import *
+from utils import log_info, log_fatal
+from utils import AdbHelper, Addr2Nearestline, get_script_dir, is_windows, Objdump, ReadElf, remove
-has_google_protobuf = True
try:
+ # pylint: disable=unused-import
import google.protobuf
-except:
- has_google_protobuf = False
+ HAS_GOOGLE_PROTOBUF = True
+except ImportError:
+ HAS_GOOGLE_PROTOBUF = False
-inferno_script = os.path.join(get_script_dir(), "inferno.bat" if is_windows() else "./inferno.sh")
+INFERNO_SCRIPT = os.path.join(get_script_dir(), "inferno.bat" if is_windows() else "./inferno.sh")
-support_trace_offcpu = None
+def get_device_features():
+ 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'])
+ return adb.check_run_and_return_output(['shell', '/data/local/tmp/simpleperf', 'list',
+ '--show-features'])
-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
+SUPPORT_TRACE_OFFCPU = 'trace-offcpu' in get_device_features()
def build_testdata():
""" Collect testdata from ../testdata and ../demo. """
@@ -77,7 +76,7 @@
from_demo_path = os.path.join('..', 'demo')
from_script_testdata_path = 'script_testdata'
if (not os.path.isdir(from_testdata_path) or not os.path.isdir(from_demo_path) or
- not from_script_testdata_path):
+ not from_script_testdata_path):
return
copy_testdata_list = ['perf_with_symbols.data', 'perf_with_trace_offcpu.data',
'perf_with_tracepoint_event.data', 'perf_with_interpreter_frames.data']
@@ -106,11 +105,12 @@
subproc = subprocess.Popen(args, stdout=subprocess.PIPE, shell=use_shell)
(output_data, _) = subproc.communicate()
returncode = subproc.returncode
- except:
+ except OSError:
returncode = None
self.assertEqual(returncode, 0, msg="failed to run cmd: %s" % args)
if return_output:
return output_data
+ return ''
class TestExampleBase(TestBase):
@@ -145,7 +145,7 @@
cls.use_compiled_java_code = android_version <= 8
def setUp(self):
- if self.id().find('TraceOffCpu') != -1 and not is_trace_offcpu_supported():
+ if self.id().find('TraceOffCpu') != -1 and not SUPPORT_TRACE_OFFCPU:
self.skipTest('trace-offcpu is not supported on device')
cls = self.__class__
if not cls.has_perf_data_for_report:
@@ -193,28 +193,28 @@
if not self.adb_root:
args.append("--disable_adb_root")
self.run_cmd(args)
- self.check_exist(file="perf.data")
+ self.check_exist(filename="perf.data")
if build_binary_cache:
- self.check_exist(dir="binary_cache")
+ self.check_exist(dirname="binary_cache")
- def check_exist(self, file=None, dir=None):
- if file:
- self.assertTrue(os.path.isfile(file), file)
- if dir:
- self.assertTrue(os.path.isdir(dir), dir)
+ def check_exist(self, filename=None, dirname=None):
+ if filename:
+ self.assertTrue(os.path.isfile(filename), filename)
+ if dirname:
+ self.assertTrue(os.path.isdir(dirname), dirname)
- def check_file_under_dir(self, dir, file):
- self.check_exist(dir=dir)
- for _, _, files in os.walk(dir):
+ def check_file_under_dir(self, dirname, filename):
+ self.check_exist(dirname=dirname)
+ for _, _, files in os.walk(dirname):
for f in files:
- if f == file:
+ if f == filename:
return
- self.fail("Failed to call check_file_under_dir(dir=%s, file=%s)" % (dir, file))
+ self.fail("Failed to call check_file_under_dir(dir=%s, file=%s)" % (dirname, filename))
- def check_strings_in_file(self, file, strings):
- self.check_exist(file=file)
- with open(file, 'r') as fh:
+ def check_strings_in_file(self, filename, strings):
+ self.check_exist(filename=filename)
+ with open(filename, 'r') as fh:
self.check_strings_in_content(fh.read(), strings)
def check_strings_in_content(self, content, strings):
@@ -226,17 +226,15 @@
This function checks for each entry, if the line containing [name]
has at least required accumulated_period and period.
"""
- self.check_exist(file=summary_file)
+ self.check_exist(filename=summary_file)
with open(summary_file, 'r') as fh:
summary = fh.read()
fulfilled = [False for x in check_entries]
- if not hasattr(self, "summary_check_re"):
- self.summary_check_re = re.compile(r'accumulated_period:\s*([\d.]+)%.*period:\s*([\d.]+)%')
+ summary_check_re = re.compile(r'accumulated_period:\s*([\d.]+)%.*period:\s*([\d.]+)%')
for line in summary.split('\n'):
- for i in range(len(check_entries)):
- (name, need_acc_period, need_period) = check_entries[i]
+ for i, (name, need_acc_period, need_period) in enumerate(check_entries):
if not fulfilled[i] and name in line:
- m = self.summary_check_re.search(line)
+ m = summary_check_re.search(line)
if m:
acc_period = float(m.group(1))
period = float(m.group(2))
@@ -244,9 +242,9 @@
fulfilled[i] = True
self.assertEqual(len(fulfilled), sum([int(x) for x in fulfilled]), fulfilled)
- def check_inferno_report_html(self, check_entries, file="report.html"):
- self.check_exist(file=file)
- with open(file, 'r') as fh:
+ def check_inferno_report_html(self, check_entries, filename="report.html"):
+ self.check_exist(filename=filename)
+ with open(filename, 'r') as fh:
data = fh.read()
fulfilled = [False for _ in check_entries]
for line in data.split('\n'):
@@ -258,7 +256,7 @@
if m and float(m.group(1)) >= entry[1]:
fulfilled[i] = True
break
- self.assertEqual(fulfilled, [True for x in check_entries])
+ self.assertEqual(fulfilled, [True for _ in check_entries])
def common_test_app_profiler(self):
self.run_cmd(["app_profiler.py", "-h"])
@@ -269,7 +267,7 @@
if not self.adb_root:
args.append("--disable_adb_root")
self.run_cmd(args)
- self.check_exist(dir="binary_cache")
+ self.check_exist(dirname="binary_cache")
remove("binary_cache")
self.run_app_profiler(build_binary_cache=True)
self.run_app_profiler()
@@ -280,13 +278,13 @@
self.run_cmd(["report.py"])
self.run_cmd(["report.py", "-i", "perf.data"])
self.run_cmd(["report.py", "-g"])
- self.run_cmd(["report.py", "--self-kill-for-testing", "-g", "--gui"])
+ self.run_cmd(["report.py", "--self-kill-for-testing", "-g", "--gui"])
def common_test_annotate(self):
self.run_cmd(["annotate.py", "-h"])
remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path])
- self.check_exist(dir="annotated_files")
+ self.check_exist(dirname="annotated_files")
def common_test_report_sample(self, check_strings):
self.run_cmd(["report_sample.py", "-h"])
@@ -296,37 +294,36 @@
def common_test_pprof_proto_generator(self, check_strings_with_lines,
check_strings_without_lines):
- if not has_google_protobuf:
+ if not HAS_GOOGLE_PROTOBUF:
log_info('Skip test for pprof_proto_generator because google.protobuf is missing')
return
self.run_cmd(["pprof_proto_generator.py", "-h"])
self.run_cmd(["pprof_proto_generator.py"])
remove("pprof.profile")
self.run_cmd(["pprof_proto_generator.py", "-i", "perf.data", "-o", "pprof.profile"])
- self.check_exist(file="pprof.profile")
+ self.check_exist(filename="pprof.profile")
self.run_cmd(["pprof_proto_generator.py", "--show"])
output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
return_output=True)
- self.check_strings_in_content(output, check_strings_with_lines +
- ["has_line_numbers: True"])
+ self.check_strings_in_content(output, check_strings_with_lines + ["has_line_numbers: True"])
remove("binary_cache")
self.run_cmd(["pprof_proto_generator.py"])
output = self.run_cmd(["pprof_proto_generator.py", "--show", "pprof.profile"],
return_output=True)
self.check_strings_in_content(output, check_strings_without_lines +
- ["has_line_numbers: False"])
+ ["has_line_numbers: False"])
def common_test_inferno(self):
- self.run_cmd([inferno_script, "-h"])
+ self.run_cmd([INFERNO_SCRIPT, "-h"])
remove("perf.data")
append_args = [] if self.adb_root else ["--disable_adb_root"]
- self.run_cmd([inferno_script, "-p", self.package_name, "-t", "3"] + append_args)
- self.check_exist(file="perf.data")
- self.run_cmd([inferno_script, "-p", self.package_name, "-f", "1000", "-du", "-t", "1"] +
+ self.run_cmd([INFERNO_SCRIPT, "-p", self.package_name, "-t", "3"] + append_args)
+ self.check_exist(filename="perf.data")
+ self.run_cmd([INFERNO_SCRIPT, "-p", self.package_name, "-f", "1000", "-du", "-t", "1"] +
append_args)
- self.run_cmd([inferno_script, "-p", self.package_name, "-e", "100000 cpu-cycles",
+ self.run_cmd([INFERNO_SCRIPT, "-p", self.package_name, "-e", "100000 cpu-cycles",
"-t", "1"] + append_args)
- self.run_cmd([inferno_script, "-sc"])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
def common_test_report_html(self):
self.run_cmd(['report_html.py', '-h'])
@@ -353,9 +350,9 @@
def test_app_profiler_profile_from_launch(self):
self.run_app_profiler(start_activity=True, build_binary_cache=False)
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
- "__start_thread"])
+ self.check_strings_in_file("report.txt", [
+ "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
+ "__start_thread"])
def test_app_profiler_multiprocesses(self):
self.adb.check_run(['shell', 'am', 'force-stop', self.package_name])
@@ -396,20 +393,20 @@
def test_report(self):
self.common_test_report()
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
- "__start_thread"])
+ self.check_strings_in_file("report.txt", [
+ "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
+ "__start_thread"])
def test_profile_with_process_id(self):
self.adb.check_run(['shell', 'am', 'start', '-n', self.package_name + '/.MainActivity'])
time.sleep(1)
- pid = self.adb.check_run_and_return_output(['shell', 'pidof',
- 'com.example.simpleperf.simpleperfexamplepurejava']).strip()
+ pid = self.adb.check_run_and_return_output([
+ 'shell', 'pidof', 'com.example.simpleperf.simpleperfexamplepurejava']).strip()
self.run_app_profiler(start_activity=False, record_arg='-g --duration 10 -p ' + pid)
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
- "__start_thread"])
+ self.check_strings_in_file("report.txt", [
+ "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
+ "__start_thread"])
def test_annotate(self):
self.common_test_annotate()
@@ -418,15 +415,15 @@
return
self.check_file_under_dir("annotated_files", "MainActivity.java")
summary_file = os.path.join("annotated_files", "summary")
- self.check_annotation_summary(summary_file,
- [("MainActivity.java", 80, 80),
- ("run", 80, 0),
- ("callFunction", 0, 0),
- ("line 23", 80, 0)])
+ self.check_annotation_summary(summary_file, [
+ ("MainActivity.java", 80, 80),
+ ("run", 80, 0),
+ ("callFunction", 0, 0),
+ ("line 23", 80, 0)])
def test_report_sample(self):
self.common_test_report_sample(
- ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()",
+ ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run",
"__start_thread"])
def test_pprof_proto_generator(self):
@@ -437,18 +434,18 @@
"run"]
self.common_test_pprof_proto_generator(
check_strings_with_lines=check_strings_with_lines,
- check_strings_without_lines=
- ["com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()"])
+ check_strings_without_lines=[
+ "com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run"])
def test_inferno(self):
self.common_test_inferno()
self.run_app_profiler()
- self.run_cmd([inferno_script, "-sc"])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
self.check_inferno_report_html(
- [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)])
- self.run_cmd([inferno_script, "-sc", "-o", "report2.html"])
+ [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run', 80)])
+ self.run_cmd([INFERNO_SCRIPT, "-sc", "-o", "report2.html"])
self.check_inferno_report_html(
- [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run()', 80)],
+ [('com.example.simpleperf.simpleperfexamplepurejava.MainActivity$1.run', 80)],
"report2.html")
remove("report2.html")
@@ -460,8 +457,8 @@
os.chdir(test_dir)
self.run_cmd(['python', os.path.join(saved_dir, 'app_profiler.py'),
'--app', self.package_name, '-r', '-e task-clock:u -g --duration 3'])
- self.check_exist(file="perf.data")
- self.run_cmd([inferno_script, "-sc"])
+ self.check_exist(filename="perf.data")
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
os.chdir(saved_dir)
remove(test_dir)
@@ -475,7 +472,7 @@
self.adb.check_run(['kill-server'])
time.sleep(3)
self.run_cmd(['run_simpleperf_without_usb_connection.py', 'stop'])
- self.check_exist(file="perf.data")
+ self.check_exist(filename="perf.data")
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
@@ -501,25 +498,25 @@
def test_smoke(self):
self.run_app_profiler(record_arg="-g -f 1000 --duration 10 -e cpu-cycles:u --trace-offcpu")
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run",
- "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction",
- "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction"
- ])
+ self.check_strings_in_file("report.txt", [
+ "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run",
+ "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction",
+ "com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.SleepFunction"
+ ])
remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path])
- self.check_exist(dir="annotated_files")
+ self.check_exist(dirname="annotated_files")
if self.use_compiled_java_code:
self.check_file_under_dir("annotated_files", "SleepActivity.java")
summary_file = os.path.join("annotated_files", "summary")
- self.check_annotation_summary(summary_file,
- [("SleepActivity.java", 80, 20),
+ self.check_annotation_summary(summary_file, [
+ ("SleepActivity.java", 80, 20),
("run", 80, 0),
("RunFunction", 20, 20),
("SleepFunction", 20, 0),
("line 24", 20, 0),
("line 32", 20, 0)])
- self.run_cmd([inferno_script, "-sc"])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
self.check_inferno_report_html(
[('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.run', 80),
('com.example.simpleperf.simpleperfexamplepurejava.SleepActivity$1.RunFunction',
@@ -541,25 +538,21 @@
def test_app_profiler_profile_from_launch(self):
self.run_app_profiler(start_activity=True, build_binary_cache=False)
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["BusyLoopThread",
- "__start_thread"])
+ self.check_strings_in_file("report.txt", ["BusyLoopThread", "__start_thread"])
def test_report(self):
self.common_test_report()
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["BusyLoopThread",
- "__start_thread"])
+ self.check_strings_in_file("report.txt", ["BusyLoopThread", "__start_thread"])
def test_annotate(self):
self.common_test_annotate()
self.check_file_under_dir("annotated_files", "native-lib.cpp")
summary_file = os.path.join("annotated_files", "summary")
- self.check_annotation_summary(summary_file,
- [("native-lib.cpp", 20, 0),
- ("BusyLoopThread", 20, 0),
- ("line 46", 20, 0)])
+ self.check_annotation_summary(summary_file, [
+ ("native-lib.cpp", 20, 0),
+ ("BusyLoopThread", 20, 0),
+ ("line 46", 20, 0)])
def test_report_sample(self):
self.common_test_report_sample(
@@ -568,16 +561,13 @@
def test_pprof_proto_generator(self):
self.common_test_pprof_proto_generator(
- check_strings_with_lines=
- ["native-lib.cpp",
- "BusyLoopThread"],
- check_strings_without_lines=
- ["BusyLoopThread"])
+ check_strings_with_lines=["native-lib.cpp", "BusyLoopThread"],
+ check_strings_without_lines=["BusyLoopThread"])
def test_inferno(self):
self.common_test_inferno()
self.run_app_profiler()
- self.run_cmd([inferno_script, "-sc"])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
self.check_inferno_report_html([('BusyLoopThread', 20)])
def test_report_html(self):
@@ -606,23 +596,23 @@
def test_smoke(self):
self.run_app_profiler(record_arg="-g -f 1000 --duration 10 -e cpu-cycles:u --trace-offcpu")
self.run_cmd(["report.py", "-g", "--comms", "SleepThread", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["SleepThread(void*)",
- "RunFunction()",
- "SleepFunction(unsigned long long)"])
+ self.check_strings_in_file("report.txt", [
+ "SleepThread(void*)",
+ "RunFunction()",
+ "SleepFunction(unsigned long long)"])
remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "SleepThread"])
- self.check_exist(dir="annotated_files")
+ self.check_exist(dirname="annotated_files")
self.check_file_under_dir("annotated_files", "native-lib.cpp")
summary_file = os.path.join("annotated_files", "summary")
- self.check_annotation_summary(summary_file,
- [("native-lib.cpp", 80, 20),
- ("SleepThread", 80, 0),
- ("RunFunction", 20, 20),
- ("SleepFunction", 20, 0),
- ("line 73", 20, 0),
- ("line 83", 20, 0)])
- self.run_cmd([inferno_script, "-sc"])
+ self.check_annotation_summary(summary_file, [
+ ("native-lib.cpp", 80, 20),
+ ("SleepThread", 80, 0),
+ ("RunFunction", 20, 20),
+ ("SleepFunction", 20, 0),
+ ("line 73", 20, 0),
+ ("line 83", 20, 0)])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
self.check_inferno_report_html([('SleepThread', 80),
('RunFunction', 20),
('SleepFunction', 20)])
@@ -638,28 +628,26 @@
def test_smoke(self):
self.run_app_profiler()
self.run_cmd(["report.py", "-g", "--comms", "BusyThread", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["void com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run()",
- "int com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction(int)",
- "Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"])
+ self.check_strings_in_file("report.txt", [
+ "com.example.simpleperf.simpleperfexamplewithnative.MixActivity$1.run",
+ "com.example.simpleperf.simpleperfexamplewithnative.MixActivity.callFunction",
+ "Java_com_example_simpleperf_simpleperfexamplewithnative_MixActivity_callFunction"])
remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path, "--comm", "BusyThread"])
- self.check_exist(dir="annotated_files")
+ self.check_exist(dirname="annotated_files")
self.check_file_under_dir("annotated_files", "native-lib.cpp")
summary_file = os.path.join("annotated_files", "summary")
- self.check_annotation_summary(summary_file,
- [("native-lib.cpp", 5, 0),
- ("line 40", 5, 0)])
+ self.check_annotation_summary(summary_file, [("native-lib.cpp", 5, 0), ("line 40", 5, 0)])
if self.use_compiled_java_code:
self.check_file_under_dir("annotated_files", "MixActivity.java")
- self.check_annotation_summary(summary_file,
- [("MixActivity.java", 80, 0),
- ("run", 80, 0),
- ("line 26", 20, 0),
- ("native-lib.cpp", 5, 0),
- ("line 40", 5, 0)])
+ self.check_annotation_summary(summary_file, [
+ ("MixActivity.java", 80, 0),
+ ("run", 80, 0),
+ ("line 26", 20, 0),
+ ("native-lib.cpp", 5, 0),
+ ("line 40", 5, 0)])
- self.run_cmd([inferno_script, "-sc"])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
class TestExampleWithNativeForceArm(TestExampleWithNative):
@@ -703,16 +691,16 @@
def test_app_profiler_profile_from_launch(self):
self.run_app_profiler(start_activity=True, build_binary_cache=False)
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
- "__start_thread"])
+ self.check_strings_in_file("report.txt", [
+ "com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1." +
+ "run", "__start_thread"])
def test_report(self):
self.common_test_report()
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
- "__start_thread"])
+ self.check_strings_in_file("report.txt", [
+ "com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1." +
+ "run", "__start_thread"])
def test_annotate(self):
if not self.use_compiled_java_code:
@@ -720,17 +708,17 @@
self.common_test_annotate()
self.check_file_under_dir("annotated_files", "MainActivity.kt")
summary_file = os.path.join("annotated_files", "summary")
- self.check_annotation_summary(summary_file,
- [("MainActivity.kt", 80, 80),
- ("run", 80, 0),
- ("callFunction", 0, 0),
- ("line 19", 80, 0),
- ("line 25", 0, 0)])
+ self.check_annotation_summary(summary_file, [
+ ("MainActivity.kt", 80, 80),
+ ("run", 80, 0),
+ ("callFunction", 0, 0),
+ ("line 19", 80, 0),
+ ("line 25", 0, 0)])
def test_report_sample(self):
- self.common_test_report_sample(
- ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()",
- "__start_thread"])
+ self.common_test_report_sample([
+ "com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1." +
+ "run", "__start_thread"])
def test_pprof_proto_generator(self):
check_strings_with_lines = []
@@ -740,16 +728,15 @@
"run"]
self.common_test_pprof_proto_generator(
check_strings_with_lines=check_strings_with_lines,
- check_strings_without_lines=
- ["com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()"])
+ check_strings_without_lines=["com.example.simpleperf.simpleperfexampleofkotlin." +
+ "MainActivity$createBusyThread$1.run"])
def test_inferno(self):
self.common_test_inferno()
self.run_app_profiler()
- self.run_cmd([inferno_script, "-sc"])
- self.check_inferno_report_html(
- [('com.example.simpleperf.simpleperfexampleofkotlin.MainActivity$createBusyThread$1.run()',
- 80)])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
+ self.check_inferno_report_html([('com.example.simpleperf.simpleperfexampleofkotlin.' +
+ 'MainActivity$createBusyThread$1.run', 80)])
def test_report_html(self):
self.common_test_report_html()
@@ -777,33 +764,32 @@
def test_smoke(self):
self.run_app_profiler(record_arg="-g -f 1000 --duration 10 -e cpu-cycles:u --trace-offcpu")
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.check_strings_in_file("report.txt",
- ["com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run",
- "com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction",
- "com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction"
- ])
+ function_prefix = "com.example.simpleperf.simpleperfexampleofkotlin." + \
+ "SleepActivity$createRunSleepThread$1."
+ self.check_strings_in_file("report.txt", [
+ function_prefix + "run",
+ function_prefix + "RunFunction",
+ function_prefix + "SleepFunction"
+ ])
if self.use_compiled_java_code:
remove("annotated_files")
self.run_cmd(["annotate.py", "-s", self.example_path])
- self.check_exist(dir="annotated_files")
+ self.check_exist(dirname="annotated_files")
self.check_file_under_dir("annotated_files", "SleepActivity.kt")
summary_file = os.path.join("annotated_files", "summary")
- self.check_annotation_summary(summary_file,
- [("SleepActivity.kt", 80, 20),
- ("run", 80, 0),
- ("RunFunction", 20, 20),
- ("SleepFunction", 20, 0),
- ("line 24", 20, 0),
- ("line 32", 20, 0)])
+ self.check_annotation_summary(summary_file, [
+ ("SleepActivity.kt", 80, 20),
+ ("run", 80, 0),
+ ("RunFunction", 20, 20),
+ ("SleepFunction", 20, 0),
+ ("line 24", 20, 0),
+ ("line 32", 20, 0)])
- self.run_cmd([inferno_script, "-sc"])
- self.check_inferno_report_html(
- [('com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.run',
- 80),
- ('com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.RunFunction',
- 20),
- ('com.example.simpleperf.simpleperfexampleofkotlin.SleepActivity$createRunSleepThread$1.SleepFunction',
- 20)])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
+ self.check_inferno_report_html([
+ (function_prefix + 'run', 80),
+ (function_prefix + 'RunFunction', 20),
+ (function_prefix + 'SleepFunction', 20)])
class TestProfilingCmd(TestBase):
@@ -819,8 +805,8 @@
if adb.switch_to_root():
self.run_cmd(["app_profiler.py", "-np", "surfaceflinger"])
self.run_cmd(["report.py", "-g", "-o", "report.txt"])
- self.run_cmd([inferno_script, "-sc"])
- self.run_cmd([inferno_script, "-np", "surfaceflinger"])
+ self.run_cmd([INFERNO_SCRIPT, "-sc"])
+ self.run_cmd([INFERNO_SCRIPT, "-np", "surfaceflinger"])
class TestReportLib(unittest.TestCase):
@@ -838,7 +824,6 @@
def test_symbol(self):
found_func2 = False
while self.report_lib.GetNextSample():
- sample = self.report_lib.GetCurrentSample()
symbol = self.report_lib.GetSymbolOfCurrentSample()
if symbol.symbol_name == 'func2(int, int)':
found_func2 = True
@@ -883,7 +868,8 @@
def test_record_cmd(self):
self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
self.assertEqual(self.report_lib.GetRecordCmd(),
- "/data/local/tmp/simpleperf record --trace-offcpu --duration 2 -g ./simpleperf_runtest_run_and_sleep64")
+ "/data/local/tmp/simpleperf record --trace-offcpu --duration 2 -g " +
+ "./simpleperf_runtest_run_and_sleep64")
def test_offcpu(self):
self.report_lib.SetRecordFile(os.path.join('testdata', 'perf_with_trace_offcpu.data'))
@@ -1010,8 +996,7 @@
actual_source = addr2line.get_addr_source(dso, test_addr['addr'])
self.assertTrue(actual_source is not None)
self.assertEqual(len(actual_source), len(expected_source))
- for i in range(len(expected_source)):
- actual_file_path, actual_line = actual_source[i]
+ for i, (actual_file_path, actual_line) in enumerate(expected_source):
self.assertEqual(actual_file_path, expected_source[i][0])
self.assertEqual(actual_line, expected_source[i][1])
@@ -1066,28 +1051,28 @@
def test_readelf(self):
test_map = {
- '/simpleperf_runtest_two_functions_arm64': {
- 'arch': 'arm64',
- 'build_id': '0xe8ecb3916d989dbdc068345c30f0c24300000000',
- 'sections': ['.interp', '.note.android.ident', '.note.gnu.build-id', '.dynsym',
- '.dynstr', '.gnu.hash', '.gnu.version', '.gnu.version_r', '.rela.dyn',
- '.rela.plt', '.plt', '.text', '.rodata', '.eh_frame', '.eh_frame_hdr',
- '.preinit_array', '.init_array', '.fini_array', '.dynamic', '.got',
- '.got.plt', '.data', '.bss', '.comment', '.debug_str', '.debug_loc',
- '.debug_abbrev', '.debug_info', '.debug_ranges', '.debug_macinfo',
- '.debug_pubnames', '.debug_pubtypes', '.debug_line',
- '.note.gnu.gold-version', '.symtab', '.strtab', '.shstrtab'],
- },
- '/simpleperf_runtest_two_functions_arm': {
- 'arch': 'arm',
- 'build_id': '0x718f5b36c4148ee1bd3f51af89ed2be600000000',
- },
- '/simpleperf_runtest_two_functions_x86_64': {
- 'arch': 'x86_64',
- },
- '/simpleperf_runtest_two_functions_x86': {
- 'arch': 'x86',
- }
+ '/simpleperf_runtest_two_functions_arm64': {
+ 'arch': 'arm64',
+ 'build_id': '0xe8ecb3916d989dbdc068345c30f0c24300000000',
+ 'sections': ['.interp', '.note.android.ident', '.note.gnu.build-id', '.dynsym',
+ '.dynstr', '.gnu.hash', '.gnu.version', '.gnu.version_r', '.rela.dyn',
+ '.rela.plt', '.plt', '.text', '.rodata', '.eh_frame', '.eh_frame_hdr',
+ '.preinit_array', '.init_array', '.fini_array', '.dynamic', '.got',
+ '.got.plt', '.data', '.bss', '.comment', '.debug_str', '.debug_loc',
+ '.debug_abbrev', '.debug_info', '.debug_ranges', '.debug_macinfo',
+ '.debug_pubnames', '.debug_pubtypes', '.debug_line',
+ '.note.gnu.gold-version', '.symtab', '.strtab', '.shstrtab'],
+ },
+ '/simpleperf_runtest_two_functions_arm': {
+ 'arch': 'arm',
+ 'build_id': '0x718f5b36c4148ee1bd3f51af89ed2be600000000',
+ },
+ '/simpleperf_runtest_two_functions_x86_64': {
+ 'arch': 'x86_64',
+ },
+ '/simpleperf_runtest_two_functions_x86': {
+ 'arch': 'x86',
+ }
}
readelf = ReadElf(None)
for dso_path in test_map:
@@ -1105,10 +1090,14 @@
class TestNativeLibDownloader(unittest.TestCase):
def test_smoke(self):
- self.adb = AdbHelper()
+ adb = AdbHelper()
+
+ def is_lib_on_device(path):
+ return adb.run(['shell', 'ls', path])
+
# Sync all native libs on device.
- self.adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
- downloader = NativeLibDownloader(None, 'arm64', self.adb)
+ adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
+ downloader = NativeLibDownloader(None, 'arm64', adb)
downloader.collect_native_libs_on_host(os.path.join(
'testdata', 'SimpleperfExampleWithNative', 'app', 'build', 'intermediates', 'cmake',
'profiling'))
@@ -1127,36 +1116,46 @@
downloader.sync_natives_libs_on_device()
downloader.collect_native_libs_on_device()
self.assertEqual(len(downloader.device_build_id_map), sync_count)
- for i in range(len(lib_list)):
- build_id = lib_list[i][0]
- name = lib_list[i][1].name
+ for i, item in enumerate(lib_list):
+ build_id = item[0]
+ name = item[1].name
if i < sync_count:
self.assertTrue(build_id in downloader.device_build_id_map)
self.assertEqual(name, downloader.device_build_id_map[build_id])
- self.assertTrue(self._is_lib_on_device(downloader.dir_on_device + name))
+ self.assertTrue(is_lib_on_device(downloader.dir_on_device + name))
else:
self.assertTrue(build_id not in downloader.device_build_id_map)
- self.assertFalse(self._is_lib_on_device(downloader.dir_on_device + name))
+ self.assertFalse(is_lib_on_device(downloader.dir_on_device + name))
if sync_count == 1:
- self.adb.run(['pull', '/data/local/tmp/native_libs/build_id_list',
- 'build_id_list'])
+ adb.run(['pull', '/data/local/tmp/native_libs/build_id_list', 'build_id_list'])
with open('build_id_list', 'rb') as fh:
self.assertEqual(fh.read(), '{}={}\n'.format(lib_list[0][0],
lib_list[0][1].name))
remove('build_id_list')
- self.adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
+ adb.run(['shell', 'rm', '-rf', '/data/local/tmp/native_libs'])
- def _is_lib_on_device(self, path):
- return self.adb.run(['shell', 'ls', path])
+
+def list_tests():
+ tests = []
+ for name, value in globals().items():
+ if isinstance(value, type) and issubclass(value, unittest.TestCase):
+ test_methods = [x for x, y in value.__dict__.items()
+ if isinstance(y, types.FunctionType) and x.startswith('test')]
+ for method in test_methods:
+ tests.append(name + '.' + method)
+ print(' '.join(sorted(tests)))
def main():
+ if len(sys.argv) == 2 and sys.argv[1] == '--list-tests':
+ list_tests()
+ return
os.chdir(get_script_dir())
build_testdata()
if AdbHelper().get_android_version() < 7:
log_info("Skip tests on Android version < N.")
sys.exit(0)
- unittest.main(failfast=True)
+ unittest.main(failfast=True, verbosity=2)
if __name__ == '__main__':
main()
diff --git a/simpleperf/scripts/update.py b/simpleperf/scripts/update.py
index 53ac74f..65281aa 100644
--- a/simpleperf/scripts/update.py
+++ b/simpleperf/scripts/update.py
@@ -34,7 +34,7 @@
self.need_strip = need_strip
-install_list = [
+INSTALL_LIST = [
# simpleperf on device
InstallEntry('sdk_arm64-sdk', 'simpleperf', 'android/arm64/simpleperf'),
InstallEntry('sdk_arm64-sdk', 'simpleperf32', 'android/arm/simpleperf'),
@@ -51,18 +51,24 @@
InstallEntry('sdk', 'simpleperf32.exe', 'windows/x86/simpleperf.exe', True),
# libsimpleperf_report.so on host
- InstallEntry('sdk_arm64-sdk', 'libsimpleperf_report.so', 'linux/x86_64/libsimpleperf_report.so', True),
- InstallEntry('sdk_arm64-sdk', 'libsimpleperf_report32.so', 'linux/x86/libsimpleperf_report.so', True),
- InstallEntry('sdk_mac', 'libsimpleperf_report.dylib', 'darwin/x86_64/libsimpleperf_report.dylib'),
+ InstallEntry('sdk_arm64-sdk', 'libsimpleperf_report.so', 'linux/x86_64/libsimpleperf_report.so',
+ True),
+ InstallEntry('sdk_arm64-sdk', 'libsimpleperf_report32.so', 'linux/x86/libsimpleperf_report.so',
+ True),
+ InstallEntry('sdk_mac', 'libsimpleperf_report.dylib',
+ 'darwin/x86_64/libsimpleperf_report.dylib'),
InstallEntry('sdk_mac', 'libsimpleperf_report32.so', 'darwin/x86/libsimpleperf_report.dylib'),
- InstallEntry('sdk', 'libsimpleperf_report.dll', 'windows/x86_64/libsimpleperf_report.dll', True),
+ InstallEntry('sdk', 'libsimpleperf_report.dll', 'windows/x86_64/libsimpleperf_report.dll',
+ True),
InstallEntry('sdk', 'libsimpleperf_report32.dll', 'windows/x86/libsimpleperf_report.dll', True),
# libwinpthread-1.dll on windows host
- InstallEntry('local:../../../../prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/bin/libwinpthread-1.dll',
- 'libwinpthread-1.dll', 'windows/x86_64/libwinpthread-1.dll', False),
- InstallEntry('local:../../../../prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8/x86_64-w64-mingw32/lib32/libwinpthread-1.dll',
- 'libwinpthread-1_32.dll', 'windows/x86/libwinpthread-1.dll', False),
+ InstallEntry('local:../../../../prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8' +
+ '/x86_64-w64-mingw32/bin/libwinpthread-1.dll', 'libwinpthread-1.dll',
+ 'windows/x86_64/libwinpthread-1.dll', False),
+ InstallEntry('local:../../../../prebuilts/gcc/linux-x86/host/x86_64-w64-mingw32-4.8' +
+ '/x86_64-w64-mingw32/lib32/libwinpthread-1.dll', 'libwinpthread-1_32.dll',
+ 'windows/x86/libwinpthread-1.dll', False),
]
@@ -123,7 +129,7 @@
def install_new_release(branch, build, install_dir):
"""Installs the new release."""
- for entry in install_list:
+ for entry in INSTALL_LIST:
install_entry(branch, build, install_dir, entry)
@@ -139,9 +145,9 @@
os.chmod(name, exe_stat.st_mode | stat.S_IEXEC)
if need_strip:
check_call(['strip', name])
- dir = os.path.dirname(install_path)
- if not os.path.isdir(dir):
- os.makedirs(dir)
+ dirname = os.path.dirname(install_path)
+ if not os.path.isdir(dirname):
+ os.makedirs(dirname)
shutil.move(name, install_path)
@@ -185,4 +191,4 @@
if __name__ == '__main__':
- main()
\ No newline at end of file
+ main()
diff --git a/simpleperf/scripts/utils.py b/simpleperf/scripts/utils.py
index b224ce8..e14436f 100644
--- a/simpleperf/scripts/utils.py
+++ b/simpleperf/scripts/utils.py
@@ -69,17 +69,17 @@
def disable_debug_log():
logging.getLogger().setLevel(logging.WARN)
-def str_to_bytes(str):
+def str_to_bytes(str_value):
if not is_python3():
- return str
+ return str_value
# In python 3, str are wide strings whereas the C api expects 8 bit strings,
# hence we have to convert. For now using utf-8 as the encoding.
- return str.encode('utf-8')
+ return str_value.encode('utf-8')
-def bytes_to_str(bytes):
+def bytes_to_str(bytes_value):
if not is_python3():
- return bytes
- return bytes.decode('utf-8')
+ return bytes_value
+ return bytes_value.decode('utf-8')
def get_target_binary_path(arch, binary_name):
if arch == 'aarch64':
@@ -94,21 +94,21 @@
def get_host_binary_path(binary_name):
- dir = os.path.join(get_script_dir(), 'bin')
+ dirname = os.path.join(get_script_dir(), 'bin')
if is_windows():
if binary_name.endswith('.so'):
binary_name = binary_name[0:-3] + '.dll'
elif '.' not in binary_name:
binary_name += '.exe'
- dir = os.path.join(dir, 'windows')
+ dirname = os.path.join(dirname, 'windows')
elif sys.platform == 'darwin': # OSX
if binary_name.endswith('.so'):
binary_name = binary_name[0:-3] + '.dylib'
- dir = os.path.join(dir, 'darwin')
+ dirname = os.path.join(dirname, 'darwin')
else:
- dir = os.path.join(dir, 'linux')
- dir = os.path.join(dir, 'x86_64' if sys.maxsize > 2 ** 32 else 'x86')
- binary_path = os.path.join(dir, binary_name)
+ dirname = os.path.join(dirname, 'linux')
+ dirname = os.path.join(dirname, 'x86_64' if sys.maxsize > 2 ** 32 else 'x86')
+ binary_path = os.path.join(dirname, binary_name)
if not os.path.isfile(binary_path):
log_fatal("can't find binary: %s" % binary_path)
return binary_path
@@ -121,7 +121,7 @@
stderr=subprocess.PIPE)
subproc.communicate()
return subproc.returncode == 0
- except:
+ except OSError:
return False
DEFAULT_NDK_PATH = {
@@ -304,6 +304,7 @@
if '86' in output:
return 'x86'
log_fatal('unsupported architecture: %s' % output.strip())
+ return ''
def get_android_version(self):
@@ -342,18 +343,14 @@
try:
subprocess.check_call(['open', report_path])
return
- except:
+ except subprocess.CalledProcessError:
pass
import webbrowser
try:
# Try to open the report with Chrome
- browser_key = ''
- for key, _ in webbrowser._browsers.items():
- if 'chrome' in key:
- browser_key = key
- browser = webbrowser.get(browser_key)
+ browser = webbrowser.get('google-chrome')
browser.open(report_path, new=0, autoraise=True)
- except:
+ except webbrowser.Error:
# webbrowser.get() doesn't work well on darwin/windows.
webbrowser.open_new_tab(report_path)
@@ -490,7 +487,7 @@
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(stdoutdata, _) = subproc.communicate(str_to_bytes(addr_request))
stdoutdata = bytes_to_str(stdoutdata)
- except:
+ except OSError:
return
addr_map = {}
cur_line_list = None
@@ -591,7 +588,7 @@
subproc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(stdoutdata, _) = subproc.communicate()
stdoutdata = bytes_to_str(stdoutdata)
- except:
+ except OSError:
return None
if not stdoutdata: