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