summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2019-08-30 15:51:33 -0700
committerYabin Cui <yabinc@google.com>2019-08-30 15:51:33 -0700
commit5422a2d3ec6eec3b9e6cd73ccdda4d7f807678e7 (patch)
tree5eba5fee896e6f8f596b1ef0a7e34205824092af
parent3562ed57ed683252de85d75c4a3683305dd61920 (diff)
downloadextras-5422a2d3ec6eec3b9e6cd73ccdda4d7f807678e7.tar.gz
simpleperf: switch to llvm-symbolizer.
Bug: 74395415 Test: run test.py. Change-Id: I4cc0013735bedcfd18813428e4892049277c38ff
-rwxr-xr-xsimpleperf/scripts/pprof_proto_generator.py4
-rwxr-xr-xsimpleperf/scripts/test.py4
-rw-r--r--simpleperf/scripts/utils.py65
3 files changed, 44 insertions, 29 deletions
diff --git a/simpleperf/scripts/pprof_proto_generator.py b/simpleperf/scripts/pprof_proto_generator.py
index 4c59cc56..6487a66e 100755
--- a/simpleperf/scripts/pprof_proto_generator.py
+++ b/simpleperf/scripts/pprof_proto_generator.py
@@ -442,8 +442,8 @@ class PprofProfileGenerator(object):
if not self.config.get('binary_cache_dir'):
log_info("Can't generate line information because binary_cache is missing.")
return
- if not find_tool_path('addr2line', self.config['ndk_path']):
- log_info("Can't generate line information because can't find addr2line.")
+ if not find_tool_path('llvm-symbolizer', self.config['ndk_path']):
+ log_info("Can't generate line information because can't find llvm-symbolizer.")
return
addr2line = Addr2Nearestline(self.config['ndk_path'], self.config['binary_cache_dir'], True)
diff --git a/simpleperf/scripts/test.py b/simpleperf/scripts/test.py
index e24f58f1..1066404b 100755
--- a/simpleperf/scripts/test.py
+++ b/simpleperf/scripts/test.py
@@ -1016,7 +1016,7 @@ class TestTools(unittest.TestCase):
{
'func_addr': 0x840,
'addr': 0x840,
- 'source': 'system/extras/simpleperf/runtest/two_functions.cpp:7',
+ 'source': 'system/extras/simpleperf/runtest/two_functions.cpp:6',
'function': 'Function1()',
},
{
@@ -1065,7 +1065,7 @@ class TestTools(unittest.TestCase):
expected_lines.append(int(items[1]))
for line in test_addr['function'].split('\n'):
expected_functions.append(line.strip())
- self.assertEquals(len(expected_files), len(expected_functions))
+ self.assertEqual(len(expected_files), len(expected_functions))
actual_source = addr2line.get_addr_source(dso, test_addr['addr'])
self.assertTrue(actual_source is not None)
diff --git a/simpleperf/scripts/utils.py b/simpleperf/scripts/utils.py
index 9891e606..2321b80f 100644
--- a/simpleperf/scripts/utils.py
+++ b/simpleperf/scripts/utils.py
@@ -148,15 +148,16 @@ EXPECTED_TOOLS = {
'adb': {
'is_binutils': False,
'test_option': 'version',
- 'path_in_ndk': '../platform-tools/adb',
+ 'path_in_ndk': lambda _: '../platform-tools/adb',
},
'readelf': {
'is_binutils': True,
'accept_tool_without_arch': True,
},
- 'addr2line': {
- 'is_binutils': True,
- 'accept_tool_without_arch': True
+ 'llvm-symbolizer': {
+ 'is_binutils': False,
+ 'path_in_ndk':
+ lambda platform: 'toolchains/llvm/prebuilt/%s-x86_64/bin/llvm-symbolizer' % platform,
},
'objdump': {
'is_binutils': True,
@@ -196,7 +197,7 @@ def find_tool_path(toolname, ndk_path=None, arch=None):
toolname_with_arch, path_in_ndk = _get_binutils_path_in_ndk(toolname, arch, platform)
else:
toolname_with_arch = toolname
- path_in_ndk = tool_info['path_in_ndk']
+ path_in_ndk = tool_info['path_in_ndk'](platform)
path_in_ndk = path_in_ndk.replace('/', os.sep)
# 1. Find tool in the given ndk path.
@@ -391,7 +392,7 @@ def find_real_dso_path(dso_path_in_record_file, binary_cache_path):
class Addr2Nearestline(object):
- """ Use addr2line to convert (dso_path, func_addr, addr) to (source_file, line) pairs.
+ """ Use llvm-symbolizer to convert (dso_path, func_addr, addr) to (source_file, line).
For instructions generated by C++ compilers without a matching statement in source code
(like stack corruption check, switch optimization, etc.), addr2line can't generate
line information. However, we want to assign the instruction to the nearest line before
@@ -435,9 +436,9 @@ class Addr2Nearestline(object):
self.source_lines = None
def __init__(self, ndk_path, binary_cache_path, with_function_name):
- self.addr2line_path = find_tool_path('addr2line', ndk_path)
- if not self.addr2line_path:
- log_exit("Can't find addr2line. Please set ndk path with --ndk_path option.")
+ self.symbolizer_path = find_tool_path('llvm-symbolizer', ndk_path)
+ if not self.symbolizer_path:
+ log_exit("Can't find llvm-symbolizer. Please set ndk path with --ndk_path option.")
self.readelf = ReadElf(ndk_path)
self.dso_map = {} # map from dso_path to Dso.
self.binary_cache_path = binary_cache_path
@@ -504,12 +505,11 @@ class Addr2Nearestline(object):
break
if not addr_set:
return
- addr_request = '\n'.join(['%x' % addr for addr in sorted(addr_set)])
+ addr_request = '\n'.join(['0x%x' % addr for addr in sorted(addr_set)])
# 2. Use addr2line to collect line info.
try:
- option = '-ai' + ('fC' if self.with_function_name else '')
- subproc = subprocess.Popen([self.addr2line_path, option, '-e', real_path],
+ subproc = subprocess.Popen(self._build_symbolizer_args(real_path),
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(stdoutdata, _) = subproc.communicate(str_to_bytes(addr_request))
stdoutdata = bytes_to_str(stdoutdata)
@@ -520,6 +520,9 @@ class Addr2Nearestline(object):
need_function_name = self.with_function_name
cur_function_name = None
for line in stdoutdata.strip().split('\n'):
+ line = line.strip()
+ if not line:
+ continue
if line[:2] == '0x':
# a new address
cur_line_list = addr_map[int(line, 16)] = []
@@ -528,27 +531,16 @@ class Addr2Nearestline(object):
need_function_name = False
else:
need_function_name = self.with_function_name
- # a file:line.
if cur_line_list is None:
continue
- # Handle lines like "C:\Users\...\file:32".
- items = line.rsplit(':', 1)
- if len(items) != 2:
- continue
- if '?' in line:
- # if ? in line, it doesn't have a valid line info.
+ file_path, line_number = self._parse_source_location(line)
+ if not file_path or not line_number:
# An addr can have a list of (file, line), when the addr belongs to an inlined
# function. Sometimes only part of the list has ? mark. In this case, we think
# the line info is valid if the first line doesn't have ? mark.
if not cur_line_list:
cur_line_list = None
continue
- (file_path, line_number) = items
- line_number = line_number.split()[0] # Remove comments after line number
- try:
- line_number = int(line_number)
- except ValueError:
- continue
file_id = self._get_file_id(file_path)
if self.with_function_name:
func_id = self._get_func_id(cur_function_name)
@@ -570,6 +562,29 @@ class Addr2Nearestline(object):
if shifted_addr == addr_obj.func_addr:
break
+ def _build_symbolizer_args(self, binary_path):
+ args = [self.symbolizer_path, '-print-address', '-inlining', '-obj=%s' % binary_path]
+ if self.with_function_name:
+ args += ['-functions=linkage', '-demangle']
+ else:
+ args.append('-functions=none')
+ return args
+
+ def _parse_source_location(self, line):
+ file_path, line_number = None, None
+ # Handle lines in format filename:line:column, like "runtest/two_functions.cpp:14:25".
+ # Filename may contain ':' like "C:\Users\...\file".
+ items = line.rsplit(':', 2)
+ if len(items) == 3:
+ file_path, line_number = items[:2]
+ if not file_path or ('?' in file_path) or not line_number or ('?' in line_number):
+ return None, None
+ try:
+ line_number = int(line_number)
+ except ValueError:
+ return None, None
+ return file_path, line_number
+
def _get_file_id(self, file_path):
file_id = self.file_name_to_id.get(file_path)
if file_id is None: