Merge "Remove parameters from symbolized stack traces."
This commit is contained in:
@@ -32,6 +32,7 @@ def main():
|
|||||||
group = parser.add_mutually_exclusive_group()
|
group = parser.add_mutually_exclusive_group()
|
||||||
group.add_argument('--symbols-dir', '--syms', '--symdir', help='the symbols directory')
|
group.add_argument('--symbols-dir', '--syms', '--symdir', help='the symbols directory')
|
||||||
group.add_argument('--symbols-zip', help='the symbols.zip file from a build')
|
group.add_argument('--symbols-zip', help='the symbols.zip file from a build')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true', help="include function parameters")
|
||||||
parser.add_argument('file',
|
parser.add_argument('file',
|
||||||
metavar='FILE',
|
metavar='FILE',
|
||||||
default='-',
|
default='-',
|
||||||
@@ -52,6 +53,7 @@ def main():
|
|||||||
with zipfile.ZipFile(args.symbols_zip) as zf:
|
with zipfile.ZipFile(args.symbols_zip) as zf:
|
||||||
zf.extractall(tmp.name)
|
zf.extractall(tmp.name)
|
||||||
symbol.SYMBOLS_DIR = glob.glob("%s/out/target/product/*/symbols" % tmp.name)[0]
|
symbol.SYMBOLS_DIR = glob.glob("%s/out/target/product/*/symbols" % tmp.name)[0]
|
||||||
|
symbol.VERBOSE = args.verbose
|
||||||
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
|
||||||
|
|||||||
@@ -498,12 +498,15 @@ class TraceConverter:
|
|||||||
# display "a -> b -> c" in the stack trace instead of just "a -> c"
|
# display "a -> b -> c" in the stack trace instead of just "a -> c"
|
||||||
info = symbol.SymbolInformation(lib, code_addr)
|
info = symbol.SymbolInformation(lib, code_addr)
|
||||||
nest_count = len(info) - 1
|
nest_count = len(info) - 1
|
||||||
for (source_symbol, source_location, object_symbol_with_offset) in info:
|
for (source_symbol, source_location, symbol_with_offset) in info:
|
||||||
if not source_symbol:
|
if not source_symbol:
|
||||||
if symbol_present:
|
if symbol_present:
|
||||||
source_symbol = symbol.CallCppFilt(symbol_name)
|
source_symbol = symbol.CallCppFilt(symbol_name)
|
||||||
else:
|
else:
|
||||||
source_symbol = "<unknown>"
|
source_symbol = "<unknown>"
|
||||||
|
if not symbol.VERBOSE:
|
||||||
|
source_symbol = symbol.FormatSymbolWithoutParameters(source_symbol)
|
||||||
|
symbol_with_offset = symbol.FormatSymbolWithoutParameters(symbol_with_offset)
|
||||||
if not source_location:
|
if not source_location:
|
||||||
source_location = area
|
source_location = area
|
||||||
if lib_name:
|
if lib_name:
|
||||||
@@ -515,11 +518,9 @@ class TraceConverter:
|
|||||||
arrow = "v-------------->"
|
arrow = "v-------------->"
|
||||||
self.trace_lines.append((arrow, source_symbol, source_location))
|
self.trace_lines.append((arrow, source_symbol, source_location))
|
||||||
else:
|
else:
|
||||||
if not object_symbol_with_offset:
|
if not symbol_with_offset:
|
||||||
object_symbol_with_offset = source_symbol
|
symbol_with_offset = source_symbol
|
||||||
self.trace_lines.append((code_addr,
|
self.trace_lines.append((code_addr, symbol_with_offset, source_location))
|
||||||
object_symbol_with_offset,
|
|
||||||
source_location))
|
|
||||||
if self.code_line.match(line):
|
if self.code_line.match(line):
|
||||||
# Code lines should be ignored. If this were exluded the 'code around'
|
# Code lines should be ignored. If this were exluded the 'code around'
|
||||||
# sections would trigger value_line matches.
|
# sections would trigger value_line matches.
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ SYMBOLS_DIR = FindSymbolsDir()
|
|||||||
|
|
||||||
ARCH = None
|
ARCH = None
|
||||||
|
|
||||||
|
VERBOSE = False
|
||||||
|
|
||||||
# These are private. Do not access them from other modules.
|
# These are private. Do not access them from other modules.
|
||||||
_CACHED_TOOLCHAIN = None
|
_CACHED_TOOLCHAIN = None
|
||||||
@@ -484,6 +485,37 @@ def FormatSymbolWithOffset(symbol, offset):
|
|||||||
return symbol
|
return symbol
|
||||||
return "%s+%d" % (symbol, offset)
|
return "%s+%d" % (symbol, offset)
|
||||||
|
|
||||||
|
def FormatSymbolWithoutParameters(symbol):
|
||||||
|
"""Remove parameters from function.
|
||||||
|
|
||||||
|
Rather than trying to parse the demangled C++ signature,
|
||||||
|
it just removes matching top level parenthesis.
|
||||||
|
"""
|
||||||
|
if not symbol:
|
||||||
|
return symbol
|
||||||
|
|
||||||
|
result = symbol
|
||||||
|
result = result.replace(") const", ")") # Strip const keyword.
|
||||||
|
result = result.replace("operator<<", "operator\u00AB") # Avoid unmatched '<'.
|
||||||
|
result = result.replace("operator>>", "operator\u00BB") # Avoid unmatched '>'.
|
||||||
|
result = result.replace("operator->", "operator\u2192") # Avoid unmatched '>'.
|
||||||
|
|
||||||
|
nested = [] # Keeps tract of current nesting level of parenthesis.
|
||||||
|
for i in reversed(range(len(result))): # Iterate backward to make cutting easier.
|
||||||
|
c = result[i]
|
||||||
|
if c == ')' or c == '>':
|
||||||
|
if len(nested) == 0:
|
||||||
|
end = i + 1 # Mark the end of top-level pair.
|
||||||
|
nested.append(c)
|
||||||
|
if c == '(' or c == '<':
|
||||||
|
if len(nested) == 0 or {')':'(', '>':'<'}[nested.pop()] != c:
|
||||||
|
return symbol # Malformed: character does not match its pair.
|
||||||
|
if len(nested) == 0 and c == '(' and (end - i) > 2:
|
||||||
|
result = result[:i] + result[end:] # Remove substring (i, end).
|
||||||
|
if len(nested) > 0:
|
||||||
|
return symbol # Malformed: missing pair.
|
||||||
|
|
||||||
|
return result.strip()
|
||||||
|
|
||||||
def GetAbiFromToolchain(toolchain_var, bits):
|
def GetAbiFromToolchain(toolchain_var, bits):
|
||||||
toolchain = os.environ.get(toolchain_var)
|
toolchain = os.environ.get(toolchain_var)
|
||||||
@@ -748,5 +780,32 @@ class SetArchTests(unittest.TestCase):
|
|||||||
"Could not determine arch from input, use --arch=XXX to specify it",
|
"Could not determine arch from input, use --arch=XXX to specify it",
|
||||||
SetAbi, [])
|
SetAbi, [])
|
||||||
|
|
||||||
|
class FormatSymbolWithoutParametersTests(unittest.TestCase):
|
||||||
|
def test_c(self):
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo"), "foo")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo+42"), "foo+42")
|
||||||
|
|
||||||
|
def test_simple(self):
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo(int i)"), "foo")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo(int i)+42"), "foo+42")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("bar::foo(int i)+42"), "bar::foo+42")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("operator()"), "operator()")
|
||||||
|
|
||||||
|
def test_templates(self):
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("bar::foo<T>(vector<T>& v)"), "bar::foo<T>")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("bar<T>::foo(vector<T>& v)"), "bar<T>::foo")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("bar::foo<T>(vector<T<U>>& v)"), "bar::foo<T>")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("bar::foo<(EnumType)0>(vector<(EnumType)0>& v)"),
|
||||||
|
"bar::foo<(EnumType)0>")
|
||||||
|
|
||||||
|
def test_nested(self):
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo(int i)::bar(int j)"), "foo::bar")
|
||||||
|
|
||||||
|
def test_unballanced(self):
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo(bar(int i)"), "foo(bar(int i)")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo)bar(int i)"), "foo)bar(int i)")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo<bar(int i)"), "foo<bar(int i)")
|
||||||
|
self.assertEqual(FormatSymbolWithoutParameters("foo>bar(int i)"), "foo>bar(int i)")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main(verbosity=2)
|
unittest.main(verbosity=2)
|
||||||
|
|||||||
Reference in New Issue
Block a user