#!/usr/bin/env python
"""
Copyright (c) 2015-2019 Cray Inc. All Rights Reserved.
Utility to print unique stack traces
"""

import re
import sys
import StringIO
import argparse
from pykdump.API import exec_crash_command

description_short = 'Print stack traces for each task.'

# outer loop indentifies PIDs
# inner loop looks for # until
# another PID is found
def sortInput(swapper, input):


    ps = re.compile("^PID:\s+(\d+)\s+TASK:\s+([0-9A-Fa-f]+).*")
    n = re.compile("^#")
    swap = re.compile((".*\"swapper/[0-9]+\""))
    info = dict()
    PID = ""
    STK = ""
    tmp = ""

    # Outer to check for PIDs
    # this loop never breaks;
    for line in input:
        line = line.strip()

        # Inner loop to check for # signs indicating lines we want.
        # Having two loops allow for the PID and TSK to be associated
        # with  a particular trace.
        # This loop breaks if a new PID is found (meaning the end of the
        # current trace) or if there are no more lines available
        while True:
            if ps.match(line): break;
            line = line.strip()
            if n.match(line):
                line = line.split()
                tmp += " ".join([line[2], line[3], line[4]])
                if len(line) == 6 : tmp += " " + line[5]
                tmp += '\n\t'
            line = input.readline()
            if not line: break

        if tmp :
            if tmp in info:
                info[tmp].append((PID,STK))
            else:
                info[tmp] = [(PID,STK)]

        m = ps.match(line)
        if m:
            PID, STK = m.group(1), m.group(2)
            tmp = ""

            # if it's swapper line move on
            # this prevents entry into inner loop
            if not swapper and swap.match(line):
                line = input.readline()

    sort = sorted(info.items(), key=lambda info: len(info[1]))
    return sort

def printRes(sort, printpid, printptr):
    """
    Prints out individual stack traces from lowest to highest.
    """
    for stack_trace, ptask_list in sort:
        if printpid and not printptr:
            print("PID: %s" % (', '.join(p[0] for p in ptask_list)))
        elif printpid and printptr:
            print("PID, TSK: %s" % (', '.join(p[0] + ': ' + p[1] for p in ptask_list)))
        elif not printpid and printptr:
            print("TSK: %s" % (', '.join(p[1] for p in ptask_list)))
        print("TASKS: %d" %(len(ptask_list)))
        print("\t%s" %(stack_trace))


def main():
    parser = argparse.ArgumentParser()

    parser.add_argument("-p", "--print-pid",
                        action="store_true", dest="printpid", default=False,
                        help="Print PIDS corresponding to each ST")
    parser.add_argument("-q", "--print-taskpntr",
                        action="store_true", dest="printptr", default=False,
                        help="Print the task pointers for each ST")
    parser.add_argument("-s", "--swapper",
                        action="store_true", dest="swapper", default=False,
                        help="Print swapper processes")
    parser.add_argument("task_select", metavar="task_selection", nargs="*",
                        help="task selection argument (passed to foreach cmd)")

    args = parser.parse_args()

    com = "foreach {ts:s} bt".format(ts=" ".join(args.task_select))

    result = exec_crash_command(com)
    input = StringIO.StringIO(result)
    printRes(sortInput(args.swapper, input), args.printpid, args.printptr)

if __name__ == '__main__':
    main()
