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