#!/usr/bin/env python3
#coding: utf-8

import argparse
import os
import subprocess

class color:
       purple = '\033[95m'
       white  = '\033[37m'
       cyan = '\033[96m'
       darkcyan = '\033[36m'
       blue = '\033[94m'
       green = '\033[92m'
       yellow = '\033[93m'
       red = '\033[91m'
       bold = '\033[1m'
       underline = '\033[4m'
       end = '\033[0m'

class nocolor:
       purple = ''
       white = ''
       cyan = ''
       darkcyan = ''
       blue = ''
       green = ''
       yellow = ''
       red = ''
       bold = ''
       underline = ''
       end = ''


def parse_clargs():
    P = argparse.ArgumentParser(description='pretty print stack traces')
    P.add_argument('input', metavar='FILE',
                   help='name of file with stack trace')
    P.add_argument('-b', '--brief', action='store_true',
                   help='print only the file locations')
    P.add_argument('-e', '--executable', metavar='FILE',
                   help='name of the executable or object file to look up symbols')
    P.add_argument('-c', '--color', action='store_true',
                   help='use color output in terminal')

    return P.parse_args()

def parse_backtrace(source):
    trace = []
    if os.path.isfile(source):
        f = open(source, 'r')
        lines = f.readlines()
        for line in lines:
            tokens = line.split()
            trace.append({'location':tokens[0], 'function':tokens[1]})
    else:
        print("error: unable to open back trace file ", source)

    return trace

def get_function_name(location, executable):
    result = os.popen('addr2line ' + location + ' -e ' + executable).read()
    descriptor = result.split()[0].split(':')
    return {'filename': descriptor[0], 'line': descriptor[1]}

def unmangle(mangled):
    unmangled = os.popen('c++filt ' + mangled).read().strip()
    return unmangled.replace('arb::', '')

#
# main
#
args = parse_clargs()

# check that a valid executable was provided
executable = args.executable
if not os.path.isfile(executable):
    print("error:", executable, "is not a valid executable")
else:
    for frame in parse_backtrace(args.input):
        location = get_function_name(frame['location'], executable)
        name = unmangle(frame['function'])
        c = color if args.color else nocolor
        fname = c.yellow + location['filename'] + c.end
        line = c.cyan + location['line'] + c.end
        if args.brief:
            print(fname + ':' + line)
        else:
            print(fname + ':' + line, '\n ', name)
