]> git.cameronkatri.com Git - apple_cmds.git/blob - system_cmds/zlog.tproj/zlog.c
gitignore: Add executables and compressed manpages
[apple_cmds.git] / system_cmds / 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 <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"
19
20 extern kern_return_t
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);
25 extern kern_return_t
26 mach_zone_get_zlog_zones(host_priv_t host,
27 mach_zone_name_array_t *namesp,
28 mach_msg_type_number_t *namesCntp);
29
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);
35
36 static void usage(FILE *stream, char **argv)
37 {
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);
46 }
47
48 static int compare_zone_btrecords(const void *left, const void *right)
49 {
50 zone_btrecord_t *btl = (zone_btrecord_t *)left;
51 zone_btrecord_t *btr = (zone_btrecord_t *)right;
52
53 return (btr->ref_count - btl->ref_count);
54 }
55
56 static void print_zone_info(const char *name)
57 {
58 mach_zone_name_t zname;
59 mach_zone_info_t zone_info;
60 kern_return_t kr;
61
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));
66 exit(1);
67 }
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);
72 printf("\n");
73 }
74
75 static void get_zone_btrecords(const char *name, int topN)
76 {
77 kern_return_t kr;
78 int i, j, index;
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;
84
85 /* Create kernel symbolicator */
86 kernelSym = CSSymbolicatorCreateWithMachKernel();
87 if (CSIsNull(kernelSym)) {
88 fprintf(stderr, "error: CSSymbolicatorCreateWithMachKernel() returned NULL\n");
89 exit(1);
90 }
91 /* Create dictionary to collect binary image info for offline symbolication */
92 binaryImages = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
93
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));
99 exit(1);
100 }
101
102 if (recs_count == 0) {
103 goto finish;
104 }
105
106 recs = recs_addr;
107 if (topN == 1) {
108 /* Print the backtrace with the highest no. of refs */
109 index = 0;
110 for (i = 0; i < recs_count; i++) {
111 if (recs[i].ref_count > recs[index].ref_count) {
112 index = i;
113 }
114 }
115 recs = recs_addr + index;
116 } else if (topN == 0) {
117 /* Print all backtraces */
118 topN = recs_count;
119 } else {
120 /* Sort the records by no. of refs, and print the top <topN> */
121 qsort(recs, recs_count, sizeof *recs, compare_zone_btrecords);
122 }
123
124 printf("printing top %d (out of %d) allocation backtrace(s) for zone %s\n", topN, recs_count, zname.mzn_name);
125
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");
129
130 for (j = 0; j < MAX_ZTRACE_DEPTH; j++) {
131 mach_vm_address_t addr = (mach_vm_address_t)recs[i].bt[j];
132 if (!addr) {
133 break;
134 }
135 PrintSymbolicatedAddress(kernelSym, addr, binaryImages);
136 }
137 }
138
139 /* Print relevant info for offline symbolication */
140 PrintBinaryImagesInfo(binaryImages);
141 CFRelease(binaryImages);
142
143 finish:
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));
148 exit(1);
149 }
150 }
151 CSRelease(kernelSym);
152 }
153
154 static void list_zones_with_zlog_enabled(void)
155 {
156 kern_return_t kr;
157 mach_zone_name_t *name = NULL;
158 unsigned int name_count = 0, i;
159
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));
164 exit(1);
165 }
166
167 if (name_count == 0) {
168 printf("zlog not enabled for any zones.\n");
169 } else {
170 printf("zlog enabled for zones...\n");
171 }
172
173 for (i = 0; i < name_count; i++) {
174 print_zone_info(name[i].mzn_name);
175 }
176
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));
181 exit(1);
182 }
183 }
184 }
185
186 #define VERSION_STRING "zlog output version: 1"
187
188 static void
189 get_osversion(char *buffer, size_t buffer_len)
190 {
191 int mib[] = {CTL_KERN, KERN_OSVERSION};
192 int ret;
193 ret = sysctl(mib, sizeof(mib) / sizeof(int), buffer, &buffer_len, NULL, 0);
194 if (ret != 0) {
195 strlcpy(buffer, "Unknown", buffer_len);
196 return;
197 }
198 }
199
200 int main(int argc, char *argv[])
201 {
202 int c, topN = 0;
203 const char *zone_name = NULL;
204
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);
210
211 if (argc == 1) {
212 /* default when no arguments are specified */
213 list_zones_with_zlog_enabled();
214 printf("Run 'zlog -h' for usage info.\n");
215 return 0;
216 }
217
218 while ((c = getopt(argc, argv, "tz:n:lh")) != -1) {
219 switch(c) {
220 case 't':
221 list_zones_with_zlog_enabled();
222 break;
223 case 'z':
224 zone_name = optarg;
225 break;
226 case 'n':
227 topN = atoi(optarg);
228 break;
229 case 'l':
230 topN = 1;
231 break;
232 case 'h':
233 usage(stdout, argv);
234 break;
235 case '?':
236 default:
237 usage(stderr, argv);
238 break;
239 }
240 }
241
242 if (optind < argc) {
243 usage(stderr, argv);
244 }
245
246 if (zone_name) {
247 print_zone_info(zone_name);
248 get_zone_btrecords(zone_name, topN);
249 } else {
250 /* -n or -l was specified without -z */
251 if (topN != 0) {
252 usage(stderr, argv);
253 }
254 }
255
256 return 0;
257 }