xref: /openbsd-src/usr.bin/systat/malloc.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: malloc.c,v 1.2 2011/03/02 06:48:17 jasper 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 FLD_TYPE_NAME		FIELD_ADDR(fields_malloc,0)
88 #define FLD_TYPE_INUSE		FIELD_ADDR(fields_malloc,1)
89 #define FLD_TYPE_MEMUSE		FIELD_ADDR(fields_malloc,2)
90 #define FLD_TYPE_HIGHUSE	FIELD_ADDR(fields_malloc,3)
91 #define FLD_TYPE_LIMIT		FIELD_ADDR(fields_malloc,4)
92 #define FLD_TYPE_REQUESTS	FIELD_ADDR(fields_malloc,5)
93 #define FLD_TYPE_TLIMIT		FIELD_ADDR(fields_malloc,6)
94 #define FLD_TYPE_KLIMIT		FIELD_ADDR(fields_malloc,7)
95 #define FLD_TYPE_SIZES		FIELD_ADDR(fields_malloc,8)
96 
97 #define FLD_BUCKET_SIZE		FIELD_ADDR(fields_malloc,9)
98 #define FLD_BUCKET_REQUESTS	FIELD_ADDR(fields_malloc,10)
99 #define FLD_BUCKET_INUSE	FIELD_ADDR(fields_malloc,11)
100 #define FLD_BUCKET_FREE		FIELD_ADDR(fields_malloc,12)
101 #define FLD_BUCKET_HIWAT	FIELD_ADDR(fields_malloc,13)
102 #define FLD_BUCKET_COULDFREE	FIELD_ADDR(fields_malloc,14)
103 
104 
105 
106 /* Define views */
107 field_def *view_malloc_0[] = {
108 	FLD_TYPE_NAME, FLD_TYPE_INUSE, FLD_TYPE_MEMUSE,
109 	FLD_TYPE_HIGHUSE, FLD_TYPE_LIMIT, FLD_TYPE_REQUESTS,
110 	FLD_TYPE_TLIMIT, FLD_TYPE_KLIMIT, FLD_TYPE_SIZES, NULL
111 };
112 
113 field_def *view_malloc_1[] = {
114 	FLD_BUCKET_SIZE, FLD_BUCKET_REQUESTS, FLD_BUCKET_INUSE,
115 	FLD_BUCKET_FREE, FLD_BUCKET_HIWAT, FLD_BUCKET_COULDFREE, NULL
116 };
117 
118 order_type type_order_list[] = {
119 	{"name", "name", 'N', sort_tname_callback},
120 	{"inuse", "in use", 'U', sort_inuse_callback},
121 	{"memuse", "mem use", 'S', sort_memuse_callback},
122 	{"requests", "requests", 'Q', sort_treq_callback},
123 	{NULL, NULL, 0, NULL}
124 };
125 
126 /* Define view managers */
127 struct view_manager types_mgr = {
128 	"Types", select_types, read_types, sort_types, print_header,
129 	print_types, keyboard_callback, type_order_list, type_order_list
130 };
131 
132 struct view_manager buckets_mgr = {
133 	"Buckets", select_buckets, read_buckets, NULL, print_header,
134 	print_buckets, keyboard_callback, NULL, NULL
135 };
136 
137 field_view views_malloc[] = {
138 	{view_malloc_0, "malloc", '6', &types_mgr},
139 	{view_malloc_1, "buckets", '7', &buckets_mgr},
140 	{NULL, NULL, 0, NULL}
141 };
142 
143 
144 int
145 sort_tname_callback(const void *s1, const void *s2)
146 {
147 	struct type_info *t1, *t2;
148 	t1 = (struct type_info *)s1;
149 	t2 = (struct type_info *)s2;
150 
151 	return strcmp(t1->name, t2->name) * sortdir;
152 }
153 
154 int
155 sort_treq_callback(const void *s1, const void *s2)
156 {
157 	struct type_info *t1, *t2;
158 	t1 = (struct type_info *)s1;
159 	t2 = (struct type_info *)s2;
160 
161 	if (t1->stats.ks_calls <  t2->stats.ks_calls)
162 		return sortdir;
163 	if (t1->stats.ks_calls >  t2->stats.ks_calls)
164 		return -sortdir;
165 
166 	return sort_tname_callback(s1, s2);
167 }
168 
169 int
170 sort_inuse_callback(const void *s1, const void *s2)
171 {
172 	struct type_info *t1, *t2;
173 	t1 = (struct type_info *)s1;
174 	t2 = (struct type_info *)s2;
175 
176 	if (t1->stats.ks_inuse <  t2->stats.ks_inuse)
177 		return sortdir;
178 	if (t1->stats.ks_inuse >  t2->stats.ks_inuse)
179 		return -sortdir;
180 
181 	return sort_tname_callback(s1, s2);
182 }
183 
184 int
185 sort_memuse_callback(const void *s1, const void *s2)
186 {
187 	struct type_info *t1, *t2;
188 	t1 = (struct type_info *)s1;
189 	t2 = (struct type_info *)s2;
190 
191 	if (t1->stats.ks_memuse <  t2->stats.ks_memuse)
192 		return sortdir;
193 	if (t1->stats.ks_memuse >  t2->stats.ks_memuse)
194 		return -sortdir;
195 
196 	return sort_tname_callback(s1, s2);
197 }
198 
199 
200 void
201 sort_types(void)
202 {
203 	order_type *ordering;
204 
205 	if (curr_mgr == NULL)
206 		return;
207 
208 	ordering = curr_mgr->order_curr;
209 
210 	if (ordering == NULL)
211 		return;
212 	if (ordering->func == NULL)
213 		return;
214 	if (num_types <= 0)
215 		return;
216 
217 	mergesort(types, num_types, sizeof(struct type_info), ordering->func);
218 }
219 
220 int
221 select_types(void)
222 {
223 	num_disp = num_types;
224 	return (0);
225 }
226 
227 int
228 select_buckets(void)
229 {
230 	num_disp = num_buckets;
231 	return (0);
232 }
233 
234 int
235 read_buckets(void)
236 {
237 	int mib[4];
238 	char buf[BUFSIZ], *bufp, *ap;
239 	const char *errstr;
240 	size_t siz;
241 
242 	mib[0] = CTL_KERN;
243 	mib[1] = KERN_MALLOCSTATS;
244 	mib[2] = KERN_MALLOC_BUCKETS;
245 
246 	siz = sizeof(buf);
247 	num_buckets = 0;
248 
249 	if (sysctl(mib, 3, buf, &siz, NULL, 0) < 0) {
250 		error("sysctl(kern.malloc.buckets): %s", strerror(errno));
251 		return (-1);
252 	}
253 
254 	bufp = buf;
255 	mib[2] = KERN_MALLOC_BUCKET;
256 	siz = sizeof(struct kmembuckets);
257 
258 	while ((ap = strsep(&bufp, ",")) != NULL) {
259 		if (num_buckets >= MAX_BUCKETS)
260 			break;
261 		bucket_sizes[num_buckets] = strtonum(ap, 0, INT_MAX, &errstr);
262 		if (errstr) {
263 			error("strtonum(%s): %s", ap, errstr);
264 			return (-1);
265 		}
266 		mib[3] = bucket_sizes[num_buckets];
267 		if (sysctl(mib, 4, &buckets[num_buckets], &siz,
268 			   NULL, 0) < 0) {
269 			error("sysctl(kern.malloc.bucket.%d): %s",
270 			    mib[3], strerror(errno));
271 			return (-1);
272 		}
273 		num_buckets++;
274 	}
275 
276 	return (0);
277 }
278 
279 int
280 read_types(void)
281 {
282 	struct type_info *ti;
283 	int i, j, k, mib[4];
284 	size_t siz;
285 
286 	bzero(types, sizeof(types));
287 	ti = types;
288 	siz = sizeof(struct kmemstats);
289 
290 	num_types = 0;
291 
292 	for (i = 0; i < M_LAST; i++) {
293 		mib[0] = CTL_KERN;
294 		mib[1] = KERN_MALLOCSTATS;
295 		mib[2] = KERN_MALLOC_KMEMSTATS;
296 		mib[3] = i;
297 
298 		/*
299 		 * Skip errors -- these are presumed to be unallocated
300 		 * entries.
301 		 */
302 		if (sysctl(mib, 4, &ti->stats, &siz, NULL, 0) < 0)
303 			continue;
304 
305 		if (ti->stats.ks_calls == 0)
306 			continue;
307 
308 		ti->name = kmemnames[i];
309 		j = 1 << MINBUCKET;
310 
311 		for (k = 0; k < MAX_BUCKETS; k++, j <<= 1)
312 			ti->buckets[k] = (ti->stats.ks_size & j) ? '|' : '.';
313 
314 		ti++;
315 		num_types++;
316 	}
317 
318 	return (0);
319 }
320 
321 
322 void
323 print_types(void)
324 {
325 	int n, count = 0;
326 
327 	for (n = dispstart; n < num_disp; n++) {
328 		showtype(n);
329 		count++;
330 		if (maxprint > 0 && count >= maxprint)
331 			break;	}
332 }
333 
334 void
335 print_buckets(void)
336 {
337 	int n, count = 0;
338 
339 	for (n = dispstart; n < num_disp; n++) {
340 		showbucket(n);
341 		count++;
342 		if (maxprint > 0 && count >= maxprint)
343 			break;
344 	}
345 }
346 
347 int
348 initmalloc(void)
349 {
350 	field_view *v;
351 
352 	for (v = views_malloc; v->name != NULL; v++)
353 		add_view(v);
354 
355 	read_buckets();
356 	read_types();
357 
358 	return(0);
359 }
360 
361 void
362 showbucket(int k)
363 {
364 	struct kmembuckets *kp = buckets + k;
365 
366 	if (k < 0 || k >= num_buckets)
367 		return;
368 
369 	print_fld_size(FLD_BUCKET_SIZE, bucket_sizes[k]);
370 	print_fld_size(FLD_BUCKET_INUSE, kp->kb_total - kp->kb_totalfree);
371 	print_fld_size(FLD_BUCKET_FREE, kp->kb_totalfree);
372 	print_fld_size(FLD_BUCKET_REQUESTS, kp->kb_calls);
373 	print_fld_size(FLD_BUCKET_HIWAT, kp->kb_highwat);
374 	print_fld_size(FLD_BUCKET_COULDFREE, kp->kb_couldfree);
375 
376 	end_line();
377 }
378 
379 
380 void
381 showtype(int k)
382 {
383 	struct type_info *t = types + k;
384 
385 	if (k < 0 || k >= num_types)
386 		return;
387 
388 
389 	print_fld_str(FLD_TYPE_NAME, t->name ? t->name : "undefined");
390 	print_fld_size(FLD_TYPE_INUSE, t->stats.ks_inuse);
391 	print_fld_size(FLD_TYPE_MEMUSE, t->stats.ks_memuse);
392 	print_fld_size(FLD_TYPE_HIGHUSE, t->stats.ks_maxused);
393 	print_fld_size(FLD_TYPE_LIMIT, t->stats.ks_limit);
394 	print_fld_size(FLD_TYPE_REQUESTS, t->stats.ks_calls);
395 	print_fld_size(FLD_TYPE_TLIMIT, t->stats.ks_limblocks);
396 	print_fld_size(FLD_TYPE_KLIMIT, t->stats.ks_mapblocks);
397 	print_fld_str(FLD_TYPE_SIZES, t->buckets);
398 
399 	end_line();
400 }
401