]> git.saurik.com Git - apple/system_cmds.git/blob - zlog.tproj/zlog.c
system_cmds-854.11.2.tar.gz
[apple/system_cmds.git] / zlog.tproj / zlog.c
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 }