]>
Commit | Line | Data |
---|---|---|
5578f58b BP |
1 | #!/usr/bin/python |
2 | ||
3 | import sys | |
4 | import time | |
5 | import getopt | |
6 | import re | |
7 | import signal | |
8 | from collections import defaultdict | |
9 | ||
10 | class Stat: | |
11 | # flag definitions based on the kmem.h | |
12 | NOTOUCH = 1 | |
13 | NODEBUG = 2 | |
14 | KMEM = 32 | |
15 | VMEM = 64 | |
16 | SLAB = 128 | |
17 | OFFSLAB = 256 | |
18 | NOEMERGENCY = 512 | |
19 | DEADLOCKED = 16384 | |
20 | GROWING = 32768 | |
21 | REAPING = 65536 | |
22 | DESTROY = 131072 | |
23 | ||
24 | fdefs = { | |
25 | NOTOUCH : "NTCH", | |
26 | NODEBUG : "NDBG", | |
27 | KMEM : "KMEM", | |
28 | VMEM : "VMEM", | |
29 | SLAB : "SLAB", | |
30 | OFFSLAB : "OFSL", | |
31 | NOEMERGENCY : "NEMG", | |
32 | DEADLOCKED : "DDLK", | |
33 | GROWING : "GROW", | |
34 | REAPING : "REAP", | |
35 | DESTROY : "DSTR" | |
36 | } | |
37 | ||
38 | def __init__(self, name, flags, size, alloc, slabsize, objsize): | |
39 | self._name = name | |
40 | self._flags = self.f2str(flags) | |
41 | self._size = size | |
42 | self._alloc = alloc | |
43 | self._slabsize = slabsize | |
44 | self._objsize = objsize | |
45 | ||
46 | def f2str(self, flags): | |
47 | fstring = '' | |
48 | for k in Stat.fdefs.keys(): | |
49 | if flags & k: | |
50 | fstring = fstring + Stat.fdefs[k] + '|' | |
51 | ||
52 | fstring = fstring[:-1] | |
53 | return fstring | |
54 | ||
55 | class CumulativeStat: | |
56 | def __init__(self, skey="a"): | |
57 | self._size = 0 | |
58 | self._alloc = 0 | |
59 | self._pct = 0 | |
60 | self._skey = skey | |
61 | self._regexp = \ | |
62 | re.compile('(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+'); | |
63 | self._stats = defaultdict(list) | |
64 | ||
65 | # Add another stat to the dictionary and re-calculate the totals | |
66 | def add(self, s): | |
67 | key = 0 | |
68 | if self._skey == "a": | |
69 | key = s._alloc | |
70 | else: | |
71 | key = s._size | |
72 | self._stats[key].append(s) | |
73 | self._size = self._size + s._size | |
74 | self._alloc = self._alloc + s._alloc | |
75 | if self._size: | |
76 | self._pct = self._alloc * 100 / self._size | |
77 | else: | |
78 | self._pct = 0 | |
79 | ||
80 | # Parse the slab info in the procfs | |
81 | # Calculate cumulative stats | |
82 | def slab_update(self): | |
83 | k = [line.strip() for line in open('/proc/spl/kmem/slab')] | |
84 | ||
85 | if not k: | |
86 | sys.stderr.write("No SPL slab stats found\n") | |
87 | sys.exit(1) | |
88 | ||
89 | del k[0:2] | |
90 | ||
91 | for s in k: | |
92 | if not s: | |
93 | continue | |
94 | m = self._regexp.match(s) | |
95 | if m: | |
96 | self.add(Stat(m.group(1), int(m.group(2),16), int(m.group(3)), | |
97 | int(m.group(4)), int(m.group(5)), int(m.group(6)))) | |
98 | else: | |
99 | sys.stderr.write("Error: unexpected input format\n" % s) | |
100 | exit(-1) | |
101 | ||
102 | def show_header(self): | |
103 | sys.stdout.write("\n%25s %20s %15s %15s %15s %15s\n\n" % \ | |
104 | ("cache name", "flags", "size", "alloc", "slabsize", "objsize")) | |
105 | ||
106 | # Show up to the number of 'rows' of output sorted in descending order | |
107 | # by the key specified earlier; if rows == 0, all rows are shown | |
108 | def show(self, rows): | |
109 | self.show_header() | |
110 | i = 1 | |
111 | done = False | |
112 | for k in reversed(sorted(self._stats.keys())): | |
113 | for s in self._stats[k]: | |
114 | sys.stdout.write("%25s %20s %15d %15d %15d %15d\n" % \ | |
115 | (s._name, s._flags, s._size, s._alloc, \ | |
116 | s._slabsize, s._objsize)) | |
117 | i = i + 1 | |
118 | if rows != 0 and i > rows: | |
119 | done = True | |
120 | break | |
121 | if done: | |
122 | break | |
123 | sys.stdout.write("%25s %36d %15d (%d%%)\n\n" % \ | |
124 | ("Totals:", self._size, self._alloc, self._pct)) | |
125 | ||
126 | def usage(): | |
127 | cmd = "Usage: splslab.py [-n|--num-rows] number [-s|--sort-by] " + \ | |
128 | "[interval] [count]"; | |
129 | sys.stderr.write("%s\n" % cmd) | |
130 | sys.stderr.write("\t-h : print help\n") | |
131 | sys.stderr.write("\t-n : --num-rows N : limit output to N top " + | |
132 | "largest slabs (default: all)\n") | |
133 | sys.stderr.write("\t-s : --sort-by key : sort output in descending " + | |
134 | "order by total size (s)\n\t\tor allocated size (a) " + | |
135 | "(default: a)\n") | |
136 | sys.stderr.write("\tinterval : repeat every interval seconds\n") | |
137 | sys.stderr.write("\tcount : output statistics count times and exit\n") | |
138 | ||
139 | ||
140 | def main(): | |
141 | ||
142 | rows = 0 | |
143 | count = 0 | |
144 | skey = "a" | |
145 | interval = 1 | |
146 | ||
147 | signal.signal(signal.SIGINT, signal.SIG_DFL) | |
148 | ||
149 | try: | |
150 | opts, args = getopt.getopt( | |
151 | sys.argv[1:], | |
152 | "n:s:h", | |
153 | [ | |
154 | "num-rows", | |
155 | "sort-by", | |
156 | "help" | |
157 | ] | |
158 | ) | |
159 | except getopt.error as e: | |
160 | sys.stderr.write("Error: %s\n" % e.msg) | |
161 | usage() | |
162 | exit(-1) | |
163 | ||
164 | i = 1 | |
165 | for opt, arg in opts: | |
166 | if opt in ('-n', '--num-rows'): | |
167 | rows = int(arg) | |
168 | i = i + 2 | |
169 | elif opt in ('-s', '--sort-by'): | |
170 | if arg != "s" and arg != "a": | |
171 | sys.stderr.write("Error: invalid sorting key \"%s\"\n" % arg) | |
172 | usage() | |
173 | exit(-1) | |
174 | skey = arg | |
175 | i = i + 2 | |
176 | elif opt in ('-h', '--help'): | |
177 | usage() | |
178 | exit(0) | |
179 | else: | |
180 | break | |
181 | ||
182 | args = sys.argv[i:] | |
183 | ||
184 | interval = int(args[0]) if len(args) else interval | |
185 | count = int(args[1]) if len(args) > 1 else count | |
186 | ||
187 | i = 0 | |
188 | while True: | |
189 | cs = CumulativeStat(skey) | |
190 | cs.slab_update() | |
191 | cs.show(rows) | |
192 | ||
193 | i = i + 1 | |
194 | if count and i >= count: | |
195 | break | |
196 | ||
197 | time.sleep(interval) | |
198 | ||
199 | return 0 | |
200 | ||
201 | if __name__ == '__main__': | |
202 | main() |