xref: /xnu-11215/libsyscall/wrappers/stackshot.c (revision bb611c8f)
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 *
stackshot_config_create(void)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;
53 	s_config->sc_delta_timestamp = 0;
54 	s_config->sc_buffer = 0;
55 	s_config->sc_size = 0;
56 	s_config->sc_pagetable_mask = 0;
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
stackshot_config_set_pid(stackshot_config_t * stackshot_config,int pid)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
stackshot_config_set_flags(stackshot_config_t * stackshot_config,uint64_t flags)95 stackshot_config_set_flags(stackshot_config_t *stackshot_config, uint64_t flags)
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
stackshot_capture_with_config(stackshot_config_t * stackshot_config)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;
140 	if (s_config->sc_buffer != 0) {
141 		return EINVAL;
142 	}
143 
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));
147 
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 *
stackshot_config_get_stackshot_buffer(stackshot_config_t * stackshot_config)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 
175 	return (void *)s_config->sc_buffer;
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
stackshot_config_get_stackshot_size(stackshot_config_t * stackshot_config)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
stackshot_config_set_size_hint(stackshot_config_t * stackshot_config,uint32_t suggested_size)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 
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
stackshot_config_set_delta_timestamp(stackshot_config_t * stackshot_config,uint64_t delta_timestamp)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 
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
stackshot_config_set_pagetable_mask(stackshot_config_t * stackshot_config,uint32_t pagetable_mask)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 
269 
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
stackshot_config_dealloc_buffer(stackshot_config_t * stackshot_config)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
stackshot_config_dealloc(stackshot_config_t * stackshot_config)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 }
327