1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <unistd.h>
5 #include <errno.h>
6 #include <ctype.h>
7 #include <limits.h>
8 
9 #define STRINGIFY_ARG(a)        #a
10 #define STRINGIFY(a)            STRINGIFY_ARG(a)
11 
12 #define DEF_SORT_FUNC		sort_nr_objs
13 #define SLABINFO_LINE_LEN	512	/* size of longest line */
14 #define SLABINFO_NAME_LEN	32	/* cache name size (will truncate) */
15 #define SLABINFO_FILE		"/proc/slabinfo"
16 #define DEF_NR_ROWS		15	/* default nr of caches to show */
17 
18 /* object representing a slab cache (each line of slabinfo) */
19 struct slab_info {
20 	char name[SLABINFO_NAME_LEN];	/* name of this cache */
21 	struct slab_info *next;
22 	unsigned long nr_pages;		/* size of cache in pages */
23 	unsigned long nr_objs;		/* number of objects in this cache */
24 	unsigned long nr_active_objs;	/* number of active objects */
25 	unsigned long obj_size;		/* size of each object */
26 	unsigned long objs_per_slab;	/* number of objects per slab */
27 	unsigned long nr_slabs;		/* number of slabs in this cache */
28 	unsigned long use;		/* percent full: total / active */
29 };
30 
31 /* object representing system-wide statistics */
32 struct slab_stat {
33 	unsigned long total_size;	/* size of all objects */
34 	unsigned long active_size;	/* size of all active objects */
35 	unsigned long nr_objs;		/* total number of objects */
36 	unsigned long nr_active_objs;	/* total number of active objects */
37 	unsigned long nr_slabs;		/* total number of slabs */
38 	unsigned long nr_active_slabs;	/* total number of active slabs*/
39 	unsigned long nr_caches;	/* number of caches */
40 	unsigned long nr_active_caches;	/* number of active caches */
41 	unsigned long avg_obj_size;	/* average object size */
42 	unsigned long min_obj_size;	/* size of smallest object */
43 	unsigned long max_obj_size;	/* size of largest object */
44 };
45 
46 typedef int (*sort_t)(const struct slab_info *, const struct slab_info *);
47 static sort_t sort_func;
48 
49 /*
50  * get_slabinfo - open, read, and parse a slabinfo 2.x file, which has the
51  * following format:
52  *
53  * slabinfo - version: 2.1
54  * <name>  <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>
55  * : tunables <limit> <batchcount> <sharedfactor>
56  * : slabdata <active_slabs> <num_slabs> <sharedavail>
57  *
58  * Returns the head of the new list of slab_info structures, or NULL on error.
59  */
get_slabinfo(struct slab_stat * stats)60 static struct slab_info * get_slabinfo(struct slab_stat *stats)
61 {
62 	struct slab_info *head = NULL, *p = NULL, *prev = NULL;
63 	FILE *slabfile;
64 	char line[SLABINFO_LINE_LEN];
65 	unsigned int major, minor;
66 
67 	slabfile = fopen(SLABINFO_FILE, "r");
68 	if (!slabfile) {
69 		perror("fopen");
70 		return NULL;
71 	}
72 
73 	if (!fgets(line, SLABINFO_LINE_LEN, slabfile)) {
74 		fprintf(stderr, "cannot read from " SLABINFO_FILE "\n");
75 		return NULL;
76 	}
77 
78 	if (sscanf(line, "slabinfo - version: %u.%u", &major, &minor) != 2) {
79 		fprintf(stderr, "unable to parse slabinfo version!\n");
80 		return NULL;
81 	}
82 
83 	if (major != 2 || minor > 1) {
84 		fprintf(stderr, "we only support slabinfo 2.0 and 2.1!\n");
85 		return NULL;
86 	}
87 
88 	stats->min_obj_size = INT_MAX;
89 
90 	while (fgets(line, SLABINFO_LINE_LEN, slabfile)) {
91 		unsigned long nr_active_slabs, pages_per_slab;
92 		int ret;
93 
94 		if (line[0] == '#')
95 			continue;
96 
97 		p = malloc(sizeof (struct slab_info));
98 		if (!p) {
99 			perror("malloc");
100 			head = NULL;
101 			break;
102 		}
103 		if (stats->nr_caches++ == 0)
104 			head = prev = p;
105 
106 		ret = sscanf(line, "%" STRINGIFY(SLABINFO_NAME_LEN) "s"
107 			     " %lu %lu %lu %lu %lu : tunables %*d %*d %*d : \
108 			     slabdata %lu %lu %*d", p->name,
109 			     &p->nr_active_objs, &p->nr_objs,
110 			     &p->obj_size, &p->objs_per_slab,
111 			     &pages_per_slab,
112 			     &nr_active_slabs,
113 			     &p->nr_slabs);
114 
115 		if (ret != 8) {
116 			fprintf(stderr, "unrecognizable data in slabinfo!\n");
117 			head = NULL;
118 			break;
119 		}
120 
121 		if (p->obj_size < stats->min_obj_size)
122 			stats->min_obj_size = p->obj_size;
123 		if (p->obj_size > stats->max_obj_size)
124 			stats->max_obj_size = p->obj_size;
125 
126 		p->nr_pages = p->nr_slabs * pages_per_slab;
127 
128 		if (p->nr_objs) {
129 			p->use = 100 * p->nr_active_objs / p->nr_objs;
130 			stats->nr_active_caches++;
131 		} else
132 			p->use = 0;
133 
134 		stats->nr_objs += p->nr_objs;
135 		stats->nr_active_objs += p->nr_active_objs;
136 		stats->total_size += p->nr_objs * p->obj_size;
137 		stats->active_size += p->nr_active_objs * p->obj_size;
138 		stats->nr_slabs += p->nr_slabs;
139 		stats->nr_active_slabs += nr_active_slabs;
140 
141 		prev->next = p;
142 		prev = p;
143 	}
144 
145 	if (fclose(slabfile))
146 		perror("fclose");
147 
148 	if (p)
149 		p->next = NULL;
150 	if (stats->nr_objs)
151 		stats->avg_obj_size = stats->total_size / stats->nr_objs;
152 
153 	return head;
154 }
155 
156 /*
157  * free_slablist - deallocate the memory associated with each node in the
158  * provided slab_info linked list
159  */
free_slablist(struct slab_info * list)160 static void free_slablist(struct slab_info *list)
161 {
162 	while (list) {
163 		struct slab_info *temp = list->next;
164 		free(list);
165 		list = temp;
166 	}
167 }
168 
merge_objs(struct slab_info * a,struct slab_info * b)169 static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b)
170 {
171 	struct slab_info list;
172 	struct slab_info *p = &list;
173 
174 	while (a && b) {
175 		if (sort_func(a, b)) {
176 			p->next = a;
177 			p = a;
178 			a = a->next;
179 		} else {
180 			p->next = b;
181 			p = b;
182 			b = b->next;
183 		}
184 	}
185 
186 	p->next = (a == NULL) ? b : a;
187 	return list.next;
188 }
189 
190 /*
191  * slabsort - merge sort the slab_info linked list based on sort_func
192  */
slabsort(struct slab_info * list)193 static struct slab_info *slabsort(struct slab_info *list)
194 {
195 	struct slab_info *a, *b;
196 
197 	if (!list || !list->next)
198 		return list;
199 
200 	a = list;
201 	b = list->next;
202 
203 	while (b && b->next) {
204 		list = list->next;
205 		b = b->next->next;
206 	}
207 
208 	b = list->next;
209 	list->next = NULL;
210 
211 	return merge_objs(slabsort(a), slabsort(b));
212 }
213 
214 /*
215  * Sort Routines.  Each of these should be associated with a command-line
216  * search option.  The functions should fit the prototype:
217  *
218  *	int sort_foo(const struct slab_info *a, const struct slab_info *b)
219  *
220  * They return zero if the first parameter is smaller than the second.
221  * Otherwise, they return nonzero.
222  */
223 
sort_name(const struct slab_info * a,const struct slab_info * b)224 static int sort_name(const struct slab_info *a, const struct slab_info *b)
225 {
226 	return (strcmp(a->name, b->name) < 0 ) ? 1: 0;
227 }
228 
229 #define BUILD_SORT_FUNC(VAL) \
230 	static int sort_ ## VAL \
231 		(const struct slab_info *a, const struct slab_info *b) { \
232 			return (a-> VAL > b-> VAL); }
233 
234 BUILD_SORT_FUNC(nr_objs)
BUILD_SORT_FUNC(nr_active_objs)235 BUILD_SORT_FUNC(nr_active_objs)
236 BUILD_SORT_FUNC(obj_size)
237 BUILD_SORT_FUNC(objs_per_slab)
238 BUILD_SORT_FUNC(nr_slabs)
239 BUILD_SORT_FUNC(use)
240 BUILD_SORT_FUNC(nr_pages)
241 
242 /*
243  * set_sort_func - return the slab_sort_func that matches the given key.
244  * On unrecognizable key, the call returns NULL.
245  */
246 static sort_t set_sort_func(char key)
247 {
248 	switch (tolower(key)) {
249 	case 'a':
250 		return sort_nr_active_objs;
251 	case 'c':
252 		return sort_nr_pages;
253 	case 'l':
254 		return sort_nr_slabs;
255 	case 'n':
256 		return sort_name;
257 	case 'o':
258 		return sort_nr_objs;
259 	case 'p':
260 		return sort_objs_per_slab;
261 	case 's':
262 		return sort_obj_size;
263 	case 'u':
264 		return sort_use;
265 	default:
266 		return NULL;
267 	}
268 }
269 
main(int argc,char * argv[])270 int main(int argc, char *argv[])
271 {
272 	struct slab_info *list, *p;
273 	struct slab_stat stats = { .nr_objs = 0 };
274 	unsigned int page_size = getpagesize() / 1024, nr_rows = DEF_NR_ROWS, i;
275 
276 	sort_func = DEF_SORT_FUNC;
277 
278 	if (argc > 1) {
279 		/* FIXME: Ugh. */
280 		if (argc == 3 && !strcmp(argv[1], "-n")) {
281 			errno = 0;
282 			nr_rows = (unsigned int) strtoul(argv[2], NULL, 0);
283 			if (errno) {
284 				perror("strtoul");
285 				exit(EXIT_FAILURE);
286 			}
287 		}
288 		else if (argc == 3 && !strcmp(argv[1], "-s"))
289 			sort_func = set_sort_func(argv[2][0]) ? : DEF_SORT_FUNC;
290 		else {
291 			fprintf(stderr, "usage: %s [options]\n\n", argv[0]);
292 			fprintf(stderr, "options:\n");
293 			fprintf(stderr, "  -s S   specify sort criteria S\n");
294 			fprintf(stderr, "  -h     display this help\n\n");
295 			fprintf(stderr, "Valid sort criteria:\n");
296 			fprintf(stderr, "  a: number of Active objects\n");
297 			fprintf(stderr, "  c: Cache size\n");
298 			fprintf(stderr, "  l: number of sLabs\n");
299 			fprintf(stderr, "  n: Name\n");
300 			fprintf(stderr, "  o: number of Objects\n");
301 			fprintf(stderr, "  p: objects Per slab\n");
302 			fprintf(stderr, "  s: object Size\n");
303 			fprintf(stderr, "  u: cache Utilization\n");
304 			exit(EXIT_FAILURE);
305 		}
306 	}
307 
308 	list = get_slabinfo (&stats);
309 	if (!list)
310 		exit(EXIT_FAILURE);
311 
312 	printf(" Active / Total Objects (%% used) : %lu / %lu (%.1f%%)\n"
313 	       " Active / Total Slabs (%% used)   : %lu / %lu (%.1f%%)\n"
314 	       " Active / Total Caches (%% used)  : %lu / %lu (%.1f%%)\n"
315 	       " Active / Total Size (%% used)    : %.2fK / %.2fK (%.1f%%)\n"
316 	       " Min / Avg / Max Object Size     : %.2fK / %.2fK / %.2fK\n\n",
317 	       stats.nr_active_objs,
318 	       stats.nr_objs,
319 	       100.0 * stats.nr_active_objs / stats.nr_objs,
320 	       stats.nr_active_slabs,
321 	       stats.nr_slabs,
322 	       100.0 * stats.nr_active_slabs / stats.nr_slabs,
323 	       stats.nr_active_caches,
324 	       stats.nr_caches,
325 	       100.0 * stats.nr_active_caches / stats.nr_caches,
326 	       stats.active_size / 1024.0,
327 	       stats.total_size / 1024.0,
328 	       100.0 * stats.active_size / stats.total_size,
329 	       stats.min_obj_size / 1024.0,
330 	       stats.avg_obj_size / 1024.0,
331 	       stats.max_obj_size / 1024.0);
332 
333 	printf("%6s %6s %4s %8s %6s %8s %10s %-23s\n",
334 	       "OBJS", "ACTIVE", "USE", "OBJ SIZE", "SLABS",
335 	       "OBJ/SLAB", "CACHE SIZE", "NAME");
336 
337 	p = list = slabsort(list);
338 	for (i = 0; i < nr_rows && p; i++) {
339 		printf("%6lu %6lu %3lu%% %7.2fK %6lu %8lu %9luK %-23s\n",
340 		       p->nr_objs, p->nr_active_objs, p->use,
341 		       p->obj_size / 1024.0, p->nr_slabs,
342 		       p->objs_per_slab,
343 		       p->nr_pages * page_size,
344 		       p->name);
345 		p = p->next;
346 	}
347 
348 	free_slablist(list);
349 
350 	return 0;
351 }
352