]>
Commit | Line | Data |
---|---|---|
3e170ce0 A |
1 | /* |
2 | * Copyright (c) 2014 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. Please obtain a copy of the License at | |
10 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
11 | * file. | |
12 | * | |
13 | * The Original Code and all software distributed under the License are | |
14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
18 | * Please see the License for the specific language governing rights and | |
19 | * limitations under the License. | |
20 | * | |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | #include <sys/stackshot.h> | |
24 | #include <mach/mach.h> | |
25 | #include <mach/mach_vm.h> | |
26 | #include <stdint.h> | |
27 | #include <stdlib.h> | |
28 | #include <errno.h> | |
29 | ||
30 | /* | |
31 | * System call entry point | |
32 | */ | |
33 | int __stack_snapshot_with_config(int stackshot_config_version, user_addr_t stackshot_config, size_t stackshot_config_size); | |
34 | ||
35 | /* | |
36 | * stackshot_config_create: create and initialize the arguments for a stackshot | |
37 | * | |
38 | * Outputs: NULL if malloc fails | |
39 | * a pointer to a new stackshot_config_t on success | |
40 | */ | |
41 | stackshot_config_t * | |
42 | stackshot_config_create(void) | |
43 | { | |
44 | stackshot_config_t *s_config; | |
45 | ||
46 | s_config = malloc(sizeof(stackshot_config_t)); | |
47 | if (s_config == NULL) { | |
48 | return NULL; | |
49 | } | |
50 | ||
51 | s_config->sc_pid = -1; | |
52 | s_config->sc_flags = 0; | |
39037602 | 53 | s_config->sc_delta_timestamp = 0; |
3e170ce0 A |
54 | s_config->sc_buffer = 0; |
55 | s_config->sc_size = 0; | |
f427ee49 | 56 | s_config->sc_pagetable_mask = 0; |
3e170ce0 A |
57 | |
58 | return s_config; | |
59 | } | |
60 | ||
61 | /* | |
62 | * stackshot_config_set_pid: set the PID to be traced | |
63 | * | |
64 | * Inputs: stackshot_config - a pointer to the stackshot_config_t we want to update | |
65 | * pid - process id of process to be traced, or -1 for the entire system | |
66 | * | |
67 | * Outputs: EINVAL if the passed stackshot_config pointer is NULL | |
68 | * 0 on success | |
69 | */ | |
70 | int | |
71 | stackshot_config_set_pid(stackshot_config_t *stackshot_config, int pid) | |
72 | { | |
73 | stackshot_config_t *s_config; | |
74 | ||
75 | if (stackshot_config == NULL) { | |
76 | return EINVAL; | |
77 | } | |
78 | ||
79 | s_config = (stackshot_config_t *) stackshot_config; | |
80 | s_config->sc_pid = pid; | |
81 | ||
82 | return 0; | |
83 | } | |
84 | ||
85 | /* | |
86 | * stackshot_config_set_flags: set the flags to be passed for the stackshot | |
87 | * | |
88 | * Inputs: stackshot_config - a pointer to the stackshot_config_t we want to update | |
89 | * flags - flags to pass to stackshot | |
90 | * | |
91 | * Outputs: EINVAL if the passed stackshot_config pointer is NULL | |
92 | * 0 on success | |
93 | */ | |
94 | int | |
f427ee49 | 95 | stackshot_config_set_flags(stackshot_config_t *stackshot_config, uint64_t flags) |
3e170ce0 A |
96 | { |
97 | stackshot_config_t *s_config; | |
98 | ||
99 | if (stackshot_config == NULL) { | |
100 | return EINVAL; | |
101 | } | |
102 | ||
103 | s_config = (stackshot_config_t *) stackshot_config; | |
104 | s_config->sc_flags = flags; | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | /* | |
110 | * stackshot_capture_with_config: take a stackshot with the provided config | |
111 | * | |
112 | * Inputs: stackshot_config - a pointer to the stackshot_config_t we want to use | |
113 | * | |
114 | * Outputs: EINVAL if the passed stackshot_config pointer is NULL, a caller is trying | |
115 | * to reuse a config without deallocating its buffer or if there is a | |
116 | * problem with the arguments | |
117 | * EFAULT if there was a problem with accessing the arguments from the kernel | |
118 | * EPERM if the caller is not privileged | |
119 | * ENOTSUP if the caller is passing a stackshot config version that is not | |
120 | * supported by the kernel (indicates libsyscall:kernel mismatch), | |
121 | * or if the caller is requesting unsupported flags | |
122 | * ENOMEM if the kernel is unable to allocate memory | |
123 | * ENOSPC if the caller doesn't have enough space in their address space for | |
124 | * the kernel to remap the buffer | |
125 | * ENOENT if the caller is requesting an existing buffer that doesn't exist | |
126 | * or the target PID isn't found | |
127 | * 0 on success | |
128 | */ | |
129 | int | |
130 | stackshot_capture_with_config(stackshot_config_t *stackshot_config) | |
131 | { | |
132 | int ret; | |
133 | stackshot_config_t *s_config; | |
134 | ||
135 | if (stackshot_config == NULL) { | |
136 | return EINVAL; | |
137 | } | |
138 | ||
139 | s_config = (stackshot_config_t *) stackshot_config; | |
0a7de745 | 140 | if (s_config->sc_buffer != 0) { |
3e170ce0 A |
141 | return EINVAL; |
142 | } | |
143 | ||
d9a64523 A |
144 | s_config->sc_out_buffer_addr = (uintptr_t)&s_config->sc_buffer; |
145 | s_config->sc_out_size_addr = (uintptr_t)&s_config->sc_size; | |
146 | ret = __stack_snapshot_with_config(STACKSHOT_CONFIG_TYPE, (uintptr_t)s_config, sizeof(stackshot_config_t)); | |
0a7de745 | 147 | |
3e170ce0 A |
148 | if (ret != 0) { |
149 | ret = errno; | |
150 | s_config->sc_buffer = 0; | |
151 | s_config->sc_size = 0; | |
152 | } | |
153 | ||
154 | return ret; | |
155 | } | |
156 | ||
157 | /* | |
158 | * stackshot_config_get_stackshot_buffer: get a pointer to the buffer containing the stackshot | |
159 | * | |
160 | * Inputs: stackshot_config - a pointer to a stackshot_config_t | |
161 | * | |
162 | * Outputs: NULL if the passed stackshot_config is NULL or if its buffer is NULL | |
163 | * a pointer to the buffer containing the stackshot on success | |
164 | */ | |
165 | void * | |
166 | stackshot_config_get_stackshot_buffer(stackshot_config_t *stackshot_config) | |
167 | { | |
168 | stackshot_config_t *s_config; | |
169 | ||
170 | if (stackshot_config == NULL) { | |
171 | return NULL; | |
172 | } | |
173 | s_config = (stackshot_config_t *) stackshot_config; | |
174 | ||
0a7de745 | 175 | return (void *)s_config->sc_buffer; |
3e170ce0 A |
176 | } |
177 | ||
178 | /* | |
179 | * stackshot_config_get_stackshot_size: get the size of the stackshot buffer | |
180 | * | |
181 | * Inputs: stackshot_config - a pointer to a stackshot_config_t | |
182 | * | |
183 | * Outputs: -1 if the passed stackshot config is NULL or there is no buffer | |
184 | * the length of the stackshot buffer on success | |
185 | */ | |
186 | uint32_t | |
187 | stackshot_config_get_stackshot_size(stackshot_config_t * stackshot_config) | |
188 | { | |
189 | if (stackshot_config == NULL || (void *)stackshot_config->sc_buffer == NULL) { | |
190 | return -1; | |
191 | } | |
192 | ||
193 | return stackshot_config->sc_size; | |
194 | } | |
195 | ||
196 | /* | |
197 | * stackshot_config_set_size_hint: set the size of the stackshot buffer | |
198 | * | |
199 | * Inputs: stackshot_config - a pointer to a stackshot_config_t | |
200 | * suggested_size - hint for size allocation of stackshot | |
201 | * | |
202 | * Outputs: -1 if the passed stackshot config is NULL or there is existing stackshot buffer set. | |
203 | * the length of the stackshot buffer on success. | |
204 | */ | |
205 | int | |
206 | stackshot_config_set_size_hint(stackshot_config_t *stackshot_config, uint32_t suggested_size) | |
207 | { | |
208 | if (stackshot_config == NULL || (void *)stackshot_config->sc_buffer != NULL) { | |
209 | return -1; | |
210 | } | |
211 | ||
212 | stackshot_config->sc_size = suggested_size; | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
39037602 A |
217 | /* |
218 | * stackshot_config_set_delta_timestamp: set the timestamp to use as the basis for the delta stackshot | |
219 | * | |
220 | * This timestamp will be used along with STACKSHOT_COLLECT_DELTA_SNAPSHOT flag to collect delta stackshots | |
221 | * | |
222 | * Inputs: stackshot_config - a pointer to a stackshot_config_t | |
223 | * delta_timestamp - timestamp in MachAbsoluteTime units to be used as the basis for a delta stackshot | |
224 | * | |
225 | * Outputs: -1 if the passed stackshot config is NULL or there is existing stackshot buffer set. | |
226 | * 0 on success | |
227 | */ | |
228 | int | |
229 | stackshot_config_set_delta_timestamp(stackshot_config_t *stackshot_config, uint64_t delta_timestamp) | |
230 | { | |
231 | if (stackshot_config == NULL || (void *)stackshot_config->sc_buffer != NULL) { | |
232 | return -1; | |
233 | } | |
234 | ||
235 | stackshot_config->sc_delta_timestamp = delta_timestamp; | |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
f427ee49 A |
240 | /* |
241 | * stackshot_config_set_pagetable_mask: set the level mask for pagetable dumping | |
242 | * | |
243 | * Each bit of the mask corresponds to a level in the paging structure. Bit 0 | |
244 | * corresponds to Level 0, bit 1 to level 1, and so on. It is undefined what | |
245 | * happens when a bit is set that's higher than the current maximum level of | |
246 | * pagetable structures. | |
247 | * | |
248 | * When using this setter, you must also pass STACKSHOT_PAGE_TABLES as a flag | |
249 | * before invoking stackshot, otherwise this setter is a no-operation. | |
250 | * | |
251 | * Inputs: stackshot_config - a pointer to a stackshot_config_t | |
252 | * level_mask - the pagetable level mask, as described above | |
253 | * | |
254 | * Outputs: -1 if the passed stackshot config is NULL or there is existing stackshot buffer set. | |
255 | * 0 on success | |
256 | */ | |
257 | int | |
258 | stackshot_config_set_pagetable_mask(stackshot_config_t *stackshot_config, uint32_t pagetable_mask) | |
259 | { | |
260 | if (stackshot_config == NULL || (void *)stackshot_config->sc_buffer != NULL) { | |
261 | return -1; | |
262 | } | |
263 | ||
264 | stackshot_config->sc_pagetable_mask = pagetable_mask; | |
265 | ||
266 | return 0; | |
267 | } | |
268 | ||
39037602 | 269 | |
3e170ce0 A |
270 | /* |
271 | * stackshot_config_dealloc_buffer: dealloc the stackshot buffer and reset the size so that a | |
272 | * stackshot_config_t can be used again | |
273 | * | |
274 | * Inputs: stackshot_config - a pointer to a stackshot_config_t | |
275 | * | |
276 | * Outputs: EINVAL if the passed stackshot_config is NULL or if its buffer is NULL | |
277 | * 0 otherwise | |
278 | */ | |
279 | int | |
280 | stackshot_config_dealloc_buffer(stackshot_config_t *stackshot_config) | |
281 | { | |
282 | stackshot_config_t *s_config; | |
283 | ||
284 | if (stackshot_config == NULL) { | |
285 | return EINVAL; | |
286 | } | |
287 | s_config = (stackshot_config_t *) stackshot_config; | |
288 | ||
289 | if (s_config->sc_size && s_config->sc_buffer) { | |
290 | mach_vm_deallocate(mach_task_self(), (mach_vm_offset_t)s_config->sc_buffer, (mach_vm_size_t)s_config->sc_size); | |
291 | } | |
292 | ||
293 | s_config->sc_buffer = 0; | |
294 | s_config->sc_size = 0; | |
295 | ||
296 | return 0; | |
297 | } | |
298 | ||
299 | /* | |
300 | * stackshot_config_dealloc: dealloc the stackshot buffer and the stackshot config | |
301 | * | |
302 | * Inputs: stackshot_config - a pointer to a stackshot_config_t | |
303 | * | |
304 | * Outputs: EINVAL if the passed stackshot_cofnig is NULL | |
305 | * 0 otherwise | |
306 | */ | |
307 | int | |
308 | stackshot_config_dealloc(stackshot_config_t *stackshot_config) | |
309 | { | |
310 | stackshot_config_t *s_config; | |
311 | ||
312 | if (stackshot_config == NULL) { | |
313 | return EINVAL; | |
314 | } | |
315 | s_config = (stackshot_config_t *) stackshot_config; | |
316 | ||
317 | if (s_config->sc_size && s_config->sc_buffer) { | |
318 | mach_vm_deallocate(mach_task_self(), (mach_vm_offset_t)s_config->sc_buffer, (mach_vm_size_t)s_config->sc_size); | |
319 | } | |
320 | ||
321 | s_config->sc_buffer = 0; | |
322 | s_config->sc_size = 0; | |
323 | ||
324 | free(s_config); | |
325 | return 0; | |
326 | } |