xref: /openbsd-src/usr.bin/systat/malloc.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: malloc.c,v 1.1 2008/11/08 06:38:27 canacar Exp $	*/
2 /*
3  * Copyright (c) 2008 Can Erkin Acar <canacar@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/types.h>
19 #include <sys/param.h>
20 #include <sys/sysctl.h>
21 #include <sys/malloc.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "systat.h"
27 
28 void print_types(void);
29 void print_buckets(void);
30 int  read_types(void);
31 int  read_buckets(void);
32 void sort_types(void);
33 int  select_types(void);
34 int  select_buckets(void);
35 void showtype(int k);
36 void showbucket(int k);
37 
38 
39 /* qsort callbacks */
40 int sort_tname_callback(const void *s1, const void *s2);
41 int sort_treq_callback(const void *s1, const void *s2);
42 int sort_inuse_callback(const void *s1, const void *s2);
43 int sort_memuse_callback(const void *s1, const void *s2);
44 
45 #define MAX_BUCKETS 16
46 
47 struct type_info {
48 	const char *name;
49 	struct kmemstats stats;
50 	char buckets[MAX_BUCKETS];
51 };
52 
53 
54 struct type_info types[M_LAST];
55 
56 struct kmembuckets buckets[MAX_BUCKETS];
57 int bucket_sizes[MAX_BUCKETS];
58 
59 int num_types = 0;
60 int num_buckets = 0;
61 
62 /*
63  * These names are defined in <sys/malloc.h>.
64  */
65 const char *kmemnames[] = INITKMEMNAMES;
66 
67 field_def fields_malloc[] = {
68 	{"TYPE", 14, 32, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
69 	{"INUSE", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
70 	{"MEMUSE", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
71 	{"HIGHUSE", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
72 	{"LIMIT", 6, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
73 	{"REQUESTS", 8, 16, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
74 	{"TYPE LIMIT", 5, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
75 	{"KERN LIMIT", 5, 12, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
76 	{"BUCKETS", MAX_BUCKETS, MAX_BUCKETS, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
77 
78 	{"BUCKET", 8, 8, 1, FLD_ALIGN_LEFT, -1, 0, 0, 0},
79 	{"REQUESTS", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
80 	{"INUSE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
81 	{"FREE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
82 	{"HIWAT", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
83 	{"COULDFREE", 8, 24, 1, FLD_ALIGN_RIGHT, -1, 0, 0, 0},
84 };
85 
86 
87 #define FIELD_ADDR(x) (&fields_malloc[x])
88 
89 #define FLD_TYPE_NAME		FIELD_ADDR(0)
90 #define FLD_TYPE_INUSE		FIELD_ADDR(1)
91 #define FLD_TYPE_MEMUSE		FIELD_ADDR(2)
92 #define FLD_TYPE_HIGHUSE	FIELD_ADDR(3)
93 #define FLD_TYPE_LIMIT		FIELD_ADDR(4)
94 #define FLD_TYPE_REQUESTS	FIELD_ADDR(5)
95 #define FLD_TYPE_TLIMIT		FIELD_ADDR(6)
96 #define FLD_TYPE_KLIMIT		FIELD_ADDR(7)
97 #define FLD_TYPE_SIZES		FIELD_ADDR(8)
98 
99 #define FLD_BUCKET_SIZE		FIELD_ADDR(9)
100 #define FLD_BUCKET_REQUESTS	FIELD_ADDR(10)
101 #define FLD_BUCKET_INUSE	FIELD_ADDR(11)
102 #define FLD_BUCKET_FREE		FIELD_ADDR(12)
103 #define FLD_BUCKET_HIWAT	FIELD_ADDR(13)
104 #define FLD_BUCKET_COULDFREE	FIELD_ADDR(14)
105 
106 
107 
108 /* Define views */
109 field_def *view_malloc_0[] = {
110 	FLD_TYPE_NAME, FLD_TYPE_INUSE, FLD_TYPE_MEMUSE,
111 	FLD_TYPE_HIGHUSE, FLD_TYPE_LIMIT, FLD_TYPE_REQUESTS,
112 	FLD_TYPE_TLIMIT, FLD_TYPE_KLIMIT, FLD_TYPE_SIZES, NULL
113 };
114 
115 field_def *view_malloc_1[] = {
116 	FLD_BUCKET_SIZE, FLD_BUCKET_REQUESTS, FLD_BUCKET_INUSE,
117 	FLD_BUCKET_FREE, FLD_BUCKET_HIWAT, FLD_BUCKET_COULDFREE, NULL
118 };
119 
120 order_type type_order_list[] = {
121 	{"name", "name", 'N', sort_tname_callback},
122 	{"inuse", "in use", 'U', sort_inuse_callback},
123 	{"memuse", "mem use", 'S', sort_memuse_callback},
124 	{"requests", "requests", 'Q', sort_treq_callback},
125 	{NULL, NULL, 0, NULL}
126 };
127 
128 /* Define view managers */
129 struct view_manager types_mgr = {
130 	"Types", select_types, read_types, sort_types, print_header,
131 	print_types, keyboard_callback, type_order_list, type_order_list
132 };
133 
134 struct view_manager buckets_mgr = {
135 	"Buckets", select_buckets, read_buckets, NULL, print_header,
136 	print_buckets, keyboard_callback, NULL, NULL
137 };
138 
139 field_view views_malloc[] = {
140 	{view_malloc_0, "malloc", '6', &types_mgr},
141 	{view_malloc_1, "buckets", '7', &buckets_mgr},
142 	{NULL, NULL, 0, NULL}
143 };
144 
145 
146 int
147 sort_tname_callback(const void *s1, const void *s2)
148 {
149 	struct type_info *t1, *t2;
150 	t1 = (struct type_info *)s1;
151 	t2 = (struct type_info *)s2;
152 
153 	return strcmp(t1->name, t2->name) * sortdir;
154 }
155 
156 int
157 sort_treq_callback(const void *s1, const void *s2)
158 {
159 	struct type_info *t1, *t2;
160 	t1 = (struct type_info *)s1;
161 	t2 = (struct type_info *)s2;
162 
163 	if (t1->stats.ks_calls <  t2->stats.ks_calls)
164 		return sortdir;
165 	if (t1->stats.ks_calls >  t2->stats.ks_calls)
166 		return -sortdir;
167 
168 	return sort_tname_callback(s1, s2);
169 }
170 
171 int
172 sort_inuse_callback(const void *s1, const void *s2)
173 {
174 	struct type_info *t1, *t2;
175 	t1 = (struct type_info *)s1;
176 	t2 = (struct type_info *)s2;
177 
178 	if (t1->stats.ks_inuse <  t2->stats.ks_inuse)
179 		return sortdir;
180 	if (t1->stats.ks_inuse >  t2->stats.ks_inuse)
181 		return -sortdir;
182 
183 	return sort_tname_callback(s1, s2);
184 }
185 
186 int
187 sort_memuse_callback(const void *s1, const void *s2)
188 {
189 	struct type_info *t1, *t2;
190 	t1 = (struct type_info *)s1;
191 	t2 = (struct type_info *)s2;
192 
193 	if (t1->stats.ks_memuse <  t2->stats.ks_memuse)
194 		return sortdir;
195 	if (t1->stats.ks_memuse >  t2->stats.ks_memuse)
196 		return -sortdir;
197 
198 	return sort_tname_callback(s1, s2);
199 }
200 
201 
202 void
203 sort_types(void)
204 {
205 	order_type *ordering;
206 
207 	if (curr_mgr == NULL)
208 		return;
209 
210 	ordering = curr_mgr->order_curr;
211 
212 	if (ordering == NULL)
213 		return;
214 	if (ordering->func == NULL)
215 		return;
216 	if (num_types <= 0)
217 		return;
218 
219 	mergesort(types, num_types, sizeof(struct type_info), ordering->func);
220 }
221 
222 int
223 select_types(void)
224 {
225 	num_disp = num_types;
226 	return (0);
227 }
228 
229 int
230 select_buckets(void)
231 {
232 	num_disp = num_buckets;
233 	return (0);
234 }
235 
236 int
237 read_buckets(void)
238 {
239 	int mib[4];
240 	char buf[BUFSIZ], *bufp, *ap;
241 	const char *errstr;
242 	size_t siz;
243 
244 	mib[0] = CTL_KERN;
245 	mib[1] = KERN_MALLOCSTATS;
246 	mib[2] = KERN_MALLOC_BUCKETS;
247 
248 	siz = sizeof(buf);
249 	num_buckets = 0;
250 
251 	if (sysctl(mib, 3, buf, &siz, NULL, 0) < 0) {
252 		error("sysctl(kern.malloc.buckets): %s", strerror(errno));
253 		return (-1);
254 	}
255 
256 	bufp = buf;
257 	mib[2] = KERN_MALLOC_BUCKET;
258 	siz = sizeof(struct kmembuckets);
259 
260 	while ((ap = strsep(&bufp, ",")) != NULL) {
261 		if (num_buckets >= MAX_BUCKETS)
262 			break;
263 		bucket_sizes[num_buckets] = strtonum(ap, 0, INT_MAX, &errstr);
264 		if (errstr) {
265 			error("strtonum(%s): %s", ap, errstr);
266 			return (-1);
267 		}
268 		mib[3] = bucket_sizes[num_buckets];
269 		if (sysctl(mib, 4, &buckets[num_buckets], &siz,
270 			   NULL, 0) < 0) {
271 			error("sysctl(kern.malloc.bucket.%d): %s",
272 			    mib[3], strerror(errno));
273 			return (-1);
274 		}
275 		num_buckets++;
276 	}
277 
278 	return (0);
279 }
280 
281 int
282 read_types(void)
283 {
284 	struct type_info *ti;
285 	int i, j, k, mib[4];
286 	size_t siz;
287 
288 	bzero(types, sizeof(types));
289 	ti = types;
290 	siz = sizeof(struct kmemstats);
291 
292 	num_types = 0;
293 
294 	for (i = 0; i < M_LAST; i++) {
295 		mib[0] = CTL_KERN;
296 		mib[1] = KERN_MALLOCSTATS;
297 		mib[2] = KERN_MALLOC_KMEMSTATS;
298 		mib[3] = i;
299 
300 		/*
301 		 * Skip errors -- these are presumed to be unallocated
302 		 * entries.
303 		 */
304 		if (sysctl(mib, 4, &ti->stats, &siz, NULL, 0) < 0)
305 			continue;
306 
307 		if (ti->stats.ks_calls == 0)
308 			continue;
309 
310 		ti->name = kmemnames[i];
311 		j = 1 << MINBUCKET;
312 
313 		for (k = 0; k < MAX_BUCKETS; k++, j <<= 1)
314 			ti->buckets[k] = (ti->stats.ks_size & j) ? '|' : '.';
315 
316 		ti++;
317 		num_types++;
318 	}
319 
320 	return (0);
321 }
322 
323 
324 void
325 print_types(void)
326 {
327 	int n, count = 0;
328 
329 	for (n = dispstart; n < num_disp; n++) {
330 		showtype(n);
331 		count++;
332 		if (maxprint > 0 && count >= maxprint)
333 			break;	}
334 }
335 
336 void
337 print_buckets(void)
338 {
339 	int n, count = 0;
340 
341 	for (n = dispstart; n < num_disp; n++) {
342 		showbucket(n);
343 		count++;
344 		if (maxprint > 0 && count >= maxprint)
345 			break;
346 	}
347 }
348 
349 int
350 initmalloc(void)
351 {
352 	field_view *v;
353 
354 	for (v = views_malloc; v->name != NULL; v++)
355 		add_view(v);
356 
357 	read_buckets();
358 	read_types();
359 
360 	return(0);
361 }
362 
363 void
364 showbucket(int k)
365 {
366 	struct kmembuckets *kp = buckets + k;
367 
368 	if (k < 0 || k >= num_buckets)
369 		return;
370 
371 	print_fld_size(FLD_BUCKET_SIZE, bucket_sizes[k]);
372 	print_fld_size(FLD_BUCKET_INUSE, kp->kb_total - kp->kb_totalfree);
373 	print_fld_size(FLD_BUCKET_FREE, kp->kb_totalfree);
374 	print_fld_size(FLD_BUCKET_REQUESTS, kp->kb_calls);
375 	print_fld_size(FLD_BUCKET_HIWAT, kp->kb_highwat);
376 	print_fld_size(FLD_BUCKET_COULDFREE, kp->kb_couldfree);
377 
378 	end_line();
379 }
380 
381 
382 void
383 showtype(int k)
384 {
385 	struct type_info *t = types + k;
386 
387 	if (k < 0 || k >= num_types)
388 		return;
389 
390 
391 	print_fld_str(FLD_TYPE_NAME, t->name ? t->name : "undefined");
392 	print_fld_size(FLD_TYPE_INUSE, t->stats.ks_inuse);
393 	print_fld_size(FLD_TYPE_MEMUSE, t->stats.ks_memuse);
394 	print_fld_size(FLD_TYPE_HIGHUSE, t->stats.ks_maxused);
395 	print_fld_size(FLD_TYPE_LIMIT, t->stats.ks_limit);
396 	print_fld_size(FLD_TYPE_REQUESTS, t->stats.ks_calls);
397 	print_fld_size(FLD_TYPE_TLIMIT, t->stats.ks_limblocks);
398 	print_fld_size(FLD_TYPE_KLIMIT, t->stats.ks_mapblocks);
399 	print_fld_str(FLD_TYPE_SIZES, t->buckets);
400 
401 	end_line();
402 }
403