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