diff --git a/scripts/stack b/scripts/stack index 718f49c0e..70bc3fbbe 100755 --- a/scripts/stack +++ b/scripts/stack @@ -32,6 +32,7 @@ def main(): group = parser.add_mutually_exclusive_group() group.add_argument('--symbols-dir', '--syms', '--symdir', help='the symbols directory') 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', metavar='FILE', default='-', @@ -52,6 +53,7 @@ def main(): with zipfile.ZipFile(args.symbols_zip) as zf: zf.extractall(tmp.name) symbol.SYMBOLS_DIR = glob.glob("%s/out/target/product/*/symbols" % tmp.name)[0] + symbol.VERBOSE = args.verbose if args.file == '-': print("Reading native crash info from stdin") f = sys.stdin diff --git a/scripts/stack_core.py b/scripts/stack_core.py index 6e84db3fe..50d5c9425 100755 --- a/scripts/stack_core.py +++ b/scripts/stack_core.py @@ -498,12 +498,15 @@ class TraceConverter: # display "a -> b -> c" in the stack trace instead of just "a -> c" info = symbol.SymbolInformation(lib, code_addr) 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 symbol_present: source_symbol = symbol.CallCppFilt(symbol_name) else: source_symbol = "" + if not symbol.VERBOSE: + source_symbol = symbol.FormatSymbolWithoutParameters(source_symbol) + symbol_with_offset = symbol.FormatSymbolWithoutParameters(symbol_with_offset) if not source_location: source_location = area if lib_name: @@ -515,11 +518,9 @@ class TraceConverter: arrow = "v-------------->" self.trace_lines.append((arrow, source_symbol, source_location)) else: - if not object_symbol_with_offset: - object_symbol_with_offset = source_symbol - self.trace_lines.append((code_addr, - object_symbol_with_offset, - source_location)) + if not symbol_with_offset: + symbol_with_offset = source_symbol + self.trace_lines.append((code_addr, symbol_with_offset, source_location)) if self.code_line.match(line): # Code lines should be ignored. If this were exluded the 'code around' # sections would trigger value_line matches. diff --git a/scripts/symbol.py b/scripts/symbol.py index 5ec4e483e..0a255e819 100755 --- a/scripts/symbol.py +++ b/scripts/symbol.py @@ -60,6 +60,7 @@ SYMBOLS_DIR = FindSymbolsDir() ARCH = None +VERBOSE = False # These are private. Do not access them from other modules. _CACHED_TOOLCHAIN = None @@ -484,6 +485,37 @@ def FormatSymbolWithOffset(symbol, offset): return symbol 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): 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", 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(vector& v)"), "bar::foo") + self.assertEqual(FormatSymbolWithoutParameters("bar::foo(vector& v)"), "bar::foo") + self.assertEqual(FormatSymbolWithoutParameters("bar::foo(vector>& v)"), "bar::foo") + 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("foobar(int i)"), "foo>bar(int i)") + if __name__ == '__main__': unittest.main(verbosity=2)