#!/usr/bin/env python

import sys
import re
from subprocess import Popen, PIPE

def get_stacks(binary, core):
    # we can use --interpreter mi2, but that's too much work for now
    gdbcmd = ["gdb", binary, "--core", core, "--nx", "--batch", "-ex", "thread apply all bt"]
    gdbpipe = Popen(gdbcmd, stdout=PIPE, stderr=PIPE)
    gdbout = gdbpipe.communicate()[0].splitlines() #does not handle errors at all
    #print gdbpipe.returncode

    stacks = []
    current = []

    for line in gdbout:
        if re.match("^Thread \d+.*:$", line):
            stacks.append(current)
            current = []
            continue

        m = re.match("^#\d+\s+(0x[0-9a-zA-Z]+)\s+in\s+(\S+)\s+.*$", line)
        if m:
            addr = int(m.group(1), 16)
            sym = m.group(2)
            current.append((addr,sym))
            continue

    stacks.append(current)
    stacks.pop(0) #remove the garbage from the beginning

    return stacks

def get_buildids(core):
    cmd = ["eu-unstrip", "--list-only", "--core", core]
    pipe = Popen(cmd, stdout=PIPE, stderr=PIPE)
    output = pipe.communicate()[0].splitlines()

    res = []

    for line in output:
        m = re.match(
                "^(0x[0-9a-zA-Z]+)\+(0x[0-9a-zA-Z]+)"+  #START+SIZE
                "\s+"+
                "([0-9a-zA-Z]+)(@0x[0-9a-zA-Z]+)?"+     #ID@ADDR
                "\s+"+
                "(\S+)\s+(\S+)\s+(\S+)$",   #FILE DEBUGFILE MODULE
                line
        )
        if m:
            start = int(m.group(1), 16)
            size = int(m.group(2), 16)
            buildid = m.group(3)
            modname = m.group(7)
            res.append((start, size, buildid, modname))

    return res

def canonize_stack(stack, ids):
    res = []

    for (addr,sym) in stack:
        if sym == "??":
            sym = "UNKNOWN"

        for (start, size, bid, mod) in ids:
            if start <= addr <= (start + size):
                res.append((bid, addr - start, sym, mod))
                break
        else:
            res.append(("UNKNOWN", addr, sym, "UNKNOWN"))

    return res


### "main" ###

if len(sys.argv) != 3:
    print "Usage: %s <executable> <core>" % sys.argv[0]
    sys.exit(1)

binary = sys.argv[1]
core = sys.argv[2]

stacks = get_stacks(binary, core)
bids = get_buildids(core)

for stack in stacks:
    cand = canonize_stack(stack, bids)
    for (bid, off, sym, mod) in cand:
        print "%s+0x%08x\t%s @ %s" % (bid, off, sym, mod)

    print ""

