From 9123b238b7be811a73bffbf2cd69f63c30c80170 Mon Sep 17 00:00:00 2001 From: Yilin Yang Date: Mon, 21 Sep 2020 14:55:55 +0800 Subject: stack_analyzer: Migrate to python3 BUG=chromium:1031705 TEST=stack_analyzer_unittest.py TEST=`make BOARD=kukui SECTION=RO analyzestack` runs successfully Signed-off-by: kerker Change-Id: I4027c9c21bdf5fb456430231f1e9bfefed3e8fdb Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2419737 Reviewed-by: Yu-Ping Wu Reviewed-by: Hung-Te Lin (cherry picked from commit 4747bf170d58917025889fedc93e5a0ac7db7fb1) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3893047 Auto-Submit: Mary Ruthven Commit-Queue: Yu-Ping Wu Tested-by: Mary Ruthven --- extra/stack_analyzer/run_tests.sh | 2 +- extra/stack_analyzer/stack_analyzer.py | 42 +++++++++++++++---------- extra/stack_analyzer/stack_analyzer_unittest.py | 41 ++++++++++++------------ 3 files changed, 47 insertions(+), 38 deletions(-) diff --git a/extra/stack_analyzer/run_tests.sh b/extra/stack_analyzer/run_tests.sh index 547a708b2f..95d3a09c02 100755 --- a/extra/stack_analyzer/run_tests.sh +++ b/extra/stack_analyzer/run_tests.sh @@ -4,5 +4,5 @@ # found in the LICENSE file. # Discover all the unit tests in extra/stack_analyzer directory and run them. -python2 -m unittest discover -b -s extra/stack_analyzer -p *_unittest.py \ +python3 -m unittest discover -b -s extra/stack_analyzer -p "*_unittest.py" \ && touch extra/stack_analyzer/.tests-passed diff --git a/extra/stack_analyzer/stack_analyzer.py b/extra/stack_analyzer/stack_analyzer.py index cf5bd2addc..c7422c3e8f 100755 --- a/extra/stack_analyzer/stack_analyzer.py +++ b/extra/stack_analyzer/stack_analyzer.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright 2017 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -246,6 +246,9 @@ class Function(object): return True + def __hash__(self): + return id(self) + class AndesAnalyzer(object): """Disassembly analyzer for Andes architecture. @@ -275,8 +278,8 @@ class AndesAnalyzer(object): PUSH_OPCODE_RE = re.compile(r'^push(\d{1,})$') PUSH_OPERAND_RE = re.compile(r'^\$r\d{1,}, \#\d{1,} \! \{([^\]]+)\}') SMW_OPCODE_RE = re.compile(r'^smw(\.\w\w|\.\w\w\w)$') - SMW_OPERAND_RE = re.compile(r'^(\$r\d{1,}|\$\w\p), \[\$\w\p\], ' - r'(\$r\d{1,}|\$\w\p), \#\d\w\d \! \{([^\]]+)\}') + SMW_OPERAND_RE = re.compile(r'^(\$r\d{1,}|\$\wp), \[\$\wp\], ' + r'(\$r\d{1,}|\$\wp), \#\d\w\d \! \{([^\]]+)\}') OPERANDGROUP_RE = re.compile(r'^\$r\d{1,}\~\$r\d{1,}') LWI_OPCODE_RE = re.compile(r'^lwi(\.\w\w)$') @@ -369,8 +372,8 @@ class AndesAnalyzer(object): if self.OPERANDGROUP_RE.match(operandgroup_text) is not None: # capture number & transfer string to integer oprandgrouphead = operandgroup_text.split(',')[0] - rx=int(filter(str.isdigit, oprandgrouphead.split('~')[0])) - ry=int(filter(str.isdigit, oprandgrouphead.split('~')[1])) + rx=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[0]))) + ry=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[1]))) stack_frame += ((len(operandgroup_text.split(','))+ry-rx) * self.GENERAL_PURPOSE_REGISTER_SIZE) @@ -387,8 +390,8 @@ class AndesAnalyzer(object): if self.OPERANDGROUP_RE.match(operandgroup_text) is not None: # capture number & transfer string to integer oprandgrouphead = operandgroup_text.split(',')[0] - rx=int(filter(str.isdigit, oprandgrouphead.split('~')[0])) - ry=int(filter(str.isdigit, oprandgrouphead.split('~')[1])) + rx=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[0]))) + ry=int(''.join(filter(str.isdigit, oprandgrouphead.split('~')[1]))) stack_frame += ((len(operandgroup_text.split(','))+ry-rx) * self.GENERAL_PURPOSE_REGISTER_SIZE) @@ -616,7 +619,7 @@ class StackAnalyzer(object): if resolve_inline: args.append('-i') - line_text = subprocess.check_output(args) + line_text = subprocess.check_output(args, encoding='utf-8') except subprocess.CalledProcessError: raise StackAnalyzerError('addr2line failed to resolve lines.') except OSError: @@ -656,9 +659,9 @@ class StackAnalyzer(object): """ disasm_lines = [line.strip() for line in disasm_text.splitlines()] - if (disasm_lines[1].find("nds") != -1): + if 'nds' in disasm_lines[1]: analyzer = AndesAnalyzer() - elif (disasm_lines[1].find("arm") != -1): + elif 'arm' in disasm_lines[1]: analyzer = ArmAnalyzer() else: raise StackAnalyzerError('Unsupported architecture.') @@ -940,7 +943,7 @@ class StackAnalyzer(object): # to symbol object. for addr in range(begin_address+offset, end_address, stride): # TODO(drinkcat): Not all architectures need to drop the first bit. - val = self.rodata[(addr-self.rodata_offset)/4] & 0xfffffffe + val = self.rodata[(addr-self.rodata_offset) // 4] & 0xfffffffe name = None for symbol in self.symbols: if (symbol.address == val): @@ -1460,7 +1463,8 @@ class StackAnalyzer(object): try: disasm_text = subprocess.check_output([self.options.objdump, '-d', - self.options.elf_path]) + self.options.elf_path], + encoding='utf-8') except subprocess.CalledProcessError: raise StackAnalyzerError('objdump failed to disassemble.') except OSError: @@ -1517,7 +1521,7 @@ class StackAnalyzer(object): text_list.append(order_text) - for _, text in sorted(text_list, key=lambda (k, _): k): + for _, text in sorted(text_list, key=lambda item: item[0]): print(text) print('Unresolved indirect callsites:') @@ -1533,7 +1537,7 @@ class StackAnalyzer(object): for address in indirect_callsites: text_list.append(OutputInlineStack(address, ' ')) - for _, text in sorted(text_list, key=lambda (k, _): k): + for _, text in sorted(text_list, key=lambda item: item[0]): print(text) print('Unresolved annotation signatures:') @@ -1674,7 +1678,9 @@ def LoadTasklist(section, export_taskinfo, symbols): tasklist = [] for index in range(taskinfo_num): taskinfo = taskinfos[index] - tasklist.append(Task(taskinfo.name, taskinfo.routine, taskinfo.stack_size)) + tasklist.append(Task(taskinfo.name.decode('utf-8'), + taskinfo.routine.decode('utf-8'), + taskinfo.stack_size)) # Resolve routine address for each task. It's more efficient to resolve all # routine addresses of tasks together. @@ -1729,11 +1735,13 @@ def main(): try: symbol_text = subprocess.check_output([options.objdump, '-t', - options.elf_path]) + options.elf_path], + encoding='utf-8') rodata_text = subprocess.check_output([options.objdump, '-s', '-j', '.rodata', - options.elf_path]) + options.elf_path], + encoding='utf-8') except subprocess.CalledProcessError: raise StackAnalyzerError('objdump failed to dump symbol table or rodata.') except OSError: diff --git a/extra/stack_analyzer/stack_analyzer_unittest.py b/extra/stack_analyzer/stack_analyzer_unittest.py index 0af1319803..0dec4a23fd 100755 --- a/extra/stack_analyzer/stack_analyzer_unittest.py +++ b/extra/stack_analyzer/stack_analyzer_unittest.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright 2017 The ChromiumOS Authors # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. @@ -197,8 +197,8 @@ class StackAnalyzerTest(unittest.TestCase): def tasklist_to_taskinfos(pointer, tasklist): taskinfos = [] for task in tasklist: - taskinfos.append(sa.TaskInfo(name=task.name, - routine=task.routine_name, + taskinfos.append(sa.TaskInfo(name=task.name.encode('utf-8'), + routine=task.routine_name.encode('utf-8'), stack_size=task.stack_max_size)) TaskInfoArray = sa.TaskInfo * len(taskinfos) @@ -598,28 +598,29 @@ class StackAnalyzerTest(unittest.TestCase): self.assertEqual(self.analyzer.AddressToLine(0x1234), [('fake_func', '/test.c', 1)]) checkoutput_mock.assert_called_once_with( - ['addr2line', '-f', '-e', './ec.RW.elf', '1234']) + ['addr2line', '-f', '-e', './ec.RW.elf', '1234'], encoding='utf-8') checkoutput_mock.reset_mock() checkoutput_mock.return_value = 'fake_func\n/a.c:1\nbake_func\n/b.c:2\n' self.assertEqual(self.analyzer.AddressToLine(0x1234, True), [('fake_func', '/a.c', 1), ('bake_func', '/b.c', 2)]) checkoutput_mock.assert_called_once_with( - ['addr2line', '-f', '-e', './ec.RW.elf', '1234', '-i']) + ['addr2line', '-f', '-e', './ec.RW.elf', '1234', '-i'], + encoding='utf-8') checkoutput_mock.reset_mock() checkoutput_mock.return_value = 'fake_func\n/test.c:1 (discriminator 128)' self.assertEqual(self.analyzer.AddressToLine(0x12345), [('fake_func', '/test.c', 1)]) checkoutput_mock.assert_called_once_with( - ['addr2line', '-f', '-e', './ec.RW.elf', '12345']) + ['addr2line', '-f', '-e', './ec.RW.elf', '12345'], encoding='utf-8') checkoutput_mock.reset_mock() checkoutput_mock.return_value = '??\n:?\nbake_func\n/b.c:2\n' self.assertEqual(self.analyzer.AddressToLine(0x123456), [None, ('bake_func', '/b.c', 2)]) checkoutput_mock.assert_called_once_with( - ['addr2line', '-f', '-e', './ec.RW.elf', '123456']) + ['addr2line', '-f', '-e', './ec.RW.elf', '123456'], encoding='utf-8') checkoutput_mock.reset_mock() with self.assertRaisesRegexp(sa.StackAnalyzerError, @@ -660,7 +661,7 @@ class StackAnalyzerTest(unittest.TestCase): 'remove': [['fake_func']], } - with mock.patch('__builtin__.print') as print_mock: + with mock.patch('builtins.print') as print_mock: checkoutput_mock.return_value = disasm_text self.analyzer.Analyze() print_mock.assert_has_calls([ @@ -719,7 +720,7 @@ class StackAnalyzerTest(unittest.TestCase): 'remove': [['fake_func']], } - with mock.patch('__builtin__.print') as print_mock: + with mock.patch('builtins.print') as print_mock: checkoutput_mock.return_value = disasm_text self.analyzer.Analyze() print_mock.assert_has_calls([ @@ -771,31 +772,31 @@ class StackAnalyzerTest(unittest.TestCase): with mock.patch('os.path.exists') as path_mock: path_mock.return_value = False - with mock.patch('__builtin__.print') as print_mock: - with mock.patch('__builtin__.open', mock.mock_open()) as open_mock: + with mock.patch('builtins.print') as print_mock: + with mock.patch('builtins.open', mock.mock_open()) as open_mock: sa.main() print_mock.assert_any_call( 'Warning: Annotation file fake does not exist.') with mock.patch('os.path.exists') as path_mock: path_mock.return_value = True - with mock.patch('__builtin__.print') as print_mock: - with mock.patch('__builtin__.open', mock.mock_open()) as open_mock: + with mock.patch('builtins.print') as print_mock: + with mock.patch('builtins.open', mock.mock_open()) as open_mock: open_mock.side_effect = IOError() sa.main() print_mock.assert_called_once_with( 'Error: Failed to open annotation file fake.') - with mock.patch('__builtin__.print') as print_mock: - with mock.patch('__builtin__.open', mock.mock_open()) as open_mock: + with mock.patch('builtins.print') as print_mock: + with mock.patch('builtins.open', mock.mock_open()) as open_mock: open_mock.return_value.read.side_effect = ['{', ''] sa.main() open_mock.assert_called_once_with('fake', 'r') print_mock.assert_called_once_with( 'Error: Failed to parse annotation file fake.') - with mock.patch('__builtin__.print') as print_mock: - with mock.patch('__builtin__.open', + with mock.patch('builtins.print') as print_mock: + with mock.patch('builtins.open', mock.mock_open(read_data='')) as open_mock: sa.main() print_mock.assert_called_once_with( @@ -803,19 +804,19 @@ class StackAnalyzerTest(unittest.TestCase): args.annotation = None - with mock.patch('__builtin__.print') as print_mock: + with mock.patch('builtins.print') as print_mock: checkoutput_mock.side_effect = [symbol_text, rodata_text] sa.main() print_mock.assert_called_once_with( 'Error: Failed to load export_taskinfo.') - with mock.patch('__builtin__.print') as print_mock: + with mock.patch('builtins.print') as print_mock: checkoutput_mock.side_effect = subprocess.CalledProcessError(1, '') sa.main() print_mock.assert_called_once_with( 'Error: objdump failed to dump symbol table or rodata.') - with mock.patch('__builtin__.print') as print_mock: + with mock.patch('builtins.print') as print_mock: checkoutput_mock.side_effect = OSError() sa.main() print_mock.assert_called_once_with('Error: Failed to run objdump.') -- cgit v1.2.1