#!/usr/bin/python

# Script to print some interesting stats about cache based on the
# isi.cache.stats sysctls

import errno
import time
import isi.misc as misc
import getopt
import sys

DOMS = [
    'l3_meta',
    'l3_data',
    'l2_meta',
    'l2_data',
    'l1_meta',
    'l1_data',
]

STATS = [
    'read.start',
    'read.hit',
    'read.miss',
    'read.wait',
    'prefetch.start',
    'prefetch.hit',
]

MISSES_L1 = 'prefetch_misses_l1'
MISSES_L2 = 'prefetch_misses_l2'

empty = {}
for dom in DOMS:
    domd = {}
    for stat in STATS:
        domd[stat] = 0
    empty[dom] = domd
    empty[MISSES_L1] = 0
    empty[MISSES_L2] = 0

def gather():
    data = {}
    for dom in DOMS:
        domd = {}
        for stat in STATS:
            ctl = 'isi.cache.stats.' + dom + '.' + stat
            val = misc.sysctlbyname(ctl)
            domd[stat] = val
        data[dom] = domd

    for st in [MISSES_L1, MISSES_L2]:
        ctl = 'isi.cache.stats.' + st
        val = misc.sysctlbyname(ctl)
        data[st] = val

    return data

def short_amt(blocks):
    names = ['B', 'K', 'M', 'G', 'T', 'P', 'E']

    amt = blocks * 8192.0
    unit = 0
    while amt > 999:
        amt /= 1024
        unit += 1

    if amt < 5:
        str = '%1.1f%s' % (amt, names[unit])
    else:
        str = '%d%s' % (round(amt, 0), names[unit])
    #print "%d blocks became %s" % (blocks, str)
    return str

def display_simple(new, old):
    first = True
    out = ''
    for dom in sorted(new):
        if dom == MISSES_L1 or dom == MISSES_L2:
            break

        newd = new[dom]
        oldd = old[dom]

        tot = newd['read.start'] - oldd['read.start']
        rhit = 0
        for rs in ['read.hit']:
            rhit += newd[rs] - oldd[rs]

        if tot == 0:
            rpct = 0
        else:
            rpct = (float(rhit) / float(tot)) * 100.0

        tot = newd['prefetch.start'] - oldd['prefetch.start']
        phit = 0
        for rs in ['prefetch.hit']:
            phit += newd[rs] - oldd[rs]

        if tot == 0:
            ppct = 0
        else:
            ppct = (float(phit) / float(tot)) * 100.0

        out += '%s%s: r %4s %3.0f%% p %4s %3.0f%%' % ('' if first else ', ', \
                                                          dom, \
                                                          short_amt(rhit), rpct,\
                                                          short_amt(phit), ppct)
        first = False

    print out

def display_verbose(new, old):
    pref_l1 = 0
    pref_l2 = 0

    for dom in sorted(new):
        if dom == MISSES_L1 or dom == MISSES_L2:
            break

        newd = new[dom]
        oldd = old[dom]

        print '%s:' % (dom)

        print '\tread (8K blocks):'
        tot = newd['read.start'] - oldd['read.start']
        for rs in ['read.start', 'read.hit', 'read.miss', 'read.wait', 'prefetch.hit']:
            st = newd[rs] - oldd[rs]
            if tot == 0:
                pct = 0
            else:
                pct = float(st) / float(tot)

            print '\t\t%s:\t%16d / %5.1f%%' % (rs, st, pct * 100.0)
        print

        print '\tprefetch (8K blocks):'
        tot = newd['prefetch.start'] - oldd['prefetch.start']

        if dom[:2] == 'l1':
            pref_l1 += tot
        else:
            pref_l2 += tot

        for rs in ['prefetch.start', 'prefetch.hit']:
            st = newd[rs] - oldd[rs]
            if tot == 0:
                pct = 0
            else:
                pct = float(st) / float(tot)

            print '\t\t%s:\t%16d / %5.1f%%' % (rs, st, pct * 100.0)
        print

    # L1
    st = new[MISSES_L1] - old[MISSES_L1]
    if pref_l1 == 0:
        pct = 0
    else:
        pct = float(st) / float(pref_l1)
    print 'l1_all:'
    print '\tprefetch.start:\t\t%16d / %5.1f%%' % (pref_l1, 100.0)
    print '\tprefetch.misses:\t%16d / %5.1f%%' % (st, pct * 100.0)

    # L1
    st = new[MISSES_L2] - old[MISSES_L2]
    if pref_l2 == 0:
        pct = 0
    else:
        pct = float(st) / float(pref_l2)
    print 'l2_all:'
    print '\tprefetch.start:\t\t%16d / %5.1f%%' % (pref_l2, 100.0)
    print '\tprefetch.misses:\t%16d / %5.1f%%' % (st, pct * 100.0)


def help():
    print "Usage: isi_cache_stats [-h|--help] [-v|--verbose] [-z] [interval]"

def main():
    delay = 0
    verbose = False
    clear = False

    try:
        opts, args = getopt.gnu_getopt(sys.argv[1:], "hvz", ["help", "verbose"])
    except getopt.GetoptError, err:
        print str(err)
        help()
        sys.exit(2)

    for o, a in opts:
        if o in ("-h", "--help"):
            help();
            sys.exit(0)
        if o in ("-v", "--verbose"):
            verbose = True
        if o in ("-z"):
            clear = True

    for a in args:
        try:
            delay = int(a)
        except:
            print "Didn't understand " + a
            help();
            sys.exit(2)

    new = gather()
    if verbose:
        print "------------------------- Totals -------------------------"
        display_verbose(new, empty)
    else:
        print "%-17s " % ('Totals'),
        display_simple(new, empty)

    if clear:
        misc.sysctlbyname('isi.cache.stats.clear')
	
    if delay == 0:
        sys.exit(0)

    while True:
        old = new
        time.sleep(delay)
        new = gather()

        if verbose:
            print '------------------------- %s -------------------------' % \
                time.strftime('%c', time.localtime(time.time()))

            display_verbose(new, old)
        else:
            print "%-17s " % (time.strftime('%y/%m/%d %T', time.localtime(time.time()))),
            display_simple(new, old)

if __name__ == "__main__":
    main()
