Skip to content

Commit

Permalink
Add a script to display SPL slab cache statistics
Browse files Browse the repository at this point in the history
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 openzfs#460
  • Loading branch information
bprotopopov authored and behlendorf committed Dec 2, 2015
1 parent e5f9a9a commit 5578f58
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 11 deletions.
12 changes: 1 addition & 11 deletions cmd/Makefile.am
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions cmd/splat/Makefile.am
Original file line number Diff line number Diff line change
@@ -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
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions cmd/splslab/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bin_SCRIPTS = splslab.py
EXTRA_DIST = $(bin_SCRIPTS)
202 changes: 202 additions & 0 deletions cmd/splslab/splslab.py
Original file line number Diff line number Diff line change
@@ -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()
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions rpm/generic/spl.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ make install DESTDIR=%{?buildroot}

%files
%doc AUTHORS COPYING DISCLAIMER
%{_bindir}/*
%{_sbindir}/*
%{_mandir}/man1/*
%{_mandir}/man5/*
Expand Down

0 comments on commit 5578f58

Please sign in to comment.