]> git.saurik.com Git - apple/system_cmds.git/blob - stackshot.tproj/stackshot.c
0a67d501bb231049e460868412ceddb97ae088bd
[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 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 static void usage(char **argv)
32 {
33 fprintf (stderr, "usage: %s [-d] [-t] >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 exit(1);
47 }
48
49 void forksleep() {
50 pid_t pid = fork();
51 if (pid < 0) {
52 perror("fork");
53 exit(1);
54 }
55
56 if (pid == 0) {
57 execlp("sleep", "sleep", "30", NULL);
58 perror("execlp");
59 exit(1);
60 }
61 }
62
63
64 int main(int argc, char **argv) {
65
66 uint32_t iostats = 0;
67 uint32_t active_kernel_threads_only = 0;
68 uint32_t bootprofile = 0;
69 uint32_t thread_group = 0;
70 uint32_t coalition = 0;
71 uint32_t instrs_cycles = 0;
72 uint32_t flags = 0;
73 uint32_t loadinfo = STACKSHOT_SAVE_LOADINFO | STACKSHOT_SAVE_KEXT_LOADINFO;
74 boolean_t delta = FALSE;
75 boolean_t sleep = FALSE;
76 boolean_t stress = FALSE;
77 pid_t pid = -1;
78 int c;
79
80 while ((c = getopt(argc, argv, "SgIikbcLdtsp:E")) != EOF) {
81 switch(c) {
82 case 'I':
83 iostats |= STACKSHOT_NO_IO_STATS;
84 break;
85 case 'k':
86 active_kernel_threads_only |= STACKSHOT_ACTIVE_KERNEL_THREADS_ONLY;
87 loadinfo &= ~STACKSHOT_SAVE_LOADINFO;
88 break;
89 case 'b':
90 bootprofile |= STACKSHOT_GET_BOOT_PROFILE;
91 break;
92 case 'c':
93 coalition |= STACKSHOT_SAVE_JETSAM_COALITIONS;
94 break;
95 case 'i':
96 instrs_cycles |= STACKSHOT_INSTRS_CYCLES;
97 break;
98 case 'L':
99 loadinfo = 0;
100 break;
101 case 'g':
102 thread_group |= STACKSHOT_THREAD_GROUP;
103 break;
104 case 'd':
105 delta = TRUE;
106 break;
107 case 's':
108 sleep = TRUE;
109 break;
110 case 'p':
111 pid = atoi(optarg);
112 break;
113 case 'S':
114 stress = TRUE;
115 break;
116 case 'E':
117 flags = flags | STACKSHOT_RETRIEVE_EXISTING_BUFFER;
118 break;
119 case '?':
120 case 'h':
121 default:
122 usage(argv);
123 break;
124 }
125 }
126
127 if (thread_group && delta) {
128 fprintf(stderr, "stackshot does not support delta snapshots with thread groups\n");
129 return 1;
130 }
131
132 if (optind < argc) {
133 usage(argv);
134 }
135
136 top:
137 ;
138
139 void * config = stackshot_config_create();
140 if (!config) {
141 perror("stackshot_config_create");
142 return 1;
143 }
144 flags = flags | loadinfo | STACKSHOT_SAVE_IMP_DONATION_PIDS | STACKSHOT_GET_DQ | STACKSHOT_KCDATA_FORMAT | STACKSHOT_THREAD_WAITINFO |
145 bootprofile | active_kernel_threads_only | iostats | thread_group | coalition | instrs_cycles;
146
147 int err = stackshot_config_set_flags(config, flags);
148 if (err != 0) {
149 perror("stackshot_config_set_flags");
150 return 1;
151 }
152
153 if (pid != -1) {
154 int err = stackshot_config_set_pid(config, pid);
155 if (err != 0) {
156 perror("stackshot_config_set_flags");
157 return 1;
158 }
159 }
160
161 err = stackshot_capture_with_config(config);
162 if (err != 0) {
163 perror("stackshot_capture_with_config");
164 return 1;
165 }
166
167 void *buf = stackshot_config_get_stackshot_buffer(config);
168 if (!buf) {
169 perror("stackshot_config_get_stackshot_buffer");
170 return 1;
171 }
172
173 uint32_t size = stackshot_config_get_stackshot_size(config);
174
175 if (delta) {
176 // output the original somewhere?
177
178 uint64_t time = stackshot_get_mach_absolute_time(buf, size);
179
180 err = stackshot_config_dealloc_buffer(config);
181 assert(!err);
182
183 flags |= STACKSHOT_COLLECT_DELTA_SNAPSHOT;
184 int err = stackshot_config_set_flags(config, flags);
185 if (err != 0) {
186 perror("stackshot_config_set_flags");
187 return 1;
188 }
189
190 err = stackshot_config_set_delta_timestamp(config, time);
191 if (err != 0) {
192 perror("stackshot_config_delta_timestamp");
193 return 1;
194 }
195
196 if (sleep) {
197 forksleep();
198 }
199 usleep(10000);
200
201 err = stackshot_capture_with_config(config);
202 if (err != 0) {
203 perror("stackshot_capture_with_config");
204 return 1;
205 }
206
207 buf = stackshot_config_get_stackshot_buffer(config);
208 if (!buf) {
209 perror("stackshot_config_get_stackshot_buffer");
210 return 1;
211 }
212
213 size = stackshot_config_get_stackshot_size(config);
214
215
216 }
217
218
219 if (stress) {
220 if (config) {
221 stackshot_config_dealloc(config);
222 config = NULL;
223 }
224 goto top;
225 }
226
227 fwrite(buf, size, 1, stdout);
228 }