summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoris Protopopov <[email protected]>2015-06-30 17:47:15 -0400
committerBrian Behlendorf <[email protected]>2015-12-02 14:08:08 -0800
commit5578f58bdc65d8a2d49b34be21f979bd1fcb0093 (patch)
treea83c115113db44470607ea88508d7e3397966f78
parente5f9a9afd27ce78ee9c08c24f63ad48dfc9ea8de (diff)
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 <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #460
-rw-r--r--cmd/Makefile.am12
-rw-r--r--cmd/splat/Makefile.am11
-rw-r--r--cmd/splat/splat.c (renamed from cmd/splat.c)0
-rw-r--r--cmd/splat/splat.h (renamed from cmd/splat.h)0
-rw-r--r--cmd/splslab/Makefile.am2
-rwxr-xr-xcmd/splslab/splslab.py202
-rw-r--r--configure.ac2
-rw-r--r--rpm/generic/spl.spec.in1
8 files changed, 219 insertions, 11 deletions
diff --git a/cmd/Makefile.am b/cmd/Makefile.am
index 01afdcf25..63a3c76f9 100644
--- a/cmd/Makefile.am
+++ b/cmd/Makefile.am
@@ -1,11 +1 @@
-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
+SUBDIRS = splat splslab
diff --git a/cmd/splat/Makefile.am b/cmd/splat/Makefile.am
new file mode 100644
index 000000000..01afdcf25
--- /dev/null
+++ b/cmd/splat/Makefile.am
@@ -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
diff --git a/cmd/splat.c b/cmd/splat/splat.c
index 92962393d..92962393d 100644
--- a/cmd/splat.c
+++ b/cmd/splat/splat.c
diff --git a/cmd/splat.h b/cmd/splat/splat.h
index 5b838af3a..5b838af3a 100644
--- a/cmd/splat.h
+++ b/cmd/splat/splat.h
diff --git a/cmd/splslab/Makefile.am b/cmd/splslab/Makefile.am
new file mode 100644
index 000000000..b18d52d7e
--- /dev/null
+++ b/cmd/splslab/Makefile.am
@@ -0,0 +1,2 @@
+bin_SCRIPTS = splslab.py
+EXTRA_DIST = $(bin_SCRIPTS)
diff --git a/cmd/splslab/splslab.py b/cmd/splslab/splslab.py
new file mode 100755
index 000000000..160fb2776
--- /dev/null
+++ b/cmd/splslab/splslab.py
@@ -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()
diff --git a/configure.ac b/configure.ac
index efeb243cb..70735ce2c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -54,6 +54,8 @@ AC_CONFIG_FILES([
man/man5/Makefile
lib/Makefile
cmd/Makefile
+ cmd/splat/Makefile
+ cmd/splslab/Makefile
module/Makefile
module/spl/Makefile
module/splat/Makefile
diff --git a/rpm/generic/spl.spec.in b/rpm/generic/spl.spec.in
index 48eafe670..43bbd98f4 100644
--- a/rpm/generic/spl.spec.in
+++ b/rpm/generic/spl.spec.in
@@ -33,6 +33,7 @@ make install DESTDIR=%{?buildroot}
%files
%doc AUTHORS COPYING DISCLAIMER
+%{_bindir}/*
%{_sbindir}/*
%{_mandir}/man1/*
%{_mandir}/man5/*