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