#!/usr/bin/python -O

from re import compile
from subprocess import PIPE, Popen
from sys import argv, stderr, stdin


########################################################################


def main():
    if len(argv) != 2:
        print >>stderr, """\
Usage: %s <executable>

Reads a raw CBI "main-backtrace" report on standard input.  For each
frame of the stack, prints one line with the following columns of
tab-delimited information:

    1. hexadecimal PC address
    2. function name
    3. source file name
    4. source line number

The hexadecimal PC address is always present.  Each of the columns may
be empty if the corresponding information is unavailable.

Typically this script will be placed in a pipeline with "extract-report"
extracting the report and "resolve-backtrace" resolving it, as in:

    %% extract-report main-backtrace | %s <executable>""" % (argv[0], argv[0])
        exit(1)

    resolver = Popen(['addr2line', '--functions', '--exe', argv[1]], stdin=PIPE, stdout=PIPE)

    framePattern = compile(r'^.*\[(0x[0-9a-f]+)\]$')
    locationPattern = compile(r'^(.*):([0-9]+)$')

    for line in stdin:
        # grab next PC and send it to resolver
        match = framePattern.match(line)
        if not match:
            print >>stderr, 'malformed backtrace'
            exit(1)

        pc = hex(int(match.group(1)) - 1)
        print >>resolver.stdin, pc

        # read back resolved function name
        function = resolver.stdout.readline().rstrip()
        if function == '??':
            function = ''

        # read back resolved source file and line number
        location = resolver.stdout.readline().rstrip()
        if location == '??:0':
            filename, line = '', '';
        else:
            match = locationPattern.match(line)
            filename, line = match.groups()

        # print what we've figured out
        print '\t'.join((pc, function, filename, line))


if __name__ == '__main__':
    main()
