#!/usr/bin/python3
# -*- coding: UTF-8 -*-

import os
import re
import sys
import string
import signal
import struct
import getopt

def usage():
    print >> sys.stderr, \
          """
    Usage:
          acrntrace_format.py [options] [formats] [trace_data]

          [options]
          -h: print this message

          Parses trace_data in binary format generated by acrntrace and
          reformats it according to the rules in the [formats] file.
          The rules in formats should have the format ({ and } show grouping
          and are not part of the syntax):

          {event_id}{whitespace}{text format string}

          The textual format string may include format specifiers, such as
          %(cpu)d, %(tsc)d, %(event)d, %(1)d, %(2)d, ....
          The 'd' format specifier outputs in decimal, alternatively 'x' will
          output in hexadecimal and 'o' will output in octal.

          These respectively correspond to the CPU number (cpu), timestamp
          counter (tsc), event ID (event) and the data logged in the trace file.
          There can be only one such rule for each type of event.
          """

def read_format(format_file):
    formats = {}

    fd = open(format_file)

    reg = re.compile('(\S+)\s+(\S.*)')

    while True:
        line = fd.readline()
        if not line:
            break

        if line[0] == '#' or line[0] == '\n':
            continue

        m = reg.match(line)

        if not m: print >> sys.stderr, "Wrong format file"; sys.exit(1)

        formats[str(eval(m.group(1)))] = m.group(2)

    return formats

exit = 0

# structure of trace data (as output by acrntrace)
# TSC(Q) HDR(Q) D1 D2 ...
# HDR consists of event:48:, n_data:8:, cpu:8:
# event means Event ID
# n_data means number of data in trace entry (like D1, D2, ...)
# cpu means cpu id this trace entry belong to
TSCREC = "Q"
HDRREC = "Q"
D2REC  = "QQ"
D4REC = "IIII"
D8REC = "BBBBBBBBBBBBBBBB"
D16REC = "bbbbbbbbbbbbbbbb"

def main_loop(formats, fd):
    global exit
    i = 0


    while not exit:
        try:
            i = i + 1

            line = fd.read(struct.calcsize(TSCREC))
            if not line:
                break
            tsc = struct.unpack(TSCREC, line)[0]

            line = fd.read(struct.calcsize(HDRREC))
            if not line:
                break
            event = struct.unpack(HDRREC, line)[0]
            n_data = event >> 48 & 0xff
            cpu = event >> 56
            event = event & 0xffffffffffff

            d1 = 0
            d2 = 0
            d3 = 0
            d4 = 0
            d5 = 0
            d6 = 0
            d7 = 0
            d8 = 0
            d9 = 0
            d10 = 0
            d11 = 0
            d12 = 0
            d13 = 0
            d14 = 0
            d15 = 0
            d16 = 0

            if n_data == 2:
                line = fd.read(struct.calcsize(D2REC))
                if not line:
                    break
                (d1, d2) = struct.unpack(D2REC, line)

            if n_data == 4:
                line = fd.read(struct.calcsize(D4REC))
                if not line:
                    break
                (d1, d2, d3, d4) = struct.unpack(D4REC, line)

            if n_data == 8:
                line = fd.read(struct.calcsize(D8REC))
                if not line:
                    break
                # TRACE_6C using the first 6 data of fields_8. Actaully we have
                # 16 data in every trace entry.
                (d1, d2, d3, d4, d5, d6, d7, d8,
                 d9, d10, d11, d12, d13, d14, d15, d16) = struct.unpack(D8REC, line)

            if n_data == 16:
                line = fd.read(struct.calcsize(D16REC))
                if not line:
                    break

                (d1, d2, d3, d4, d5, d6, d7, d8,
                 d9, d10, d11, d12, d13, d14, d15, d16) = struct.unpack(D16REC, line)

            args = {'cpu'   : cpu,
                    'tsc'   : tsc,
                    'event' : event,
                    '1'     : d1,
                    '2'     : d2,
                    '3'     : d3,
                    '4'     : d4,
                    '5'     : d5,
                    '6'     : d6,
                    '7'     : d7,
                    '8'     : d8,
                    '9'     : d9,
                    '10'    : d10,
                    '11'    : d11,
                    '12'    : d12,
                    '13'    : d13,
                    '14'    : d14,
                    '15'    : d15,
                    '16'    : d16      }

            try:
                if str(event) in formats.keys():
                    print (formats[str(event)] % args)
            except TypeError:
                if str(event) in formats.key():
                    print (formats[str(event)])
                    print (args)

        except struct.error:
            sys.exit()

def main(argv):
    try:
        opts, arg = getopt.getopt(sys.argv[1:], "h")

        for opt in opts:
            if opt[0] == '-h':
                usage()
                sys.exit()

    except getopt.GetoptError:
        usage()
        sys.exit(1)

    try:
        formats = read_format(arg[0])
        fd = open(arg[1], 'rb')
    except IOError:
        sys.exit(1)

    main_loop(formats, fd)

if __name__ == "__main__":
    main(sys.argv[1:])
