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