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