src/usr.bin/systat/malloc.c

401 lines
9.1 KiB
C

/* $OpenBSD: malloc.c,v 1.5 2019/11/28 16:27:25 guenther 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/signal.h>
#include <sys/sysctl.h>
#include <sys/malloc.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <limits.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 FLD_TYPE_NAME FIELD_ADDR(fields_malloc,0)
#define FLD_TYPE_INUSE FIELD_ADDR(fields_malloc,1)
#define FLD_TYPE_MEMUSE FIELD_ADDR(fields_malloc,2)
#define FLD_TYPE_HIGHUSE FIELD_ADDR(fields_malloc,3)
#define FLD_TYPE_LIMIT FIELD_ADDR(fields_malloc,4)
#define FLD_TYPE_REQUESTS FIELD_ADDR(fields_malloc,5)
#define FLD_TYPE_TLIMIT FIELD_ADDR(fields_malloc,6)
#define FLD_TYPE_KLIMIT FIELD_ADDR(fields_malloc,7)
#define FLD_TYPE_SIZES FIELD_ADDR(fields_malloc,8)
#define FLD_BUCKET_SIZE FIELD_ADDR(fields_malloc,9)
#define FLD_BUCKET_REQUESTS FIELD_ADDR(fields_malloc,10)
#define FLD_BUCKET_INUSE FIELD_ADDR(fields_malloc,11)
#define FLD_BUCKET_FREE FIELD_ADDR(fields_malloc,12)
#define FLD_BUCKET_HIWAT FIELD_ADDR(fields_malloc,13)
#define FLD_BUCKET_COULDFREE FIELD_ADDR(fields_malloc,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) == -1) {
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) == -1) {
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) == -1)
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_str(FLD_TYPE_SIZES, t->buckets);
end_line();
}