#!/usr/bin/env python

# by Mark Williamson, (C) 2004 Intel Research Cambridge

# Program for reformatting trace buffer output according to user-supplied rules

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

def usage():
    print >> sys.stderr, \
          "Usage: " + sys.argv[0] + """ defs-file
          Parses trace data in binary format, as output by Xentrace and
          reformats it according to the rules in a file of definitions.  The
          rules in this file 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, %(3)d, %(4)d, %(5)d
          [ the 'd' format specifier outputs in decimal, alternatively 'x'
            will output in hexadecimal and 'o' will output in octal ]

          Which correspond to the CPU number, event ID, timestamp counter and
          the 5 data fields from the trace record.  There should be one such
          rule for each type of event.
          
          Depending on your system and the volume of trace buffer data,
          this script may not be able to keep up with the output of xentrace
          if it is piped directly.  In these circumstances you should have
          xentrace output to a file for processing off-line.
          """
    sys.exit(1)

def read_defs(defs_file):
    defs = {}
    
    fd = open(defs_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, "Bad format file" ; sys.exit(1)
        
        defs[str(eval(m.group(1)))] = m.group(2)

    return defs

def sighand(x,y):
    global interrupted
    interrupted = 1

##### Main code

mhz = 0

if len(sys.argv) < 2:
    usage()

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

    for opt in opts:
	if opt[0] == '-c' : mhz = int(opt[1])

except getopt.GetoptError:
    usage()

signal.signal(signal.SIGTERM, sighand)
signal.signal(signal.SIGHUP,  sighand)
signal.signal(signal.SIGINT,  sighand)

interrupted = 0

defs = read_defs(arg[0])

# structure of trace record + prepended CPU id (as output by xentrace):
# CPU(I) TSC(Q) EVENT(L) D1(L) D2(L) D3(L) D4(L) D5(L)
# read CPU id separately to avoid structure packing problems on 64-bit arch.
CPUREC = "I"
TRCREC = "QLLLLLL"

last_tsc = [0]

i=0

while not interrupted:
    try:
	i=i+1
        line = sys.stdin.read(struct.calcsize(CPUREC))
        if not line:
            break
        cpu = struct.unpack(CPUREC, line)[0]

        line = sys.stdin.read(struct.calcsize(TRCREC))
        if not line:
            break

        (tsc, event, d1, d2, d3, d4, d5) = struct.unpack(TRCREC, line)

	#tsc = (tscH<<32) | tscL

	#print i, tsc

        if cpu >= len(last_tsc):
            last_tsc += [0] * (cpu - len(last_tsc) + 1)
	elif tsc < last_tsc[cpu]:
	    print "TSC stepped backward cpu %d !  %d %d" % (cpu,tsc,last_tsc[cpu])

	last_tsc[cpu] = tsc

	if mhz:
	    tsc = tsc / (mhz*1000000.0)

        args = {'cpu'   : cpu,
                'tsc'   : tsc,
                'event' : event,
                '1'     : d1,
                '2'     : d2,
                '3'     : d3,
                '4'     : d4,
                '5'     : d5    }

	try:

	    if defs.has_key(str(event)): 
		print defs[str(event)] % args
	    else: 
		if defs.has_key(str(0)): print defs[str(0)] % args
	except TypeError:
	    print defs[str(event)]
	    print args
	    

    except IOError, struct.error: sys.exit()
