/* * slab.c - slab related functions for libproc * * Chris Rivera * Robert Love * * This program is licensed under the GNU Library General Public License, v2 * * Copyright (C) 2003 Chris Rivera * Copyright 2004, Albert Cahalan */ #include #include #include #include #include #include "slab.h" #include "procps.h" #define SLABINFO_LINE_LEN 2048 #define SLABINFO_VER_LEN 100 #define SLABINFO_FILE "/proc/slabinfo" static struct slab_info *free_index; /* * get_slabnode - allocate slab_info structures using a free list * * In the fast path, we simply return a node off the free list. In the slow * list, we malloc() a new node. The free list is never automatically reaped, * both for simplicity and because the number of slab caches is fairly * constant. */ static struct slab_info *get_slabnode(void) { struct slab_info *node; if (free_index) { node = free_index; free_index = free_index->next; } else { node = malloc(sizeof(struct slab_info)); if (!node) perror("malloc"); } return node; } /* * slab_badname_detect - return true if current slab was declared with * whitespaces for instance * FIXME :Other cases ? */ static int slab_badname_detect(const char *restrict buffer) { int numberarea=0; while (*buffer){ if((*buffer)==' ') numberarea=1; if(isalpha(*buffer)&&numberarea) return 1; buffer++; } return 0; } /* * put_slabinfo - return all allocated nodes to the free list */ void put_slabinfo(struct slab_info *head) { free_index = head; } /* * free_slabinfo - deallocate the memory associated with each node in the * slab_info linked list */ void free_slabinfo(struct slab_info *list) { while (list) { struct slab_info *temp = list->next; free(list); list = temp; } } // parse_slabinfo20 - actual parse routine for slabinfo 2.x (2.6 kernels) // Note: difference between 2.0 and 2.1 is in the ": globalstat" part where version 2.1 // has extra column . We don't use ": globalstat" part in both versions. // // Formats (we don't use "statistics" extensions) // // slabinfo - version: 2.1 // # name \ // : tunables \ // : slabdata // // slabinfo - version: 2.1 (statistics) // # name \ // : tunables \ // : slabdata \ // : globalstat \ // : cpustat // // slabinfo - version: 2.0 // # name \ // : tunables \ // : slabdata // // slabinfo - version: 2.0 (statistics) // # name \ // : tunables \ // : slabdata \ // : globalstat \ // : cpustat static int parse_slabinfo20(struct slab_info **list, struct slab_stat *stats, FILE *f) { struct slab_info *curr = NULL, *prev = NULL; char buffer[SLABINFO_LINE_LEN]; int entries = 0; int page_size = getpagesize(); stats->min_obj_size = INT_MAX; stats->max_obj_size = 0; while (fgets(buffer, SLABINFO_LINE_LEN, f)) { int assigned; if (buffer[0] == '#') continue; curr = get_slabnode(); if (!curr) break; if (entries++ == 0) *list = curr; else prev->next = curr; assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) "s %d %d %d %d %d : tunables %*d %*d %*d : \ slabdata %d %d %*d", curr->name, &curr->nr_active_objs, &curr->nr_objs, &curr->obj_size, &curr->objs_per_slab, &curr->pages_per_slab, &curr->nr_active_slabs, &curr->nr_slabs); if (assigned < 8) { fprintf(stderr, "unrecognizable data in slabinfo!\n"); curr = NULL; break; } if (curr->obj_size < stats->min_obj_size) stats->min_obj_size = curr->obj_size; if (curr->obj_size > stats->max_obj_size) stats->max_obj_size = curr->obj_size; curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; if (curr->nr_objs) { curr->use = 100 * curr->nr_active_objs / curr->nr_objs; stats->nr_active_caches++; } else curr->use = 0; stats->nr_objs += curr->nr_objs; stats->nr_active_objs += curr->nr_active_objs; stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size; stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size; stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; stats->nr_slabs += curr->nr_slabs; stats->nr_active_slabs += curr->nr_active_slabs; prev = curr; } if (!curr) { fprintf(stderr, "\rerror reading slabinfo!\n"); return 1; } curr->next = NULL; stats->nr_caches = entries; if (stats->nr_objs) stats->avg_obj_size = stats->total_size / stats->nr_objs; return 0; } /* * parse_slabinfo11 - actual parsing routine for slabinfo 1.1 (2.4 kernels) */ static int parse_slabinfo11(struct slab_info **list, struct slab_stat *stats, FILE *f) { struct slab_info *curr = NULL, *prev = NULL; char buffer[SLABINFO_LINE_LEN]; int entries = 0; int page_size = getpagesize(); stats->min_obj_size = INT_MAX; stats->max_obj_size = 0; while (fgets(buffer, SLABINFO_LINE_LEN, f)) { int assigned; curr = get_slabnode(); if (!curr) break; if (entries++ == 0) *list = curr; else prev->next = curr; assigned = sscanf(buffer, "%" STRINGIFY(SLAB_INFO_NAME_LEN) "s %d %d %d %d %d %d", curr->name, &curr->nr_active_objs, &curr->nr_objs, &curr->obj_size, &curr->nr_active_slabs, &curr->nr_slabs, &curr->pages_per_slab); if (assigned < 6) { fprintf(stderr, "unrecognizable data in your slabinfo version 1.1\n\r"); if(slab_badname_detect(buffer)) fprintf(stderr, "Found an error in cache name at line %s\n", buffer); curr = NULL; break; } if (curr->obj_size < stats->min_obj_size) stats->min_obj_size = curr->obj_size; if (curr->obj_size > stats->max_obj_size) stats->max_obj_size = curr->obj_size; curr->cache_size = (unsigned long)curr->nr_slabs * curr->pages_per_slab * page_size; if (curr->nr_objs) { curr->use = 100 * curr->nr_active_objs / curr->nr_objs; stats->nr_active_caches++; } else curr->use = 0; if (curr->obj_size) curr->objs_per_slab = curr->pages_per_slab * page_size / curr->obj_size; stats->nr_objs += curr->nr_objs; stats->nr_active_objs += curr->nr_active_objs; stats->total_size += (unsigned long)curr->nr_objs * curr->obj_size; stats->active_size += (unsigned long)curr->nr_active_objs * curr->obj_size; stats->nr_pages += curr->nr_slabs * curr->pages_per_slab; stats->nr_slabs += curr->nr_slabs; stats->nr_active_slabs += curr->nr_active_slabs; prev = curr; } if (!curr) { fprintf(stderr, "\rerror reading slabinfo!\n"); return 1; } curr->next = NULL; stats->nr_caches = entries; if (stats->nr_objs) stats->avg_obj_size = stats->total_size / stats->nr_objs; return 0; } /* * parse_slabinfo10 - actual parsing routine for slabinfo 1.0 (2.2 kernels) * * Not yet implemented. Please feel free. */ static int parse_slabinfo10(struct slab_info **list, struct slab_stat *stats, FILE *f) { (void) list, (void) stats, (void) f; fprintf(stderr, "slabinfo version 1.0 not yet supported\n"); return 1; } /* * slabinfo - parse the system's slabinfo and fill out both a linked list of * slab_info structures and the slab_stat structure * * The function returns zero on success, in which case 'list' and 'stats' are * valid. Nonzero is returned on failure and the state of 'list' and 'stats' * are undefined. */ int get_slabinfo(struct slab_info **list, struct slab_stat *stats) { FILE *slabfile; char buffer[SLABINFO_VER_LEN]; int major, minor, ret = 0; slabfile = fopen(SLABINFO_FILE, "r"); if (!slabfile) { perror("fopen " SLABINFO_FILE); return 1; } if (!fgets(buffer, SLABINFO_VER_LEN, slabfile)) { fprintf(stderr, "cannot read from slabinfo\n"); free(slabfile); return 1; } if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) { fprintf(stderr, "not the good old slabinfo we know\n"); free(slabfile); return 1; } if (major == 2) ret = parse_slabinfo20(list, stats, slabfile); else if (major == 1 && minor == 1) ret = parse_slabinfo11(list, stats, slabfile); else if (major == 1 && minor == 0) ret = parse_slabinfo10(list, stats, slabfile); else { fprintf(stderr, "unrecognizable slabinfo version\n"); free(slabfile); return 1; } fclose(slabfile); return ret; }