jcs's openbsd hax
openbsd
at jcs 400 lines 9.4 kB view raw
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}