Upgrade the 'stack' script to python3.

Also hook up the test to Android.bp.

Test: unit test
Test: Ran with a tombstone file as parameter.
Test: Ran pasting in stack to stdin.
Change-Id: I25f40569cc49b7487553611bcc25d061179bfa8d
This commit is contained in:
Krzysztof Kosiński
2021-03-11 18:05:01 -08:00
committed by Christopher Ferris
parent ea57fde601
commit b136111f17
5 changed files with 90 additions and 98 deletions

View File

@@ -21,14 +21,6 @@ python_library_host {
srcs: [ srcs: [
"symbol.py", "symbol.py",
], ],
version: {
py2: {
enabled: true,
},
py3: {
enabled: true,
},
},
} }
python_test_host { python_test_host {
@@ -52,6 +44,17 @@ python_test_host {
test_suites: ["general-tests"], test_suites: ["general-tests"],
} }
python_test_host {
name: "python-stack_core_test",
main: "stack_core.py",
srcs: [
"example_crashes.py",
"stack_core.py",
],
libs: ["python-symbol"],
test_suites: ["general-tests"],
}
python_test_host { python_test_host {
name: "add3prf_test", name: "add3prf_test",
srcs: [ srcs: [

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env python
#
# Copyright (C) 2014 The Android Open Source Project # Copyright (C) 2014 The Android Open Source Project
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# #
# Copyright (C) 2013 The Android Open Source Project # Copyright (C) 2013 The Android Open Source Project
# #
@@ -42,10 +42,10 @@ def main():
if args.syms: if args.syms:
symbol.SYMBOLS_DIR = args.syms symbol.SYMBOLS_DIR = args.syms
if args.file == '-': if args.file == '-':
print "Reading native crash info from stdin" print("Reading native crash info from stdin")
f = sys.stdin f = sys.stdin
else: else:
print "Searching for native crashes in %s" % args.file print("Searching for native crashes in %s" % args.file)
f = open(args.file, "r") f = open(args.file, "r")
lines = f.readlines() lines = f.readlines()

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python3
# #
# Copyright (C) 2013 The Android Open Source Project # Copyright (C) 2013 The Android Open Source Project
# #
@@ -27,15 +27,15 @@ import example_crashes
def ConvertTrace(lines): def ConvertTrace(lines):
tracer = TraceConverter() tracer = TraceConverter()
print "Reading symbols from", symbol.SYMBOLS_DIR print("Reading symbols from", symbol.SYMBOLS_DIR)
tracer.ConvertTrace(lines) tracer.ConvertTrace(lines)
class TraceConverter: class TraceConverter:
process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)") process_info_line = re.compile(r"(pid: [0-9]+, tid: [0-9]+.*)")
revision_line = re.compile("(Revision: \'(.*)\')") revision_line = re.compile(r"(Revision: '(.*)')")
signal_line = re.compile("(signal [0-9]+ \(.*\).*)") signal_line = re.compile(r"(signal [0-9]+ \(.*\).*)")
abort_message_line = re.compile("(Abort message: '.*')") abort_message_line = re.compile(r"(Abort message: '.*')")
thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-") thread_line = re.compile(r"(.*)(--- ){15}---")
dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)") dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)") dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")
register_line = re.compile("$a") register_line = re.compile("$a")
@@ -43,14 +43,14 @@ class TraceConverter:
sanitizer_trace_line = re.compile("$a") sanitizer_trace_line = re.compile("$a")
value_line = re.compile("$a") value_line = re.compile("$a")
code_line = re.compile("$a") code_line = re.compile("$a")
zipinfo_central_directory_line = re.compile("Central\s+directory\s+entry") zipinfo_central_directory_line = re.compile(r"Central\s+directory\s+entry")
zipinfo_central_info_match = re.compile( zipinfo_central_info_match = re.compile(
"^\s*(\S+)$\s*offset of local header from start of archive:\s*(\d+)" r"^\s*(\S+)$\s*offset of local header from start of archive:\s*(\d+)"
".*^\s*compressed size:\s+(\d+)", re.M | re.S) r".*^\s*compressed size:\s+(\d+)", re.M | re.S)
unreachable_line = re.compile("((\d+ bytes in \d+ unreachable allocations)|"+\ unreachable_line = re.compile(r"((\d+ bytes in \d+ unreachable allocations)|"
"(\d+ bytes unreachable at [0-9a-f]+)|"+\ r"(\d+ bytes unreachable at [0-9a-f]+)|"
"(referencing \d+ unreachable bytes in \d+ allocation(s)?)|"+\ r"(referencing \d+ unreachable bytes in \d+ allocation(s)?)|"
"(and \d+ similar unreachable bytes in \d+ allocation(s)?))") r"(and \d+ similar unreachable bytes in \d+ allocation(s)?))")
trace_lines = [] trace_lines = []
value_lines = [] value_lines = []
last_frame = -1 last_frame = -1
@@ -89,35 +89,35 @@ class TraceConverter:
# 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so # 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
# Please note the spacing differences. # Please note the spacing differences.
self.trace_line = re.compile( self.trace_line = re.compile(
".*" # Random start stuff. r".*" # Random start stuff.
"\#(?P<frame>[0-9]+)" # Frame number. r"\#(?P<frame>[0-9]+)" # Frame number.
"[ \t]+..[ \t]+" # (space)pc(space). r"[ \t]+..[ \t]+" # (space)pc(space).
"(?P<offset>[0-9a-f]" + self.width + ")[ \t]+" # Offset (hex number given without r"(?P<offset>[0-9a-f]" + self.width + ")[ \t]+" # Offset (hex number given without
# 0x prefix). # 0x prefix).
"(?P<dso>\[[^\]]+\]|[^\r\n \t]*)" # Library name. r"(?P<dso>\[[^\]]+\]|[^\r\n \t]*)" # Library name.
"( \(offset (?P<so_offset>0x[0-9a-fA-F]+)\))?" # Offset into the file to find the start of the shared so. r"( \(offset (?P<so_offset>0x[0-9a-fA-F]+)\))?" # Offset into the file to find the start of the shared so.
"(?P<symbolpresent> \((?P<symbol>.*)\))?") # Is the symbol there? r"(?P<symbolpresent> \((?P<symbol>.*)\))?") # Is the symbol there?
# pylint: disable-msg=C6310 # pylint: disable-msg=C6310
# Sanitizer output. This is different from debuggerd output, and it is easier to handle this as # Sanitizer output. This is different from debuggerd output, and it is easier to handle this as
# its own regex. Example: # its own regex. Example:
# 08-19 05:29:26.283 397 403 I : #0 0xb6a15237 (/system/lib/libclang_rt.asan-arm-android.so+0x4f237) # 08-19 05:29:26.283 397 403 I : #0 0xb6a15237 (/system/lib/libclang_rt.asan-arm-android.so+0x4f237)
self.sanitizer_trace_line = re.compile( self.sanitizer_trace_line = re.compile(
".*" # Random start stuff. r".*" # Random start stuff.
"\#(?P<frame>[0-9]+)" # Frame number. r"\#(?P<frame>[0-9]+)" # Frame number.
"[ \t]+0x[0-9a-f]+[ \t]+" # PC, not interesting to us. r"[ \t]+0x[0-9a-f]+[ \t]+" # PC, not interesting to us.
"\(" # Opening paren. r"\(" # Opening paren.
"(?P<dso>[^+]+)" # Library name. r"(?P<dso>[^+]+)" # Library name.
"\+" # '+' r"\+" # '+'
"0x(?P<offset>[0-9a-f]+)" # Offset (hex number given with r"0x(?P<offset>[0-9a-f]+)" # Offset (hex number given with
# 0x prefix). # 0x prefix).
"\)") # Closin paren. r"\)") # Closing paren.
# pylint: disable-msg=C6310 # pylint: disable-msg=C6310
# Examples of matched value lines include: # Examples of matched value lines include:
# bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
# bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so (symbol) # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so (symbol)
# 03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so # 03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
# Again, note the spacing differences. # Again, note the spacing differences.
self.value_line = re.compile("(.*)([0-9a-f]" + self.width + ")[ \t]+([0-9a-f]" + self.width + ")[ \t]+([^\r\n \t]*)( \((.*)\))?") self.value_line = re.compile(r"(.*)([0-9a-f]" + self.width + r")[ \t]+([0-9a-f]" + self.width + r")[ \t]+([^\r\n \t]*)( \((.*)\))?")
# Lines from 'code around' sections of the output will be matched before # Lines from 'code around' sections of the output will be matched before
# value lines because otheriwse the 'code around' sections will be confused as # value lines because otheriwse the 'code around' sections will be confused as
# value lines. # value lines.
@@ -125,39 +125,35 @@ class TraceConverter:
# Examples include: # Examples include:
# 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8 # 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
# 03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8 # 03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
self.code_line = re.compile("(.*)[ \t]*[a-f0-9]" + self.width + self.code_line = re.compile(r"(.*)[ \t]*[a-f0-9]" + self.width +
"[ \t]*[a-f0-9]" + self.width + r"[ \t]*[a-f0-9]" + self.width +
"[ \t]*[a-f0-9]" + self.width + r"[ \t]*[a-f0-9]" + self.width +
"[ \t]*[a-f0-9]" + self.width + r"[ \t]*[a-f0-9]" + self.width +
"[ \t]*[a-f0-9]" + self.width + r"[ \t]*[a-f0-9]" + self.width +
"[ \t]*[ \r\n]") # pylint: disable-msg=C6310 r"[ \t]*[ \r\n]") # pylint: disable-msg=C6310
def CleanLine(self, ln): def CleanLine(self, ln):
# AndroidFeedback adds zero width spaces into its crash reports. These # AndroidFeedback adds zero width spaces into its crash reports. These
# should be removed or the regular expresssions will fail to match. # should be removed or the regular expresssions will fail to match.
return unicode(ln, errors='ignore') return ln.encode().decode(encoding='utf8', errors='ignore')
def PrintTraceLines(self, trace_lines): def PrintTraceLines(self, trace_lines):
"""Print back trace.""" """Print back trace."""
maxlen = max(map(lambda tl: len(tl[1]), trace_lines)) maxlen = max(len(tl[1]) for tl in trace_lines)
print print("\nStack Trace:")
print "Stack Trace:" print(" RELADDR " + self.spacing + "FUNCTION".ljust(maxlen) + " FILE:LINE")
print " RELADDR " + self.spacing + "FUNCTION".ljust(maxlen) + " FILE:LINE"
for tl in self.trace_lines: for tl in self.trace_lines:
(addr, symbol_with_offset, location) = tl (addr, symbol_with_offset, location) = tl
print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location) print(" %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location))
return
def PrintValueLines(self, value_lines): def PrintValueLines(self, value_lines):
"""Print stack data values.""" """Print stack data values."""
maxlen = max(map(lambda tl: len(tl[2]), self.value_lines)) maxlen = max(len(tl[2]) for tl in self.value_lines)
print print("\nStack Data:")
print "Stack Data:" print(" ADDR " + self.spacing + "VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE")
print " ADDR " + self.spacing + "VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE"
for vl in self.value_lines: for vl in self.value_lines:
(addr, value, symbol_with_offset, location) = vl (addr, value, symbol_with_offset, location) = vl
print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location) print(" %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location))
return
def PrintOutput(self, trace_lines, value_lines): def PrintOutput(self, trace_lines, value_lines):
if self.trace_lines: if self.trace_lines:
@@ -166,8 +162,7 @@ class TraceConverter:
self.PrintValueLines(self.value_lines) self.PrintValueLines(self.value_lines)
def PrintDivider(self): def PrintDivider(self):
print print("\n-----------------------------------------------------\n")
print "-----------------------------------------------------\n"
def DeleteApkTmpFiles(self): def DeleteApkTmpFiles(self):
for _, _, tmp_files in self.apk_info.values(): for _, _, tmp_files in self.apk_info.values():
@@ -175,7 +170,7 @@ class TraceConverter:
os.unlink(tmp_file) os.unlink(tmp_file)
def ConvertTrace(self, lines): def ConvertTrace(self, lines):
lines = map(self.CleanLine, lines) lines = [self.CleanLine(line) for line in lines]
try: try:
if not symbol.ARCH: if not symbol.ARCH:
symbol.SetAbi(lines) symbol.SetAbi(lines)
@@ -252,18 +247,18 @@ class TraceConverter:
return None, None return None, None
if not "ANDROID_PRODUCT_OUT" in os.environ: if not "ANDROID_PRODUCT_OUT" in os.environ:
print "ANDROID_PRODUCT_OUT environment variable not set." print("ANDROID_PRODUCT_OUT environment variable not set.")
return None, None return None, None
out_dir = os.environ["ANDROID_PRODUCT_OUT"] out_dir = os.environ["ANDROID_PRODUCT_OUT"]
if not os.path.exists(out_dir): if not os.path.exists(out_dir):
print "ANDROID_PRODUCT_OUT " + out_dir + " does not exist." print("ANDROID_PRODUCT_OUT", out_dir, "does not exist.")
return None, None return None, None
if apk.startswith("/"): if apk.startswith("/"):
apk_full_path = out_dir + apk apk_full_path = out_dir + apk
else: else:
apk_full_path = os.path.join(out_dir, apk) apk_full_path = os.path.join(out_dir, apk)
if not os.path.exists(apk_full_path): if not os.path.exists(apk_full_path):
print "Cannot find apk " + apk print("Cannot find apk", apk)
return None, None return None, None
cmd = subprocess.Popen(["zipinfo", "-v", apk_full_path], stdout=subprocess.PIPE) cmd = subprocess.Popen(["zipinfo", "-v", apk_full_path], stdout=subprocess.PIPE)
@@ -322,23 +317,23 @@ class TraceConverter:
self.value_lines = [] self.value_lines = []
self.last_frame = -1 self.last_frame = -1
if process_header: if process_header:
print process_header.group(1) print(process_header.group(1))
if signal_header: if signal_header:
print signal_header.group(1) print(signal_header.group(1))
if abort_message_header: if abort_message_header:
print abort_message_header.group(1) print(abort_message_header.group(1))
if register_header: if register_header:
print register_header.group(1) print(register_header.group(1))
if thread_header: if thread_header:
print thread_header.group(1) print(thread_header.group(1))
if dalvik_jni_thread_header: if dalvik_jni_thread_header:
print dalvik_jni_thread_header.group(1) print(dalvik_jni_thread_header.group(1))
if dalvik_native_thread_header: if dalvik_native_thread_header:
print dalvik_native_thread_header.group(1) print(dalvik_native_thread_header.group(1))
if revision_header: if revision_header:
print revision_header.group(1) print(revision_header.group(1))
if unreachable_header: if unreachable_header:
print unreachable_header.group(1) print(unreachable_header.group(1))
return True return True
trace_line_dict = self.MatchTraceLine(line) trace_line_dict = self.MatchTraceLine(line)
if trace_line_dict is not None: if trace_line_dict is not None:
@@ -470,7 +465,7 @@ class RegisterPatternTests(unittest.TestCase):
tc.ProcessLine(line) tc.ProcessLine(line)
is_register = (re.search(stupid_pattern, line) is not None) is_register = (re.search(stupid_pattern, line) is not None)
matched = (tc.register_line.search(line) is not None) matched = (tc.register_line.search(line) is not None)
self.assertEquals(matched, is_register, line) self.assertEqual(matched, is_register, line)
tc.PrintOutput(tc.trace_lines, tc.value_lines) tc.PrintOutput(tc.trace_lines, tc.value_lines)
def test_arm_registers(self): def test_arm_registers(self):
@@ -497,7 +492,7 @@ class LibmemunreachablePatternTests(unittest.TestCase):
lines = example_crashes.libmemunreachable.split('\n') lines = example_crashes.libmemunreachable.split('\n')
symbol.SetAbi(lines) symbol.SetAbi(lines)
self.assertEquals(symbol.ARCH, "arm") self.assertEqual(symbol.ARCH, "arm")
tc.UpdateAbiRegexes() tc.UpdateAbiRegexes()
header_lines = 0 header_lines = 0
@@ -508,8 +503,8 @@ class LibmemunreachablePatternTests(unittest.TestCase):
header_lines += 1 header_lines += 1
if tc.MatchTraceLine(line) is not None: if tc.MatchTraceLine(line) is not None:
trace_lines += 1 trace_lines += 1
self.assertEquals(header_lines, 3) self.assertEqual(header_lines, 3)
self.assertEquals(trace_lines, 2) self.assertEqual(trace_lines, 2)
tc.PrintOutput(tc.trace_lines, tc.value_lines) tc.PrintOutput(tc.trace_lines, tc.value_lines)
class LongASANStackTests(unittest.TestCase): class LongASANStackTests(unittest.TestCase):
@@ -542,4 +537,4 @@ class ValueLinesTest(unittest.TestCase):
self.assertEqual([], tc.value_lines) self.assertEqual([], tc.value_lines)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main(verbosity=2)

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/env python3
# #
# Copyright (C) 2013 The Android Open Source Project # Copyright (C) 2013 The Android Open Source Project
# #
@@ -28,12 +28,7 @@ import signal
import subprocess import subprocess
import unittest import unittest
try: ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP", ".")
ANDROID_BUILD_TOP = str(os.environ["ANDROID_BUILD_TOP"])
if not ANDROID_BUILD_TOP:
ANDROID_BUILD_TOP = "."
except:
ANDROID_BUILD_TOP = "."
def FindSymbolsDir(): def FindSymbolsDir():
saveddir = os.getcwd() saveddir = os.getcwd()
@@ -41,8 +36,8 @@ def FindSymbolsDir():
stream = None stream = None
try: try:
cmd = "build/soong/soong_ui.bash --dumpvar-mode --abs TARGET_OUT_UNSTRIPPED" cmd = "build/soong/soong_ui.bash --dumpvar-mode --abs TARGET_OUT_UNSTRIPPED"
stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True).stdout stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding='utf8', shell=True).stdout
return os.path.join(ANDROID_BUILD_TOP, str(stream.read().strip())) return str(stream.read().strip())
finally: finally:
if stream is not None: if stream is not None:
stream.close() stream.close()
@@ -96,7 +91,7 @@ class ProcessCache:
return pipe return pipe
def SpawnProcess(self, cmd): def SpawnProcess(self, cmd):
return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE) return subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf8')
def TerminateProcess(self, pipe): def TerminateProcess(self, pipe):
pipe.stdin.close() pipe.stdin.close()
@@ -156,7 +151,7 @@ def FindToolchain():
_CACHED_TOOLCHAIN = llvm_binutils_dir _CACHED_TOOLCHAIN = llvm_binutils_dir
_CACHED_TOOLCHAIN_ARCH = ARCH _CACHED_TOOLCHAIN_ARCH = ARCH
print("Using %s toolchain from: %s" % (_CACHED_TOOLCHAIN_ARCH, _CACHED_TOOLCHAIN)) print("Using", _CACHED_TOOLCHAIN_ARCH, "toolchain from:", _CACHED_TOOLCHAIN)
return _CACHED_TOOLCHAIN return _CACHED_TOOLCHAIN
@@ -303,6 +298,7 @@ def CallLlvmSymbolizerForSet(lib, unique_addrs):
# reading inlines from the output. # reading inlines from the output.
# The blank line will cause llvm-symbolizer to emit a blank line. # The blank line will cause llvm-symbolizer to emit a blank line.
child.stdin.write("\n") child.stdin.write("\n")
child.stdin.flush()
first = False first = False
except IOError as e: except IOError as e:
# Remove the / in front of the library name to match other output. # Remove the / in front of the library name to match other output.
@@ -394,7 +390,7 @@ def CallObjdumpForSet(lib, unique_addrs):
current_symbol_addr = 0 # The address of the current function. current_symbol_addr = 0 # The address of the current function.
addr_index = 0 # The address that we are currently looking for. addr_index = 0 # The address that we are currently looking for.
stream = subprocess.Popen(cmd, stdout=subprocess.PIPE).stdout stream = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding='utf8').stdout
for line in stream: for line in stream:
# Is it a function line like: # Is it a function line like:
# 000177b0 <android::IBinder::~IBinder()>: # 000177b0 <android::IBinder::~IBinder()>: