mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-10 17:41:54 +00:00
extra/stack_analyzer: Fix cbz/cbnz and addr2line parsing.
Fix the cbz/cbnz operands parsing. Parse the discriminator output of addr2line. BUG=none BRANCH=none TEST=extra/stack_analyzer/stack_analyzer_unittest.py Change-Id: Iade1c14db0dc63fa65ef0f5df778b4f4f1e4f802 Signed-off-by: Che-yu Wu <cheyuw@google.com> Reviewed-on: https://chromium-review.googlesource.com/625498 Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
This commit is contained in:
@@ -267,16 +267,20 @@ class ArmAnalyzer(object):
|
||||
CONDITION_CODES = ['', 'eq', 'ne', 'cs', 'hs', 'cc', 'lo', 'mi', 'pl', 'vs',
|
||||
'vc', 'hi', 'ls', 'ge', 'lt', 'gt', 'le']
|
||||
CONDITION_CODES_RE = '({})'.format('|'.join(CONDITION_CODES))
|
||||
# Assume there is no function name containing ">".
|
||||
IMM_ADDRESS_RE = r'([0-9A-Fa-f]+)\s+<([^>]+)>'
|
||||
|
||||
# Fuzzy regular expressions for instruction and operand parsing.
|
||||
# Branch instructions.
|
||||
JUMP_OPCODE_RE = re.compile(
|
||||
r'^(b{0}|bx{0}|cbz|cbnz)(\.\w)?$'.format(CONDITION_CODES_RE))
|
||||
r'^(b{0}|bx{0})(\.\w)?$'.format(CONDITION_CODES_RE))
|
||||
# Call instructions.
|
||||
CALL_OPCODE_RE = re.compile(
|
||||
r'^(bl{0}|blx{0})(\.\w)?$'.format(CONDITION_CODES_RE))
|
||||
# Assume there is no function name containing ">".
|
||||
CALL_OPERAND_RE = re.compile(r'^([0-9A-Fa-f]+)\s+<([^>]+)>$')
|
||||
CALL_OPERAND_RE = re.compile(r'^{}$'.format(IMM_ADDRESS_RE))
|
||||
CBZ_CBNZ_OPCODE_RE = re.compile(r'^(cbz|cbnz)(\.\w)?$')
|
||||
# Example: "r0, 1009bcbe <host_cmd_motion_sense+0x1d2>"
|
||||
CBZ_CBNZ_OPERAND_RE = re.compile(r'^[^,]+,\s+{}$'.format(IMM_ADDRESS_RE))
|
||||
# TODO(cheyuw): Handle conditional versions of following
|
||||
# instructions.
|
||||
# TODO(cheyuw): Handle other kinds of stm instructions.
|
||||
@@ -296,40 +300,29 @@ class ArmAnalyzer(object):
|
||||
Returns:
|
||||
(stack_frame, callsites): Size of stack frame and callsite list.
|
||||
"""
|
||||
def DetectCallsite(operand_text):
|
||||
"""Check if the instruction is a callsite.
|
||||
|
||||
Args:
|
||||
operand_text: Text of instruction operands.
|
||||
|
||||
Returns:
|
||||
target_address: Target address. None if it isn't a callsite.
|
||||
"""
|
||||
result = self.CALL_OPERAND_RE.match(operand_text)
|
||||
if result is None:
|
||||
return None
|
||||
|
||||
target_address = int(result.group(1), 16)
|
||||
|
||||
if (function_symbol.size > 0 and
|
||||
function_symbol.address < target_address <
|
||||
(function_symbol.address + function_symbol.size)):
|
||||
# Filter out the in-function target (branches and in-function calls,
|
||||
# which are actually branches).
|
||||
return None
|
||||
|
||||
return target_address
|
||||
|
||||
stack_frame = 0
|
||||
callsites = []
|
||||
for address, opcode, operand_text in instructions:
|
||||
is_jump_opcode = self.JUMP_OPCODE_RE.match(opcode) is not None
|
||||
is_call_opcode = self.CALL_OPCODE_RE.match(opcode) is not None
|
||||
if is_jump_opcode or is_call_opcode:
|
||||
target_address = DetectCallsite(operand_text)
|
||||
if target_address is not None:
|
||||
# Maybe it's a callsite.
|
||||
callsites.append(Callsite(address, target_address, is_jump_opcode))
|
||||
is_cbz_cbnz_opcode = self.CBZ_CBNZ_OPCODE_RE.match(opcode) is not None
|
||||
if is_jump_opcode or is_call_opcode or is_cbz_cbnz_opcode:
|
||||
if is_cbz_cbnz_opcode:
|
||||
result = self.CBZ_CBNZ_OPERAND_RE.match(operand_text)
|
||||
else:
|
||||
result = self.CALL_OPERAND_RE.match(operand_text)
|
||||
|
||||
if result is not None:
|
||||
target_address = int(result.group(1), 16)
|
||||
# Filter out the in-function target (branches and in-function calls,
|
||||
# which are actually branches).
|
||||
if not (function_symbol.size > 0 and
|
||||
function_symbol.address < target_address <
|
||||
(function_symbol.address + function_symbol.size)):
|
||||
# Maybe it's a callsite.
|
||||
callsites.append(Callsite(address,
|
||||
target_address,
|
||||
is_jump_opcode or is_cbz_cbnz_opcode))
|
||||
|
||||
elif self.PUSH_OPCODE_RE.match(opcode) is not None:
|
||||
# Example: "{r4, r5, r6, r7, lr}"
|
||||
@@ -596,7 +589,8 @@ class StackAnalyzer(object):
|
||||
annotation_signature_regex = re.compile(
|
||||
r'^(?P<name>[{}]+)(\[(?P<path>.+)\])?$'.format(C_FUNCTION_NAME))
|
||||
# Example: driver/accel_kionix.c:321 and ??:0
|
||||
addrtoline_regex = re.compile(r'^(?P<path>.+):\d+$')
|
||||
addrtoline_regex = re.compile(
|
||||
r'^(?P<path>[^:]+):\d+(\s+\(discriminator\s+\d+\))?$')
|
||||
|
||||
# Build the symbol map indexed by symbol name. If there are multiple symbols
|
||||
# with the same name, add them into a set. (e.g. symbols of static function
|
||||
|
||||
@@ -57,7 +57,7 @@ class ArmAnalyzerTest(unittest.TestCase):
|
||||
return rets
|
||||
|
||||
def testInstructionMatching(self):
|
||||
jump_list = self.AppendConditionCode(['b', 'bx']) + ['cbz', 'cbnz']
|
||||
jump_list = self.AppendConditionCode(['b', 'bx'])
|
||||
jump_list += (list(opcode + '.n' for opcode in jump_list) +
|
||||
list(opcode + '.w' for opcode in jump_list))
|
||||
for opcode in jump_list:
|
||||
@@ -66,6 +66,12 @@ class ArmAnalyzerTest(unittest.TestCase):
|
||||
self.assertIsNone(sa.ArmAnalyzer.JUMP_OPCODE_RE.match('bl'))
|
||||
self.assertIsNone(sa.ArmAnalyzer.JUMP_OPCODE_RE.match('blx'))
|
||||
|
||||
cbz_list = ['cbz', 'cbnz', 'cbz.n', 'cbnz.n', 'cbz.w', 'cbnz.w']
|
||||
for opcode in cbz_list:
|
||||
self.assertIsNotNone(sa.ArmAnalyzer.CBZ_CBNZ_OPCODE_RE.match(opcode))
|
||||
|
||||
self.assertIsNone(sa.ArmAnalyzer.CBZ_CBNZ_OPCODE_RE.match('cbn'))
|
||||
|
||||
call_list = self.AppendConditionCode(['bl', 'blx'])
|
||||
call_list += list(opcode + '.n' for opcode in call_list)
|
||||
for opcode in call_list:
|
||||
@@ -78,6 +84,11 @@ class ArmAnalyzerTest(unittest.TestCase):
|
||||
self.assertEqual(result.group(1), '53f90')
|
||||
self.assertEqual(result.group(2), 'get_time+0x18')
|
||||
|
||||
result = sa.ArmAnalyzer.CBZ_CBNZ_OPERAND_RE.match('r6, 53f90 <get+0x0>')
|
||||
self.assertIsNotNone(result)
|
||||
self.assertEqual(result.group(1), '53f90')
|
||||
self.assertEqual(result.group(2), 'get+0x0')
|
||||
|
||||
self.assertIsNotNone(sa.ArmAnalyzer.PUSH_OPCODE_RE.match('push'))
|
||||
self.assertIsNone(sa.ArmAnalyzer.PUSH_OPCODE_RE.match('pushal'))
|
||||
self.assertIsNotNone(sa.ArmAnalyzer.STM_OPCODE_RE.match('stmdb'))
|
||||
@@ -313,7 +324,8 @@ class StackAnalyzerTest(unittest.TestCase):
|
||||
'00001000 <hook_task>:\n'
|
||||
' 1000: dead beef\tfake\n'
|
||||
' 1004: 4770\t\tbx lr\n'
|
||||
' 1006: 00015cfc\t.word 0x00015cfc\n'
|
||||
' 1006: b113\tcbz r3, 100929de <flash_command_write>\n'
|
||||
' 1008: 00015cfc\t.word 0x00015cfc\n'
|
||||
'00002000 <console_task>:\n'
|
||||
' 2000: b508\t\tpush {r3, lr} ; malformed comments,; r0, r1 \n'
|
||||
' 2002: f00e fcc5\tbl 1000 <hook_task>\n'
|
||||
@@ -324,7 +336,8 @@ class StackAnalyzerTest(unittest.TestCase):
|
||||
'00010000 <look_task>:'
|
||||
)
|
||||
function_map = self.analyzer.AnalyzeDisassembly(disasm_text)
|
||||
func_hook_task = sa.Function(0x1000, 'hook_task', 0, [])
|
||||
func_hook_task = sa.Function(0x1000, 'hook_task', 0, [
|
||||
sa.Callsite(0x1006, 0x100929de, True, None)])
|
||||
expect_funcmap = {
|
||||
0x1000: func_hook_task,
|
||||
0x2000: sa.Function(0x2000, 'console_task', 8,
|
||||
@@ -391,8 +404,13 @@ class StackAnalyzerTest(unittest.TestCase):
|
||||
|
||||
@mock.patch('subprocess.check_output')
|
||||
def testAddressToLine(self, checkoutput_mock):
|
||||
checkoutput_mock.return_value = 'test.c [1]'
|
||||
self.assertEqual(self.analyzer.AddressToLine(0x1234), 'test.c [1]')
|
||||
checkoutput_mock.return_value = 'test.c:1'
|
||||
self.assertEqual(self.analyzer.AddressToLine(0x1234), 'test.c:1')
|
||||
checkoutput_mock.assert_called_once_with(
|
||||
['addr2line', '-e', './ec.RW.elf', '1234'])
|
||||
|
||||
checkoutput_mock.return_value = 'test.c:1 (discriminator 1289031)'
|
||||
self.assertEqual(self.analyzer.AddressToLine(0x1234), 'test.c:1')
|
||||
checkoutput_mock.assert_called_once_with(
|
||||
['addr2line', '-e', './ec.RW.elf', '1234'])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user