Merge "Remove parameters from symbolized stack traces." am: a3fe9b1729

Original change: https://android-review.googlesource.com/c/platform/development/+/1875691

Change-Id: I490dacde561bec1ddef34de6982ea84ab9adf78a
This commit is contained in:
Treehugger Robot
2022-01-06 18:08:28 +00:00
committed by Automerger Merge Worker
3 changed files with 68 additions and 6 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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)