1 /*
2 * Copyright (c) 2000-2016 Apple Inc. All rights reserved.
3 *
4 * @Apple_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
21 */
22
23 #include <libkern/libkern.h>
24 #include <mach/mach_types.h>
25 #include <sys/errno.h>
26 #include <sys/kauth.h>
27 #include <sys/proc_internal.h>
28 #include <sys/stackshot.h>
29 #include <sys/sysproto.h>
30 #include <sys/sysctl.h>
31 #include <pexpert/device_tree.h>
32 #include <pexpert/pexpert.h>
33
34 extern uint32_t stackshot_estimate_adj;
35 EXPERIMENT_FACTOR_UINT(_kern, stackshot_estimate_adj, &stackshot_estimate_adj, 0, 100,
36 "adjust stackshot estimates up by this percentage");
37
38
39 /*
40 * Stackshot system calls
41 */
42
43 #if CONFIG_TELEMETRY
44 extern kern_return_t stack_microstackshot(user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t flags, int32_t *retval);
45 #endif /* CONFIG_TELEMETRY */
46 extern kern_return_t kern_stack_snapshot_with_reason(char* reason);
47 extern kern_return_t kern_stack_snapshot_internal(int stackshot_config_version, void *stackshot_config, size_t stackshot_config_size, boolean_t stackshot_from_user);
48
49 static int
stackshot_kern_return_to_bsd_error(kern_return_t kr)50 stackshot_kern_return_to_bsd_error(kern_return_t kr)
51 {
52 switch (kr) {
53 case KERN_SUCCESS:
54 return 0;
55 case KERN_RESOURCE_SHORTAGE:
56 /* could not allocate memory, or stackshot is actually bigger than
57 * SANE_TRACEBUF_SIZE */
58 return ENOMEM;
59 case KERN_INSUFFICIENT_BUFFER_SIZE:
60 case KERN_NO_SPACE:
61 /* ran out of buffer to write the stackshot. Normally this error
62 * causes a larger buffer to be allocated in-kernel, rather than
63 * being returned to the user. */
64 return ENOSPC;
65 case KERN_NO_ACCESS:
66 return EPERM;
67 case KERN_MEMORY_PRESENT:
68 return EEXIST;
69 case KERN_NOT_SUPPORTED:
70 return ENOTSUP;
71 case KERN_NOT_IN_SET:
72 /* requested existing buffer, but there isn't one. */
73 return ENOENT;
74 case KERN_ABORTED:
75 /* kdp did not report an error, but also did not produce any data */
76 return EINTR;
77 case KERN_FAILURE:
78 /* stackshot came across inconsistent data and needed to bail out */
79 return EBUSY;
80 case KERN_OPERATION_TIMED_OUT:
81 /* debugger synchronization timed out */
82 return ETIMEDOUT;
83 default:
84 return EINVAL;
85 }
86 }
87
88 /*
89 * stack_snapshot_with_config: Obtains a coherent set of stack traces for specified threads on the sysem,
90 * tracing both kernel and user stacks where available. Allocates a buffer from the
91 * kernel and maps the buffer into the calling task's address space.
92 *
93 * Inputs: uap->stackshot_config_version - version of the stackshot config that is being passed
94 * uap->stackshot_config - pointer to the stackshot config
95 * uap->stackshot_config_size- size of the stackshot config being passed
96 * Outputs: EINVAL if there is a problem with the arguments
97 * EFAULT if we failed to copy in the arguments succesfully
98 * EPERM if the caller is not privileged
99 * ENOTSUP if the caller is passing a version of arguments that is not supported by the kernel
100 * (indicates libsyscall:kernel mismatch) or if the caller is requesting unsupported flags
101 * ENOENT if the caller is requesting an existing buffer that doesn't exist or if the
102 * requested PID isn't found
103 * ENOMEM if the kernel is unable to allocate enough memory to serve the request
104 * ENOSPC if there isn't enough space in the caller's address space to remap the buffer
105 * ESRCH if the target PID isn't found
106 * returns KERN_SUCCESS on success
107 */
108 int
stack_snapshot_with_config(struct proc * p,struct stack_snapshot_with_config_args * uap,__unused int * retval)109 stack_snapshot_with_config(struct proc *p, struct stack_snapshot_with_config_args *uap, __unused int *retval)
110 {
111 int error = 0;
112 kern_return_t kr;
113
114 if ((error = suser(kauth_cred_get(), &p->p_acflag))) {
115 return error;
116 }
117
118 if ((void*)uap->stackshot_config == NULL) {
119 return EINVAL;
120 }
121
122 switch (uap->stackshot_config_version) {
123 case STACKSHOT_CONFIG_TYPE:
124 if (uap->stackshot_config_size != sizeof(stackshot_config_t)) {
125 return EINVAL;
126 }
127 stackshot_config_t config;
128 error = copyin(uap->stackshot_config, &config, sizeof(stackshot_config_t));
129 if (error != KERN_SUCCESS) {
130 return EFAULT;
131 }
132 kr = kern_stack_snapshot_internal(uap->stackshot_config_version, &config, sizeof(stackshot_config_t), TRUE);
133 return stackshot_kern_return_to_bsd_error(kr);
134 default:
135 return ENOTSUP;
136 }
137 }
138
139 #if CONFIG_TELEMETRY
140 /*
141 * microstackshot: Catch all system call for microstackshot related operations, including
142 * enabling/disabling both global and windowed microstackshots as well
143 * as retrieving windowed or global stackshots and the boot profile.
144 * Inputs: uap->tracebuf - address of the user space destination
145 * buffer
146 * uap->tracebuf_size - size of the user space trace buffer
147 * uap->flags - various flags
148 * Outputs: EPERM if the caller is not privileged
149 * EINVAL if the supplied mss_args is NULL, mss_args.tracebuf is NULL or mss_args.tracebuf_size is not sane
150 * ENOMEM if we don't have enough memory to satisfy the request
151 * *retval contains the number of bytes traced, if successful
152 * and -1 otherwise.
153 */
154 int
microstackshot(struct proc * p,struct microstackshot_args * uap,int32_t * retval)155 microstackshot(struct proc *p, struct microstackshot_args *uap, int32_t *retval)
156 {
157 int error = 0;
158 kern_return_t kr;
159
160 if ((error = suser(kauth_cred_get(), &p->p_acflag))) {
161 return error;
162 }
163
164 kr = stack_microstackshot(uap->tracebuf, uap->tracebuf_size, uap->flags, retval);
165 return stackshot_kern_return_to_bsd_error(kr);
166 }
167 #endif /* CONFIG_TELEMETRY */
168
169 /*
170 * kern_stack_snapshot_with_reason: Obtains a coherent set of stack traces for specified threads on the sysem,
171 * tracing both kernel and user stacks where available. Allocates a buffer from the
172 * kernel and stores the address of this buffer.
173 *
174 * Inputs: reason - the reason for triggering a stackshot (unused at the moment, but in the
175 * future will be saved in the stackshot)
176 * Outputs: EINVAL/ENOTSUP if there is a problem with the arguments
177 * EPERM if the caller doesn't pass at least one KERNEL stackshot flag
178 * ENOMEM if the kernel is unable to allocate enough memory to serve the request
179 * ESRCH if the target PID isn't found
180 * returns KERN_SUCCESS on success
181 */
182 int
kern_stack_snapshot_with_reason(__unused char * reason)183 kern_stack_snapshot_with_reason(__unused char *reason)
184 {
185 stackshot_config_t config;
186 kern_return_t kr;
187
188 config.sc_pid = -1;
189 config.sc_flags = (STACKSHOT_SAVE_LOADINFO | STACKSHOT_GET_GLOBAL_MEM_STATS | STACKSHOT_SAVE_IN_KERNEL_BUFFER |
190 STACKSHOT_KCDATA_FORMAT | STACKSHOT_ENABLE_UUID_FAULTING | STACKSHOT_ENABLE_BT_FAULTING | STACKSHOT_THREAD_WAITINFO |
191 STACKSHOT_NO_IO_STATS | STACKSHOT_COLLECT_SHAREDCACHE_LAYOUT);
192 config.sc_delta_timestamp = 0;
193 config.sc_out_buffer_addr = 0;
194 config.sc_out_size_addr = 0;
195
196 kr = kern_stack_snapshot_internal(STACKSHOT_CONFIG_TYPE, &config, sizeof(stackshot_config_t), FALSE);
197 return stackshot_kern_return_to_bsd_error(kr);
198 }
199