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 */ 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 */ 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 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 */ 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 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) 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 void * 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 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