/* $OpenBSD: malloc.c,v 1.1 2008/11/08 06:38:27 canacar Exp $ */ /* * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <sys/types.h> #include <sys/param.h> #include <sys/sysctl.h> #include <sys/malloc.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include "systat.h" void print_types(void); void print_buckets(void); int read_types(void); int read_buckets(void); void sort_types(void); int select_types(void); int select_buckets(void); void showtype(int k); void showbucket(int k); /* qsort callbacks */ int sort_tname_callback(const void *s1, const void *s2); int sort_treq_callback(const void *s1, const void *s2); int sort_inuse_callback(const void *s1, const void *s2); int sort_memuse_callback(const void *s1, const void *s2); #define MAX_BUCKETS 16 struct type_info { const char *name; struct kmemstats stats; char buckets[MAX_BUCKETS]; }; struct type_info types[M_LAST]; struct kmembuckets buckets[MAX_BUCKETS]; int bucket_sizes[MAX_BUCKETS]; int num_types = 0; int num_buckets = 0; /* * These names are defined in <sys/malloc.h>. */ const char *kmemnames[] = INITKMEMNAMES; field_def fields_malloc[] = { {"TYPE", 14, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"INUSE", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"MEMUSE", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"HIGHUSE", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"LIMIT", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"REQUESTS", 8, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"TYPE LIMIT", 5, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"KERN LIMIT", 5, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"BUCKETS", MAX_BUCKETS, MAX_BUCKETS, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"BUCKET", 8, 8, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0}, {"REQUESTS", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"INUSE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"FREE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"HIWAT", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, {"COULDFREE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0}, }; #define FIELD_ADDR(x) (&fields_malloc[x]) #define FLD_TYPE_NAME FIELD_ADDR(0) #define FLD_TYPE_INUSE FIELD_ADDR(1) #define FLD_TYPE_MEMUSE FIELD_ADDR(2) #define FLD_TYPE_HIGHUSE FIELD_ADDR(3) #define FLD_TYPE_LIMIT FIELD_ADDR(4) #define FLD_TYPE_REQUESTS FIELD_ADDR(5) #define FLD_TYPE_TLIMIT FIELD_ADDR(6) #define FLD_TYPE_KLIMIT FIELD_ADDR(7) #define FLD_TYPE_SIZES FIELD_ADDR(8) #define FLD_BUCKET_SIZE FIELD_ADDR(9) #define FLD_BUCKET_REQUESTS FIELD_ADDR(10) #define FLD_BUCKET_INUSE FIELD_ADDR(11) #define FLD_BUCKET_FREE FIELD_ADDR(12) #define FLD_BUCKET_HIWAT FIELD_ADDR(13) #define FLD_BUCKET_COULDFREE FIELD_ADDR(14) /* Define views */ field_def *view_malloc_0[] = { FLD_TYPE_NAME, FLD_TYPE_INUSE, FLD_TYPE_MEMUSE, FLD_TYPE_HIGHUSE, FLD_TYPE_LIMIT, FLD_TYPE_REQUESTS, FLD_TYPE_TLIMIT, FLD_TYPE_KLIMIT, FLD_TYPE_SIZES, NULL }; field_def *view_malloc_1[] = { FLD_BUCKET_SIZE, FLD_BUCKET_REQUESTS, FLD_BUCKET_INUSE, FLD_BUCKET_FREE, FLD_BUCKET_HIWAT, FLD_BUCKET_COULDFREE, NULL }; order_type type_order_list[] = { {"name", "name", 'N', sort_tname_callback}, {"inuse", "in use", 'U', sort_inuse_callback}, {"memuse", "mem use", 'S', sort_memuse_callback}, {"requests", "requests", 'Q', sort_treq_callback}, {NULL, NULL, 0, NULL} }; /* Define view managers */ struct view_manager types_mgr = { "Types", select_types, read_types, sort_types, print_header, print_types, keyboard_callback, type_order_list, type_order_list }; struct view_manager buckets_mgr = { "Buckets", select_buckets, read_buckets, NULL, print_header, print_buckets, keyboard_callback, NULL, NULL }; field_view views_malloc[] = { {view_malloc_0, "malloc", '6', &types_mgr}, {view_malloc_1, "buckets", '7', &buckets_mgr}, {NULL, NULL, 0, NULL} }; int sort_tname_callback(const void *s1, const void *s2) { struct type_info *t1, *t2; t1 = (struct type_info *)s1; t2 = (struct type_info *)s2; return strcmp(t1->name, t2->name) * sortdir; } int sort_treq_callback(const void *s1, const void *s2) { struct type_info *t1, *t2; t1 = (struct type_info *)s1; t2 = (struct type_info *)s2; if (t1->stats.ks_calls < t2->stats.ks_calls) return sortdir; if (t1->stats.ks_calls > t2->stats.ks_calls) return -sortdir; return sort_tname_callback(s1, s2); } int sort_inuse_callback(const void *s1, const void *s2) { struct type_info *t1, *t2; t1 = (struct type_info *)s1; t2 = (struct type_info *)s2; if (t1->stats.ks_inuse < t2->stats.ks_inuse) return sortdir; if (t1->stats.ks_inuse > t2->stats.ks_inuse) return -sortdir; return sort_tname_callback(s1, s2); } int sort_memuse_callback(const void *s1, const void *s2) { struct type_info *t1, *t2; t1 = (struct type_info *)s1; t2 = (struct type_info *)s2; if (t1->stats.ks_memuse < t2->stats.ks_memuse) return sortdir; if (t1->stats.ks_memuse > t2->stats.ks_memuse) return -sortdir; return sort_tname_callback(s1, s2); } void sort_types(void) { order_type *ordering; if (curr_mgr == NULL) return; ordering = curr_mgr->order_curr; if (ordering == NULL) return; if (ordering->func == NULL) return; if (num_types <= 0) return; mergesort(types, num_types, sizeof(struct type_info), ordering->func); } int select_types(void) { num_disp = num_types; return (0); } int select_buckets(void) { num_disp = num_buckets; return (0); } int read_buckets(void) { int mib[4]; char buf[BUFSIZ], *bufp, *ap; const char *errstr; size_t siz; mib[0] = CTL_KERN; mib[1] = KERN_MALLOCSTATS; mib[2] = KERN_MALLOC_BUCKETS; siz = sizeof(buf); num_buckets = 0; if (sysctl(mib, 3, buf, &siz, NULL, 0) < 0) { error("sysctl(kern.malloc.buckets): %s", strerror(errno)); return (-1); } bufp = buf; mib[2] = KERN_MALLOC_BUCKET; siz = sizeof(struct kmembuckets); while ((ap = strsep(&bufp, ",")) != NULL) { if (num_buckets >= MAX_BUCKETS) break; bucket_sizes[num_buckets] = strtonum(ap, 0, INT_MAX, &errstr); if (errstr) { error("strtonum(%s): %s", ap, errstr); return (-1); } mib[3] = bucket_sizes[num_buckets]; if (sysctl(mib, 4, &buckets[num_buckets], &siz, NULL, 0) < 0) { error("sysctl(kern.malloc.bucket.%d): %s", mib[3], strerror(errno)); return (-1); } num_buckets++; } return (0); } int read_types(void) { struct type_info *ti; int i, j, k, mib[4]; size_t siz; bzero(types, sizeof(types)); ti = types; siz = sizeof(struct kmemstats); num_types = 0; for (i = 0; i < M_LAST; i++) { mib[0] = CTL_KERN; mib[1] = KERN_MALLOCSTATS; mib[2] = KERN_MALLOC_KMEMSTATS; mib[3] = i; /* * Skip errors -- these are presumed to be unallocated * entries. */ if (sysctl(mib, 4, &ti->stats, &siz, NULL, 0) < 0) continue; if (ti->stats.ks_calls == 0) continue; ti->name = kmemnames[i]; j = 1 << MINBUCKET; for (k = 0; k < MAX_BUCKETS; k++, j <<= 1) ti->buckets[k] = (ti->stats.ks_size & j) ? '|' : '.'; ti++; num_types++; } return (0); } void print_types(void) { int n, count = 0; for (n = dispstart; n < num_disp; n++) { showtype(n); count++; if (maxprint > 0 && count >= maxprint) break; } } void print_buckets(void) { int n, count = 0; for (n = dispstart; n < num_disp; n++) { showbucket(n); count++; if (maxprint > 0 && count >= maxprint) break; } } int initmalloc(void) { field_view *v; for (v = views_malloc; v->name != NULL; v++) add_view(v); read_buckets(); read_types(); return(0); } void showbucket(int k) { struct kmembuckets *kp = buckets + k; if (k < 0 || k >= num_buckets) return; print_fld_size(FLD_BUCKET_SIZE, bucket_sizes[k]); print_fld_size(FLD_BUCKET_INUSE, kp->kb_total - kp->kb_totalfree); print_fld_size(FLD_BUCKET_FREE, kp->kb_totalfree); print_fld_size(FLD_BUCKET_REQUESTS, kp->kb_calls); print_fld_size(FLD_BUCKET_HIWAT, kp->kb_highwat); print_fld_size(FLD_BUCKET_COULDFREE, kp->kb_couldfree); end_line(); } void showtype(int k) { struct type_info *t = types + k; if (k < 0 || k >= num_types) return; print_fld_str(FLD_TYPE_NAME, t->name ? t->name : "undefined"); print_fld_size(FLD_TYPE_INUSE, t->stats.ks_inuse); print_fld_size(FLD_TYPE_MEMUSE, t->stats.ks_memuse); print_fld_size(FLD_TYPE_HIGHUSE, t->stats.ks_maxused); print_fld_size(FLD_TYPE_LIMIT, t->stats.ks_limit); print_fld_size(FLD_TYPE_REQUESTS, t->stats.ks_calls); print_fld_size(FLD_TYPE_TLIMIT, t->stats.ks_limblocks); print_fld_size(FLD_TYPE_KLIMIT, t->stats.ks_mapblocks); print_fld_str(FLD_TYPE_SIZES, t->buckets); end_line(); }