]> granicus.if.org Git - spl/commitdiff
Add a script to display SPL slab cache statistics
authorBoris Protopopov <boris.protopopov@actifio.com>
Tue, 30 Jun 2015 21:47:15 +0000 (17:47 -0400)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Wed, 2 Dec 2015 22:08:08 +0000 (14:08 -0800)
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

cmd/Makefile.am
cmd/splat/Makefile.am [new file with mode: 0644]
cmd/splat/splat.c [moved from cmd/splat.c with 100% similarity]
cmd/splat/splat.h [moved from cmd/splat.h with 100% similarity]
cmd/splslab/Makefile.am [new file with mode: 0644]
cmd/splslab/splslab.py [new file with mode: 0755]
configure.ac
rpm/generic/spl.spec.in

index 01afdcf2566d504ac3e9bcc169daed0792fdcd70..63a3c76f9754dc293006e9d37c8687c07801b7eb 100644 (file)
@@ -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 (file)
index 0000000..01afdcf
--- /dev/null
@@ -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
similarity index 100%
rename from cmd/splat.c
rename to cmd/splat/splat.c
similarity index 100%
rename from cmd/splat.h
rename to cmd/splat/splat.h
diff --git a/cmd/splslab/Makefile.am b/cmd/splslab/Makefile.am
new file mode 100644 (file)
index 0000000..b18d52d
--- /dev/null
@@ -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 (executable)
index 0000000..160fb27
--- /dev/null
@@ -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()
index efeb243cba692f6b5fc751de65259378fb81b512..70735ce2cf42deee7fa40c4fb3828c70df18fa9d 100644 (file)
@@ -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
index 48eafe67086ff83d14309e306affb3ae1e6b13c4..43bbd98f4903201e0346ba6ccb9c6f698c58d929 100644 (file)
@@ -33,6 +33,7 @@ make install DESTDIR=%{?buildroot}
 
 %files
 %doc AUTHORS COPYING DISCLAIMER
+%{_bindir}/*
 %{_sbindir}/*
 %{_mandir}/man1/*
 %{_mandir}/man5/*