]> git.saurik.com Git - apple/system_cmds.git/blob - zprint.tproj/zprint.c
8d095d2d23744bf44c0e49310d3d1b063715bcbf
[apple/system_cmds.git] / zprint.tproj / zprint.c
1 /*
2 * Copyright (c) 2009-2016 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 <vm_statistics.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <string.h>
72 #include <mach/mach.h>
73 #include <mach_debug/mach_debug.h>
74 #include <mach/mach_error.h>
75 #include <libutil.h>
76 #include <errno.h>
77 #include <sysexits.h>
78 #include <getopt.h>
79 #include <malloc/malloc.h>
80 #include <Kernel/IOKit/IOKitDebug.h>
81 #include <Kernel/libkern/OSKextLibPrivate.h>
82 #include <IOKit/IOKitLib.h>
83 #include <IOKit/IOKitKeys.h>
84 #include <IOKit/kext/OSKext.h>
85 #include <CoreFoundation/CoreFoundation.h>
86 #include <CoreSymbolication/CoreSymbolication.h>
87
88 #define streql(a, b) (strcmp((a), (b)) == 0)
89 #define strneql(a, b, n) (strncmp((a), (b), (n)) == 0)
90 #define PRINTK(fmt, value) \
91 printf(fmt "K", (value) / 1024 ) /* ick */
92
93 static void usage(FILE *stream);
94 static void printzone(mach_zone_name_t *, mach_zone_info_t *);
95 static void colprintzone(mach_zone_name_t *, mach_zone_info_t *);
96 static int find_deltas(mach_zone_name_t *, mach_zone_info_t *, mach_zone_info_t *, char *, int, int);
97 static void colprintzoneheader(void);
98 static boolean_t substr(const char *a, size_t alen, const char *b, size_t blen);
99
100 static int SortName(void * thunk, const void * left, const void * right);
101 static int SortSize(void * thunk, const void * left, const void * right);
102 static void PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt,
103 mach_zone_info_t *zoneInfo, mach_zone_name_t *zoneNames,
104 unsigned int zoneCnt, uint64_t zoneElements,
105 int (*func)(void *, const void *, const void *), boolean_t column);
106
107 static char *program;
108
109 static boolean_t ShowDeltas = FALSE;
110 static boolean_t ShowWasted = FALSE;
111 static boolean_t ShowTotal = FALSE;
112 static boolean_t ShowLarge = TRUE;
113 static boolean_t SortZones = FALSE;
114 static boolean_t ColFormat = TRUE;
115 static boolean_t PrintHeader = TRUE;
116
117 static unsigned long long totalsize = 0;
118 static unsigned long long totalused = 0;
119 static unsigned long long totalsum = 0;
120 static unsigned long long totalfragmented = 0;
121 static unsigned long long totalcollectable = 0;
122
123 static int last_time = 0;
124
125 static char *zname = NULL;
126 static size_t znamelen = 0;
127
128 #define LEFTALIGN -1
129 #define RIGHTALIGN 1
130
131 typedef struct {
132 char *line1;
133 char *line2;
134 int colwidth;
135 int alignment;
136 bool visible;
137 } column_format;
138
139 enum {
140 COL_ZONE_NAME,
141 COL_ELEM_SIZE,
142 COL_CUR_SIZE,
143 COL_MAX_SIZE,
144 COL_CUR_ELTS,
145 COL_MAX_ELTS,
146 COL_CUR_INUSE,
147 COL_ALLOC_SIZE,
148 COL_ALLOC_COUNT,
149 COL_ZONE_FLAGS,
150 COL_FRAG_SIZE,
151 COL_FREE_SIZE,
152 COL_TOTAL_ALLOCS,
153 COL_MAX
154 };
155
156 /*
157 * The order in which the columns appear below should match
158 * the order in which the values are printed in colprintzone().
159 */
160 static column_format columns[] = {
161 [COL_ZONE_NAME] = { "", "zone name", 25, LEFTALIGN, true },
162 [COL_ELEM_SIZE] = { "elem", "size", 6, RIGHTALIGN, true },
163 [COL_CUR_SIZE] = { "cur", "size", 11, RIGHTALIGN, true },
164 [COL_MAX_SIZE] = { "max", "size", 11, RIGHTALIGN, true },
165 [COL_CUR_ELTS] = { "cur", "#elts", 10, RIGHTALIGN, true },
166 [COL_MAX_ELTS] = { "max", "#elts", 11, RIGHTALIGN, true },
167 [COL_CUR_INUSE] = { "cur", "inuse", 11, RIGHTALIGN, true },
168 [COL_ALLOC_SIZE] = { "alloc", "size", 6, RIGHTALIGN, true },
169 [COL_ALLOC_COUNT] = { "alloc", "count", 6, RIGHTALIGN, true },
170 [COL_ZONE_FLAGS] = { "", "", 2, RIGHTALIGN, true },
171 /* additional columns for special flags, not visible by default */
172 [COL_FRAG_SIZE] = { "frag", "size", 9, RIGHTALIGN, false },
173 [COL_FREE_SIZE] = { "free", "size", 9, RIGHTALIGN, false },
174 [COL_TOTAL_ALLOCS] = { "total", "allocs", 17, RIGHTALIGN, false }
175 };
176
177 static void
178 sigintr(__unused int signum)
179 {
180 last_time = 1;
181 }
182
183 static void
184 usage(FILE *stream)
185 {
186 fprintf(stream, "usage: %s [-w] [-s] [-c] [-h] [-H] [-t] [-d] [-l] [-L] [name]\n\n", program);
187 fprintf(stream, "\t-w\tshow wasted memory for each zone\n");
188 fprintf(stream, "\t-s\tsort zones by wasted memory\n");
189 fprintf(stream, "\t-c\t(default) display output formatted in columns\n");
190 fprintf(stream, "\t-h\tdisplay this help message\n");
191 fprintf(stream, "\t-H\thide column names\n");
192 fprintf(stream, "\t-t\tdisplay the total size of allocations over the life of the zone\n");
193 fprintf(stream, "\t-d\tdisplay deltas over time\n");
194 fprintf(stream, "\t-l\t(default) display wired memory info after zone info\n");
195 fprintf(stream, "\t-L\tdo not show wired memory info, only show zone info\n");
196 fprintf(stream, "\nAny option (including default options) can be overridden by specifying the option in upper-case.\n\n");
197 exit(stream != stdout);
198 }
199
200 int
201 main(int argc, char **argv)
202 {
203 mach_zone_name_t *name = NULL;
204 unsigned int nameCnt = 0;
205 mach_zone_info_t *info = NULL;
206 unsigned int infoCnt = 0;
207 mach_memory_info_t *wiredInfo = NULL;
208 unsigned int wiredInfoCnt = 0;
209 mach_zone_info_t *max_info = NULL;
210 char *deltas = NULL;
211 uint64_t zoneElements;
212
213 kern_return_t kr;
214 int i, j;
215 int first_time = 1;
216 int must_print = 1;
217 int interval = 1;
218
219 signal(SIGINT, sigintr);
220
221 program = strrchr(argv[0], '/');
222 if (program == NULL)
223 program = argv[0];
224 else
225 program++;
226
227 for (i = 1; i < argc; i++) {
228 if (streql(argv[i], "-d"))
229 ShowDeltas = TRUE;
230 else if (streql(argv[i], "-t"))
231 ShowTotal = TRUE;
232 else if (streql(argv[i], "-T"))
233 ShowTotal = FALSE;
234 else if (streql(argv[i], "-w"))
235 ShowWasted = TRUE;
236 else if (streql(argv[i], "-W"))
237 ShowWasted = FALSE;
238 else if (streql(argv[i], "-l"))
239 ShowLarge = TRUE;
240 else if (streql(argv[i], "-L"))
241 ShowLarge = FALSE;
242 else if (streql(argv[i], "-s"))
243 SortZones = TRUE;
244 else if (streql(argv[i], "-S"))
245 SortZones = FALSE;
246 else if (streql(argv[i], "-c"))
247 ColFormat = TRUE;
248 else if (streql(argv[i], "-C"))
249 ColFormat = FALSE;
250 else if (streql(argv[i], "-h"))
251 usage(stdout);
252 else if (streql(argv[i], "-H"))
253 PrintHeader = FALSE;
254 else if (streql(argv[i], "--")) {
255 i++;
256 break;
257 } else if (argv[i][0] == '-')
258 usage(stderr);
259 else
260 break;
261 }
262
263 switch (argc - i) {
264 case 0:
265 zname = "";
266 znamelen = 0;
267 break;
268
269 case 1:
270 zname = argv[i];
271 znamelen = strlen(zname);
272 break;
273
274 default:
275 usage(stderr);
276 }
277
278 if (ShowDeltas) {
279 SortZones = FALSE;
280 ColFormat = TRUE;
281 PrintHeader = TRUE;
282 }
283
284 if (ShowWasted) {
285 columns[COL_FRAG_SIZE].visible = true;
286 columns[COL_FREE_SIZE].visible = true;
287 }
288 if (ShowTotal) {
289 columns[COL_TOTAL_ALLOCS].visible = true;
290 }
291
292 for (;;) {
293 kr = mach_memory_info(mach_host_self(),
294 &name, &nameCnt, &info, &infoCnt,
295 &wiredInfo, &wiredInfoCnt);
296 if (kr != KERN_SUCCESS) {
297 fprintf(stderr, "%s: mach_zone_info: %s\n",
298 program, mach_error_string(kr));
299 exit(1);
300 }
301
302 if (nameCnt != infoCnt) {
303 fprintf(stderr, "%s: mach_zone_name/ mach_zone_info: counts not equal?\n",
304 program);
305 exit(1);
306 }
307
308 if (first_time) {
309 deltas = (char *)malloc(infoCnt);
310 max_info = (mach_zone_info_t *)malloc((infoCnt * sizeof *info));
311 }
312
313 if (SortZones) {
314 for (i = 0; i < nameCnt-1; i++) {
315 for (j = i+1; j < nameCnt; j++) {
316 unsigned long long wastei, wastej;
317
318 wastei = (info[i].mzi_cur_size -
319 (info[i].mzi_elem_size *
320 info[i].mzi_count));
321 wastej = (info[j].mzi_cur_size -
322 (info[j].mzi_elem_size *
323 info[j].mzi_count));
324
325 if (wastej > wastei) {
326 mach_zone_info_t tinfo;
327 mach_zone_name_t tname;
328
329 tinfo = info[i];
330 info[i] = info[j];
331 info[j] = tinfo;
332
333 tname = name[i];
334 name[i] = name[j];
335 name[j] = tname;
336 }
337 }
338 }
339 }
340
341 must_print = find_deltas(name, info, max_info, deltas, infoCnt, first_time);
342 zoneElements = 0;
343 if (must_print) {
344 if (ColFormat) {
345 if (!first_time)
346 printf("\n");
347 colprintzoneheader();
348 }
349 for (i = 0; i < nameCnt; i++) {
350 if (deltas[i]) {
351 if (ColFormat)
352 colprintzone(&name[i], &info[i]);
353 else
354 printzone(&name[i], &info[i]);
355 zoneElements += info[i].mzi_count;
356 }
357 }
358 }
359
360 if (ShowLarge && first_time) {
361 PrintLarge(wiredInfo, wiredInfoCnt, &info[0], &name[0],
362 nameCnt, zoneElements,
363 SortZones ? &SortSize : &SortName, ColFormat);
364 }
365
366 first_time = 0;
367
368 if ((name != NULL) && (nameCnt != 0)) {
369 kr = vm_deallocate(mach_task_self(), (vm_address_t) name,
370 (vm_size_t) (nameCnt * sizeof *name));
371 if (kr != KERN_SUCCESS) {
372 fprintf(stderr, "%s: vm_deallocate: %s\n",
373 program, mach_error_string(kr));
374 exit(1);
375 }
376 }
377
378 if ((info != NULL) && (infoCnt != 0)) {
379 kr = vm_deallocate(mach_task_self(), (vm_address_t) info,
380 (vm_size_t) (infoCnt * sizeof *info));
381 if (kr != KERN_SUCCESS) {
382 fprintf(stderr, "%s: vm_deallocate: %s\n",
383 program, mach_error_string(kr));
384 exit(1);
385 }
386 }
387
388 if ((wiredInfo != NULL) && (wiredInfoCnt != 0)) {
389 kr = vm_deallocate(mach_task_self(), (vm_address_t) wiredInfo,
390 (vm_size_t) (wiredInfoCnt * sizeof *wiredInfo));
391 if (kr != KERN_SUCCESS) {
392 fprintf(stderr, "%s: vm_deallocate: %s\n",
393 program, mach_error_string(kr));
394 exit(1);
395 }
396 }
397
398 if ((ShowWasted||ShowTotal) && PrintHeader && !ShowDeltas) {
399 printf("\nZONE TOTALS\n");
400 printf("---------------------------------------------\n");
401 printf("TOTAL SIZE = %llu\n", totalsize);
402 printf("TOTAL USED = %llu\n", totalused);
403 if (ShowWasted) {
404 printf("TOTAL WASTED = %llu\n", totalsize - totalused);
405 printf("TOTAL FRAGMENTED = %llu\n", totalfragmented);
406 printf("TOTAL COLLECTABLE = %llu\n", totalcollectable);
407 }
408 if (ShowTotal)
409 printf("TOTAL ALLOCS = %llu\n", totalsum);
410 }
411
412 if (ShowDeltas == FALSE || last_time)
413 break;
414
415 sleep(interval);
416 }
417 exit(0);
418 }
419
420 static boolean_t
421 substr(const char *a, size_t alen, const char *b, size_t blen)
422 {
423 int i;
424
425 if (alen > blen) return FALSE;
426
427 for (i = 0; i <= blen - alen; i++)
428 if (strneql(a, b+i, alen))
429 return TRUE;
430
431 return FALSE;
432 }
433
434 static void
435 printzone(mach_zone_name_t *name, mach_zone_info_t *info)
436 {
437 unsigned long long used, size, fragmented, collectable;
438
439 printf("%.*s zone:\n", (int)sizeof name->mzn_name, name->mzn_name);
440 printf("\tcur_size: %lluK bytes (%llu elements)\n",
441 info->mzi_cur_size/1024,
442 (info->mzi_elem_size == 0) ? 0 :
443 info->mzi_cur_size/info->mzi_elem_size);
444 printf("\tmax_size: %lluK bytes (%llu elements)\n",
445 info->mzi_max_size/1024,
446 (info->mzi_elem_size == 0) ? 0 :
447 info->mzi_max_size/info->mzi_elem_size);
448 printf("\telem_size: %llu bytes\n",
449 info->mzi_elem_size);
450 printf("\t# of elems: %llu\n",
451 info->mzi_count);
452 printf("\talloc_size: %lluK bytes (%llu elements)\n",
453 info->mzi_alloc_size/1024,
454 (info->mzi_elem_size == 0) ? 0 :
455 info->mzi_alloc_size/info->mzi_elem_size);
456 if (info->mzi_exhaustible)
457 printf("\tEXHAUSTIBLE\n");
458 if (GET_MZI_COLLECTABLE_FLAG(info->mzi_collectable))
459 printf("\tCOLLECTABLE\n");
460 if (ShowWasted) {
461 totalused += used = info->mzi_elem_size * info->mzi_count;
462 totalsize += size = info->mzi_cur_size;
463 totalcollectable += collectable = GET_MZI_COLLECTABLE_BYTES(info->mzi_collectable);
464 totalfragmented += fragmented = size - used - collectable;
465 printf("\t\t\t\t\tWASTED: %llu\n", size - used);
466 printf("\t\t\t\t\tFRAGMENTED: %llu\n", fragmented);
467 printf("\t\t\t\t\tCOLLECTABLE: %llu\n", collectable);
468 }
469 if (ShowTotal) {
470 totalsum += info->mzi_sum_size;
471 printf("\t\t\t\t\tTOTAL: %llu\n", totalsum);
472 }
473 }
474
475 static void
476 colprintzone(mach_zone_name_t *zone_name, mach_zone_info_t *info)
477 {
478 char *name = zone_name->mzn_name;
479 int j, namewidth;
480 unsigned long long used, size, fragmented, collectable;
481
482 namewidth = columns[COL_ZONE_NAME].colwidth;
483
484 for (j = 0; j < namewidth - 1 && name[j]; j++) {
485 if (name[j] == ' ') {
486 putchar('.');
487 } else {
488 putchar(name[j]);
489 }
490 }
491 if (j == namewidth - 1) {
492 if (name[j]) {
493 putchar('$');
494 } else {
495 putchar(' ');
496 }
497 } else {
498 for (; j < namewidth; j++) {
499 putchar(' ');
500 }
501 }
502
503
504 #define PRINTCOL(value, index) \
505 if (columns[(index)].visible) { \
506 printf(" %*llu", columns[(index)].colwidth * columns[(index)].alignment, (value)); \
507 }
508 #define PRINTCOLSTR(value, index) \
509 if (columns[(index)].visible) { \
510 printf(" %*s", columns[(index)].colwidth * columns[(index)].alignment, (value)); \
511 }
512 #define PRINTCOLK(value, index) \
513 if (columns[(index)].visible) { \
514 printf(" %*lluK", (columns[(index)].colwidth - 1) * columns[(index)].alignment, (value) / 1024 ); \
515 }
516 #define PRINTCOLSZ(value, index) \
517 if (columns[(index)].visible) { \
518 if ((value) < 1024) { \
519 printf(" %*lluB", (columns[(index)].colwidth - 1) * columns[(index)].alignment, (value)); \
520 } else { \
521 PRINTCOLK(value, index) \
522 } \
523 }
524
525
526 PRINTCOL(info->mzi_elem_size, COL_ELEM_SIZE);
527 PRINTCOLK(info->mzi_cur_size, COL_CUR_SIZE);
528 if (info->mzi_max_size / 1024 > 9999999) {
529 /*
530 * Zones with preposterously large maximum sizes are shown with `-------'
531 * in the max size and max num elts fields.
532 */
533 PRINTCOLSTR("-------", COL_MAX_SIZE);
534 } else {
535 PRINTCOLK(info->mzi_max_size, COL_MAX_SIZE);
536 }
537 PRINTCOL(info->mzi_cur_size / info->mzi_elem_size, COL_CUR_ELTS);
538 if (info->mzi_max_size / 1024 > 9999999) {
539 PRINTCOLSTR("-------", COL_MAX_ELTS);
540 } else {
541 PRINTCOL(info->mzi_max_size / info->mzi_elem_size, COL_MAX_ELTS);
542 }
543 PRINTCOL(info->mzi_count, COL_CUR_INUSE);
544 PRINTCOLK(info->mzi_alloc_size, COL_ALLOC_SIZE);
545 PRINTCOL(info->mzi_alloc_size / info->mzi_elem_size, COL_ALLOC_COUNT);
546
547 totalused += used = info->mzi_elem_size * info->mzi_count;
548 totalsize += size = info->mzi_cur_size;
549 totalsum += info->mzi_sum_size;
550 totalcollectable += collectable = GET_MZI_COLLECTABLE_BYTES(info->mzi_collectable);
551 totalfragmented += fragmented = size - used - collectable;
552
553 printf(" %c%c",
554 (info->mzi_exhaustible ? 'X' : ' '),
555 (GET_MZI_COLLECTABLE_FLAG(info->mzi_collectable) ? 'C' : ' '));
556
557 PRINTCOLSZ(fragmented, COL_FRAG_SIZE);
558 PRINTCOLSZ(collectable, COL_FREE_SIZE);
559 PRINTCOLSZ(info->mzi_sum_size, COL_TOTAL_ALLOCS);
560
561 printf("\n");
562 }
563
564
565 static void
566 colprintzoneheader(void)
567 {
568 int i, totalwidth = 0;
569
570 if (! PrintHeader) {
571 return;
572 }
573
574 for (i = 0; i < COL_MAX; i++) {
575 if (columns[i].visible) {
576 printf("%*s ", columns[i].colwidth * columns[i].alignment, columns[i].line1);
577 }
578 }
579 printf("\n");
580
581 for (i = 0; i < COL_MAX; i++) {
582 if (columns[i].visible) {
583 printf("%*s ", columns[i].colwidth * columns[i].alignment, columns[i].line2);
584 totalwidth += (columns[i].colwidth + 1);
585 }
586 }
587
588 printf("\n");
589 for (i = 0; i < totalwidth; i++) {
590 printf("-");
591 }
592 printf("\n");
593 }
594
595 int
596 find_deltas(mach_zone_name_t *name, mach_zone_info_t *info, mach_zone_info_t *max_info,
597 char *deltas, int cnt, int first_time)
598 {
599 int i;
600 int found_one = 0;
601
602 for (i = 0; i < cnt; i++) {
603 deltas[i] = 0;
604 if (substr(zname, znamelen, name[i].mzn_name,
605 strnlen(name[i].mzn_name, sizeof name[i].mzn_name))) {
606 if (first_time || info->mzi_cur_size > max_info->mzi_cur_size ||
607 (ShowTotal && ((info->mzi_sum_size >> 1) > max_info->mzi_sum_size))) {
608 max_info->mzi_cur_size = info->mzi_cur_size;
609 max_info->mzi_sum_size = info->mzi_sum_size;
610 deltas[i] = 1;
611 found_one = 1;
612 }
613 }
614 info++;
615 max_info++;
616 }
617 return(found_one);
618 }
619
620 /*********************************************************************
621 *********************************************************************/
622
623 static char *
624 kern_vm_tag_name(uint64_t tag)
625 {
626 char * result;
627 const char * name;
628 switch (tag)
629 {
630 case (VM_KERN_MEMORY_NONE): name = "VM_KERN_MEMORY_NONE"; break;
631 case (VM_KERN_MEMORY_OSFMK): name = "VM_KERN_MEMORY_OSFMK"; break;
632 case (VM_KERN_MEMORY_BSD): name = "VM_KERN_MEMORY_BSD"; break;
633 case (VM_KERN_MEMORY_IOKIT): name = "VM_KERN_MEMORY_IOKIT"; break;
634 case (VM_KERN_MEMORY_LIBKERN): name = "VM_KERN_MEMORY_LIBKERN"; break;
635 case (VM_KERN_MEMORY_OSKEXT): name = "VM_KERN_MEMORY_OSKEXT"; break;
636 case (VM_KERN_MEMORY_KEXT): name = "VM_KERN_MEMORY_KEXT"; break;
637 case (VM_KERN_MEMORY_IPC): name = "VM_KERN_MEMORY_IPC"; break;
638 case (VM_KERN_MEMORY_STACK): name = "VM_KERN_MEMORY_STACK"; break;
639 case (VM_KERN_MEMORY_CPU): name = "VM_KERN_MEMORY_CPU"; break;
640 case (VM_KERN_MEMORY_PMAP): name = "VM_KERN_MEMORY_PMAP"; break;
641 case (VM_KERN_MEMORY_PTE): name = "VM_KERN_MEMORY_PTE"; break;
642 case (VM_KERN_MEMORY_ZONE): name = "VM_KERN_MEMORY_ZONE"; break;
643 case (VM_KERN_MEMORY_KALLOC): name = "VM_KERN_MEMORY_KALLOC"; break;
644 case (VM_KERN_MEMORY_COMPRESSOR): name = "VM_KERN_MEMORY_COMPRESSOR"; break;
645 case (VM_KERN_MEMORY_COMPRESSED_DATA): name = "VM_KERN_MEMORY_COMPRESSED_DATA"; break;
646 case (VM_KERN_MEMORY_PHANTOM_CACHE): name = "VM_KERN_MEMORY_PHANTOM_CACHE"; break;
647 case (VM_KERN_MEMORY_WAITQ): name = "VM_KERN_MEMORY_WAITQ"; break;
648 case (VM_KERN_MEMORY_DIAG): name = "VM_KERN_MEMORY_DIAG"; break;
649 case (VM_KERN_MEMORY_LOG): name = "VM_KERN_MEMORY_LOG"; break;
650 case (VM_KERN_MEMORY_FILE): name = "VM_KERN_MEMORY_FILE"; break;
651 case (VM_KERN_MEMORY_MBUF): name = "VM_KERN_MEMORY_MBUF"; break;
652 case (VM_KERN_MEMORY_UBC): name = "VM_KERN_MEMORY_UBC"; break;
653 case (VM_KERN_MEMORY_SECURITY): name = "VM_KERN_MEMORY_SECURITY"; break;
654 case (VM_KERN_MEMORY_MLOCK): name = "VM_KERN_MEMORY_MLOCK"; break;
655 case (VM_KERN_MEMORY_REASON): name = "VM_KERN_MEMORY_REASON"; break;
656 case (VM_KERN_MEMORY_SKYWALK): name = "VM_KERN_MEMORY_SKYWALK"; break;
657 case (VM_KERN_MEMORY_LTABLE): name = "VM_KERN_MEMORY_LTABLE"; break;
658 case (VM_KERN_MEMORY_ANY): name = "VM_KERN_MEMORY_ANY"; break;
659 default: name = NULL; break;
660 }
661 if (name) asprintf(&result, "%s", name);
662 else asprintf(&result, "VM_KERN_MEMORY_%lld", tag);
663 return (result);
664 }
665
666 static char *
667 kern_vm_counter_name(uint64_t tag)
668 {
669 char * result;
670 const char * name;
671 switch (tag)
672 {
673 case (VM_KERN_COUNT_MANAGED): name = "VM_KERN_COUNT_MANAGED"; break;
674 case (VM_KERN_COUNT_RESERVED): name = "VM_KERN_COUNT_RESERVED"; break;
675 case (VM_KERN_COUNT_WIRED): name = "VM_KERN_COUNT_WIRED"; break;
676 case (VM_KERN_COUNT_WIRED_BOOT): name = "VM_KERN_COUNT_WIRED_BOOT"; break;
677 case (VM_KERN_COUNT_WIRED_MANAGED): name = "VM_KERN_COUNT_WIRED_MANAGED"; break;
678 case (VM_KERN_COUNT_STOLEN): name = "VM_KERN_COUNT_STOLEN"; break;
679 case (VM_KERN_COUNT_BOOT_STOLEN): name = "VM_KERN_COUNT_BOOT_STOLEN"; break;
680 case (VM_KERN_COUNT_LOPAGE): name = "VM_KERN_COUNT_LOPAGE"; break;
681 case (VM_KERN_COUNT_MAP_KERNEL): name = "VM_KERN_COUNT_MAP_KERNEL"; break;
682 case (VM_KERN_COUNT_MAP_ZONE): name = "VM_KERN_COUNT_MAP_ZONE"; break;
683 case (VM_KERN_COUNT_MAP_KALLOC): name = "VM_KERN_COUNT_MAP_KALLOC"; break;
684 default: name = NULL; break;
685 }
686 if (name) asprintf(&result, "%s", name);
687 else asprintf(&result, "VM_KERN_COUNT_%lld", tag);
688 return (result);
689 }
690
691 static void
692 MakeLoadTagKeys(const void * key, const void * value, void * context)
693 {
694 CFMutableDictionaryRef newDict = context;
695 CFDictionaryRef kextInfo = value;
696 CFNumberRef loadTag;
697 uint32_t loadTagValue;
698
699 loadTag = (CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleLoadTagKey));
700 CFNumberGetValue(loadTag, kCFNumberSInt32Type, &loadTagValue);
701 key = (const void *)(uintptr_t) loadTagValue;
702 CFDictionarySetValue(newDict, key, value);
703 }
704
705 static CSSymbolicatorRef gSym;
706 static CFMutableDictionaryRef gTagDict;
707 static mach_memory_info_t * gSites;
708
709 static char *
710 GetSiteName(int siteIdx, mach_zone_name_t * zoneNames, unsigned int zoneNamesCnt)
711 {
712 const char * name;
713 uintptr_t kmodid;
714 char * result;
715 char * append;
716 mach_vm_address_t addr;
717 CFDictionaryRef kextInfo;
718 CFStringRef bundleID;
719 uint32_t type;
720
721 const mach_memory_info_t * site;
722 const char * fileName;
723 CSSymbolRef symbol;
724 const char * symbolName;
725 CSSourceInfoRef sourceInfo;
726
727 name = NULL;
728 result = NULL;
729 site = &gSites[siteIdx];
730 addr = site->site;
731 type = (VM_KERN_SITE_TYPE & site->flags);
732 kmodid = 0;
733
734 if (VM_KERN_SITE_NAMED & site->flags)
735 {
736 asprintf(&result, "%s", &site->name[0]);
737 }
738 else switch (type)
739 {
740 case VM_KERN_SITE_TAG:
741 result = kern_vm_tag_name(addr);
742 break;
743
744 case VM_KERN_SITE_COUNTER:
745 result = kern_vm_counter_name(addr);
746 break;
747
748 case VM_KERN_SITE_KMOD:
749
750 kmodid = (uintptr_t) addr;
751 kextInfo = CFDictionaryGetValue(gTagDict, (const void *)kmodid);
752 if (kextInfo)
753 {
754 bundleID = (CFStringRef)CFDictionaryGetValue(kextInfo, kCFBundleIdentifierKey);
755 name = CFStringGetCStringPtr(bundleID, kCFStringEncodingUTF8);
756 // wiredSize = (CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleWiredSizeKey));
757 }
758
759 if (name) asprintf(&result, "%s", name);
760 else asprintf(&result, "(unloaded kmod)");
761 break;
762
763 case VM_KERN_SITE_KERNEL:
764 symbolName = NULL;
765 if (addr)
766 {
767 symbol = CSSymbolicatorGetSymbolWithAddressAtTime(gSym, addr, kCSNow);
768 symbolName = CSSymbolGetName(symbol);
769 }
770 if (symbolName)
771 {
772 asprintf(&result, "%s", symbolName);
773 sourceInfo = CSSymbolicatorGetSourceInfoWithAddressAtTime(gSym, addr, kCSNow);
774 fileName = CSSourceInfoGetPath(sourceInfo);
775 if (fileName) printf(" (%s:%d)", fileName, CSSourceInfoGetLineNumber(sourceInfo));
776 }
777 else
778 {
779 asprintf(&result, "site 0x%qx", addr);
780 }
781 break;
782 default:
783 asprintf(&result, "");
784 break;
785 }
786
787 if (result
788 && (VM_KERN_SITE_ZONE & site->flags)
789 && zoneNames
790 && (site->zone < zoneNamesCnt))
791 {
792 size_t namelen, zonelen;
793 namelen = strlen(result);
794 zonelen = strnlen(zoneNames[site->zone].mzn_name, sizeof(zoneNames[site->zone].mzn_name));
795 if (((namelen + zonelen) > 61) && (zonelen < 61)) namelen = (61 - zonelen);
796 asprintf(&append, "%.*s[%.*s]",
797 (int)namelen,
798 result,
799 (int)zonelen,
800 zoneNames[site->zone].mzn_name);
801 free(result);
802 result = append;
803 }
804 if (result && kmodid)
805 {
806 asprintf(&append, "%-64s%3ld", result, kmodid);
807 free(result);
808 result = append;
809 }
810
811 return (result);
812 }
813
814 struct CompareThunk
815 {
816 mach_zone_name_t *zoneNames;
817 unsigned int zoneNamesCnt;
818 };
819
820 static int
821 SortName(void * thunk, const void * left, const void * right)
822 {
823 const struct CompareThunk * t = (typeof(t)) thunk;
824 const int * idxL;
825 const int * idxR;
826 char * l;
827 char * r;
828 CFStringRef lcf;
829 CFStringRef rcf;
830 int result;
831
832 idxL = (typeof(idxL)) left;
833 idxR = (typeof(idxR)) right;
834 l = GetSiteName(*idxL, t->zoneNames, t->zoneNamesCnt);
835 r = GetSiteName(*idxR, t->zoneNames, t->zoneNamesCnt);
836
837 lcf = CFStringCreateWithCString(kCFAllocatorDefault, l, kCFStringEncodingUTF8);
838 rcf = CFStringCreateWithCString(kCFAllocatorDefault, r, kCFStringEncodingUTF8);
839
840 result = (int) CFStringCompareWithOptionsAndLocale(lcf, rcf, CFRangeMake(0, CFStringGetLength(lcf)), kCFCompareNumerically, NULL);
841
842 CFRelease(lcf);
843 CFRelease(rcf);
844 free(l);
845 free(r);
846
847 return (result);
848 }
849
850 static int
851 SortSize(void * thunk, const void * left, const void * right)
852 {
853 const mach_memory_info_t * siteL;
854 const mach_memory_info_t * siteR;
855 const int * idxL;
856 const int * idxR;
857
858 idxL = (typeof(idxL)) left;
859 idxR = (typeof(idxR)) right;
860 siteL = &gSites[*idxL];
861 siteR = &gSites[*idxR];
862
863 if (siteL->size > siteR->size) return (-1);
864 else if (siteL->size < siteR->size) return (1);
865 return (0);
866 }
867
868
869 static void
870 PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt,
871 mach_zone_info_t *zoneInfo, mach_zone_name_t *zoneNames,
872 unsigned int zoneCnt, uint64_t zoneElements,
873 int (*func)(void *, const void *, const void *), boolean_t column)
874 {
875 uint64_t zonetotal;
876 uint64_t top_wired;
877 uint64_t size;
878 uint64_t elemsTagged;
879
880 CFDictionaryRef allKexts;
881 unsigned int idx, site, first;
882 int sorted[wiredInfoCnt];
883 char totalstr[40];
884 char * name;
885 bool headerPrinted;
886
887 zonetotal = totalsize;
888
889 gSites = wiredInfo;
890
891 gSym = CSSymbolicatorCreateWithMachKernel();
892
893 allKexts = OSKextCopyLoadedKextInfo(NULL, NULL);
894 gTagDict = CFDictionaryCreateMutable(
895 kCFAllocatorDefault, (CFIndex) 0,
896 (CFDictionaryKeyCallBacks *) 0,
897 &kCFTypeDictionaryValueCallBacks);
898
899 CFDictionaryApplyFunction(allKexts, &MakeLoadTagKeys, gTagDict);
900 CFRelease(allKexts);
901
902 top_wired = 0;
903
904 for (idx = 0; idx < wiredInfoCnt; idx++) sorted[idx] = idx;
905 first = 0; // VM_KERN_MEMORY_FIRST_DYNAMIC
906 struct CompareThunk thunk;
907 thunk.zoneNames = zoneNames;
908 thunk.zoneNamesCnt = zoneCnt;
909 qsort_r(&sorted[first],
910 wiredInfoCnt - first,
911 sizeof(sorted[0]),
912 &thunk,
913 func);
914
915 elemsTagged = 0;
916 for (headerPrinted = false, idx = 0; idx < wiredInfoCnt; idx++)
917 {
918 site = sorted[idx];
919 if ((VM_KERN_SITE_COUNTER & gSites[site].flags)
920 && (VM_KERN_COUNT_WIRED == gSites[site].site)) top_wired = gSites[site].size;
921 if (VM_KERN_SITE_HIDE & gSites[site].flags) continue;
922 if (!((VM_KERN_SITE_WIRED | VM_KERN_SITE_ZONE) & gSites[site].flags)) continue;
923
924 if ((VM_KERN_SITE_ZONE & gSites[site].flags)
925 && gSites[site].zone < zoneCnt)
926 {
927 elemsTagged += gSites[site].size / zoneInfo[gSites[site].zone].mzi_elem_size;
928 }
929
930 if ((gSites[site].size < 1024) && (gSites[site].peak < 1024)) continue;
931
932 name = GetSiteName(site, zoneNames, zoneCnt);
933 if (!substr(zname, znamelen, name, strlen(name))) continue;
934 if (!headerPrinted)
935 {
936 printf("-------------------------------------------------------------------------------------------------------------\n");
937 printf(" kmod vm peak cur\n");
938 printf("wired memory id tag size waste size\n");
939 printf("-------------------------------------------------------------------------------------------------------------\n");
940 headerPrinted = true;
941 }
942 printf("%-67s", name);
943 free(name);
944 printf("%12d", gSites[site].tag);
945
946 if (gSites[site].peak) PRINTK(" %10llu", gSites[site].peak);
947 else printf(" %11s", "");
948
949 if (gSites[site].collectable_bytes) PRINTK(" %5llu", gSites[site].collectable_bytes);
950 else printf(" %6s", "");
951
952 PRINTK(" %9llu", gSites[site].size);
953
954 if (!(VM_KERN_SITE_ZONE & gSites[site].flags)) totalsize += gSites[site].size;
955
956 printf("\n");
957 }
958
959 if (!znamelen)
960 {
961 printf("%-67s", "zones");
962 printf("%12s", "");
963 printf(" %11s", "");
964 printf(" %6s", "");
965 PRINTK(" %9llu", zonetotal);
966 printf("\n");
967 }
968 if (headerPrinted)
969 {
970 if (elemsTagged)
971 {
972 snprintf(totalstr, sizeof(totalstr), "%lld of %lld", elemsTagged, zoneElements);
973 printf("zone tags%100s\n", totalstr);
974 }
975 snprintf(totalstr, sizeof(totalstr), "%6.2fM of %6.2fM", totalsize / 1024.0 / 1024.0, top_wired / 1024.0 / 1024.0);
976 printf("total%104s\n", totalstr);
977 }
978 for (headerPrinted = false, idx = 0; idx < wiredInfoCnt; idx++)
979 {
980 site = sorted[idx];
981 size = gSites[site].mapped;
982 if (!size) continue;
983 if (VM_KERN_SITE_HIDE & gSites[site].flags) continue;
984 if ((size == gSites[site].size)
985 && ((VM_KERN_SITE_WIRED | VM_KERN_SITE_ZONE) & gSites[site].flags)) continue;
986
987 name = GetSiteName(site, NULL, 0);
988 if (!substr(zname, znamelen, name, strlen(name))) continue;
989 if (!headerPrinted)
990 {
991 printf("-------------------------------------------------------------------------------------------------------------\n");
992 printf(" largest peak cur\n");
993 printf("maps free free size size\n");
994 printf("-------------------------------------------------------------------------------------------------------------\n");
995 headerPrinted = true;
996 }
997 printf("%-55s", name);
998 free(name);
999
1000 if (gSites[site].free) PRINTK(" %10llu", gSites[site].free);
1001 else printf(" %11s", "");
1002 if (gSites[site].largest) PRINTK(" %10llu", gSites[site].largest);
1003 else printf(" %11s", "");
1004 if (gSites[site].peak) PRINTK(" %10llu", gSites[site].peak);
1005 else printf(" %11s", "");
1006 PRINTK(" %16llu", size);
1007
1008 printf("\n");
1009 }
1010 totalsize = zonetotal;
1011 }