Add a script to display SPL slab cache statistics
Useful when looking for the info on ZFS/SPL related memory consumption. Signed-off-by: Boris Protopopov <boris.protopopov@actifio.com> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #460
This commit is contained in:
parent
e5f9a9afd2
commit
5578f58bdc
|
@ -1,11 +1 @@
|
||||||
include $(top_srcdir)/config/Rules.am
|
SUBDIRS = splat splslab
|
||||||
|
|
||||||
DEFAULT_INCLUDES += \
|
|
||||||
-I$(top_srcdir)/lib
|
|
||||||
|
|
||||||
sbin_PROGRAMS = splat
|
|
||||||
|
|
||||||
splat_SOURCES = splat.c
|
|
||||||
splat_LDFLAGS = $(top_builddir)/lib/libcommon.la
|
|
||||||
|
|
||||||
EXTRA_DIST = splat.h
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
include $(top_srcdir)/config/Rules.am
|
||||||
|
|
||||||
|
DEFAULT_INCLUDES += \
|
||||||
|
-I$(top_srcdir)/lib
|
||||||
|
|
||||||
|
sbin_PROGRAMS = splat
|
||||||
|
|
||||||
|
splat_SOURCES = splat.c
|
||||||
|
splat_LDFLAGS = $(top_builddir)/lib/libcommon.la
|
||||||
|
|
||||||
|
EXTRA_DIST = splat.h
|
|
@ -0,0 +1,2 @@
|
||||||
|
bin_SCRIPTS = splslab.py
|
||||||
|
EXTRA_DIST = $(bin_SCRIPTS)
|
|
@ -0,0 +1,202 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import getopt
|
||||||
|
import re
|
||||||
|
import signal
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
class Stat:
|
||||||
|
# flag definitions based on the kmem.h
|
||||||
|
NOTOUCH = 1
|
||||||
|
NODEBUG = 2
|
||||||
|
KMEM = 32
|
||||||
|
VMEM = 64
|
||||||
|
SLAB = 128
|
||||||
|
OFFSLAB = 256
|
||||||
|
NOEMERGENCY = 512
|
||||||
|
DEADLOCKED = 16384
|
||||||
|
GROWING = 32768
|
||||||
|
REAPING = 65536
|
||||||
|
DESTROY = 131072
|
||||||
|
|
||||||
|
fdefs = {
|
||||||
|
NOTOUCH : "NTCH",
|
||||||
|
NODEBUG : "NDBG",
|
||||||
|
KMEM : "KMEM",
|
||||||
|
VMEM : "VMEM",
|
||||||
|
SLAB : "SLAB",
|
||||||
|
OFFSLAB : "OFSL",
|
||||||
|
NOEMERGENCY : "NEMG",
|
||||||
|
DEADLOCKED : "DDLK",
|
||||||
|
GROWING : "GROW",
|
||||||
|
REAPING : "REAP",
|
||||||
|
DESTROY : "DSTR"
|
||||||
|
}
|
||||||
|
|
||||||
|
def __init__(self, name, flags, size, alloc, slabsize, objsize):
|
||||||
|
self._name = name
|
||||||
|
self._flags = self.f2str(flags)
|
||||||
|
self._size = size
|
||||||
|
self._alloc = alloc
|
||||||
|
self._slabsize = slabsize
|
||||||
|
self._objsize = objsize
|
||||||
|
|
||||||
|
def f2str(self, flags):
|
||||||
|
fstring = ''
|
||||||
|
for k in Stat.fdefs.keys():
|
||||||
|
if flags & k:
|
||||||
|
fstring = fstring + Stat.fdefs[k] + '|'
|
||||||
|
|
||||||
|
fstring = fstring[:-1]
|
||||||
|
return fstring
|
||||||
|
|
||||||
|
class CumulativeStat:
|
||||||
|
def __init__(self, skey="a"):
|
||||||
|
self._size = 0
|
||||||
|
self._alloc = 0
|
||||||
|
self._pct = 0
|
||||||
|
self._skey = skey
|
||||||
|
self._regexp = \
|
||||||
|
re.compile('(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+');
|
||||||
|
self._stats = defaultdict(list)
|
||||||
|
|
||||||
|
# Add another stat to the dictionary and re-calculate the totals
|
||||||
|
def add(self, s):
|
||||||
|
key = 0
|
||||||
|
if self._skey == "a":
|
||||||
|
key = s._alloc
|
||||||
|
else:
|
||||||
|
key = s._size
|
||||||
|
self._stats[key].append(s)
|
||||||
|
self._size = self._size + s._size
|
||||||
|
self._alloc = self._alloc + s._alloc
|
||||||
|
if self._size:
|
||||||
|
self._pct = self._alloc * 100 / self._size
|
||||||
|
else:
|
||||||
|
self._pct = 0
|
||||||
|
|
||||||
|
# Parse the slab info in the procfs
|
||||||
|
# Calculate cumulative stats
|
||||||
|
def slab_update(self):
|
||||||
|
k = [line.strip() for line in open('/proc/spl/kmem/slab')]
|
||||||
|
|
||||||
|
if not k:
|
||||||
|
sys.stderr.write("No SPL slab stats found\n")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
del k[0:2]
|
||||||
|
|
||||||
|
for s in k:
|
||||||
|
if not s:
|
||||||
|
continue
|
||||||
|
m = self._regexp.match(s)
|
||||||
|
if m:
|
||||||
|
self.add(Stat(m.group(1), int(m.group(2),16), int(m.group(3)),
|
||||||
|
int(m.group(4)), int(m.group(5)), int(m.group(6))))
|
||||||
|
else:
|
||||||
|
sys.stderr.write("Error: unexpected input format\n" % s)
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
def show_header(self):
|
||||||
|
sys.stdout.write("\n%25s %20s %15s %15s %15s %15s\n\n" % \
|
||||||
|
("cache name", "flags", "size", "alloc", "slabsize", "objsize"))
|
||||||
|
|
||||||
|
# Show up to the number of 'rows' of output sorted in descending order
|
||||||
|
# by the key specified earlier; if rows == 0, all rows are shown
|
||||||
|
def show(self, rows):
|
||||||
|
self.show_header()
|
||||||
|
i = 1
|
||||||
|
done = False
|
||||||
|
for k in reversed(sorted(self._stats.keys())):
|
||||||
|
for s in self._stats[k]:
|
||||||
|
sys.stdout.write("%25s %20s %15d %15d %15d %15d\n" % \
|
||||||
|
(s._name, s._flags, s._size, s._alloc, \
|
||||||
|
s._slabsize, s._objsize))
|
||||||
|
i = i + 1
|
||||||
|
if rows != 0 and i > rows:
|
||||||
|
done = True
|
||||||
|
break
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
sys.stdout.write("%25s %36d %15d (%d%%)\n\n" % \
|
||||||
|
("Totals:", self._size, self._alloc, self._pct))
|
||||||
|
|
||||||
|
def usage():
|
||||||
|
cmd = "Usage: splslab.py [-n|--num-rows] number [-s|--sort-by] " + \
|
||||||
|
"[interval] [count]";
|
||||||
|
sys.stderr.write("%s\n" % cmd)
|
||||||
|
sys.stderr.write("\t-h : print help\n")
|
||||||
|
sys.stderr.write("\t-n : --num-rows N : limit output to N top " +
|
||||||
|
"largest slabs (default: all)\n")
|
||||||
|
sys.stderr.write("\t-s : --sort-by key : sort output in descending " +
|
||||||
|
"order by total size (s)\n\t\tor allocated size (a) " +
|
||||||
|
"(default: a)\n")
|
||||||
|
sys.stderr.write("\tinterval : repeat every interval seconds\n")
|
||||||
|
sys.stderr.write("\tcount : output statistics count times and exit\n")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
rows = 0
|
||||||
|
count = 0
|
||||||
|
skey = "a"
|
||||||
|
interval = 1
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(
|
||||||
|
sys.argv[1:],
|
||||||
|
"n:s:h",
|
||||||
|
[
|
||||||
|
"num-rows",
|
||||||
|
"sort-by",
|
||||||
|
"help"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
except getopt.error as e:
|
||||||
|
sys.stderr.write("Error: %s\n" % e.msg)
|
||||||
|
usage()
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
|
i = 1
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt in ('-n', '--num-rows'):
|
||||||
|
rows = int(arg)
|
||||||
|
i = i + 2
|
||||||
|
elif opt in ('-s', '--sort-by'):
|
||||||
|
if arg != "s" and arg != "a":
|
||||||
|
sys.stderr.write("Error: invalid sorting key \"%s\"\n" % arg)
|
||||||
|
usage()
|
||||||
|
exit(-1)
|
||||||
|
skey = arg
|
||||||
|
i = i + 2
|
||||||
|
elif opt in ('-h', '--help'):
|
||||||
|
usage()
|
||||||
|
exit(0)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
args = sys.argv[i:]
|
||||||
|
|
||||||
|
interval = int(args[0]) if len(args) else interval
|
||||||
|
count = int(args[1]) if len(args) > 1 else count
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
cs = CumulativeStat(skey)
|
||||||
|
cs.slab_update()
|
||||||
|
cs.show(rows)
|
||||||
|
|
||||||
|
i = i + 1
|
||||||
|
if count and i >= count:
|
||||||
|
break
|
||||||
|
|
||||||
|
time.sleep(interval)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
|
@ -54,6 +54,8 @@ AC_CONFIG_FILES([
|
||||||
man/man5/Makefile
|
man/man5/Makefile
|
||||||
lib/Makefile
|
lib/Makefile
|
||||||
cmd/Makefile
|
cmd/Makefile
|
||||||
|
cmd/splat/Makefile
|
||||||
|
cmd/splslab/Makefile
|
||||||
module/Makefile
|
module/Makefile
|
||||||
module/spl/Makefile
|
module/spl/Makefile
|
||||||
module/splat/Makefile
|
module/splat/Makefile
|
||||||
|
|
|
@ -33,6 +33,7 @@ make install DESTDIR=%{?buildroot}
|
||||||
|
|
||||||
%files
|
%files
|
||||||
%doc AUTHORS COPYING DISCLAIMER
|
%doc AUTHORS COPYING DISCLAIMER
|
||||||
|
%{_bindir}/*
|
||||||
%{_sbindir}/*
|
%{_sbindir}/*
|
||||||
%{_mandir}/man1/*
|
%{_mandir}/man1/*
|
||||||
%{_mandir}/man5/*
|
%{_mandir}/man5/*
|
||||||
|
|
Loading…
Reference in New Issue