| /* |
| * slab.c - slab related functions for libproc |
| * |
| * Chris Rivera <cmrivera@ufl.edu> |
| * Robert Love <rml@tech9.net> |
| * |
| * This program is licensed under the GNU Library General Public License, v2 |
| * |
| * Copyright (C) 2003 Chris Rivera |
| * Copyright 2004, Albert Cahalan |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <limits.h> |
| #include <ctype.h> |
| |
| #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 <nodeallocs>. We don't use ": globalstat" part in both versions. |
| // |
| // Formats (we don't use "statistics" extensions) |
| // |
| // slabinfo - version: 2.1 |
| // # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \ |
| // : tunables <batchcount> <limit> <sharedfactor> \ |
| // : slabdata <active_slabs> <num_slabs> <sharedavail> |
| // |
| // slabinfo - version: 2.1 (statistics) |
| // # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \ |
| // : tunables <batchcount> <limit> <sharedfactor> \ |
| // : slabdata <active_slabs> <num_slabs> <sharedavail> \ |
| // : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> <nodeallocs> \ |
| // : cpustat <allochit> <allocmiss> <freehit> <freemiss> |
| // |
| // slabinfo - version: 2.0 |
| // # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \ |
| // : tunables <batchcount> <limit> <sharedfactor> \ |
| // : slabdata <active_slabs> <num_slabs> <sharedavail> |
| // |
| // slabinfo - version: 2.0 (statistics) |
| // # name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> \ |
| // : tunables <batchcount> <limit> <sharedfactor> \ |
| // : slabdata <active_slabs> <num_slabs> <sharedavail> \ |
| // : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> \ |
| // : cpustat <allochit> <allocmiss> <freehit> <freemiss> |
| 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"); |
| return 1; |
| } |
| |
| if (sscanf(buffer, "slabinfo - version: %d.%d", &major, &minor) != 2) { |
| fprintf(stderr, "not the good old slabinfo we know\n"); |
| 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"); |
| return 1; |
| } |
| |
| fclose(slabfile); |
| |
| return ret; |
| } |