]>
Commit | Line | Data |
---|---|---|
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(void); | |
94 | static void printzone(mach_zone_name_t *, task_zone_info_t *); | |
95 | static void colprintzone(mach_zone_name_t *, task_zone_info_t *); | |
96 | static int find_deltas(mach_zone_name_t *, task_zone_info_t *, task_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(const void * left, const void * right); | |
101 | static int SortSize(const void * left, const void * right); | |
102 | static void PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt, int (*func)(const void *, const void *), boolean_t column); | |
103 | ||
104 | static char *program; | |
105 | ||
106 | static pid_t pid = 0; | |
107 | static task_t task = TASK_NULL; | |
108 | static boolean_t ShowPid = FALSE; | |
109 | ||
110 | static boolean_t ShowDeltas = FALSE; | |
111 | static boolean_t ShowWasted = FALSE; | |
112 | static boolean_t ShowTotal = FALSE; | |
113 | static boolean_t ShowLarge = TRUE; | |
114 | static boolean_t SortZones = FALSE; | |
115 | static boolean_t ColFormat = TRUE; | |
116 | static boolean_t PrintHeader = TRUE; | |
117 | ||
118 | static unsigned long long totalsize = 0; | |
119 | static unsigned long long totalused = 0; | |
120 | static unsigned long long totalsum = 0; | |
121 | static unsigned long long pidsum = 0; | |
122 | ||
123 | static int last_time = 0; | |
124 | ||
125 | static char *zname = NULL; | |
126 | static size_t znamelen = 0; | |
127 | ||
128 | static void | |
129 | sigintr(__unused int signum) | |
130 | { | |
131 | last_time = 1; | |
132 | } | |
133 | ||
134 | static void | |
135 | usage(void) | |
136 | { | |
137 | fprintf(stderr, "usage: %s [-w] [-s] [-c] [-h] [-t] [-d] [-l] [-L] [-p <pid>] [name]\n", program); | |
138 | exit(1); | |
139 | } | |
140 | ||
141 | int | |
142 | main(int argc, char **argv) | |
143 | { | |
144 | mach_zone_name_t *name = NULL; | |
145 | unsigned int nameCnt = 0; | |
146 | task_zone_info_t *info = NULL; | |
147 | unsigned int infoCnt = 0; | |
148 | mach_memory_info_t *wiredInfo = NULL; | |
149 | unsigned int wiredInfoCnt = 0; | |
150 | task_zone_info_t *max_info = NULL; | |
151 | char *deltas = NULL; | |
152 | ||
153 | kern_return_t kr; | |
154 | int i, j; | |
155 | int first_time = 1; | |
156 | int must_print = 1; | |
157 | int interval = 1; | |
158 | ||
159 | signal(SIGINT, sigintr); | |
160 | ||
161 | program = strrchr(argv[0], '/'); | |
162 | if (program == NULL) | |
163 | program = argv[0]; | |
164 | else | |
165 | program++; | |
166 | ||
167 | for (i = 1; i < argc; i++) { | |
168 | if (streql(argv[i], "-d")) | |
169 | ShowDeltas = TRUE; | |
170 | else if (streql(argv[i], "-t")) | |
171 | ShowTotal = TRUE; | |
172 | else if (streql(argv[i], "-T")) | |
173 | ShowTotal = FALSE; | |
174 | else if (streql(argv[i], "-w")) | |
175 | ShowWasted = TRUE; | |
176 | else if (streql(argv[i], "-W")) | |
177 | ShowWasted = FALSE; | |
178 | else if (streql(argv[i], "-l")) | |
179 | ShowLarge = TRUE; | |
180 | else if (streql(argv[i], "-L")) | |
181 | ShowLarge = FALSE; | |
182 | else if (streql(argv[i], "-s")) | |
183 | SortZones = TRUE; | |
184 | else if (streql(argv[i], "-S")) | |
185 | SortZones = FALSE; | |
186 | else if (streql(argv[i], "-c")) | |
187 | ColFormat = TRUE; | |
188 | else if (streql(argv[i], "-C")) | |
189 | ColFormat = FALSE; | |
190 | else if (streql(argv[i], "-H")) | |
191 | PrintHeader = FALSE; | |
192 | else if (streql(argv[i], "-p")) { | |
193 | ShowPid = TRUE; | |
194 | if (i < argc - 1) { | |
195 | pid = atoi(argv[i+1]); | |
196 | i++; | |
197 | } else | |
198 | usage(); | |
199 | } else if (streql(argv[i], "--")) { | |
200 | i++; | |
201 | break; | |
202 | } else if (argv[i][0] == '-') | |
203 | usage(); | |
204 | else | |
205 | break; | |
206 | } | |
207 | ||
208 | switch (argc - i) { | |
209 | case 0: | |
210 | zname = ""; | |
211 | znamelen = 0; | |
212 | break; | |
213 | ||
214 | case 1: | |
215 | zname = argv[i]; | |
216 | znamelen = strlen(zname); | |
217 | break; | |
218 | ||
219 | default: | |
220 | usage(); | |
221 | } | |
222 | ||
223 | if (ShowDeltas) { | |
224 | SortZones = FALSE; | |
225 | ColFormat = TRUE; | |
226 | PrintHeader = TRUE; | |
227 | } | |
228 | ||
229 | if (ShowPid) { | |
230 | kr = task_for_pid(mach_task_self(), pid, &task); | |
231 | if (kr != KERN_SUCCESS) { | |
232 | fprintf(stderr, "%s: task_for_pid(%d) failed: %s (try running as root)\n", | |
233 | program, pid, mach_error_string(kr)); | |
234 | exit(1); | |
235 | } | |
236 | } | |
237 | ||
238 | for (;;) { | |
239 | if (ShowPid) { | |
240 | kr = task_zone_info(task, &name, &nameCnt, &info, &infoCnt); | |
241 | if (kr != KERN_SUCCESS) { | |
242 | fprintf(stderr, "%s: task_zone_info: %s\n", | |
243 | program, mach_error_string(kr)); | |
244 | exit(1); | |
245 | } | |
246 | } else { | |
247 | mach_zone_info_t *zinfo = NULL; | |
248 | ||
249 | kr = mach_memory_info(mach_host_self(), | |
250 | &name, &nameCnt, &zinfo, &infoCnt, | |
251 | &wiredInfo, &wiredInfoCnt); | |
252 | if (kr != KERN_SUCCESS) { | |
253 | fprintf(stderr, "%s: mach_zone_info: %s\n", | |
254 | program, mach_error_string(kr)); | |
255 | exit(1); | |
256 | } | |
257 | ||
258 | kr = vm_allocate(mach_task_self(), (vm_address_t *)&info, | |
259 | infoCnt * sizeof *info, VM_FLAGS_ANYWHERE); | |
260 | if (kr != KERN_SUCCESS) { | |
261 | fprintf(stderr, "%s vm_allocate: %s\n", | |
262 | program, mach_error_string(kr)); | |
263 | exit(1); | |
264 | } | |
265 | for (i = 0; i < infoCnt; i++) { | |
266 | *(mach_zone_info_t *)(info + i) = zinfo[i]; | |
267 | info[i].tzi_caller_acct = 0; | |
268 | info[i].tzi_task_alloc = 0; | |
269 | info[i].tzi_task_free = 0; | |
270 | } | |
271 | kr = vm_deallocate(mach_task_self(), (vm_address_t) zinfo, | |
272 | (vm_size_t) (infoCnt * sizeof *zinfo)); | |
273 | if (kr != KERN_SUCCESS) { | |
274 | fprintf(stderr, "%s: vm_deallocate: %s\n", | |
275 | program, mach_error_string(kr)); | |
276 | exit(1); | |
277 | } | |
278 | } | |
279 | ||
280 | if (nameCnt != infoCnt) { | |
281 | fprintf(stderr, "%s: mach/task_zone_info: counts not equal?\n", | |
282 | program); | |
283 | exit(1); | |
284 | } | |
285 | ||
286 | if (first_time) { | |
287 | deltas = (char *)malloc(infoCnt); | |
288 | max_info = (task_zone_info_t *)malloc((infoCnt * sizeof *info)); | |
289 | } | |
290 | ||
291 | if (SortZones) { | |
292 | for (i = 0; i < nameCnt-1; i++) | |
293 | for (j = i+1; j < nameCnt; j++) { | |
294 | unsigned long long wastei, wastej; | |
295 | ||
296 | wastei = (info[i].tzi_cur_size - | |
297 | (info[i].tzi_elem_size * | |
298 | info[i].tzi_count)); | |
299 | wastej = (info[j].tzi_cur_size - | |
300 | (info[j].tzi_elem_size * | |
301 | info[j].tzi_count)); | |
302 | ||
303 | if (wastej > wastei) { | |
304 | task_zone_info_t tinfo; | |
305 | mach_zone_name_t tname; | |
306 | ||
307 | tinfo = info[i]; | |
308 | info[i] = info[j]; | |
309 | info[j] = tinfo; | |
310 | ||
311 | tname = name[i]; | |
312 | name[i] = name[j]; | |
313 | name[j] = tname; | |
314 | } | |
315 | } | |
316 | } | |
317 | ||
318 | must_print = find_deltas(name, info, max_info, deltas, infoCnt, first_time); | |
319 | if (must_print) { | |
320 | if (ColFormat) { | |
321 | if (!first_time) | |
322 | printf("\n"); | |
323 | colprintzoneheader(); | |
324 | } | |
325 | for (i = 0; i < nameCnt; i++) { | |
326 | if (deltas[i]) { | |
327 | if (ColFormat) | |
328 | colprintzone(&name[i], &info[i]); | |
329 | else | |
330 | printzone(&name[i], &info[i]); | |
331 | } | |
332 | } | |
333 | } | |
334 | ||
335 | if (ShowLarge && first_time) { | |
336 | PrintLarge(wiredInfo, wiredInfoCnt, | |
337 | SortZones ? &SortSize : &SortName, ColFormat); | |
338 | } | |
339 | ||
340 | first_time = 0; | |
341 | ||
342 | if ((name != NULL) && (nameCnt != 0)) { | |
343 | kr = vm_deallocate(mach_task_self(), (vm_address_t) name, | |
344 | (vm_size_t) (nameCnt * sizeof *name)); | |
345 | if (kr != KERN_SUCCESS) { | |
346 | fprintf(stderr, "%s: vm_deallocate: %s\n", | |
347 | program, mach_error_string(kr)); | |
348 | exit(1); | |
349 | } | |
350 | } | |
351 | ||
352 | if ((info != NULL) && (infoCnt != 0)) { | |
353 | kr = vm_deallocate(mach_task_self(), (vm_address_t) info, | |
354 | (vm_size_t) (infoCnt * sizeof *info)); | |
355 | if (kr != KERN_SUCCESS) { | |
356 | fprintf(stderr, "%s: vm_deallocate: %s\n", | |
357 | program, mach_error_string(kr)); | |
358 | exit(1); | |
359 | } | |
360 | } | |
361 | ||
362 | if ((wiredInfo != NULL) && (wiredInfoCnt != 0)) { | |
363 | kr = vm_deallocate(mach_task_self(), (vm_address_t) wiredInfo, | |
364 | (vm_size_t) (wiredInfoCnt * sizeof *wiredInfo)); | |
365 | if (kr != KERN_SUCCESS) { | |
366 | fprintf(stderr, "%s: vm_deallocate: %s\n", | |
367 | program, mach_error_string(kr)); | |
368 | exit(1); | |
369 | } | |
370 | } | |
371 | ||
372 | if ((ShowWasted||ShowTotal) && PrintHeader && !ShowDeltas) { | |
373 | printf("TOTAL SIZE = %llu\n", totalsize); | |
374 | printf("TOTAL USED = %llu\n", totalused); | |
375 | if (ShowWasted) | |
376 | printf("TOTAL WASTED = %llu\n", totalsize - totalused); | |
377 | if (ShowTotal) | |
378 | printf("TOTAL ALLOCS = %llu\n", totalsum); | |
379 | } | |
380 | ||
381 | if (ShowDeltas == FALSE || last_time) | |
382 | break; | |
383 | ||
384 | sleep(interval); | |
385 | } | |
386 | exit(0); | |
387 | } | |
388 | ||
389 | static boolean_t | |
390 | substr(const char *a, size_t alen, const char *b, size_t blen) | |
391 | { | |
392 | int i; | |
393 | ||
394 | if (alen > blen) return FALSE; | |
395 | ||
396 | for (i = 0; i <= blen - alen; i++) | |
397 | if (strneql(a, b+i, alen)) | |
398 | return TRUE; | |
399 | ||
400 | return FALSE; | |
401 | } | |
402 | ||
403 | static void | |
404 | printzone(mach_zone_name_t *name, task_zone_info_t *info) | |
405 | { | |
406 | unsigned long long used, size; | |
407 | ||
408 | printf("%.*s zone:\n", (int)sizeof name->mzn_name, name->mzn_name); | |
409 | printf("\tcur_size: %lluK bytes (%llu elements)\n", | |
410 | info->tzi_cur_size/1024, | |
411 | (info->tzi_elem_size == 0) ? 0 : | |
412 | info->tzi_cur_size/info->tzi_elem_size); | |
413 | printf("\tmax_size: %lluK bytes (%llu elements)\n", | |
414 | info->tzi_max_size/1024, | |
415 | (info->tzi_elem_size == 0) ? 0 : | |
416 | info->tzi_max_size/info->tzi_elem_size); | |
417 | printf("\telem_size: %llu bytes\n", | |
418 | info->tzi_elem_size); | |
419 | printf("\t# of elems: %llu\n", | |
420 | info->tzi_count); | |
421 | printf("\talloc_size: %lluK bytes (%llu elements)\n", | |
422 | info->tzi_alloc_size/1024, | |
423 | (info->tzi_elem_size == 0) ? 0 : | |
424 | info->tzi_alloc_size/info->tzi_elem_size); | |
425 | if (info->tzi_exhaustible) | |
426 | printf("\tEXHAUSTIBLE\n"); | |
427 | if (info->tzi_collectable) | |
428 | printf("\tCOLLECTABLE\n"); | |
429 | if (ShowPid && info->tzi_caller_acct) | |
430 | printf("\tCALLER ACCOUNTED\n"); | |
431 | if (ShowPid) { | |
432 | pidsum += info->tzi_task_alloc - info->tzi_task_free; | |
433 | printf("\tproc_alloc_size: %8dK bytes (%llu elements)\n", | |
434 | (int)((info->tzi_task_alloc - info->tzi_task_free)/1024), | |
435 | (info->tzi_elem_size == 0) ? 0 : | |
436 | (info->tzi_task_alloc - info->tzi_task_free)/info->tzi_elem_size); | |
437 | } | |
438 | if (ShowWasted) { | |
439 | totalused += used = info->tzi_elem_size * info->tzi_count; | |
440 | totalsize += size = info->tzi_cur_size; | |
441 | printf("\t\t\t\t\tWASTED: %llu\n", size - used); | |
442 | } | |
443 | if (ShowTotal) { | |
444 | totalsum += info->tzi_sum_size; | |
445 | printf("\t\t\t\t\tTOTAL: %llu\n", totalsum); | |
446 | if (ShowPid) | |
447 | printf("\t\t\t\t\tPID TOTAL: %llu\n", pidsum); | |
448 | } | |
449 | } | |
450 | ||
451 | static void | |
452 | colprintzone(mach_zone_name_t *zone_name, task_zone_info_t *info) | |
453 | { | |
454 | char *name = zone_name->mzn_name; | |
455 | int j, namewidth; | |
456 | unsigned long long used, size; | |
457 | ||
458 | namewidth = 25; | |
459 | if (ShowWasted || ShowTotal) { | |
460 | namewidth -= 7; | |
461 | } | |
462 | for (j = 0; j < namewidth - 1 && name[j]; j++) { | |
463 | if (name[j] == ' ') { | |
464 | putchar('.'); | |
465 | } else { | |
466 | putchar(name[j]); | |
467 | } | |
468 | } | |
469 | if (j == namewidth - 1) { | |
470 | if (name[j]) { | |
471 | putchar('$'); | |
472 | } else { | |
473 | putchar(' '); | |
474 | } | |
475 | } else { | |
476 | for (; j < namewidth; j++) { | |
477 | putchar(' '); | |
478 | } | |
479 | } | |
480 | printf(" %6llu", info->tzi_elem_size); | |
481 | PRINTK(" %10llu", info->tzi_cur_size); | |
482 | if (info->tzi_max_size / 1024 > 9999999) { | |
483 | printf(" --------"); | |
484 | } else { | |
485 | PRINTK(" %10llu", info->tzi_max_size); | |
486 | } | |
487 | printf(" %10llu", info->tzi_cur_size / info->tzi_elem_size); | |
488 | if (info->tzi_max_size / 1024 >= 999999999) { | |
489 | printf(" ----------"); | |
490 | } else { | |
491 | printf(" %11llu", info->tzi_max_size / info->tzi_elem_size); | |
492 | } | |
493 | printf(" %11llu", info->tzi_count); | |
494 | PRINTK(" %5llu", info->tzi_alloc_size); | |
495 | printf(" %6llu", info->tzi_alloc_size / info->tzi_elem_size); | |
496 | ||
497 | totalused += used = info->tzi_elem_size * info->tzi_count; | |
498 | totalsize += size = info->tzi_cur_size; | |
499 | totalsum += info->tzi_sum_size; | |
500 | ||
501 | printf(" %c%c%c", | |
502 | (info->tzi_exhaustible ? 'X' : ' '), | |
503 | (info->tzi_caller_acct ? 'A' : ' '), | |
504 | (info->tzi_collectable ? 'C' : ' ')); | |
505 | if (ShowWasted) { | |
506 | PRINTK(" %8llu", size - used); | |
507 | } | |
508 | if (ShowPid) { | |
509 | printf("%8dK", (int)((info->tzi_task_alloc - info->tzi_task_free)/1024)); | |
510 | } | |
511 | if (ShowTotal) { | |
512 | if (info->tzi_sum_size < 1024) | |
513 | printf(" %16lluB", info->tzi_sum_size); | |
514 | else | |
515 | PRINTK(" %16llu", info->tzi_sum_size); | |
516 | } | |
517 | printf("\n"); | |
518 | } | |
519 | ||
520 | static void | |
521 | colprintzoneheader(void) | |
522 | { | |
523 | if (! PrintHeader) { | |
524 | return; | |
525 | } | |
526 | printf("%s elem cur max cur max" | |
527 | " cur alloc alloc %s%s\n", | |
528 | (ShowWasted||ShowTotal)? "" : " ", | |
529 | (ShowWasted)? " ":"", | |
530 | (ShowPid) ? " PID" : "" ); | |
531 | printf("zone name%s size size size #elts #elts" | |
532 | " inuse size count ", (ShowWasted||ShowTotal)? " " : " " ); | |
533 | if (ShowWasted) | |
534 | printf(" wasted"); | |
535 | if (ShowPid) | |
536 | printf(" Allocs"); | |
537 | if (ShowTotal) | |
538 | printf(" Total Allocs"); | |
539 | printf("\n%s-------------------------------------------------------" | |
540 | "-----------------------------------------------", | |
541 | (ShowWasted||ShowTotal)? "" : "-------"); | |
542 | if (ShowWasted) | |
543 | printf("----------"); | |
544 | if (ShowPid) | |
545 | printf("---------"); | |
546 | if (ShowTotal) | |
547 | printf("------------------"); | |
548 | printf("\n"); | |
549 | } | |
550 | ||
551 | int | |
552 | find_deltas(mach_zone_name_t *name, task_zone_info_t *info, task_zone_info_t *max_info, | |
553 | char *deltas, int cnt, int first_time) | |
554 | { | |
555 | int i; | |
556 | int found_one = 0; | |
557 | ||
558 | for (i = 0; i < cnt; i++) { | |
559 | deltas[i] = 0; | |
560 | if (substr(zname, znamelen, name[i].mzn_name, | |
561 | strnlen(name[i].mzn_name, sizeof name[i].mzn_name))) { | |
562 | if (first_time || info->tzi_cur_size > max_info->tzi_cur_size || | |
563 | (ShowTotal && ((info->tzi_sum_size >> 1) > max_info->tzi_sum_size))) { | |
564 | max_info->tzi_cur_size = info->tzi_cur_size; | |
565 | max_info->tzi_sum_size = info->tzi_sum_size; | |
566 | deltas[i] = 1; | |
567 | found_one = 1; | |
568 | } | |
569 | } | |
570 | info++; | |
571 | max_info++; | |
572 | } | |
573 | return(found_one); | |
574 | } | |
575 | ||
576 | /********************************************************************* | |
577 | *********************************************************************/ | |
578 | ||
579 | static char * | |
580 | kern_vm_tag_name(uint64_t tag) | |
581 | { | |
582 | char * result; | |
583 | const char * name; | |
584 | switch (tag) | |
585 | { | |
586 | case (VM_KERN_MEMORY_NONE): name = "VM_KERN_MEMORY_NONE"; break; | |
587 | case (VM_KERN_MEMORY_OSFMK): name = "VM_KERN_MEMORY_OSFMK"; break; | |
588 | case (VM_KERN_MEMORY_BSD): name = "VM_KERN_MEMORY_BSD"; break; | |
589 | case (VM_KERN_MEMORY_IOKIT): name = "VM_KERN_MEMORY_IOKIT"; break; | |
590 | case (VM_KERN_MEMORY_LIBKERN): name = "VM_KERN_MEMORY_LIBKERN"; break; | |
591 | case (VM_KERN_MEMORY_OSKEXT): name = "VM_KERN_MEMORY_OSKEXT"; break; | |
592 | case (VM_KERN_MEMORY_KEXT): name = "VM_KERN_MEMORY_KEXT"; break; | |
593 | case (VM_KERN_MEMORY_IPC): name = "VM_KERN_MEMORY_IPC"; break; | |
594 | case (VM_KERN_MEMORY_STACK): name = "VM_KERN_MEMORY_STACK"; break; | |
595 | case (VM_KERN_MEMORY_CPU): name = "VM_KERN_MEMORY_CPU"; break; | |
596 | case (VM_KERN_MEMORY_PMAP): name = "VM_KERN_MEMORY_PMAP"; break; | |
597 | case (VM_KERN_MEMORY_PTE): name = "VM_KERN_MEMORY_PTE"; break; | |
598 | case (VM_KERN_MEMORY_ZONE): name = "VM_KERN_MEMORY_ZONE"; break; | |
599 | case (VM_KERN_MEMORY_KALLOC): name = "VM_KERN_MEMORY_KALLOC"; break; | |
600 | case (VM_KERN_MEMORY_COMPRESSOR): name = "VM_KERN_MEMORY_COMPRESSOR"; break; | |
601 | case (VM_KERN_MEMORY_COMPRESSED_DATA): name = "VM_KERN_MEMORY_COMPRESSED_DATA"; break; | |
602 | case (VM_KERN_MEMORY_PHANTOM_CACHE): name = "VM_KERN_MEMORY_PHANTOM_CACHE"; break; | |
603 | case (VM_KERN_MEMORY_WAITQ): name = "VM_KERN_MEMORY_WAITQ"; break; | |
604 | case (VM_KERN_MEMORY_DIAG): name = "VM_KERN_MEMORY_DIAG"; break; | |
605 | case (VM_KERN_MEMORY_LOG): name = "VM_KERN_MEMORY_LOG"; break; | |
606 | case (VM_KERN_MEMORY_FILE): name = "VM_KERN_MEMORY_FILE"; break; | |
607 | case (VM_KERN_MEMORY_MBUF): name = "VM_KERN_MEMORY_MBUF"; break; | |
608 | case (VM_KERN_MEMORY_UBC): name = "VM_KERN_MEMORY_UBC"; break; | |
609 | case (VM_KERN_MEMORY_SECURITY): name = "VM_KERN_MEMORY_SECURITY"; break; | |
610 | case (VM_KERN_MEMORY_MLOCK): name = "VM_KERN_MEMORY_MLOCK"; break; | |
611 | case (VM_KERN_MEMORY_REASON): name = "VM_KERN_MEMORY_REASON"; break; | |
612 | case (VM_KERN_MEMORY_SKYWALK): name = "VM_KERN_MEMORY_SKYWALK"; break; | |
613 | case (VM_KERN_MEMORY_LTABLE): name = "VM_KERN_MEMORY_LTABLE"; break; | |
614 | case (VM_KERN_MEMORY_ANY): name = "VM_KERN_MEMORY_ANY"; break; | |
615 | default: name = NULL; break; | |
616 | } | |
617 | if (name) asprintf(&result, "%s", name); | |
618 | else asprintf(&result, "VM_KERN_MEMORY_%lld", tag); | |
619 | return (result); | |
620 | } | |
621 | ||
622 | static char * | |
623 | kern_vm_counter_name(uint64_t tag) | |
624 | { | |
625 | char * result; | |
626 | const char * name; | |
627 | switch (tag) | |
628 | { | |
629 | case (VM_KERN_COUNT_MANAGED): name = "VM_KERN_COUNT_MANAGED"; break; | |
630 | case (VM_KERN_COUNT_RESERVED): name = "VM_KERN_COUNT_RESERVED"; break; | |
631 | case (VM_KERN_COUNT_WIRED): name = "VM_KERN_COUNT_WIRED"; break; | |
632 | case (VM_KERN_COUNT_WIRED_MANAGED): name = "VM_KERN_COUNT_WIRED_MANAGED"; break; | |
633 | case (VM_KERN_COUNT_STOLEN): name = "VM_KERN_COUNT_STOLEN"; break; | |
634 | case (VM_KERN_COUNT_LOPAGE): name = "VM_KERN_COUNT_LOPAGE"; break; | |
635 | case (VM_KERN_COUNT_MAP_KERNEL): name = "VM_KERN_COUNT_MAP_KERNEL"; break; | |
636 | case (VM_KERN_COUNT_MAP_ZONE): name = "VM_KERN_COUNT_MAP_ZONE"; break; | |
637 | case (VM_KERN_COUNT_MAP_KALLOC): name = "VM_KERN_COUNT_MAP_KALLOC"; break; | |
638 | default: name = NULL; break; | |
639 | } | |
640 | if (name) asprintf(&result, "%s", name); | |
641 | else asprintf(&result, "VM_KERN_COUNT_%lld", tag); | |
642 | return (result); | |
643 | } | |
644 | ||
645 | static void | |
646 | MakeLoadTagKeys(const void * key, const void * value, void * context) | |
647 | { | |
648 | CFMutableDictionaryRef newDict = context; | |
649 | CFDictionaryRef kextInfo = value; | |
650 | CFNumberRef loadTag; | |
651 | uint32_t loadTagValue; | |
652 | ||
653 | loadTag = (CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleLoadTagKey)); | |
654 | CFNumberGetValue(loadTag, kCFNumberSInt32Type, &loadTagValue); | |
655 | key = (const void *)(uintptr_t) loadTagValue; | |
656 | CFDictionarySetValue(newDict, key, value); | |
657 | } | |
658 | ||
659 | static CSSymbolicatorRef gSym; | |
660 | static CFMutableDictionaryRef gTagDict; | |
661 | static mach_memory_info_t * gSites; | |
662 | ||
663 | static char * | |
664 | GetSiteName(int siteIdx) | |
665 | { | |
666 | const char * name; | |
667 | char * result; | |
668 | mach_vm_address_t addr; | |
669 | CFDictionaryRef kextInfo; | |
670 | CFStringRef bundleID; | |
671 | uint32_t type; | |
672 | ||
673 | const mach_memory_info_t * site; | |
674 | const char * fileName; | |
675 | CSSymbolRef symbol; | |
676 | const char * symbolName; | |
677 | CSSourceInfoRef sourceInfo; | |
678 | ||
679 | name = NULL; | |
680 | result = NULL; | |
681 | site = &gSites[siteIdx]; | |
682 | addr = site->site; | |
683 | type = (VM_KERN_SITE_TYPE & site->flags); | |
684 | switch (type) | |
685 | { | |
686 | case VM_KERN_SITE_TAG: | |
687 | result = kern_vm_tag_name(addr); | |
688 | break; | |
689 | ||
690 | case VM_KERN_SITE_COUNTER: | |
691 | result = kern_vm_counter_name(addr); | |
692 | break; | |
693 | ||
694 | case VM_KERN_SITE_KMOD: | |
695 | kextInfo = CFDictionaryGetValue(gTagDict, (const void *)(uintptr_t) addr); | |
696 | if (kextInfo) | |
697 | { | |
698 | bundleID = (CFStringRef)CFDictionaryGetValue(kextInfo, kCFBundleIdentifierKey); | |
699 | name = CFStringGetCStringPtr(bundleID, kCFStringEncodingUTF8); | |
700 | // wiredSize = (CFNumberRef)CFDictionaryGetValue(kextInfo, CFSTR(kOSBundleWiredSizeKey)); | |
701 | } | |
702 | asprintf(&result, "%-64s%3lld", name ? name : "(unknown kmod)", addr); | |
703 | break; | |
704 | ||
705 | case VM_KERN_SITE_KERNEL: | |
706 | symbolName = NULL; | |
707 | if (addr) | |
708 | { | |
709 | symbol = CSSymbolicatorGetSymbolWithAddressAtTime(gSym, addr, kCSNow); | |
710 | symbolName = CSSymbolGetName(symbol); | |
711 | } | |
712 | if (symbolName) | |
713 | { | |
714 | asprintf(&result, "%s", symbolName); | |
715 | sourceInfo = CSSymbolicatorGetSourceInfoWithAddressAtTime(gSym, addr, kCSNow); | |
716 | fileName = CSSourceInfoGetPath(sourceInfo); | |
717 | if (fileName) printf(" (%s:%d)", fileName, CSSourceInfoGetLineNumber(sourceInfo)); | |
718 | } | |
719 | else | |
720 | { | |
721 | asprintf(&result, "site 0x%qx", addr); | |
722 | } | |
723 | break; | |
724 | default: | |
725 | asprintf(&result, ""); | |
726 | break; | |
727 | } | |
728 | ||
729 | return (result); | |
730 | } | |
731 | ||
732 | static int | |
733 | SortName(const void * left, const void * right) | |
734 | { | |
735 | const int * idxL; | |
736 | const int * idxR; | |
737 | char * l; | |
738 | char * r; | |
739 | int result; | |
740 | ||
741 | idxL = (typeof(idxL)) left; | |
742 | idxR = (typeof(idxR)) right; | |
743 | l = GetSiteName(*idxL); | |
744 | r = GetSiteName(*idxR); | |
745 | ||
746 | result = strcmp(l, r); | |
747 | free(l); | |
748 | free(r); | |
749 | ||
750 | return (result); | |
751 | } | |
752 | ||
753 | static int | |
754 | SortSize(const void * left, const void * right) | |
755 | { | |
756 | const mach_memory_info_t * siteL; | |
757 | const mach_memory_info_t * siteR; | |
758 | const int * idxL; | |
759 | const int * idxR; | |
760 | ||
761 | idxL = (typeof(idxL)) left; | |
762 | idxR = (typeof(idxR)) right; | |
763 | siteL = &gSites[*idxL]; | |
764 | siteR = &gSites[*idxR]; | |
765 | ||
766 | if (siteL->size > siteR->size) return (-1); | |
767 | else if (siteL->size < siteR->size) return (1); | |
768 | return (0); | |
769 | } | |
770 | ||
771 | ||
772 | static void | |
773 | PrintLarge(mach_memory_info_t *wiredInfo, unsigned int wiredInfoCnt, | |
774 | int (*func)(const void *, const void *), boolean_t column) | |
775 | { | |
776 | uint64_t zonetotal; | |
777 | uint64_t top_wired; | |
778 | ||
779 | CFDictionaryRef allKexts; | |
780 | unsigned int idx, site, first; | |
781 | int sorted[wiredInfoCnt]; | |
782 | char totalstr[40]; | |
783 | char * name; | |
784 | bool headerPrinted; | |
785 | ||
786 | zonetotal = totalsize; | |
787 | ||
788 | gSites = wiredInfo; | |
789 | ||
790 | gSym = CSSymbolicatorCreateWithMachKernel(); | |
791 | ||
792 | allKexts = OSKextCopyLoadedKextInfo(NULL, NULL); | |
793 | gTagDict = CFDictionaryCreateMutable( | |
794 | kCFAllocatorDefault, (CFIndex) 0, | |
795 | (CFDictionaryKeyCallBacks *) 0, | |
796 | &kCFTypeDictionaryValueCallBacks); | |
797 | ||
798 | CFDictionaryApplyFunction(allKexts, &MakeLoadTagKeys, gTagDict); | |
799 | CFRelease(allKexts); | |
800 | ||
801 | top_wired = 0; | |
802 | ||
803 | for (idx = 0; idx < wiredInfoCnt; idx++) sorted[idx] = idx; | |
804 | first = 0; // VM_KERN_MEMORY_FIRST_DYNAMIC | |
805 | qsort(&sorted[first], | |
806 | wiredInfoCnt - first, | |
807 | sizeof(sorted[0]), | |
808 | func); | |
809 | ||
810 | for (headerPrinted = false, idx = 0; idx < wiredInfoCnt; idx++) | |
811 | { | |
812 | site = sorted[idx]; | |
813 | if (!gSites[site].size) continue; | |
814 | if (VM_KERN_COUNT_WIRED == gSites[site].site) top_wired = gSites[site].size; | |
815 | if (VM_KERN_SITE_HIDE & gSites[site].flags) continue; | |
816 | if (!(VM_KERN_SITE_WIRED & gSites[site].flags)) continue; | |
817 | ||
818 | name = GetSiteName(site); | |
819 | if (!substr(zname, znamelen, name, strlen(name))) continue; | |
820 | if (!headerPrinted) | |
821 | { | |
822 | printf("-------------------------------------------------------------------------------------------------------------\n"); | |
823 | printf(" kmod vm cur\n"); | |
824 | printf("wired memory id tag size\n"); | |
825 | printf("-------------------------------------------------------------------------------------------------------------\n"); | |
826 | headerPrinted = true; | |
827 | } | |
828 | printf("%-67s", name); | |
829 | free(name); | |
830 | printf("%12d", site); | |
831 | ||
832 | printf(" %11s", ""); | |
833 | PRINTK(" %12llu", gSites[site].size); | |
834 | totalsize += gSites[site].size; | |
835 | ||
836 | printf("\n"); | |
837 | } | |
838 | ||
839 | if (!znamelen) | |
840 | { | |
841 | printf("%-67s", "zones"); | |
842 | printf("%12s", ""); | |
843 | printf(" %11s", ""); | |
844 | PRINTK(" %12llu", zonetotal); | |
845 | printf("\n"); | |
846 | } | |
847 | if (headerPrinted) | |
848 | { | |
849 | snprintf(totalstr, sizeof(totalstr), "%6.2fM of %6.2fM", totalsize / 1024.0 / 1024.0, top_wired / 1024.0 / 1024.0); | |
850 | printf("total%100s\n", totalstr); | |
851 | } | |
852 | for (headerPrinted = false, idx = 0; idx < wiredInfoCnt; idx++) | |
853 | { | |
854 | site = sorted[idx]; | |
855 | if (!gSites[site].size) continue; | |
856 | if (VM_KERN_SITE_HIDE & gSites[site].flags) continue; | |
857 | if (VM_KERN_SITE_WIRED & gSites[site].flags) continue; | |
858 | ||
859 | name = GetSiteName(site); | |
860 | if (!substr(zname, znamelen, name, strlen(name))) continue; | |
861 | if (!headerPrinted) | |
862 | { | |
863 | printf("-------------------------------------------------------------------------------------------------------------\n"); | |
864 | printf(" largest\n"); | |
865 | printf("maps free free size\n"); | |
866 | printf("-------------------------------------------------------------------------------------------------------------\n"); | |
867 | headerPrinted = true; | |
868 | } | |
869 | printf("%-67s", name); | |
870 | free(name); | |
871 | ||
872 | PRINTK(" %10llu", gSites[site].free); | |
873 | PRINTK(" %10llu", gSites[site].largest); | |
874 | PRINTK(" %12llu", gSites[site].size); | |
875 | ||
876 | printf("\n"); | |
877 | } | |
878 | } |