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; 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 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 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 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 * 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 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 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 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 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 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 } 327