| #!/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 print_function |
| |
| import argparse |
| import re |
| import sys |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| description="""Checks that a stacktrace dump follows canonical |
| formatting.""") |
| parser.add_argument( |
| "-u", "--check-unavailable", action='store_true', |
| help="Checks if any symbols were unavailable") |
| args = parser.parse_args() |
| |
| TARGET_RE = re.compile( |
| "(?P<index>\d+) +(?P<object>\S+) +(?P<address>0x[0-9a-fA-F]{16}) " |
| "(?P<routine>[^+]+) [+] (?P<offset>\d+)") |
| |
| lines = sys.stdin.readlines() |
| |
| found_stack_trace_start = False |
| found_stack_trace_entry = False |
| for l in lines: |
| l = l.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 l == "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. |
| m = TARGET_RE.match(l) |
| # If we fail to match, we have exited the stack trace entry region |
| if m is None: |
| break |
| |
| # At this point, we know that we have some sort of match. |
| found_stack_trace_entry = True |
| print("Stack Trace Entry:") |
| print("\tIndex: '%(index)s'\n\tObject File: '%(object)s'\n\tAddress: " |
| "'%(address)s'\n\tRoutine: '%(routine)s'\n\tOffset: '%(offset)s'" |
| "\n" % m.groupdict()) |
| |
| # Check for unavailable symbols, if that was requested. |
| if args.check_unavailable: |
| assert("unavailable" not in m.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() |