5 // Created by Rasha Eqbal on 1/4/18.
12 #include <mach/mach.h>
13 #include <mach/mach_error.h>
14 #include <mach_debug/mach_debug.h>
15 #include <CoreFoundation/CoreFoundation.h>
16 #include <CoreSymbolication/CoreSymbolication.h>
17 #include "SymbolicationHelper.h"
20 mach_zone_get_btlog_records(host_priv_t host
,
21 mach_zone_name_t name
,
22 zone_btrecord_array_t
*recsp
,
23 mach_msg_type_number_t
*recsCntp
);
25 mach_zone_get_zlog_zones(host_priv_t host
,
26 mach_zone_name_array_t
*namesp
,
27 mach_msg_type_number_t
*namesCntp
);
29 static int compare_zone_btrecords(const void *left
, const void *right
);
30 static void usage(FILE *stream
, char **argv
);
31 static void print_zone_info(const char *name
);
32 static void get_zone_btrecords(const char *name
, int topN
);
33 static void list_zones_with_zlog_enabled(void);
35 static void usage(FILE *stream
, char **argv
)
37 fprintf (stream
, "usage: %s [-t] [-z name [-n num | -l]] [-h]\n", argv
[0]);
38 fprintf (stream
, " -t : list all the zones that have logging enabled\n");
39 fprintf (stream
, " -z <name> : show all allocation backtraces for zone <name>\n");
40 fprintf (stream
, " -n <num> : show top <num> backtraces with the most active references in zone <name>\n");
41 fprintf (stream
, " -l : show the backtrace most likely contributing to a leak in zone <name>\n");
42 fprintf (stream
, " (prints the backtrace with the most active references)\n");
43 fprintf (stream
, " -h : print this help text\n");
44 exit(stream
!= stdout
);
47 static int compare_zone_btrecords(const void *left
, const void *right
)
49 zone_btrecord_t
*btl
= (zone_btrecord_t
*)left
;
50 zone_btrecord_t
*btr
= (zone_btrecord_t
*)right
;
52 return (btr
->ref_count
- btl
->ref_count
);
55 static void print_zone_info(const char *name
)
57 mach_zone_name_t zname
;
58 mach_zone_info_t zone_info
;
61 strcpy(zname
.mzn_name
, name
);
62 kr
= mach_zone_info_for_zone(mach_host_self(), zname
, &zone_info
);
63 if (kr
!= KERN_SUCCESS
) {
64 fprintf(stderr
, "error: call to mach_zone_info_for_zone() failed: %s\n", mach_error_string(kr
));
67 printf("zone name : %s\n", name
);
68 printf("element size (bytes) : %lld\n", zone_info
.mzi_elem_size
);
69 printf("in-use size (bytes) : %lld\n", zone_info
.mzi_count
* zone_info
.mzi_elem_size
);
70 printf("total size (bytes) : %lld\n", zone_info
.mzi_cur_size
);
74 static void get_zone_btrecords(const char *name
, int topN
)
78 mach_zone_name_t zname
;
79 unsigned int recs_count
= 0;
80 zone_btrecord_t
*recs
, *recs_addr
= NULL
;
81 CSSymbolicatorRef kernelSym
;
82 CFMutableDictionaryRef binaryImages
;
84 /* Create kernel symbolicator */
85 kernelSym
= CSSymbolicatorCreateWithMachKernel();
86 if (CSIsNull(kernelSym
)) {
87 fprintf(stderr
, "error: CSSymbolicatorCreateWithMachKernel() returned NULL\n");
90 /* Create dictionary to collect binary image info for offline symbolication */
91 binaryImages
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
93 /* Query the kernel for backtrace records */
94 strcpy(zname
.mzn_name
, name
);
95 kr
= mach_zone_get_btlog_records(mach_host_self(), zname
, &recs_addr
, &recs_count
);
96 if (kr
!= KERN_SUCCESS
) {
97 fprintf(stderr
, "error: call to mach_zone_get_btlog_records() failed: %s\n", mach_error_string(kr
));
101 if (recs_count
== 0) {
107 /* Print the backtrace with the highest no. of refs */
109 for (i
= 0; i
< recs_count
; i
++) {
110 if (recs
[i
].ref_count
> recs
[index
].ref_count
) {
114 recs
= recs_addr
+ index
;
115 } else if (topN
== 0) {
116 /* Print all backtraces */
119 /* Sort the records by no. of refs, and print the top <topN> */
120 qsort(recs
, recs_count
, sizeof *recs
, compare_zone_btrecords
);
123 printf("printing top %d (out of %d) allocation backtrace(s) for zone %s\n", topN
, recs_count
, zname
.mzn_name
);
125 for (i
= 0; i
< topN
; i
++) {
126 printf("\nactive refs: %d operation type: %s\n", recs
[i
].ref_count
,
127 (recs
[i
].operation_type
== ZOP_ALLOC
)? "ALLOC": (recs
[i
].operation_type
== ZOP_FREE
)? "FREE": "UNKNOWN");
129 for (j
= 0; j
< MAX_ZTRACE_DEPTH
; j
++) {
130 mach_vm_address_t addr
= (mach_vm_address_t
)recs
[i
].bt
[j
];
134 PrintSymbolicatedAddress(kernelSym
, addr
, binaryImages
);
138 /* Print relevant info for offline symbolication */
139 PrintBinaryImagesInfo(binaryImages
);
140 CFRelease(binaryImages
);
143 if ((recs_addr
!= NULL
) && (recs_count
!= 0)) {
144 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) recs_addr
, (vm_size_t
) (recs_count
* sizeof *recs
));
145 if (kr
!= KERN_SUCCESS
) {
146 fprintf(stderr
, "call to vm_deallocate() failed: %s\n", mach_error_string(kr
));
150 CSRelease(kernelSym
);
153 static void list_zones_with_zlog_enabled(void)
156 mach_zone_name_t
*name
= NULL
;
157 unsigned int name_count
= 0, i
;
159 /* Get names for zones that have zone logging enabled */
160 kr
= mach_zone_get_zlog_zones(mach_host_self(), &name
, &name_count
);
161 if (kr
!= KERN_SUCCESS
) {
162 fprintf(stderr
, "error: call to mach_zone_get_zlog_zones() failed: %s\n", mach_error_string(kr
));
166 if (name_count
== 0) {
167 printf("zlog not enabled for any zones.\n");
169 printf("zlog enabled for zones...\n");
172 for (i
= 0; i
< name_count
; i
++) {
173 print_zone_info(name
[i
].mzn_name
);
176 if ((name
!= NULL
) && (name_count
!= 0)) {
177 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) name
, (vm_size_t
) (name_count
* sizeof *name
));
178 if (kr
!= KERN_SUCCESS
) {
179 fprintf(stderr
, "call to vm_deallocate() failed: %s\n", mach_error_string(kr
));
185 #define VERSION_STRING "zlog output version: 1"
187 int main(int argc
, char *argv
[])
190 const char *zone_name
= NULL
;
192 /* Identifier string for SpeedTracer parsing */
193 printf("%s\n\n", VERSION_STRING
);
196 /* default when no arguments are specified */
197 list_zones_with_zlog_enabled();
198 printf("Run 'zlog -h' for usage info.\n");
202 while ((c
= getopt(argc
, argv
, "tz:n:lh")) != -1) {
205 list_zones_with_zlog_enabled();
231 print_zone_info(zone_name
);
232 get_zone_btrecords(zone_name
, topN
);
234 /* -n or -l was specified without -z */