1547 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Slabinfo: Tool to get reports about slabs4 *5 * (C) 2007 sgi, Christoph Lameter6 * (C) 2011 Linux Foundation, Christoph Lameter7 *8 * Compile with:9 *10 * gcc -o slabinfo slabinfo.c11 */12#include <stdio.h>13#include <stdlib.h>14#include <sys/types.h>15#include <dirent.h>16#include <strings.h>17#include <string.h>18#include <unistd.h>19#include <stdarg.h>20#include <getopt.h>21#include <regex.h>22#include <errno.h>23 24#define MAX_SLABS 50025#define MAX_ALIASES 50026#define MAX_NODES 102427 28struct slabinfo {29 char *name;30 int alias;31 int refs;32 int aliases, align, cache_dma, cpu_slabs, destroy_by_rcu;33 unsigned int hwcache_align, object_size, objs_per_slab;34 unsigned int sanity_checks, slab_size, store_user, trace;35 int order, poison, reclaim_account, red_zone;36 unsigned long partial, objects, slabs, objects_partial, objects_total;37 unsigned long alloc_fastpath, alloc_slowpath;38 unsigned long free_fastpath, free_slowpath;39 unsigned long free_frozen, free_add_partial, free_remove_partial;40 unsigned long alloc_from_partial, alloc_slab, free_slab, alloc_refill;41 unsigned long cpuslab_flush, deactivate_full, deactivate_empty;42 unsigned long deactivate_to_head, deactivate_to_tail;43 unsigned long deactivate_remote_frees, order_fallback;44 unsigned long cmpxchg_double_cpu_fail, cmpxchg_double_fail;45 unsigned long alloc_node_mismatch, deactivate_bypass;46 unsigned long cpu_partial_alloc, cpu_partial_free;47 int numa[MAX_NODES];48 int numa_partial[MAX_NODES];49} slabinfo[MAX_SLABS];50 51struct aliasinfo {52 char *name;53 char *ref;54 struct slabinfo *slab;55} aliasinfo[MAX_ALIASES];56 57int slabs;58int actual_slabs;59int aliases;60int alias_targets;61int highest_node;62 63char buffer[4096];64 65int show_empty;66int show_report;67int show_alias;68int show_slab;69int skip_zero = 1;70int show_numa;71int show_track;72int show_first_alias;73int validate;74int shrink;75int show_inverted;76int show_single_ref;77int show_totals;78int sort_size;79int sort_active;80int set_debug;81int show_ops;82int sort_partial;83int show_activity;84int output_lines = -1;85int sort_loss;86int extended_totals;87int show_bytes;88int unreclaim_only;89 90/* Debug options */91int sanity;92int redzone;93int poison;94int tracking;95int tracing;96 97int page_size;98 99regex_t pattern;100 101static void fatal(const char *x, ...)102{103 va_list ap;104 105 va_start(ap, x);106 vfprintf(stderr, x, ap);107 va_end(ap);108 exit(EXIT_FAILURE);109}110 111static void usage(void)112{113 printf("slabinfo 4/15/2011. (c) 2007 sgi/(c) 2011 Linux Foundation.\n\n"114 "slabinfo [-aABDefhilLnoPrsStTUvXz1] [N=K] [-dafzput] [slab-regexp]\n"115 "-a|--aliases Show aliases\n"116 "-A|--activity Most active slabs first\n"117 "-B|--Bytes Show size in bytes\n"118 "-D|--display-active Switch line format to activity\n"119 "-e|--empty Show empty slabs\n"120 "-f|--first-alias Show first alias\n"121 "-h|--help Show usage information\n"122 "-i|--inverted Inverted list\n"123 "-l|--slabs Show slabs\n"124 "-L|--Loss Sort by loss\n"125 "-n|--numa Show NUMA information\n"126 "-N|--lines=K Show the first K slabs\n"127 "-o|--ops Show kmem_cache_ops\n"128 "-P|--partial Sort by number of partial slabs\n"129 "-r|--report Detailed report on single slabs\n"130 "-s|--shrink Shrink slabs\n"131 "-S|--Size Sort by size\n"132 "-t|--tracking Show alloc/free information\n"133 "-T|--Totals Show summary information\n"134 "-U|--Unreclaim Show unreclaimable slabs only\n"135 "-v|--validate Validate slabs\n"136 "-X|--Xtotals Show extended summary information\n"137 "-z|--zero Include empty slabs\n"138 "-1|--1ref Single reference\n"139 140 "\n"141 "-d | --debug Switch off all debug options\n"142 "-da | --debug=a Switch on all debug options (--debug=FZPU)\n"143 144 "\n"145 "-d[afzput] | --debug=[afzput]\n"146 " f | F Sanity Checks (SLAB_CONSISTENCY_CHECKS)\n"147 " z | Z Redzoning\n"148 " p | P Poisoning\n"149 " u | U Tracking\n"150 " t | T Tracing\n"151 152 "\nSorting options (--Loss, --Size, --Partial) are mutually exclusive\n"153 );154}155 156static unsigned long read_obj(const char *name)157{158 FILE *f = fopen(name, "r");159 160 if (!f) {161 buffer[0] = 0;162 if (errno == EACCES)163 fatal("%s, Try using superuser\n", strerror(errno));164 } else {165 if (!fgets(buffer, sizeof(buffer), f))166 buffer[0] = 0;167 fclose(f);168 if (buffer[strlen(buffer)] == '\n')169 buffer[strlen(buffer)] = 0;170 }171 return strlen(buffer);172}173 174 175/*176 * Get the contents of an attribute177 */178static unsigned long get_obj(const char *name)179{180 if (!read_obj(name))181 return 0;182 183 return atol(buffer);184}185 186static unsigned long get_obj_and_str(const char *name, char **x)187{188 unsigned long result = 0;189 char *p;190 191 *x = NULL;192 193 if (!read_obj(name)) {194 x = NULL;195 return 0;196 }197 result = strtoul(buffer, &p, 10);198 while (*p == ' ')199 p++;200 if (*p)201 *x = strdup(p);202 return result;203}204 205static void set_obj(struct slabinfo *s, const char *name, int n)206{207 char x[100];208 FILE *f;209 210 snprintf(x, 100, "%s/%s", s->name, name);211 f = fopen(x, "w");212 if (!f)213 fatal("Cannot write to %s\n", x);214 215 fprintf(f, "%d\n", n);216 fclose(f);217}218 219static unsigned long read_slab_obj(struct slabinfo *s, const char *name)220{221 char x[100];222 FILE *f;223 size_t l;224 225 snprintf(x, 100, "%s/%s", s->name, name);226 f = fopen(x, "r");227 if (!f) {228 buffer[0] = 0;229 l = 0;230 } else {231 l = fread(buffer, 1, sizeof(buffer), f);232 buffer[l] = 0;233 fclose(f);234 }235 return l;236}237 238static unsigned long read_debug_slab_obj(struct slabinfo *s, const char *name)239{240 char x[128];241 FILE *f;242 size_t l;243 244 snprintf(x, 128, "/sys/kernel/debug/slab/%s/%s", s->name, name);245 f = fopen(x, "r");246 if (!f) {247 buffer[0] = 0;248 l = 0;249 } else {250 l = fread(buffer, 1, sizeof(buffer), f);251 buffer[l] = 0;252 fclose(f);253 }254 return l;255}256 257/*258 * Put a size string together259 */260static int store_size(char *buffer, unsigned long value)261{262 unsigned long divisor = 1;263 char trailer = 0;264 int n;265 266 if (!show_bytes) {267 if (value > 1000000000UL) {268 divisor = 100000000UL;269 trailer = 'G';270 } else if (value > 1000000UL) {271 divisor = 100000UL;272 trailer = 'M';273 } else if (value > 1000UL) {274 divisor = 100;275 trailer = 'K';276 }277 }278 279 value /= divisor;280 n = sprintf(buffer, "%ld",value);281 if (trailer) {282 buffer[n] = trailer;283 n++;284 buffer[n] = 0;285 }286 if (divisor != 1) {287 memmove(buffer + n - 2, buffer + n - 3, 4);288 buffer[n-2] = '.';289 n++;290 }291 return n;292}293 294static void decode_numa_list(int *numa, char *t)295{296 int node;297 int nr;298 299 memset(numa, 0, MAX_NODES * sizeof(int));300 301 if (!t)302 return;303 304 while (*t == 'N') {305 t++;306 node = strtoul(t, &t, 10);307 if (*t == '=') {308 t++;309 nr = strtoul(t, &t, 10);310 numa[node] = nr;311 if (node > highest_node)312 highest_node = node;313 }314 while (*t == ' ')315 t++;316 }317}318 319static void slab_validate(struct slabinfo *s)320{321 if (strcmp(s->name, "*") == 0)322 return;323 324 set_obj(s, "validate", 1);325}326 327static void slab_shrink(struct slabinfo *s)328{329 if (strcmp(s->name, "*") == 0)330 return;331 332 set_obj(s, "shrink", 1);333}334 335int line = 0;336 337static void first_line(void)338{339 if (show_activity)340 printf("Name Objects Alloc Free"341 " %%Fast Fallb O CmpX UL\n");342 else343 printf("Name Objects Objsize %s "344 "Slabs/Part/Cpu O/S O %%Fr %%Ef Flg\n",345 sort_loss ? " Loss" : "Space");346}347 348/*349 * Find the shortest alias of a slab350 */351static struct aliasinfo *find_one_alias(struct slabinfo *find)352{353 struct aliasinfo *a;354 struct aliasinfo *best = NULL;355 356 for(a = aliasinfo;a < aliasinfo + aliases; a++) {357 if (a->slab == find &&358 (!best || strlen(best->name) < strlen(a->name))) {359 best = a;360 if (strncmp(a->name,"kmall", 5) == 0)361 return best;362 }363 }364 return best;365}366 367static unsigned long slab_size(struct slabinfo *s)368{369 return s->slabs * (page_size << s->order);370}371 372static unsigned long slab_activity(struct slabinfo *s)373{374 return s->alloc_fastpath + s->free_fastpath +375 s->alloc_slowpath + s->free_slowpath;376}377 378static unsigned long slab_waste(struct slabinfo *s)379{380 return slab_size(s) - s->objects * s->object_size;381}382 383static void slab_numa(struct slabinfo *s, int mode)384{385 int node;386 387 if (strcmp(s->name, "*") == 0)388 return;389 390 if (!highest_node) {391 printf("\n%s: No NUMA information available.\n", s->name);392 return;393 }394 395 if (skip_zero && !s->slabs)396 return;397 398 if (!line) {399 printf("\n%-21s:", mode ? "NUMA nodes" : "Slab");400 for(node = 0; node <= highest_node; node++)401 printf(" %4d", node);402 printf("\n----------------------");403 for(node = 0; node <= highest_node; node++)404 printf("-----");405 printf("\n");406 }407 printf("%-21s ", mode ? "All slabs" : s->name);408 for(node = 0; node <= highest_node; node++) {409 char b[20];410 411 store_size(b, s->numa[node]);412 printf(" %4s", b);413 }414 printf("\n");415 if (mode) {416 printf("%-21s ", "Partial slabs");417 for(node = 0; node <= highest_node; node++) {418 char b[20];419 420 store_size(b, s->numa_partial[node]);421 printf(" %4s", b);422 }423 printf("\n");424 }425 line++;426}427 428static void show_tracking(struct slabinfo *s)429{430 printf("\n%s: Kernel object allocation\n", s->name);431 printf("-----------------------------------------------------------------------\n");432 if (read_debug_slab_obj(s, "alloc_traces"))433 printf("%s", buffer);434 else if (read_slab_obj(s, "alloc_calls"))435 printf("%s", buffer);436 else437 printf("No Data\n");438 439 printf("\n%s: Kernel object freeing\n", s->name);440 printf("------------------------------------------------------------------------\n");441 if (read_debug_slab_obj(s, "free_traces"))442 printf("%s", buffer);443 else if (read_slab_obj(s, "free_calls"))444 printf("%s", buffer);445 else446 printf("No Data\n");447 448}449 450static void ops(struct slabinfo *s)451{452 if (strcmp(s->name, "*") == 0)453 return;454 455 if (read_slab_obj(s, "ops")) {456 printf("\n%s: kmem_cache operations\n", s->name);457 printf("--------------------------------------------\n");458 printf("%s", buffer);459 } else460 printf("\n%s has no kmem_cache operations\n", s->name);461}462 463static const char *onoff(int x)464{465 if (x)466 return "On ";467 return "Off";468}469 470static void slab_stats(struct slabinfo *s)471{472 unsigned long total_alloc;473 unsigned long total_free;474 unsigned long total;475 476 if (!s->alloc_slab)477 return;478 479 total_alloc = s->alloc_fastpath + s->alloc_slowpath;480 total_free = s->free_fastpath + s->free_slowpath;481 482 if (!total_alloc)483 return;484 485 printf("\n");486 printf("Slab Perf Counter Alloc Free %%Al %%Fr\n");487 printf("--------------------------------------------------\n");488 printf("Fastpath %8lu %8lu %3lu %3lu\n",489 s->alloc_fastpath, s->free_fastpath,490 s->alloc_fastpath * 100 / total_alloc,491 total_free ? s->free_fastpath * 100 / total_free : 0);492 printf("Slowpath %8lu %8lu %3lu %3lu\n",493 total_alloc - s->alloc_fastpath, s->free_slowpath,494 (total_alloc - s->alloc_fastpath) * 100 / total_alloc,495 total_free ? s->free_slowpath * 100 / total_free : 0);496 printf("Page Alloc %8lu %8lu %3lu %3lu\n",497 s->alloc_slab, s->free_slab,498 s->alloc_slab * 100 / total_alloc,499 total_free ? s->free_slab * 100 / total_free : 0);500 printf("Add partial %8lu %8lu %3lu %3lu\n",501 s->deactivate_to_head + s->deactivate_to_tail,502 s->free_add_partial,503 (s->deactivate_to_head + s->deactivate_to_tail) * 100 / total_alloc,504 total_free ? s->free_add_partial * 100 / total_free : 0);505 printf("Remove partial %8lu %8lu %3lu %3lu\n",506 s->alloc_from_partial, s->free_remove_partial,507 s->alloc_from_partial * 100 / total_alloc,508 total_free ? s->free_remove_partial * 100 / total_free : 0);509 510 printf("Cpu partial list %8lu %8lu %3lu %3lu\n",511 s->cpu_partial_alloc, s->cpu_partial_free,512 s->cpu_partial_alloc * 100 / total_alloc,513 total_free ? s->cpu_partial_free * 100 / total_free : 0);514 515 printf("RemoteObj/SlabFrozen %8lu %8lu %3lu %3lu\n",516 s->deactivate_remote_frees, s->free_frozen,517 s->deactivate_remote_frees * 100 / total_alloc,518 total_free ? s->free_frozen * 100 / total_free : 0);519 520 printf("Total %8lu %8lu\n\n", total_alloc, total_free);521 522 if (s->cpuslab_flush)523 printf("Flushes %8lu\n", s->cpuslab_flush);524 525 total = s->deactivate_full + s->deactivate_empty +526 s->deactivate_to_head + s->deactivate_to_tail + s->deactivate_bypass;527 528 if (total) {529 printf("\nSlab Deactivation Occurrences %%\n");530 printf("-------------------------------------------------\n");531 printf("Slab full %7lu %3lu%%\n",532 s->deactivate_full, (s->deactivate_full * 100) / total);533 printf("Slab empty %7lu %3lu%%\n",534 s->deactivate_empty, (s->deactivate_empty * 100) / total);535 printf("Moved to head of partial list %7lu %3lu%%\n",536 s->deactivate_to_head, (s->deactivate_to_head * 100) / total);537 printf("Moved to tail of partial list %7lu %3lu%%\n",538 s->deactivate_to_tail, (s->deactivate_to_tail * 100) / total);539 printf("Deactivation bypass %7lu %3lu%%\n",540 s->deactivate_bypass, (s->deactivate_bypass * 100) / total);541 printf("Refilled from foreign frees %7lu %3lu%%\n",542 s->alloc_refill, (s->alloc_refill * 100) / total);543 printf("Node mismatch %7lu %3lu%%\n",544 s->alloc_node_mismatch, (s->alloc_node_mismatch * 100) / total);545 }546 547 if (s->cmpxchg_double_fail || s->cmpxchg_double_cpu_fail) {548 printf("\nCmpxchg_double Looping\n------------------------\n");549 printf("Locked Cmpxchg Double redos %lu\nUnlocked Cmpxchg Double redos %lu\n",550 s->cmpxchg_double_fail, s->cmpxchg_double_cpu_fail);551 }552}553 554static void report(struct slabinfo *s)555{556 if (strcmp(s->name, "*") == 0)557 return;558 559 printf("\nSlabcache: %-15s Aliases: %2d Order : %2d Objects: %lu\n",560 s->name, s->aliases, s->order, s->objects);561 if (s->hwcache_align)562 printf("** Hardware cacheline aligned\n");563 if (s->cache_dma)564 printf("** Memory is allocated in a special DMA zone\n");565 if (s->destroy_by_rcu)566 printf("** Slabs are destroyed via RCU\n");567 if (s->reclaim_account)568 printf("** Reclaim accounting active\n");569 570 printf("\nSizes (bytes) Slabs Debug Memory\n");571 printf("------------------------------------------------------------------------\n");572 printf("Object : %7d Total : %7ld Sanity Checks : %s Total: %7ld\n",573 s->object_size, s->slabs, onoff(s->sanity_checks),574 s->slabs * (page_size << s->order));575 printf("SlabObj: %7d Full : %7ld Redzoning : %s Used : %7ld\n",576 s->slab_size, s->slabs - s->partial - s->cpu_slabs,577 onoff(s->red_zone), s->objects * s->object_size);578 printf("SlabSiz: %7d Partial: %7ld Poisoning : %s Loss : %7ld\n",579 page_size << s->order, s->partial, onoff(s->poison),580 s->slabs * (page_size << s->order) - s->objects * s->object_size);581 printf("Loss : %7d CpuSlab: %7d Tracking : %s Lalig: %7ld\n",582 s->slab_size - s->object_size, s->cpu_slabs, onoff(s->store_user),583 (s->slab_size - s->object_size) * s->objects);584 printf("Align : %7d Objects: %7d Tracing : %s Lpadd: %7ld\n",585 s->align, s->objs_per_slab, onoff(s->trace),586 ((page_size << s->order) - s->objs_per_slab * s->slab_size) *587 s->slabs);588 589 ops(s);590 show_tracking(s);591 slab_numa(s, 1);592 slab_stats(s);593}594 595static void slabcache(struct slabinfo *s)596{597 char size_str[20];598 char dist_str[40];599 char flags[20];600 char *p = flags;601 602 if (strcmp(s->name, "*") == 0)603 return;604 605 if (unreclaim_only && s->reclaim_account)606 return;607 608 if (actual_slabs == 1) {609 report(s);610 return;611 }612 613 if (skip_zero && !show_empty && !s->slabs)614 return;615 616 if (show_empty && s->slabs)617 return;618 619 if (sort_loss == 0)620 store_size(size_str, slab_size(s));621 else622 store_size(size_str, slab_waste(s));623 snprintf(dist_str, 40, "%lu/%lu/%d", s->slabs - s->cpu_slabs,624 s->partial, s->cpu_slabs);625 626 if (!line++)627 first_line();628 629 if (s->aliases)630 *p++ = '*';631 if (s->cache_dma)632 *p++ = 'd';633 if (s->hwcache_align)634 *p++ = 'A';635 if (s->poison)636 *p++ = 'P';637 if (s->reclaim_account)638 *p++ = 'a';639 if (s->red_zone)640 *p++ = 'Z';641 if (s->sanity_checks)642 *p++ = 'F';643 if (s->store_user)644 *p++ = 'U';645 if (s->trace)646 *p++ = 'T';647 648 *p = 0;649 if (show_activity) {650 unsigned long total_alloc;651 unsigned long total_free;652 653 total_alloc = s->alloc_fastpath + s->alloc_slowpath;654 total_free = s->free_fastpath + s->free_slowpath;655 656 printf("%-21s %8ld %10ld %10ld %3ld %3ld %5ld %1d %4ld %4ld\n",657 s->name, s->objects,658 total_alloc, total_free,659 total_alloc ? (s->alloc_fastpath * 100 / total_alloc) : 0,660 total_free ? (s->free_fastpath * 100 / total_free) : 0,661 s->order_fallback, s->order, s->cmpxchg_double_fail,662 s->cmpxchg_double_cpu_fail);663 } else {664 printf("%-21s %8ld %7d %15s %14s %4d %1d %3ld %3ld %s\n",665 s->name, s->objects, s->object_size, size_str, dist_str,666 s->objs_per_slab, s->order,667 s->slabs ? (s->partial * 100) / s->slabs : 100,668 s->slabs ? (s->objects * s->object_size * 100) /669 (s->slabs * (page_size << s->order)) : 100,670 flags);671 }672}673 674/*675 * Analyze debug options. Return false if something is amiss.676 */677static int debug_opt_scan(char *opt)678{679 if (!opt || !opt[0] || strcmp(opt, "-") == 0)680 return 1;681 682 if (strcasecmp(opt, "a") == 0) {683 sanity = 1;684 poison = 1;685 redzone = 1;686 tracking = 1;687 return 1;688 }689 690 for ( ; *opt; opt++)691 switch (*opt) {692 case 'F' : case 'f':693 if (sanity)694 return 0;695 sanity = 1;696 break;697 case 'P' : case 'p':698 if (poison)699 return 0;700 poison = 1;701 break;702 703 case 'Z' : case 'z':704 if (redzone)705 return 0;706 redzone = 1;707 break;708 709 case 'U' : case 'u':710 if (tracking)711 return 0;712 tracking = 1;713 break;714 715 case 'T' : case 't':716 if (tracing)717 return 0;718 tracing = 1;719 break;720 default:721 return 0;722 }723 return 1;724}725 726static int slab_empty(struct slabinfo *s)727{728 if (s->objects > 0)729 return 0;730 731 /*732 * We may still have slabs even if there are no objects. Shrinking will733 * remove them.734 */735 if (s->slabs != 0)736 set_obj(s, "shrink", 1);737 738 return 1;739}740 741static void slab_debug(struct slabinfo *s)742{743 if (strcmp(s->name, "*") == 0)744 return;745 746 if (sanity && !s->sanity_checks) {747 set_obj(s, "sanity_checks", 1);748 }749 if (!sanity && s->sanity_checks) {750 if (slab_empty(s))751 set_obj(s, "sanity_checks", 0);752 else753 fprintf(stderr, "%s not empty cannot disable sanity checks\n", s->name);754 }755 if (redzone && !s->red_zone) {756 if (slab_empty(s))757 set_obj(s, "red_zone", 1);758 else759 fprintf(stderr, "%s not empty cannot enable redzoning\n", s->name);760 }761 if (!redzone && s->red_zone) {762 if (slab_empty(s))763 set_obj(s, "red_zone", 0);764 else765 fprintf(stderr, "%s not empty cannot disable redzoning\n", s->name);766 }767 if (poison && !s->poison) {768 if (slab_empty(s))769 set_obj(s, "poison", 1);770 else771 fprintf(stderr, "%s not empty cannot enable poisoning\n", s->name);772 }773 if (!poison && s->poison) {774 if (slab_empty(s))775 set_obj(s, "poison", 0);776 else777 fprintf(stderr, "%s not empty cannot disable poisoning\n", s->name);778 }779 if (tracking && !s->store_user) {780 if (slab_empty(s))781 set_obj(s, "store_user", 1);782 else783 fprintf(stderr, "%s not empty cannot enable tracking\n", s->name);784 }785 if (!tracking && s->store_user) {786 if (slab_empty(s))787 set_obj(s, "store_user", 0);788 else789 fprintf(stderr, "%s not empty cannot disable tracking\n", s->name);790 }791 if (tracing && !s->trace) {792 if (slabs == 1)793 set_obj(s, "trace", 1);794 else795 fprintf(stderr, "%s can only enable trace for one slab at a time\n", s->name);796 }797 if (!tracing && s->trace)798 set_obj(s, "trace", 1);799}800 801static void totals(void)802{803 struct slabinfo *s;804 805 int used_slabs = 0;806 char b1[20], b2[20], b3[20], b4[20];807 unsigned long long max = 1ULL << 63;808 809 /* Object size */810 unsigned long long min_objsize = max, max_objsize = 0, avg_objsize;811 812 /* Number of partial slabs in a slabcache */813 unsigned long long min_partial = max, max_partial = 0,814 avg_partial, total_partial = 0;815 816 /* Number of slabs in a slab cache */817 unsigned long long min_slabs = max, max_slabs = 0,818 avg_slabs, total_slabs = 0;819 820 /* Size of the whole slab */821 unsigned long long min_size = max, max_size = 0,822 avg_size, total_size = 0;823 824 /* Bytes used for object storage in a slab */825 unsigned long long min_used = max, max_used = 0,826 avg_used, total_used = 0;827 828 /* Waste: Bytes used for alignment and padding */829 unsigned long long min_waste = max, max_waste = 0,830 avg_waste, total_waste = 0;831 /* Number of objects in a slab */832 unsigned long long min_objects = max, max_objects = 0,833 avg_objects, total_objects = 0;834 /* Waste per object */835 unsigned long long min_objwaste = max,836 max_objwaste = 0, avg_objwaste,837 total_objwaste = 0;838 839 /* Memory per object */840 unsigned long long min_memobj = max,841 max_memobj = 0, avg_memobj,842 total_objsize = 0;843 844 /* Percentage of partial slabs per slab */845 unsigned long min_ppart = 100, max_ppart = 0,846 avg_ppart, total_ppart = 0;847 848 /* Number of objects in partial slabs */849 unsigned long min_partobj = max, max_partobj = 0,850 avg_partobj, total_partobj = 0;851 852 /* Percentage of partial objects of all objects in a slab */853 unsigned long min_ppartobj = 100, max_ppartobj = 0,854 avg_ppartobj, total_ppartobj = 0;855 856 857 for (s = slabinfo; s < slabinfo + slabs; s++) {858 unsigned long long size;859 unsigned long used;860 unsigned long long wasted;861 unsigned long long objwaste;862 unsigned long percentage_partial_slabs;863 unsigned long percentage_partial_objs;864 865 if (!s->slabs || !s->objects)866 continue;867 868 used_slabs++;869 870 size = slab_size(s);871 used = s->objects * s->object_size;872 wasted = size - used;873 objwaste = s->slab_size - s->object_size;874 875 percentage_partial_slabs = s->partial * 100 / s->slabs;876 if (percentage_partial_slabs > 100)877 percentage_partial_slabs = 100;878 879 percentage_partial_objs = s->objects_partial * 100880 / s->objects;881 882 if (percentage_partial_objs > 100)883 percentage_partial_objs = 100;884 885 if (s->object_size < min_objsize)886 min_objsize = s->object_size;887 if (s->partial < min_partial)888 min_partial = s->partial;889 if (s->slabs < min_slabs)890 min_slabs = s->slabs;891 if (size < min_size)892 min_size = size;893 if (wasted < min_waste)894 min_waste = wasted;895 if (objwaste < min_objwaste)896 min_objwaste = objwaste;897 if (s->objects < min_objects)898 min_objects = s->objects;899 if (used < min_used)900 min_used = used;901 if (s->objects_partial < min_partobj)902 min_partobj = s->objects_partial;903 if (percentage_partial_slabs < min_ppart)904 min_ppart = percentage_partial_slabs;905 if (percentage_partial_objs < min_ppartobj)906 min_ppartobj = percentage_partial_objs;907 if (s->slab_size < min_memobj)908 min_memobj = s->slab_size;909 910 if (s->object_size > max_objsize)911 max_objsize = s->object_size;912 if (s->partial > max_partial)913 max_partial = s->partial;914 if (s->slabs > max_slabs)915 max_slabs = s->slabs;916 if (size > max_size)917 max_size = size;918 if (wasted > max_waste)919 max_waste = wasted;920 if (objwaste > max_objwaste)921 max_objwaste = objwaste;922 if (s->objects > max_objects)923 max_objects = s->objects;924 if (used > max_used)925 max_used = used;926 if (s->objects_partial > max_partobj)927 max_partobj = s->objects_partial;928 if (percentage_partial_slabs > max_ppart)929 max_ppart = percentage_partial_slabs;930 if (percentage_partial_objs > max_ppartobj)931 max_ppartobj = percentage_partial_objs;932 if (s->slab_size > max_memobj)933 max_memobj = s->slab_size;934 935 total_partial += s->partial;936 total_slabs += s->slabs;937 total_size += size;938 total_waste += wasted;939 940 total_objects += s->objects;941 total_used += used;942 total_partobj += s->objects_partial;943 total_ppart += percentage_partial_slabs;944 total_ppartobj += percentage_partial_objs;945 946 total_objwaste += s->objects * objwaste;947 total_objsize += s->objects * s->slab_size;948 }949 950 if (!total_objects) {951 printf("No objects\n");952 return;953 }954 if (!used_slabs) {955 printf("No slabs\n");956 return;957 }958 959 /* Per slab averages */960 avg_partial = total_partial / used_slabs;961 avg_slabs = total_slabs / used_slabs;962 avg_size = total_size / used_slabs;963 avg_waste = total_waste / used_slabs;964 965 avg_objects = total_objects / used_slabs;966 avg_used = total_used / used_slabs;967 avg_partobj = total_partobj / used_slabs;968 avg_ppart = total_ppart / used_slabs;969 avg_ppartobj = total_ppartobj / used_slabs;970 971 /* Per object object sizes */972 avg_objsize = total_used / total_objects;973 avg_objwaste = total_objwaste / total_objects;974 avg_partobj = total_partobj * 100 / total_objects;975 avg_memobj = total_objsize / total_objects;976 977 printf("Slabcache Totals\n");978 printf("----------------\n");979 printf("Slabcaches : %15d Aliases : %11d->%-3d Active: %3d\n",980 slabs, aliases, alias_targets, used_slabs);981 982 store_size(b1, total_size);store_size(b2, total_waste);983 store_size(b3, total_waste * 100 / total_used);984 printf("Memory used: %15s # Loss : %15s MRatio:%6s%%\n", b1, b2, b3);985 986 store_size(b1, total_objects);store_size(b2, total_partobj);987 store_size(b3, total_partobj * 100 / total_objects);988 printf("# Objects : %15s # PartObj: %15s ORatio:%6s%%\n", b1, b2, b3);989 990 printf("\n");991 printf("Per Cache Average "992 "Min Max Total\n");993 printf("---------------------------------------"994 "-------------------------------------\n");995 996 store_size(b1, avg_objects);store_size(b2, min_objects);997 store_size(b3, max_objects);store_size(b4, total_objects);998 printf("#Objects %15s %15s %15s %15s\n",999 b1, b2, b3, b4);1000 1001 store_size(b1, avg_slabs);store_size(b2, min_slabs);1002 store_size(b3, max_slabs);store_size(b4, total_slabs);1003 printf("#Slabs %15s %15s %15s %15s\n",1004 b1, b2, b3, b4);1005 1006 store_size(b1, avg_partial);store_size(b2, min_partial);1007 store_size(b3, max_partial);store_size(b4, total_partial);1008 printf("#PartSlab %15s %15s %15s %15s\n",1009 b1, b2, b3, b4);1010 store_size(b1, avg_ppart);store_size(b2, min_ppart);1011 store_size(b3, max_ppart);1012 store_size(b4, total_partial * 100 / total_slabs);1013 printf("%%PartSlab%15s%% %15s%% %15s%% %15s%%\n",1014 b1, b2, b3, b4);1015 1016 store_size(b1, avg_partobj);store_size(b2, min_partobj);1017 store_size(b3, max_partobj);1018 store_size(b4, total_partobj);1019 printf("PartObjs %15s %15s %15s %15s\n",1020 b1, b2, b3, b4);1021 1022 store_size(b1, avg_ppartobj);store_size(b2, min_ppartobj);1023 store_size(b3, max_ppartobj);1024 store_size(b4, total_partobj * 100 / total_objects);1025 printf("%% PartObj%15s%% %15s%% %15s%% %15s%%\n",1026 b1, b2, b3, b4);1027 1028 store_size(b1, avg_size);store_size(b2, min_size);1029 store_size(b3, max_size);store_size(b4, total_size);1030 printf("Memory %15s %15s %15s %15s\n",1031 b1, b2, b3, b4);1032 1033 store_size(b1, avg_used);store_size(b2, min_used);1034 store_size(b3, max_used);store_size(b4, total_used);1035 printf("Used %15s %15s %15s %15s\n",1036 b1, b2, b3, b4);1037 1038 store_size(b1, avg_waste);store_size(b2, min_waste);1039 store_size(b3, max_waste);store_size(b4, total_waste);1040 printf("Loss %15s %15s %15s %15s\n",1041 b1, b2, b3, b4);1042 1043 printf("\n");1044 printf("Per Object Average "1045 "Min Max\n");1046 printf("---------------------------------------"1047 "--------------------\n");1048 1049 store_size(b1, avg_memobj);store_size(b2, min_memobj);1050 store_size(b3, max_memobj);1051 printf("Memory %15s %15s %15s\n",1052 b1, b2, b3);1053 store_size(b1, avg_objsize);store_size(b2, min_objsize);1054 store_size(b3, max_objsize);1055 printf("User %15s %15s %15s\n",1056 b1, b2, b3);1057 1058 store_size(b1, avg_objwaste);store_size(b2, min_objwaste);1059 store_size(b3, max_objwaste);1060 printf("Loss %15s %15s %15s\n",1061 b1, b2, b3);1062}1063 1064static void sort_slabs(void)1065{1066 struct slabinfo *s1,*s2;1067 1068 for (s1 = slabinfo; s1 < slabinfo + slabs; s1++) {1069 for (s2 = s1 + 1; s2 < slabinfo + slabs; s2++) {1070 int result;1071 1072 if (sort_size) {1073 if (slab_size(s1) == slab_size(s2))1074 result = strcasecmp(s1->name, s2->name);1075 else1076 result = slab_size(s1) < slab_size(s2);1077 } else if (sort_active) {1078 if (slab_activity(s1) == slab_activity(s2))1079 result = strcasecmp(s1->name, s2->name);1080 else1081 result = slab_activity(s1) < slab_activity(s2);1082 } else if (sort_loss) {1083 if (slab_waste(s1) == slab_waste(s2))1084 result = strcasecmp(s1->name, s2->name);1085 else1086 result = slab_waste(s1) < slab_waste(s2);1087 } else if (sort_partial) {1088 if (s1->partial == s2->partial)1089 result = strcasecmp(s1->name, s2->name);1090 else1091 result = s1->partial < s2->partial;1092 } else1093 result = strcasecmp(s1->name, s2->name);1094 1095 if (show_inverted)1096 result = -result;1097 1098 if (result > 0) {1099 struct slabinfo t;1100 1101 memcpy(&t, s1, sizeof(struct slabinfo));1102 memcpy(s1, s2, sizeof(struct slabinfo));1103 memcpy(s2, &t, sizeof(struct slabinfo));1104 }1105 }1106 }1107}1108 1109static void sort_aliases(void)1110{1111 struct aliasinfo *a1,*a2;1112 1113 for (a1 = aliasinfo; a1 < aliasinfo + aliases; a1++) {1114 for (a2 = a1 + 1; a2 < aliasinfo + aliases; a2++) {1115 char *n1, *n2;1116 1117 n1 = a1->name;1118 n2 = a2->name;1119 if (show_alias && !show_inverted) {1120 n1 = a1->ref;1121 n2 = a2->ref;1122 }1123 if (strcasecmp(n1, n2) > 0) {1124 struct aliasinfo t;1125 1126 memcpy(&t, a1, sizeof(struct aliasinfo));1127 memcpy(a1, a2, sizeof(struct aliasinfo));1128 memcpy(a2, &t, sizeof(struct aliasinfo));1129 }1130 }1131 }1132}1133 1134static void link_slabs(void)1135{1136 struct aliasinfo *a;1137 struct slabinfo *s;1138 1139 for (a = aliasinfo; a < aliasinfo + aliases; a++) {1140 1141 for (s = slabinfo; s < slabinfo + slabs; s++)1142 if (strcmp(a->ref, s->name) == 0) {1143 a->slab = s;1144 s->refs++;1145 break;1146 }1147 if (s == slabinfo + slabs)1148 fatal("Unresolved alias %s\n", a->ref);1149 }1150}1151 1152static void alias(void)1153{1154 struct aliasinfo *a;1155 char *active = NULL;1156 1157 sort_aliases();1158 link_slabs();1159 1160 for(a = aliasinfo; a < aliasinfo + aliases; a++) {1161 1162 if (!show_single_ref && a->slab->refs == 1)1163 continue;1164 1165 if (!show_inverted) {1166 if (active) {1167 if (strcmp(a->slab->name, active) == 0) {1168 printf(" %s", a->name);1169 continue;1170 }1171 }1172 printf("\n%-12s <- %s", a->slab->name, a->name);1173 active = a->slab->name;1174 }1175 else1176 printf("%-15s -> %s\n", a->name, a->slab->name);1177 }1178 if (active)1179 printf("\n");1180}1181 1182 1183static void rename_slabs(void)1184{1185 struct slabinfo *s;1186 struct aliasinfo *a;1187 1188 for (s = slabinfo; s < slabinfo + slabs; s++) {1189 if (*s->name != ':')1190 continue;1191 1192 if (s->refs > 1 && !show_first_alias)1193 continue;1194 1195 a = find_one_alias(s);1196 1197 if (a)1198 s->name = a->name;1199 else {1200 s->name = "*";1201 actual_slabs--;1202 }1203 }1204}1205 1206static int slab_mismatch(char *slab)1207{1208 return regexec(&pattern, slab, 0, NULL, 0);1209}1210 1211static void read_slab_dir(void)1212{1213 DIR *dir;1214 struct dirent *de;1215 struct slabinfo *slab = slabinfo;1216 struct aliasinfo *alias = aliasinfo;1217 char *p;1218 char *t;1219 int count;1220 1221 if (chdir("/sys/kernel/slab") && chdir("/sys/slab"))1222 fatal("SYSFS support for SLUB not active\n");1223 1224 dir = opendir(".");1225 while ((de = readdir(dir))) {1226 if (de->d_name[0] == '.' ||1227 (de->d_name[0] != ':' && slab_mismatch(de->d_name)))1228 continue;1229 switch (de->d_type) {1230 case DT_LNK:1231 alias->name = strdup(de->d_name);1232 count = readlink(de->d_name, buffer, sizeof(buffer)-1);1233 1234 if (count < 0)1235 fatal("Cannot read symlink %s\n", de->d_name);1236 1237 buffer[count] = 0;1238 p = buffer + count;1239 while (p > buffer && p[-1] != '/')1240 p--;1241 alias->ref = strdup(p);1242 alias++;1243 break;1244 case DT_DIR:1245 if (chdir(de->d_name))1246 fatal("Unable to access slab %s\n", slab->name);1247 slab->name = strdup(de->d_name);1248 slab->alias = 0;1249 slab->refs = 0;1250 slab->aliases = get_obj("aliases");1251 slab->align = get_obj("align");1252 slab->cache_dma = get_obj("cache_dma");1253 slab->cpu_slabs = get_obj("cpu_slabs");1254 slab->destroy_by_rcu = get_obj("destroy_by_rcu");1255 slab->hwcache_align = get_obj("hwcache_align");1256 slab->object_size = get_obj("object_size");1257 slab->objects = get_obj("objects");1258 slab->objects_partial = get_obj("objects_partial");1259 slab->objects_total = get_obj("objects_total");1260 slab->objs_per_slab = get_obj("objs_per_slab");1261 slab->order = get_obj("order");1262 slab->partial = get_obj("partial");1263 slab->partial = get_obj_and_str("partial", &t);1264 decode_numa_list(slab->numa_partial, t);1265 free(t);1266 slab->poison = get_obj("poison");1267 slab->reclaim_account = get_obj("reclaim_account");1268 slab->red_zone = get_obj("red_zone");1269 slab->sanity_checks = get_obj("sanity_checks");1270 slab->slab_size = get_obj("slab_size");1271 slab->slabs = get_obj_and_str("slabs", &t);1272 decode_numa_list(slab->numa, t);1273 free(t);1274 slab->store_user = get_obj("store_user");1275 slab->trace = get_obj("trace");1276 slab->alloc_fastpath = get_obj("alloc_fastpath");1277 slab->alloc_slowpath = get_obj("alloc_slowpath");1278 slab->free_fastpath = get_obj("free_fastpath");1279 slab->free_slowpath = get_obj("free_slowpath");1280 slab->free_frozen= get_obj("free_frozen");1281 slab->free_add_partial = get_obj("free_add_partial");1282 slab->free_remove_partial = get_obj("free_remove_partial");1283 slab->alloc_from_partial = get_obj("alloc_from_partial");1284 slab->alloc_slab = get_obj("alloc_slab");1285 slab->alloc_refill = get_obj("alloc_refill");1286 slab->free_slab = get_obj("free_slab");1287 slab->cpuslab_flush = get_obj("cpuslab_flush");1288 slab->deactivate_full = get_obj("deactivate_full");1289 slab->deactivate_empty = get_obj("deactivate_empty");1290 slab->deactivate_to_head = get_obj("deactivate_to_head");1291 slab->deactivate_to_tail = get_obj("deactivate_to_tail");1292 slab->deactivate_remote_frees = get_obj("deactivate_remote_frees");1293 slab->order_fallback = get_obj("order_fallback");1294 slab->cmpxchg_double_cpu_fail = get_obj("cmpxchg_double_cpu_fail");1295 slab->cmpxchg_double_fail = get_obj("cmpxchg_double_fail");1296 slab->cpu_partial_alloc = get_obj("cpu_partial_alloc");1297 slab->cpu_partial_free = get_obj("cpu_partial_free");1298 slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");1299 slab->deactivate_bypass = get_obj("deactivate_bypass");1300 if (chdir(".."))1301 fatal("Unable to chdir from slab ../%s\n",1302 slab->name);1303 if (slab->name[0] == ':')1304 alias_targets++;1305 slab++;1306 break;1307 default :1308 fatal("Unknown file type %lx\n", de->d_type);1309 }1310 }1311 closedir(dir);1312 slabs = slab - slabinfo;1313 actual_slabs = slabs;1314 aliases = alias - aliasinfo;1315 if (slabs > MAX_SLABS)1316 fatal("Too many slabs\n");1317 if (aliases > MAX_ALIASES)1318 fatal("Too many aliases\n");1319}1320 1321static void output_slabs(void)1322{1323 struct slabinfo *slab;1324 int lines = output_lines;1325 1326 for (slab = slabinfo; (slab < slabinfo + slabs) &&1327 lines != 0; slab++) {1328 1329 if (slab->alias)1330 continue;1331 1332 if (lines != -1)1333 lines--;1334 1335 if (show_numa)1336 slab_numa(slab, 0);1337 else if (show_track)1338 show_tracking(slab);1339 else if (validate)1340 slab_validate(slab);1341 else if (shrink)1342 slab_shrink(slab);1343 else if (set_debug)1344 slab_debug(slab);1345 else if (show_ops)1346 ops(slab);1347 else if (show_slab)1348 slabcache(slab);1349 else if (show_report)1350 report(slab);1351 }1352}1353 1354static void _xtotals(char *heading, char *underline,1355 int loss, int size, int partial)1356{1357 printf("%s%s", heading, underline);1358 line = 0;1359 sort_loss = loss;1360 sort_size = size;1361 sort_partial = partial;1362 sort_slabs();1363 output_slabs();1364}1365 1366static void xtotals(void)1367{1368 char *heading, *underline;1369 1370 totals();1371 1372 link_slabs();1373 rename_slabs();1374 1375 heading = "\nSlabs sorted by size\n";1376 underline = "--------------------\n";1377 _xtotals(heading, underline, 0, 1, 0);1378 1379 heading = "\nSlabs sorted by loss\n";1380 underline = "--------------------\n";1381 _xtotals(heading, underline, 1, 0, 0);1382 1383 heading = "\nSlabs sorted by number of partial slabs\n";1384 underline = "---------------------------------------\n";1385 _xtotals(heading, underline, 0, 0, 1);1386 1387 printf("\n");1388}1389 1390struct option opts[] = {1391 { "aliases", no_argument, NULL, 'a' },1392 { "activity", no_argument, NULL, 'A' },1393 { "Bytes", no_argument, NULL, 'B'},1394 { "debug", optional_argument, NULL, 'd' },1395 { "display-activity", no_argument, NULL, 'D' },1396 { "empty", no_argument, NULL, 'e' },1397 { "first-alias", no_argument, NULL, 'f' },1398 { "help", no_argument, NULL, 'h' },1399 { "inverted", no_argument, NULL, 'i'},1400 { "slabs", no_argument, NULL, 'l' },1401 { "Loss", no_argument, NULL, 'L'},1402 { "numa", no_argument, NULL, 'n' },1403 { "lines", required_argument, NULL, 'N'},1404 { "ops", no_argument, NULL, 'o' },1405 { "partial", no_argument, NULL, 'p'},1406 { "report", no_argument, NULL, 'r' },1407 { "shrink", no_argument, NULL, 's' },1408 { "Size", no_argument, NULL, 'S'},1409 { "tracking", no_argument, NULL, 't'},1410 { "Totals", no_argument, NULL, 'T'},1411 { "Unreclaim", no_argument, NULL, 'U'},1412 { "validate", no_argument, NULL, 'v' },1413 { "Xtotals", no_argument, NULL, 'X'},1414 { "zero", no_argument, NULL, 'z' },1415 { "1ref", no_argument, NULL, '1'},1416 { NULL, 0, NULL, 0 }1417};1418 1419int main(int argc, char *argv[])1420{1421 int c;1422 int err;1423 char *pattern_source;1424 1425 page_size = getpagesize();1426 1427 while ((c = getopt_long(argc, argv, "aABd::DefhilLnN:oPrsStTUvXz1",1428 opts, NULL)) != -1)1429 switch (c) {1430 case 'a':1431 show_alias = 1;1432 break;1433 case 'A':1434 sort_active = 1;1435 break;1436 case 'B':1437 show_bytes = 1;1438 break;1439 case 'd':1440 set_debug = 1;1441 if (!debug_opt_scan(optarg))1442 fatal("Invalid debug option '%s'\n", optarg);1443 break;1444 case 'D':1445 show_activity = 1;1446 break;1447 case 'e':1448 show_empty = 1;1449 break;1450 case 'f':1451 show_first_alias = 1;1452 break;1453 case 'h':1454 usage();1455 return 0;1456 case 'i':1457 show_inverted = 1;1458 break;1459 case 'l':1460 show_slab = 1;1461 break;1462 case 'L':1463 sort_loss = 1;1464 break;1465 case 'n':1466 show_numa = 1;1467 break;1468 case 'N':1469 if (optarg) {1470 output_lines = atoi(optarg);1471 if (output_lines < 1)1472 output_lines = 1;1473 }1474 break;1475 case 'o':1476 show_ops = 1;1477 break;1478 case 'r':1479 show_report = 1;1480 break;1481 case 'P':1482 sort_partial = 1;1483 break;1484 case 's':1485 shrink = 1;1486 break;1487 case 'S':1488 sort_size = 1;1489 break;1490 case 't':1491 show_track = 1;1492 break;1493 case 'T':1494 show_totals = 1;1495 break;1496 case 'U':1497 unreclaim_only = 1;1498 break;1499 case 'v':1500 validate = 1;1501 break;1502 case 'X':1503 if (output_lines == -1)1504 output_lines = 1;1505 extended_totals = 1;1506 show_bytes = 1;1507 break;1508 case 'z':1509 skip_zero = 0;1510 break;1511 case '1':1512 show_single_ref = 1;1513 break;1514 default:1515 fatal("%s: Invalid option '%c'\n", argv[0], optopt);1516 1517 }1518 1519 if (!show_slab && !show_alias && !show_track && !show_report1520 && !validate && !shrink && !set_debug && !show_ops)1521 show_slab = 1;1522 1523 if (argc > optind)1524 pattern_source = argv[optind];1525 else1526 pattern_source = ".*";1527 1528 err = regcomp(&pattern, pattern_source, REG_ICASE|REG_NOSUB);1529 if (err)1530 fatal("%s: Invalid pattern '%s' code %d\n",1531 argv[0], pattern_source, err);1532 read_slab_dir();1533 if (show_alias) {1534 alias();1535 } else if (extended_totals) {1536 xtotals();1537 } else if (show_totals) {1538 totals();1539 } else {1540 link_slabs();1541 rename_slabs();1542 sort_slabs();1543 output_slabs();1544 }1545 return 0;1546}1547