5 // Created by Rasha Eqbal on 1/4/18.
12 #include <sys/sysctl.h>
13 #include <mach/mach.h>
14 #include <mach/mach_error.h>
15 #include <mach_debug/mach_debug.h>
16 #include <CoreFoundation/CoreFoundation.h>
17 #include <CoreSymbolication/CoreSymbolication.h>
18 #include "SymbolicationHelper.h"
21 mach_zone_get_btlog_records(host_priv_t host
,
22 mach_zone_name_t name
,
23 zone_btrecord_array_t
*recsp
,
24 mach_msg_type_number_t
*recsCntp
);
26 mach_zone_get_zlog_zones(host_priv_t host
,
27 mach_zone_name_array_t
*namesp
,
28 mach_msg_type_number_t
*namesCntp
);
30 static int compare_zone_btrecords(const void *left
, const void *right
);
31 static void usage(FILE *stream
, char **argv
);
32 static void print_zone_info(const char *name
);
33 static void get_zone_btrecords(const char *name
, int topN
);
34 static void list_zones_with_zlog_enabled(void);
36 static void usage(FILE *stream
, char **argv
)
38 fprintf (stream
, "usage: %s [-t] [-z name [-n num | -l]] [-h]\n", argv
[0]);
39 fprintf (stream
, " -t : list all the zones that have logging enabled\n");
40 fprintf (stream
, " -z <name> : show all allocation backtraces for zone <name>\n");
41 fprintf (stream
, " -n <num> : show top <num> backtraces with the most active references in zone <name>\n");
42 fprintf (stream
, " -l : show the backtrace most likely contributing to a leak in zone <name>\n");
43 fprintf (stream
, " (prints the backtrace with the most active references)\n");
44 fprintf (stream
, " -h : print this help text\n");
45 exit(stream
!= stdout
);
48 static int compare_zone_btrecords(const void *left
, const void *right
)
50 zone_btrecord_t
*btl
= (zone_btrecord_t
*)left
;
51 zone_btrecord_t
*btr
= (zone_btrecord_t
*)right
;
53 return (btr
->ref_count
- btl
->ref_count
);
56 static void print_zone_info(const char *name
)
58 mach_zone_name_t zname
;
59 mach_zone_info_t zone_info
;
62 strcpy(zname
.mzn_name
, name
);
63 kr
= mach_zone_info_for_zone(mach_host_self(), zname
, &zone_info
);
64 if (kr
!= KERN_SUCCESS
) {
65 fprintf(stderr
, "error: call to mach_zone_info_for_zone() failed: %s\n", mach_error_string(kr
));
68 printf("zone name : %s\n", name
);
69 printf("element size (bytes) : %lld\n", zone_info
.mzi_elem_size
);
70 printf("in-use size (bytes) : %lld\n", zone_info
.mzi_count
* zone_info
.mzi_elem_size
);
71 printf("total size (bytes) : %lld\n", zone_info
.mzi_cur_size
);
75 static void get_zone_btrecords(const char *name
, int topN
)
79 mach_zone_name_t zname
;
80 unsigned int recs_count
= 0;
81 zone_btrecord_t
*recs
, *recs_addr
= NULL
;
82 CSSymbolicatorRef kernelSym
;
83 CFMutableDictionaryRef binaryImages
;
85 /* Create kernel symbolicator */
86 kernelSym
= CSSymbolicatorCreateWithMachKernel();
87 if (CSIsNull(kernelSym
)) {
88 fprintf(stderr
, "error: CSSymbolicatorCreateWithMachKernel() returned NULL\n");
91 /* Create dictionary to collect binary image info for offline symbolication */
92 binaryImages
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
94 /* Query the kernel for backtrace records */
95 strcpy(zname
.mzn_name
, name
);
96 kr
= mach_zone_get_btlog_records(mach_host_self(), zname
, &recs_addr
, &recs_count
);
97 if (kr
!= KERN_SUCCESS
) {
98 fprintf(stderr
, "error: call to mach_zone_get_btlog_records() failed: %s\n", mach_error_string(kr
));
102 if (recs_count
== 0) {
108 /* Print the backtrace with the highest no. of refs */
110 for (i
= 0; i
< recs_count
; i
++) {
111 if (recs
[i
].ref_count
> recs
[index
].ref_count
) {
115 recs
= recs_addr
+ index
;
116 } else if (topN
== 0) {
117 /* Print all backtraces */
120 /* Sort the records by no. of refs, and print the top <topN> */
121 qsort(recs
, recs_count
, sizeof *recs
, compare_zone_btrecords
);
124 printf("printing top %d (out of %d) allocation backtrace(s) for zone %s\n", topN
, recs_count
, zname
.mzn_name
);
126 for (i
= 0; i
< topN
; i
++) {
127 printf("\nactive refs: %d operation type: %s\n", recs
[i
].ref_count
,
128 (recs
[i
].operation_type
== ZOP_ALLOC
)? "ALLOC": (recs
[i
].operation_type
== ZOP_FREE
)? "FREE": "UNKNOWN");
130 for (j
= 0; j
< MAX_ZTRACE_DEPTH
; j
++) {
131 mach_vm_address_t addr
= (mach_vm_address_t
)recs
[i
].bt
[j
];
135 PrintSymbolicatedAddress(kernelSym
, addr
, binaryImages
);
139 /* Print relevant info for offline symbolication */
140 PrintBinaryImagesInfo(binaryImages
);
141 CFRelease(binaryImages
);
144 if ((recs_addr
!= NULL
) && (recs_count
!= 0)) {
145 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) recs_addr
, (vm_size_t
) (recs_count
* sizeof *recs
));
146 if (kr
!= KERN_SUCCESS
) {
147 fprintf(stderr
, "call to vm_deallocate() failed: %s\n", mach_error_string(kr
));
151 CSRelease(kernelSym
);
154 static void list_zones_with_zlog_enabled(void)
157 mach_zone_name_t
*name
= NULL
;
158 unsigned int name_count
= 0, i
;
160 /* Get names for zones that have zone logging enabled */
161 kr
= mach_zone_get_zlog_zones(mach_host_self(), &name
, &name_count
);
162 if (kr
!= KERN_SUCCESS
) {
163 fprintf(stderr
, "error: call to mach_zone_get_zlog_zones() failed: %s\n", mach_error_string(kr
));
167 if (name_count
== 0) {
168 printf("zlog not enabled for any zones.\n");
170 printf("zlog enabled for zones...\n");
173 for (i
= 0; i
< name_count
; i
++) {
174 print_zone_info(name
[i
].mzn_name
);
177 if ((name
!= NULL
) && (name_count
!= 0)) {
178 kr
= vm_deallocate(mach_task_self(), (vm_address_t
) name
, (vm_size_t
) (name_count
* sizeof *name
));
179 if (kr
!= KERN_SUCCESS
) {
180 fprintf(stderr
, "call to vm_deallocate() failed: %s\n", mach_error_string(kr
));
186 #define VERSION_STRING "zlog output version: 1"
189 get_osversion(char *buffer
, size_t buffer_len
)
191 int mib
[] = {CTL_KERN
, KERN_OSVERSION
};
193 ret
= sysctl(mib
, sizeof(mib
) / sizeof(int), buffer
, &buffer_len
, NULL
, 0);
195 strlcpy(buffer
, "Unknown", buffer_len
);
200 int main(int argc
, char *argv
[])
203 const char *zone_name
= NULL
;
205 /* Identifier string for SpeedTracer parsing */
206 printf("%s\n\n", VERSION_STRING
);
207 char version_buffer
[32] = {0};
208 get_osversion(version_buffer
, sizeof(version_buffer
));
209 printf("Collected on build: %s\n\n", version_buffer
);
212 /* default when no arguments are specified */
213 list_zones_with_zlog_enabled();
214 printf("Run 'zlog -h' for usage info.\n");
218 while ((c
= getopt(argc
, argv
, "tz:n:lh")) != -1) {
221 list_zones_with_zlog_enabled();
247 print_zone_info(zone_name
);
248 get_zone_btrecords(zone_name
, topN
);
250 /* -n or -l was specified without -z */