Bump to procps-ng 3.3.16
[platform/upstream/procps-ng.git] / slabtop.c
1 /*
2  * slabtop.c - utility to display kernel slab information.
3  *
4  * Chris Rivera <cmrivera@ufl.edu>
5  * Robert Love <rml@tech9.net>
6  *
7  * Copyright (C) 2003 Chris Rivera
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23
24 #include <limits.h>
25 #include <locale.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <ncurses.h>
32 #include <termios.h>
33 #include <getopt.h>
34 #include <ctype.h>
35 #include <sys/ioctl.h>
36
37 #include <sys/select.h>
38 #include <sys/time.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41
42 #include "c.h"
43 #include "fileutils.h"
44 #include "nls.h"
45 #include "strutils.h"
46 #include "proc/slab.h"
47 #include "proc/version.h"
48
49 #define DEF_SORT_FUNC           sort_nr_objs
50
51 static int run_once;
52 static unsigned short cols, rows;
53 static struct termios saved_tty;
54 static long delay = 3;
55 static int (*sort_func)(const struct slab_info *, const struct slab_info *);
56
57 static struct slab_info *merge_objs(struct slab_info *a, struct slab_info *b)
58 {
59         struct slab_info sorted_list;
60         struct slab_info *curr = &sorted_list;
61
62         while ((a != NULL) && (b != NULL)) {
63                 if (sort_func(a, b)) {
64                         curr->next = a;
65                         curr = a;
66                         a = a->next;
67                 } else {
68                         curr->next = b;
69                         curr = b;
70                         b = b->next;
71                 }
72         }
73
74         curr->next = (a == NULL) ? b : a;
75         return sorted_list.next;
76 }
77
78 /*
79  * slabsort - merge sort the slab_info linked list based on sort_func
80  */
81 static struct slab_info *slabsort(struct slab_info *list)
82 {
83         struct slab_info *a, *b;
84
85         if ((list == NULL) || (list->next == NULL))
86                 return list;
87
88         a = list;
89         b = list->next;
90
91         while ((b != NULL) && (b->next != NULL)) {
92                 list = list->next;
93                 b = b->next->next;
94         }
95
96         b = list->next;
97         list->next = NULL;
98
99         return merge_objs(slabsort(a), slabsort(b));
100 }
101
102 /*
103  * Sort Routines.  Each of these should be associated with a command-line
104  * search option.  The functions should fit the prototype:
105  *
106  *      int sort_foo(const struct slab_info *a, const struct slab_info *b)
107  *
108  * They return one if the first parameter is larger than the second
109  * Otherwise, they return zero.
110  */
111
112 static int sort_name(const struct slab_info *a, const struct slab_info *b)
113 {
114         return (strcmp(a->name, b->name) < 0) ? 1 : 0;
115 }
116
117 static int sort_nr_objs(const struct slab_info *a, const struct slab_info *b)
118 {
119         return (a->nr_objs > b->nr_objs);
120 }
121
122 static int sort_nr_active_objs(const struct slab_info *a,
123                                 const struct slab_info *b)
124 {
125         return (a->nr_active_objs > b->nr_active_objs);
126 }
127
128 static int sort_obj_size(const struct slab_info *a, const struct slab_info *b)
129 {
130         return (a->obj_size > b->obj_size);
131 }
132
133 static int sort_objs_per_slab(const struct slab_info *a,
134                                 const struct slab_info *b)
135 {
136         return (a->objs_per_slab > b->objs_per_slab);
137 }
138
139 static int sort_pages_per_slab(const struct slab_info *a,
140                 const struct slab_info *b)
141 {
142         return (a->pages_per_slab > b->pages_per_slab);
143 }
144
145 static int sort_nr_slabs(const struct slab_info *a, const struct slab_info *b)
146 {
147         return (a->nr_slabs > b->nr_slabs);
148 }
149
150 static int sort_nr_active_slabs(const struct slab_info *a,
151                         const struct slab_info *b)
152 {
153         return (a->nr_active_slabs > b->nr_active_slabs);
154 }
155
156
157 static int sort_use(const struct slab_info *a, const struct slab_info *b)
158 {
159         return (a->use > b->use);
160 }
161
162 static int sort_cache_size(const struct slab_info *a, const struct slab_info *b)
163 {
164         return (a->cache_size > b->cache_size);
165 }
166
167 /*
168  * term_size - set the globals 'cols' and 'rows' to the current terminal size
169  */
170 static void term_size(int unusused __attribute__ ((__unused__)))
171 {
172         struct winsize ws;
173
174         if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) && ws.ws_row > 10) {
175                 cols = ws.ws_col;
176                 rows = ws.ws_row;
177         } else {
178                 cols = 80;
179                 rows = 24;
180         }
181         if (run_once)
182                 rows = USHRT_MAX;
183 }
184
185 static void sigint_handler(int unused __attribute__ ((__unused__)))
186 {
187         delay = 0;
188 }
189
190 static void __attribute__((__noreturn__)) usage(FILE *out)
191 {
192         fputs(USAGE_HEADER, out);
193         fprintf(out, _(" %s [options]\n"), program_invocation_short_name);
194         fputs(USAGE_OPTIONS, out);
195         fputs(_(" -d, --delay <secs>  delay updates\n"), out);
196         fputs(_(" -o, --once          only display once, then exit\n"), out);
197         fputs(_(" -s, --sort <char>   specify sort criteria by character (see below)\n"), out);
198         fputs(USAGE_SEPARATOR, out);
199         fputs(USAGE_HELP, out);
200         fputs(USAGE_VERSION, out);
201
202         fputs(_("\nThe following are valid sort criteria:\n"), out);
203         fputs(_(" a: sort by number of active objects\n"), out);
204         fputs(_(" b: sort by objects per slab\n"), out);
205         fputs(_(" c: sort by cache size\n"), out);
206         fputs(_(" l: sort by number of slabs\n"), out);
207         fputs(_(" v: sort by number of active slabs\n"), out);
208         fputs(_(" n: sort by name\n"), out);
209         fputs(_(" o: sort by number of objects (the default)\n"), out);
210         fputs(_(" p: sort by pages per slab\n"), out);
211         fputs(_(" s: sort by object size\n"), out);
212         fputs(_(" u: sort by cache utilization\n"), out);
213         fprintf(out, USAGE_MAN_TAIL("slabtop(1)"));
214
215         exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
216 }
217
218 /*
219  * set_sort_func - return the slab_sort_func that matches the given key.
220  * On unrecognizable key, DEF_SORT_FUNC is returned.
221  */
222 static void * set_sort_func(char key)
223 {
224         switch (key) {
225         case 'n':
226                 return (void *) sort_name;
227         case 'o':
228                 return (void *) sort_nr_objs;
229         case 'a':
230                 return (void *) sort_nr_active_objs;
231         case 's':
232                 return (void *) sort_obj_size;
233         case 'b':
234                 return (void *) sort_objs_per_slab;
235         case 'p':
236                 return (void *) sort_pages_per_slab;
237         case 'l':
238                 return (void *) sort_nr_slabs;
239         case 'v':
240                 return (void *) sort_nr_active_slabs;
241         case 'c':
242                 return (void *) sort_cache_size;
243         case 'u':
244                 return (void *) sort_use;
245         default:
246                 return (void *) DEF_SORT_FUNC;
247         }
248 }
249
250 static void parse_input(char c)
251 {
252         c = toupper(c);
253         switch(c) {
254         case 'A':
255                 sort_func = sort_nr_active_objs;
256                 break;
257         case 'B':
258                 sort_func = sort_objs_per_slab;
259                 break;
260         case 'C':
261                 sort_func = sort_cache_size;
262                 break;
263         case 'L':
264                 sort_func = sort_nr_slabs;
265                 break;
266         case 'V':
267                 sort_func = sort_nr_active_slabs;
268                 break;
269         case 'N':
270                 sort_func = sort_name;
271                 break;
272         case 'O':
273                 sort_func = sort_nr_objs;
274                 break;
275         case 'P':
276                 sort_func = sort_pages_per_slab;
277                 break;
278         case 'S':
279                 sort_func = sort_obj_size;
280                 break;
281         case 'U':
282                 sort_func = sort_use;
283                 break;
284         case 'Q':
285                 delay = 0;
286                 break;
287         }
288 }
289
290 #define print_line(fmt, ...) if (run_once) printf(fmt, __VA_ARGS__); else printw(fmt, __VA_ARGS__)
291 int main(int argc, char *argv[])
292 {
293         int is_tty, o;
294         unsigned short old_rows;
295         struct slab_info *slab_list = NULL;
296         int retval = EXIT_SUCCESS;
297
298         static const struct option longopts[] = {
299                 { "delay",      required_argument, NULL, 'd' },
300                 { "sort",       required_argument, NULL, 's' },
301                 { "once",       no_argument,       NULL, 'o' },
302                 { "help",       no_argument,       NULL, 'h' },
303                 { "version",    no_argument,       NULL, 'V' },
304                 {  NULL, 0, NULL, 0 }
305         };
306
307 #ifdef HAVE_PROGRAM_INVOCATION_NAME
308         program_invocation_name = program_invocation_short_name;
309 #endif
310         setlocale (LC_ALL, "");
311         bindtextdomain(PACKAGE, LOCALEDIR);
312         textdomain(PACKAGE);
313         atexit(close_stdout);
314
315         sort_func = DEF_SORT_FUNC;
316
317         while ((o = getopt_long(argc, argv, "d:s:ohV", longopts, NULL)) != -1) {
318                 switch (o) {
319                 case 'd':
320                         errno = 0;
321                         delay = strtol_or_err(optarg, _("illegal delay"));
322                         if (delay < 1)
323                                 xerrx(EXIT_FAILURE,
324                                         _("delay must be positive integer"));
325                         break;
326                 case 's':
327                         sort_func = (int (*)(const struct slab_info*,
328                                 const struct slab_info *)) set_sort_func(optarg[0]);
329                         break;
330                 case 'o':
331                         run_once=1;
332                         delay = 0;
333                         break;
334                 case 'V':
335                         printf(PROCPS_NG_VERSION);
336                         return EXIT_SUCCESS;
337                 case 'h':
338                         usage(stdout);
339                 default:
340                         usage(stderr);
341                 }
342         }
343
344         is_tty = isatty(STDIN_FILENO);
345         if (is_tty && tcgetattr(STDIN_FILENO, &saved_tty) == -1)
346                 xwarn(_("terminal setting retrieval"));
347
348         old_rows = rows;
349         term_size(0);
350         if (!run_once) {
351                 initscr();
352                 resizeterm(rows, cols);
353                 signal(SIGWINCH, term_size);
354         }
355         signal(SIGINT, sigint_handler);
356
357         do {
358                 struct slab_info *curr;
359                 struct slab_stat stats;
360                 struct timeval tv;
361                 fd_set readfds;
362                 char c;
363                 int i;
364                 memset(&stats, 0, sizeof(struct slab_stat));
365
366                 if (get_slabinfo(&slab_list, &stats)) {
367                         slab_list = NULL;
368                         retval = EXIT_FAILURE;
369                         break;
370                 }
371
372                 if (!run_once && old_rows != rows) {
373                         resizeterm(rows, cols);
374                         old_rows = rows;
375                 }
376
377                 move(0, 0);
378                 print_line(" %-35s: %d / %d (%.1f%%)\n"
379                        " %-35s: %d / %d (%.1f%%)\n"
380                        " %-35s: %d / %d (%.1f%%)\n"
381                        " %-35s: %.2fK / %.2fK (%.1f%%)\n"
382                        " %-35s: %.2fK / %.2fK / %.2fK\n\n",
383                        /* Translation Hint: Next five strings must not
384                         * exceed 35 length in characters.  */
385                        /* xgettext:no-c-format */
386                        _("Active / Total Objects (% used)"),
387                        stats.nr_active_objs, stats.nr_objs,
388                        100.0 * stats.nr_active_objs / stats.nr_objs,
389                        /* xgettext:no-c-format */
390                        _("Active / Total Slabs (% used)"),
391                        stats.nr_active_slabs, stats.nr_slabs,
392                        100.0 * stats.nr_active_slabs / stats.nr_slabs,
393                        /* xgettext:no-c-format */
394                        _("Active / Total Caches (% used)"),
395                        stats.nr_active_caches, stats.nr_caches,
396                        100.0 * stats.nr_active_caches / stats.nr_caches,
397                        /* xgettext:no-c-format */
398                        _("Active / Total Size (% used)"),
399                        stats.active_size / 1024.0, stats.total_size / 1024.0,
400                        100.0 * stats.active_size / stats.total_size,
401                        _("Minimum / Average / Maximum Object"),
402                        stats.min_obj_size / 1024.0, stats.avg_obj_size / 1024.0,
403                        stats.max_obj_size / 1024.0);
404
405                 slab_list = slabsort(slab_list);
406
407                 attron(A_REVERSE);
408                 /* Translation Hint: Please keep alignment of the
409                  * following intact. */
410                 print_line("%-78s\n", _("  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME"));
411                 attroff(A_REVERSE);
412
413                 curr = slab_list;
414                 for (i = 0; i < rows - 8 && curr; i++) {
415                         print_line("%6u %6u %3u%% %7.2fK %6u %8u %9uK %-23s\n",
416                                 curr->nr_objs, curr->nr_active_objs, curr->use,
417                                 curr->obj_size / 1024.0, curr->nr_slabs,
418                                 curr->objs_per_slab, (unsigned)(curr->cache_size / 1024),
419                                 curr->name);
420                         curr = curr->next;
421                 }
422
423                 put_slabinfo(slab_list);
424                 if (!run_once) {
425                         refresh();
426                         FD_ZERO(&readfds);
427                         FD_SET(STDIN_FILENO, &readfds);
428                         tv.tv_sec = delay;
429                         tv.tv_usec = 0;
430                         if (select(STDOUT_FILENO, &readfds, NULL, NULL, &tv) > 0) {
431                                 if (read(STDIN_FILENO, &c, 1) != 1)
432                                         break;
433                                 parse_input(c);
434                         }
435                 }
436         } while (delay);
437
438         if (is_tty)
439                 tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tty);
440         if (slab_list)
441                 free_slabinfo(slab_list);
442         if (!run_once)
443                 endwin();
444         return retval;
445 }