]> git.saurik.com Git - apple/xnu.git/blame - libsyscall/wrappers/stackshot.c
xnu-7195.81.3.tar.gz
[apple/xnu.git] / libsyscall / wrappers / stackshot.c
CommitLineData
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 */
33int __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 */
41stackshot_config_t *
42stackshot_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 */
70int
71stackshot_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 */
94int
f427ee49 95stackshot_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 */
129int
130stackshot_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 */
165void *
166stackshot_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 */
186uint32_t
187stackshot_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 */
205int
206stackshot_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 */
228int
229stackshot_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 */
257int
258stackshot_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 */
279int
280stackshot_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 */
307int
308stackshot_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}