| #!/usr/bin/env python |
| |
| |
| """ |
| This script uses a regex to validate the output of our backtraces. |
| The layout we assume is: |
| |
| <frame number> <library name> <address> <demangled name> + <offset> |
| |
| It currently just checks that the backtrace results in at least one correctly |
| formatted entry. It does not directly validate the input since the output |
| would not be robust against standard library changes. |
| |
| TODO: We could have the user pass in the frame number, library name, and |
| demangled name. These we can always validate as true in a robust way. On the |
| other hand, address and offset are more difficult to validate in a robust way |
| in the face of small codegen differences, so without any further thought I |
| imagine we can just check the format. |
| |
| 11 libswiftCore.dylib 0x000000000dce84d0l _fatalErrorMessage(StaticString, |
| StaticString, StaticString, UInt, flags : UInt32) -> () + 444 |
| """ |
| |
| |
| from __future__ import absolute_import, print_function, unicode_literals |
| |
| import argparse |
| import re |
| import sys |
| |
| |
| # ----------------------------------------------------------------------------- |
| # Constants |
| |
| DESCRIPTION = """ |
| Checks that a stacktrace dump follows canonical formatting. |
| """ |
| |
| TARGET_PATTERN = re.compile( |
| r'(?P<index>\d+) +(?P<object>\S+) +(?P<address>0x[0-9a-fA-F]{16}) ' |
| r'(?P<routine>[^+]+) [+] (?P<offset>\d+)') |
| |
| RESULTS_FORMAT = """Stack Trace Entry: |
| \tIndex: '%(index)s' |
| \tObject File: '%(object)s' |
| \tAddress: '%(address)s' |
| \tRoutine: '%(routine)s' |
| \tOffset: '%(offset)s' |
| """ |
| |
| |
| # ----------------------------------------------------------------------------- |
| |
| def parse_args(): |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| description=DESCRIPTION) |
| |
| parser.add_argument( |
| '-u', '--check-unavailable', |
| action='store_true', |
| help='Checks if any symbols were unavailable') |
| |
| return parser.parse_args() |
| |
| |
| def main(): |
| args = parse_args() |
| |
| lines = sys.stdin.readlines() |
| |
| found_stack_trace_start = False |
| found_stack_trace_entry = False |
| for line in lines: |
| # In Python 2, string objects can contain Unicode characters. |
| if sys.version_info.major == 2: |
| line = line.decode('utf-8', 'replace') |
| |
| line = line.rstrip('\n') |
| |
| # First see if we found the start of our stack trace start. If so, set |
| # the found stack trace flag and continue. |
| if line == 'Current stack trace:': |
| assert(not found_stack_trace_start) |
| found_stack_trace_start = True |
| continue |
| |
| # Otherwise, if we have not yet found the stack trace start, continue. |
| # We need to find the stack trace start line. |
| if not found_stack_trace_start: |
| continue |
| |
| # Ok, we are in the middle of matching a stack trace entry. |
| matches = TARGET_PATTERN.match(line) |
| |
| # If we fail to match, we have exited the stack trace entry region |
| if matches is None: |
| break |
| |
| # At this point, we know that we have some sort of match. |
| found_stack_trace_entry = True |
| print(RESULTS_FORMAT.format(**matches.groupdict())) |
| |
| # Check for unavailable symbols, if that was requested. |
| if args.check_unavailable: |
| assert('unavailable' not in matches.group('routine')) |
| |
| # Once we have processed all of the lines, make sure that we found at least |
| # one stack trace entry. |
| assert(found_stack_trace_entry) |
| |
| |
| if __name__ == '__main__': |
| main() |