Commit | Line | Data |
---|---|---|
530d02b6 A |
1 | // |
2 | // zlog.c | |
3 | // zlog | |
4 | // | |
5 | // Created by Rasha Eqbal on 1/4/18. | |
6 | // | |
7 | ||
8 | #include <stdio.h> | |
9 | #include <stdlib.h> | |
10 | #include <unistd.h> | |
11 | #include <string.h> | |
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" | |
18 | ||
19 | extern kern_return_t | |
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); | |
24 | extern kern_return_t | |
25 | mach_zone_get_zlog_zones(host_priv_t host, | |
26 | mach_zone_name_array_t *namesp, | |
27 | mach_msg_type_number_t *namesCntp); | |
28 | ||
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); | |
34 | ||
35 | static void usage(FILE *stream, char **argv) | |
36 | { | |
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); | |
45 | } | |
46 | ||
47 | static int compare_zone_btrecords(const void *left, const void *right) | |
48 | { | |
49 | zone_btrecord_t *btl = (zone_btrecord_t *)left; | |
50 | zone_btrecord_t *btr = (zone_btrecord_t *)right; | |
51 | ||
52 | return (btr->ref_count - btl->ref_count); | |
53 | } | |
54 | ||
55 | static void print_zone_info(const char *name) | |
56 | { | |
57 | mach_zone_name_t zname; | |
58 | mach_zone_info_t zone_info; | |
59 | kern_return_t kr; | |
60 | ||
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)); | |
65 | exit(1); | |
66 | } | |
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); | |
71 | printf("\n"); | |
72 | } | |
73 | ||
74 | static void get_zone_btrecords(const char *name, int topN) | |
75 | { | |
76 | kern_return_t kr; | |
77 | int i, j, index; | |
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; | |
83 | ||
84 | /* Create kernel symbolicator */ | |
85 | kernelSym = CSSymbolicatorCreateWithMachKernel(); | |
86 | if (CSIsNull(kernelSym)) { | |
87 | fprintf(stderr, "error: CSSymbolicatorCreateWithMachKernel() returned NULL\n"); | |
88 | exit(1); | |
89 | } | |
90 | /* Create dictionary to collect binary image info for offline symbolication */ | |
91 | binaryImages = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); | |
92 | ||
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)); | |
98 | exit(1); | |
99 | } | |
100 | ||
101 | if (recs_count == 0) { | |
102 | goto finish; | |
103 | } | |
104 | ||
105 | recs = recs_addr; | |
106 | if (topN == 1) { | |
107 | /* Print the backtrace with the highest no. of refs */ | |
108 | index = 0; | |
109 | for (i = 0; i < recs_count; i++) { | |
110 | if (recs[i].ref_count > recs[index].ref_count) { | |
111 | index = i; | |
112 | } | |
113 | } | |
114 | recs = recs_addr + index; | |
115 | } else if (topN == 0) { | |
116 | /* Print all backtraces */ | |
117 | topN = recs_count; | |
118 | } else { | |
119 | /* Sort the records by no. of refs, and print the top <topN> */ | |
120 | qsort(recs, recs_count, sizeof *recs, compare_zone_btrecords); | |
121 | } | |
122 | ||
123 | printf("printing top %d (out of %d) allocation backtrace(s) for zone %s\n", topN, recs_count, zname.mzn_name); | |
124 | ||
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"); | |
128 | ||
129 | for (j = 0; j < MAX_ZTRACE_DEPTH; j++) { | |
130 | mach_vm_address_t addr = (mach_vm_address_t)recs[i].bt[j]; | |
131 | if (!addr) { | |
132 | break; | |
133 | } | |
134 | PrintSymbolicatedAddress(kernelSym, addr, binaryImages); | |
135 | } | |
136 | } | |
137 | ||
138 | /* Print relevant info for offline symbolication */ | |
139 | PrintBinaryImagesInfo(binaryImages); | |
140 | CFRelease(binaryImages); | |
141 | ||
142 | finish: | |
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)); | |
147 | exit(1); | |
148 | } | |
149 | } | |
150 | CSRelease(kernelSym); | |
151 | } | |
152 | ||
153 | static void list_zones_with_zlog_enabled(void) | |
154 | { | |
155 | kern_return_t kr; | |
156 | mach_zone_name_t *name = NULL; | |
157 | unsigned int name_count = 0, i; | |
158 | ||
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)); | |
163 | exit(1); | |
164 | } | |
165 | ||
166 | if (name_count == 0) { | |
167 | printf("zlog not enabled for any zones.\n"); | |
168 | } else { | |
169 | printf("zlog enabled for zones...\n"); | |
170 | } | |
171 | ||
172 | for (i = 0; i < name_count; i++) { | |
173 | print_zone_info(name[i].mzn_name); | |
174 | } | |
175 | ||
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)); | |
180 | exit(1); | |
181 | } | |
182 | } | |
183 | } | |
184 | ||
185 | #define VERSION_STRING "zlog output version: 1" | |
186 | ||
187 | int main(int argc, char *argv[]) | |
188 | { | |
189 | int c, topN = 0; | |
190 | const char *zone_name = NULL; | |
191 | ||
192 | /* Identifier string for SpeedTracer parsing */ | |
193 | printf("%s\n\n", VERSION_STRING); | |
194 | ||
195 | if (argc == 1) { | |
196 | /* default when no arguments are specified */ | |
197 | list_zones_with_zlog_enabled(); | |
198 | printf("Run 'zlog -h' for usage info.\n"); | |
199 | return 0; | |
200 | } | |
201 | ||
202 | while ((c = getopt(argc, argv, "tz:n:lh")) != -1) { | |
203 | switch(c) { | |
204 | case 't': | |
205 | list_zones_with_zlog_enabled(); | |
206 | break; | |
207 | case 'z': | |
208 | zone_name = optarg; | |
209 | break; | |
210 | case 'n': | |
211 | topN = atoi(optarg); | |
212 | break; | |
213 | case 'l': | |
214 | topN = 1; | |
215 | break; | |
216 | case 'h': | |
217 | usage(stdout, argv); | |
218 | break; | |
219 | case '?': | |
220 | default: | |
221 | usage(stderr, argv); | |
222 | break; | |
223 | } | |
224 | } | |
225 | ||
226 | if (optind < argc) { | |
227 | usage(stderr, argv); | |
228 | } | |
229 | ||
230 | if (zone_name) { | |
231 | print_zone_info(zone_name); | |
232 | get_zone_btrecords(zone_name, topN); | |
233 | } else { | |
234 | /* -n or -l was specified without -z */ | |
235 | if (topN != 0) { | |
236 | usage(stderr, argv); | |
237 | } | |
238 | } | |
239 | ||
240 | return 0; | |
241 | } |