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 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 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 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 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 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 222 select_types(void) 223 { 224 num_disp = num_types; 225 return (0); 226 } 227 228 int 229 select_buckets(void) 230 { 231 num_disp = num_buckets; 232 return (0); 233 } 234 235 int 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 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 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 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 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 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 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