#!/usr/bin/env python
#
# casort - show sorted info from /proc/cache_account
#
# Written: by Tim Bird <tim.bird@am.sony.com>
# Copyright: 2007 Sony Corporation
# License: GPL v2

import sys, re

if "-h" in sys.argv or "--help" in sys.argv:
	print """Usage: sasort [options]
-h,--help  Show this usage help
-f <file>  Read slab account data from <file>, instead of /proc/cache_account
-s <sort_field>  Sort by the indicated field.  Fields are:
     'n' = net, 's'=slack, 't'=total, 'a'='allocs', 'h'=highwater, 'c'=caller

total = total cumulative amount of memory that a caller has allocated

slack = cumulative overhead for a caller's allocations.  This is the
cumulative difference between what the size the caller requested and
what the size of memory they received.

net = amount of memory currently allocated and actually in-use.
This does not include overhead for cache and slab structures.

allocs = number of allocations by a caller
frees = number of frees by a caller

highwater = maximum net memory that a caller had in use since the
kernel booted."""
	sys.exit(0)

cacheaccount_file = "/proc/cache_account"
if "-f" in sys.argv:
	cacheaccount_file = sys.argv[sys.argv.index('-f')+1]

sort_field = 'c'
if "-s" in sys.argv:
	sort_field = sys.argv[sys.argv.index('-s')+1]

class stub_class:
	pass

try:
	lines = open(cacheaccount_file).readlines()
except:
	print "Error: Can't open file '%s'." % cacheaccount_file
	if cacheaccount_file.startswith("/proc/"):
		print """Missing file in /proc.  Make sure your kernel is configured
with support for CONFIG_DEBUG_SLAB_ACCOUNT."""
	sys.exit(1)

allocators = {}
active_memory = 0
total_memory = 0

# collect data from the file
in_data = 0
for line in lines[2:]:
	items = line.split()
	if not in_data:
		if items and items[0]=="total":
			in_data = 1
		else:
			print line,
		continue

	allocator = stub_class()
	allocator.total = int(items[0])
	allocator.slack = int(items[1])
	allocator.net = int(items[2])
	allocator.allocs = int(items[3].split('/')[0])
	allocator.frees = int(items[3].split('/')[1])
	allocator.highwater = int(items[4])
	caller = items[5]
	allocator.caller = caller
	allocators[caller] = allocator

def sort_value(allocator):
	global sort_field

	if sort_field=='a':
		return allocator.allocs
	if sort_field=='t':
		return allocator.total
	if sort_field=='n':
		return allocator.net
	if sort_field=='h':
		return allocator.highwater
	if sort_field=='c':
		return allocator.caller
	return allocator.caller

def sort_allocator(a, b):
	return cmp(sort_value(a), sort_value(b))

# display the collected data
line_format = "%8s %8s %8s %5s/%-5s %8s %s"
print line_format % ("total", "slack", "net", "alloc", "free", "highwater", "caller")
allocator_list = allocators.values()
allocator_list.sort(sort_allocator)
for a in allocator_list:
	print line_format % (a.total, a.slack, a.net, a.allocs, a.frees,
			a.highwater, a.caller)
