]> git.saurik.com Git - apple/system_cmds.git/blob - stackshot.tproj/stackshot.c
system_cmds-880.60.2.tar.gz
[apple/system_cmds.git] / stackshot.tproj / stackshot.c
1 /* Copyright (c) 2017 Apple Inc. All rights reserved. */
2
3 #include <stdio.h>
4 #include <dispatch/dispatch.h>
5 #include <sysexits.h>
6 #include <inttypes.h>
7 #include <string.h>
8 #include <stdlib.h>
9 #include <sys/syscall.h>
10 #include <sys/wait.h>
11 #include <mach/mach_time.h>
12 #include <sys/stackshot.h>
13 #include <sys/types.h>
14 #include <kern/debug.h>
15 #include <unistd.h>
16 #include <assert.h>
17
18 #include <kern/kcdata.h>
19
20 static uint64_t
21 stackshot_get_mach_absolute_time(void *buffer, uint32_t size)
22 {
23 kcdata_iter_t iter = kcdata_iter_find_type(kcdata_iter(buffer, size), KCDATA_TYPE_MACH_ABSOLUTE_TIME);
24 if (!kcdata_iter_valid(iter) || kcdata_iter_size(iter) < sizeof(uint64_t)) {
25 fprintf(stderr, "bad kcdata\n");
26 exit(1);
27 }
28 return *(uint64_t *)kcdata_iter_payload(iter);
29 }
30
31 __dead2 static void usage(char **argv)
32 {
33 fprintf (stderr, "usage: %s [options] [file]\n", argv[0]);
34 fprintf (stderr, " -d : take delta stackshot\n");
35 fprintf (stderr, " -b : get bootprofile\n");
36 fprintf (stderr, " -c : get coalition data\n");
37 fprintf (stderr, " -i : get instructions and cycles\n");
38 fprintf (stderr, " -g : get thread group data\n");
39 fprintf (stderr, " -s : fork a sleep process\n");
40 fprintf (stderr, " -L : disable loadinfo\n");
41 fprintf (stderr, " -k : active kernel threads only\n");
42 fprintf (stderr, " -I : disable io statistics\n");
43 fprintf (stderr, " -S : stress test: while(1) stackshot; \n");
44 fprintf (stderr, " -p PID : target a pid\n");
45 fprintf (stderr, " -E : grab existing kernel buffer\n");
46 fprintf (stderr, "If no file is provided and stdout is not a TTY, the stackshot will be written to stdout.\n");
47 exit(1);
48 }
49
50 static void forksleep() {
51 pid_t pid = fork();
52 if (pid < 0) {
53 perror("fork");
54 exit(1);
55 }
56
57 if (pid == 0) {
58 execlp("sleep", "sleep", "30", NULL);
59 perror("execlp");
60 exit(1);
61 }
62 }
63
64
65 int main(int argc, char **argv) {
66
67 uint32_t iostats = 0;
68 uint32_t active_kernel_threads_only = 0;
69 uint32_t bootprofile = 0;
70 uint32_t thread_group = 0;
71 uint32_t coalition = 0;
72 uint32_t instrs_cycles = 0;
73 uint32_t flags = 0;
74 uint32_t loadinfo = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO;
75 boolean_t delta = FALSE;
76 boolean_t sleep = FALSE;
77 boolean_t stress = FALSE;
78 pid_t pid = -1;
79 int c;
80 FILE *file;
81 bool closefile;
82
83 while ((c = getopt(argc, argv, "SgIikbcLdtsp:E")) != -1) {
84 switch(c) {
85 case 'I':
86 iostats |= STACKSHOT_NO_IO_STATS;
87 break;
88 case 'k':
89 active_kernel_threads_only |= STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY;
90 loadinfo &= ~STACKSHOT_SAVE_LOADINFO;
91 break;
92 case 'b':
93 bootprofile |= STACKSHOT_GET_BOOT_PROFILE;
94 break;
95 case 'c':
96 coalition |= STACKSHOT_SAVE_JETSAM_COALITIONS;
97 break;
98 case 'i':
99 instrs_cycles |= STACKSHOT_INSTRS_CYCLES;
100 break;
101 case 'L':
102 loadinfo = 0;
103 break;
104 case 'g':
105 thread_group |= STACKSHOT_THREAD_GROUP;
106 break;
107 case 'd':
108 delta = TRUE;
109 break;
110 case 's':
111 sleep = TRUE;
112 break;
113 case 'p':
114 pid = atoi(optarg);
115 break;
116 case 'S':
117 stress = TRUE;
118 break;
119 case 'E':
120 flags = flags | STACKSHOT_RETRIEVE_EXISTING_BUFFER;
121 break;
122 case '?':
123 case 'h':
124 default:
125 usage(argv);
126 break;
127 }
128 }
129
130 if (thread_group && delta) {
131 fprintf(stderr, "stackshot does not support delta snapshots with thread groups\n");
132 return 1;
133 }
134
135 if (optind == argc - 1) {
136 const char *const filename = argv[optind];
137 file = fopen(filename, "wx");
138 closefile = true;
139
140 if (file == NULL) {
141 perror("fopen");
142 return EX_CANTCREAT;
143 }
144 } else if (optind == argc && !isatty(STDOUT_FILENO)) {
145 file = stdout;
146 closefile = false;
147 } else {
148 usage(argv);
149 }
150
151 top:
152 ;
153
154 void * config = stackshot_config_create();
155 if (!config) {
156 perror("stackshot_config_create");
157 return 1;
158 }
159 flags = flags | loadinfo | STACKSHOT_SAVE_IMP_DONATION_PIDS | STACKSHOT_GET_DQ | STACKSHOT_KCDATA_FORMAT | STACKSHOT_THREAD_WAITINFO |
160 bootprofile | active_kernel_threads_only | iostats | thread_group | coalition | instrs_cycles;
161
162 int err = stackshot_config_set_flags(config, flags);
163 if (err != 0) {
164 perror("stackshot_config_set_flags");
165 return 1;
166 }
167
168 if (pid != -1) {
169 int err = stackshot_config_set_pid(config, pid);
170 if (err != 0) {
171 perror("stackshot_config_set_flags");
172 return 1;
173 }
174 }
175
176 err = stackshot_capture_with_config(config);
177 if (err != 0) {
178 perror("stackshot_capture_with_config");
179 return 1;
180 }
181
182 void *buf = stackshot_config_get_stackshot_buffer(config);
183 if (!buf) {
184 perror("stackshot_config_get_stackshot_buffer");
185 return 1;
186 }
187
188 uint32_t size = stackshot_config_get_stackshot_size(config);
189
190 if (delta) {
191 // output the original somewhere?
192
193 uint64_t time = stackshot_get_mach_absolute_time(buf, size);
194
195 err = stackshot_config_dealloc_buffer(config);
196 assert(!err);
197
198 flags |= STACKSHOT_COLLECT_DELTA_SNAPSHOT;
199 int err = stackshot_config_set_flags(config, flags);
200 if (err != 0) {
201 perror("stackshot_config_set_flags");
202 return 1;
203 }
204
205 err = stackshot_config_set_delta_timestamp(config, time);
206 if (err != 0) {
207 perror("stackshot_config_delta_timestamp");
208 return 1;
209 }
210
211 if (sleep) {
212 forksleep();
213 }
214 usleep(10000);
215
216 err = stackshot_capture_with_config(config);
217 if (err != 0) {
218 perror("stackshot_capture_with_config");
219 return 1;
220 }
221
222 buf = stackshot_config_get_stackshot_buffer(config);
223 if (!buf) {
224 perror("stackshot_config_get_stackshot_buffer");
225 return 1;
226 }
227
228 size = stackshot_config_get_stackshot_size(config);
229
230
231 }
232
233 if (stress) {
234 if (config) {
235 stackshot_config_dealloc(config);
236 config = NULL;
237 }
238 goto top;
239 }
240
241 fwrite(buf, size, 1, file);
242
243 if (closefile) {
244 fclose(file);
245 }
246
247 return 0;
248 }