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