jcs's openbsd hax
openbsd
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
29void print_types(void);
30void print_buckets(void);
31int read_types(void);
32int read_buckets(void);
33void sort_types(void);
34int select_types(void);
35int select_buckets(void);
36void showtype(int k);
37void showbucket(int k);
38
39
40/* qsort callbacks */
41int sort_tname_callback(const void *s1, const void *s2);
42int sort_treq_callback(const void *s1, const void *s2);
43int sort_inuse_callback(const void *s1, const void *s2);
44int sort_memuse_callback(const void *s1, const void *s2);
45
46#define MAX_BUCKETS 16
47
48struct type_info {
49 const char *name;
50 struct kmemstats stats;
51 char buckets[MAX_BUCKETS];
52};
53
54
55struct type_info types[M_LAST];
56
57struct kmembuckets buckets[MAX_BUCKETS];
58int bucket_sizes[MAX_BUCKETS];
59
60int num_types = 0;
61int num_buckets = 0;
62
63/*
64 * These names are defined in <sys/malloc.h>.
65 */
66const char *kmemnames[] = INITKMEMNAMES;
67
68field_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 */
108field_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
114field_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
119order_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 */
128struct 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
133struct view_manager buckets_mgr = {
134 "Buckets", select_buckets, read_buckets, NULL, print_header,
135 print_buckets, keyboard_callback, NULL, NULL
136};
137
138field_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
145int
146sort_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
155int
156sort_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
170int
171sort_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
185int
186sort_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
201void
202sort_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
221int
222select_types(void)
223{
224 num_disp = num_types;
225 return (0);
226}
227
228int
229select_buckets(void)
230{
231 num_disp = num_buckets;
232 return (0);
233}
234
235int
236read_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
280int
281read_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
323void
324print_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
335void
336print_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
348int
349initmalloc(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
362void
363showbucket(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
381void
382showtype(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}