1/* 2 * Copyright (c) 2009 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * @OSF_COPYRIGHT@ 30 */ 31/* 32 * Mach Operating System 33 * Copyright (c) 1991,1990,1989 Carnegie Mellon University 34 * All Rights Reserved. 35 * 36 * Permission to use, copy, modify and distribute this software and its 37 * documentation is hereby granted, provided that both the copyright 38 * notice and this permission notice appear in all copies of the 39 * software, derivative works or modified versions, and any portions 40 * thereof, and that both notices appear in supporting documentation. 41 * 42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 45 * 46 * Carnegie Mellon requests users of this software to return to 47 * 48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 49 * School of Computer Science 50 * Carnegie Mellon University 51 * Pittsburgh PA 15213-3890 52 * 53 * any improvements or extensions that they make and grant Carnegie the 54 * rights to redistribute these changes. 55 */ 56/* 57 * zprint.c 58 * 59 * utility for printing out zone structures 60 * 61 * With no arguments, prints information on all zone structures. 62 * With an argument, prints information only on those zones for 63 * which the given name is a substring of the zone's name. 64 * With a "-w" flag, calculates how much much space is allocated 65 * to zones but not currently in use. 66 */ 67 68#include <stdio.h> 69#include <stdlib.h> 70#include <string.h> 71#include <mach/mach.h> 72#include <mach_debug/mach_debug.h> 73#include <mach/mach_error.h> 74#include <libutil.h> 75#include <errno.h> 76 77#define streql(a, b) (strcmp((a), (b)) == 0) 78#define strneql(a, b, n) (strncmp((a), (b), (n)) == 0) 79 80static void usage(void); 81static void printzone(mach_zone_name_t *, task_zone_info_t *); 82static void colprintzone(mach_zone_name_t *, task_zone_info_t *); 83static int find_deltas(mach_zone_name_t *, task_zone_info_t *, task_zone_info_t *, char *, int, int); 84static void colprintzoneheader(void); 85static boolean_t substr(const char *a, int alen, const char *b, int blen); 86 87static char *program; 88 89static pid_t pid = 0; 90static task_t task = TASK_NULL; 91static boolean_t ShowPid = FALSE; 92 93static boolean_t ShowDeltas = FALSE; 94static boolean_t ShowWasted = FALSE; 95static boolean_t ShowTotal = FALSE; 96static boolean_t SortZones = FALSE; 97static boolean_t ColFormat = TRUE; 98static boolean_t PrintHeader = TRUE; 99 100static unsigned long long totalsize = 0; 101static unsigned long long totalused = 0; 102static unsigned long long totalsum = 0; 103static unsigned long long pidsum = 0; 104 105static int last_time = 0; 106 107static char *zname = NULL; 108static int znamelen = 0; 109 110static void 111sigintr(__unused int signum) 112{ 113 last_time = 1; 114} 115 116static void 117usage(void) 118{ 119 fprintf(stderr, "usage: %s [-w] [-s] [-c] [-h] [-t] [-d] [-p <pid>] [name]\n", program); 120 exit(1); 121} 122 123int 124main(int argc, char **argv) 125{ 126 mach_zone_name_t *name = NULL; 127 unsigned int nameCnt = 0; 128 task_zone_info_t *info = NULL; 129 unsigned int infoCnt = 0; 130 131 task_zone_info_t *max_info = NULL; 132 char *deltas = NULL; 133 134 kern_return_t kr; 135 int i, j; 136 int first_time = 1; 137 int must_print = 1; 138 int interval = 1; 139 140 signal(SIGINT, sigintr); 141 142 program = strrchr(argv[0], '/'); 143 if (program == NULL) 144 program = argv[0]; 145 else 146 program++; 147 148 for (i = 1; i < argc; i++) { 149 if (streql(argv[i], "-d")) 150 ShowDeltas = TRUE; 151 else if (streql(argv[i], "-t")) 152 ShowTotal = TRUE; 153 else if (streql(argv[i], "-T")) 154 ShowTotal = FALSE; 155 else if (streql(argv[i], "-w")) 156 ShowWasted = TRUE; 157 else if (streql(argv[i], "-W")) 158 ShowWasted = FALSE; 159 else if (streql(argv[i], "-s")) 160 SortZones = TRUE; 161 else if (streql(argv[i], "-S")) 162 SortZones = FALSE; 163 else if (streql(argv[i], "-c")) 164 ColFormat = TRUE; 165 else if (streql(argv[i], "-C")) 166 ColFormat = FALSE; 167 else if (streql(argv[i], "-H")) 168 PrintHeader = FALSE; 169 else if (streql(argv[i], "-p")) { 170 ShowPid = TRUE; 171 if (i < argc - 1) { 172 pid = atoi(argv[i+1]); 173 i++; 174 } else 175 usage(); 176 } else if (streql(argv[i], "--")) { 177 i++; 178 break; 179 } else if (argv[i][0] == '-') 180 usage(); 181 else 182 break; 183 } 184 185 switch (argc - i) { 186 case 0: 187 zname = ""; 188 znamelen = 0; 189 break; 190 191 case 1: 192 zname = argv[i]; 193 znamelen = strlen(zname); 194 break; 195 196 default: 197 usage(); 198 } 199 200 if (ShowDeltas) { 201 SortZones = FALSE; 202 ColFormat = TRUE; 203 PrintHeader = TRUE; 204 } 205 206 if (ShowPid) { 207 kr = task_for_pid(mach_task_self(), pid, &task); 208 if (kr != KERN_SUCCESS) { 209 fprintf(stderr, "%s: task_for_pid(%d) failed: %s (try running as root)\n", 210 program, pid, mach_error_string(kr)); 211 exit(1); 212 } 213 } 214 215 for (;;) { 216 if (ShowPid) { 217 kr = task_zone_info(task, &name, &nameCnt, &info, &infoCnt); 218 if (kr != KERN_SUCCESS) { 219 fprintf(stderr, "%s: task_zone_info: %s\n", 220 program, mach_error_string(kr)); 221 exit(1); 222 } 223 } else { 224 mach_zone_info_t *zinfo = NULL; 225 226 kr = mach_zone_info(mach_host_self(), 227 &name, &nameCnt, &zinfo, &infoCnt); 228 if (kr != KERN_SUCCESS) { 229 fprintf(stderr, "%s: mach_zone_info: %s\n", 230 program, mach_error_string(kr)); 231 exit(1); 232 } 233 kr = vm_allocate(mach_task_self(), (vm_address_t *)&info, 234 infoCnt * sizeof *info, VM_FLAGS_ANYWHERE); 235 if (kr != KERN_SUCCESS) { 236 fprintf(stderr, "%s vm_allocate: %s\n", 237 program, mach_error_string(kr)); 238 exit(1); 239 } 240 for (i = 0; i < infoCnt; i++) { 241 *(mach_zone_info_t *)(info + i) = zinfo[i]; 242 info[i].tzi_caller_acct = 0; 243 info[i].tzi_task_alloc = 0; 244 info[i].tzi_task_free = 0; 245 } 246 kr = vm_deallocate(mach_task_self(), (vm_address_t) zinfo, 247 (vm_size_t) (infoCnt * sizeof *zinfo)); 248 if (kr != KERN_SUCCESS) { 249 fprintf(stderr, "%s: vm_deallocate: %s\n", 250 program, mach_error_string(kr)); 251 exit(1); 252 } 253 } 254 255 if (nameCnt != infoCnt) { 256 fprintf(stderr, "%s: mach/task_zone_info: counts not equal?\n", 257 program); 258 exit(1); 259 } 260 261 if (first_time) { 262 deltas = (char *)malloc(infoCnt); 263 max_info = (task_zone_info_t *)malloc((infoCnt * sizeof *info)); 264 } 265 266 if (SortZones) { 267 for (i = 0; i < nameCnt-1; i++) 268 for (j = i+1; j < nameCnt; j++) { 269 int wastei, wastej; 270 271 wastei = (info[i].tzi_cur_size - 272 (info[i].tzi_elem_size * 273 info[i].tzi_count)); 274 wastej = (info[j].tzi_cur_size - 275 (info[j].tzi_elem_size * 276 info[j].tzi_count)); 277 278 if (wastej > wastei) { 279 task_zone_info_t tinfo; 280 mach_zone_name_t tname; 281 282 tinfo = info[i]; 283 info[i] = info[j]; 284 info[j] = tinfo; 285 286 tname = name[i]; 287 name[i] = name[j]; 288 name[j] = tname; 289 } 290 } 291 } 292 293 must_print = find_deltas(name, info, max_info, deltas, infoCnt, first_time); 294 if (must_print) { 295 if (ColFormat) { 296 if (!first_time) 297 printf("\n"); 298 colprintzoneheader(); 299 } 300 for (i = 0; i < nameCnt; i++) { 301 if (deltas[i]) { 302 if (ColFormat) 303 colprintzone(&name[i], &info[i]); 304 else 305 printzone(&name[i], &info[i]); 306 } 307 } 308 } 309 310 first_time = 0; 311 312 if ((name != NULL) && (nameCnt != 0)) { 313 kr = vm_deallocate(mach_task_self(), (vm_address_t) name, 314 (vm_size_t) (nameCnt * sizeof *name)); 315 if (kr != KERN_SUCCESS) { 316 fprintf(stderr, "%s: vm_deallocate: %s\n", 317 program, mach_error_string(kr)); 318 exit(1); 319 } 320 } 321 322 if ((info != NULL) && (infoCnt != 0)) { 323 kr = vm_deallocate(mach_task_self(), (vm_address_t) info, 324 (vm_size_t) (infoCnt * sizeof *info)); 325 if (kr != KERN_SUCCESS) { 326 fprintf(stderr, "%s: vm_deallocate: %s\n", 327 program, mach_error_string(kr)); 328 exit(1); 329 } 330 } 331 332 if ((ShowWasted||ShowTotal) && PrintHeader && !ShowDeltas) { 333 printf("TOTAL SIZE = %llu\n", totalsize); 334 printf("TOTAL USED = %llu\n", totalused); 335 if (ShowWasted) 336 printf("TOTAL WASTED = %llu\n", totalsize - totalused); 337 if (ShowTotal) 338 printf("TOTAL ALLOCS = %llu\n", totalsum); 339 } 340 341 if (ShowDeltas == FALSE || last_time) 342 break; 343 344 sleep(interval); 345 } 346 exit(0); 347} 348 349static boolean_t 350substr(const char *a, int alen, const char *b, int blen) 351{ 352 int i; 353 354 for (i = 0; i <= blen - alen; i++) 355 if (strneql(a, b+i, alen)) 356 return TRUE; 357 358 return FALSE; 359} 360 361static void 362printzone(mach_zone_name_t *name, task_zone_info_t *info) 363{ 364 unsigned long long used, size; 365 366 printf("%.*s zone:\n", (int)sizeof name->mzn_name, name->mzn_name); 367 printf("\tcur_size: %lluK bytes (%llu elements)\n", 368 info->tzi_cur_size/1024, 369 (info->tzi_elem_size == 0) ? 0 : 370 info->tzi_cur_size/info->tzi_elem_size); 371 printf("\tmax_size: %lluK bytes (%llu elements)\n", 372 info->tzi_max_size/1024, 373 (info->tzi_elem_size == 0) ? 0 : 374 info->tzi_max_size/info->tzi_elem_size); 375 printf("\telem_size: %llu bytes\n", 376 info->tzi_elem_size); 377 printf("\t# of elems: %llu\n", 378 info->tzi_count); 379 printf("\talloc_size: %lluK bytes (%llu elements)\n", 380 info->tzi_alloc_size/1024, 381 (info->tzi_elem_size == 0) ? 0 : 382 info->tzi_alloc_size/info->tzi_elem_size); 383 if (info->tzi_exhaustible) 384 printf("\tEXHAUSTIBLE\n"); 385 if (info->tzi_collectable) 386 printf("\tCOLLECTABLE\n"); 387 if (ShowPid && info->tzi_caller_acct) 388 printf("\tCALLER ACCOUNTED\n"); 389 if (ShowPid) { 390 pidsum += info->tzi_task_alloc - info->tzi_task_free; 391 printf("\tproc_alloc_size: %8dK bytes (%llu elements)\n", 392 (int)((info->tzi_task_alloc - info->tzi_task_free)/1024), 393 (info->tzi_elem_size == 0) ? 0 : 394 (info->tzi_task_alloc - info->tzi_task_free)/info->tzi_elem_size); 395 } 396 if (ShowWasted) { 397 totalused += used = info->tzi_elem_size * info->tzi_count; 398 totalsize += size = info->tzi_cur_size; 399 printf("\t\t\t\t\tWASTED: %llu\n", size - used); 400 } 401 if (ShowTotal) { 402 totalsum += info->tzi_sum_size; 403 printf("\t\t\t\t\tTOTAL: %llu\n", totalsum); 404 if (ShowPid) 405 printf("\t\t\t\t\tPID TOTAL: %llu\n", pidsum); 406 } 407} 408 409#define PRINTK(fmt, value) \ 410 printf(fmt "K", (value) / 1024 ) /* ick */ 411 412static void 413colprintzone(mach_zone_name_t *zone_name, task_zone_info_t *info) 414{ 415 char *name = zone_name->mzn_name; 416 int j, namewidth; 417 unsigned long long used, size; 418 419 namewidth = 25; 420 if (ShowWasted || ShowTotal) { 421 namewidth -= 7; 422 } 423 for (j = 0; j < namewidth - 1 && name[j]; j++) { 424 if (name[j] == ' ') { 425 putchar('.'); 426 } else { 427 putchar(name[j]); 428 } 429 } 430 if (j == namewidth - 1) { 431 if (name[j]) { 432 putchar('$'); 433 } else { 434 putchar(' '); 435 } 436 } else { 437 for (; j < namewidth; j++) { 438 putchar(' '); 439 } 440 } 441 printf(" %6llu", info->tzi_elem_size); 442 PRINTK(" %10llu", info->tzi_cur_size); 443 if (info->tzi_max_size / 1024 > 9999999) { 444 printf(" --------"); 445 } else { 446 PRINTK(" %10llu", info->tzi_max_size); 447 } 448 printf(" %10llu", info->tzi_cur_size / info->tzi_elem_size); 449 if (info->tzi_max_size / 1024 >= 999999999) { 450 printf(" ----------"); 451 } else { 452 printf(" %11llu", info->tzi_max_size / info->tzi_elem_size); 453 } 454 printf(" %11llu", info->tzi_count); 455 PRINTK(" %5llu", info->tzi_alloc_size); 456 printf(" %6llu", info->tzi_alloc_size / info->tzi_elem_size); 457 458 totalused += used = info->tzi_elem_size * info->tzi_count; 459 totalsize += size = info->tzi_cur_size; 460 totalsum += info->tzi_sum_size; 461 462 printf(" %c%c%c", 463 (info->tzi_exhaustible ? 'X' : ' '), 464 (info->tzi_caller_acct ? 'A' : ' '), 465 (info->tzi_collectable ? 'C' : ' ')); 466 if (ShowWasted) { 467 PRINTK(" %8llu", size - used); 468 } 469 if (ShowPid) { 470 printf("%8dK", (int)((info->tzi_task_alloc - info->tzi_task_free)/1024)); 471 } 472 if (ShowTotal) { 473 if (info->tzi_sum_size < 1024) 474 printf(" %16lluB", info->tzi_sum_size); 475 else 476 PRINTK(" %16llu", info->tzi_sum_size); 477 } 478 printf("\n"); 479} 480 481static void 482colprintzoneheader(void) 483{ 484 if (! PrintHeader) { 485 return; 486 } 487 printf("%s elem cur max cur max" 488 " cur alloc alloc %s%s\n", 489 (ShowWasted||ShowTotal)? "" : " ", 490 (ShowWasted)? " ":"", 491 (ShowPid) ? " PID" : "" ); 492 printf("zone name%s size size size #elts #elts" 493 " inuse size count ", (ShowWasted||ShowTotal)? " " : " " ); 494 if (ShowWasted) 495 printf(" wasted"); 496 if (ShowPid) 497 printf(" Allocs"); 498 if (ShowTotal) 499 printf(" Total Allocs"); 500 printf("\n%s-------------------------------------------------------" 501 "-----------------------------------------------", 502 (ShowWasted||ShowTotal)? "" : "-------"); 503 if (ShowWasted) 504 printf("----------"); 505 if (ShowPid) 506 printf("---------"); 507 if (ShowTotal) 508 printf("------------------"); 509 printf("\n"); 510} 511 512int 513find_deltas(mach_zone_name_t *name, task_zone_info_t *info, task_zone_info_t *max_info, 514 char *deltas, int cnt, int first_time) 515{ 516 int i; 517 int found_one = 0; 518 519 for (i = 0; i < cnt; i++) { 520 deltas[i] = 0; 521 if (substr(zname, znamelen, name[i].mzn_name, 522 strnlen(name[i].mzn_name, sizeof name[i].mzn_name))) { 523 if (first_time || info->tzi_cur_size > max_info->tzi_cur_size || 524 (ShowTotal && ((info->tzi_sum_size >> 1) > max_info->tzi_sum_size))) { 525 max_info->tzi_cur_size = info->tzi_cur_size; 526 max_info->tzi_sum_size = info->tzi_sum_size; 527 deltas[i] = 1; 528 found_one = 1; 529 } 530 } 531 info++; 532 max_info++; 533 } 534 return(found_one); 535} 536