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
31/*
32 * Stackshot system calls
33 */
34
35#if CONFIG_TELEMETRY
36extern kern_return_t stack_microstackshot(user_addr_t tracebuf, uint32_t tracebuf_size, uint32_t flags, int32_t *retval);
37#endif /* CONFIG_TELEMETRY */
38extern kern_return_t kern_stack_snapshot_with_reason(char* reason);
39extern kern_return_t kern_stack_snapshot_internal(int stackshot_config_version, void *stackshot_config, size_t stackshot_config_size, boolean_t stackshot_from_user);
40
41static int
42stackshot_kern_return_to_bsd_error(kern_return_t kr)
43{
44 switch (kr) {
45 case KERN_SUCCESS:
46 return 0;
47 case KERN_RESOURCE_SHORTAGE:
48 /* could not allocate memory, or stackshot is actually bigger than
49 * SANE_TRACEBUF_SIZE */
50 return ENOMEM;
51 case KERN_INSUFFICIENT_BUFFER_SIZE:
52 case KERN_NO_SPACE:
53 /* ran out of buffer to write the stackshot. Normally this error
54 * causes a larger buffer to be allocated in-kernel, rather than
55 * being returned to the user. */
56 return ENOSPC;
57 case KERN_NO_ACCESS:
58 return EPERM;
59 case KERN_MEMORY_PRESENT:
60 return EEXIST;
61 case KERN_NOT_SUPPORTED:
62 return ENOTSUP;
63 case KERN_NOT_IN_SET:
64 /* requested existing buffer, but there isn't one. */
65 return ENOENT;
66 case KERN_ABORTED:
67 /* kdp did not report an error, but also did not produce any data */
68 return EINTR;
69 case KERN_FAILURE:
70 /* stackshot came across inconsistent data and needed to bail out */
71 return EBUSY;
72 case KERN_OPERATION_TIMED_OUT:
73 /* debugger synchronization timed out */
74 return ETIMEDOUT;
75 default:
76 return EINVAL;
77 }
78}
79
80/*
81 * stack_snapshot_with_config: Obtains a coherent set of stack traces for specified threads on the sysem,
82 * tracing both kernel and user stacks where available. Allocates a buffer from the
83 * kernel and maps the buffer into the calling task's address space.
84 *
85 * Inputs: uap->stackshot_config_version - version of the stackshot config that is being passed
86 * uap->stackshot_config - pointer to the stackshot config
87 * uap->stackshot_config_size- size of the stackshot config being passed
88 * Outputs: EINVAL if there is a problem with the arguments
89 * EFAULT if we failed to copy in the arguments succesfully
90 * EPERM if the caller is not privileged
91 * ENOTSUP if the caller is passing a version of arguments that is not supported by the kernel
92 * (indicates libsyscall:kernel mismatch) or if the caller is requesting unsupported flags
93 * ENOENT if the caller is requesting an existing buffer that doesn't exist or if the
94 * requested PID isn't found
95 * ENOMEM if the kernel is unable to allocate enough memory to serve the request
96 * ENOSPC if there isn't enough space in the caller's address space to remap the buffer
97 * ESRCH if the target PID isn't found
98 * returns KERN_SUCCESS on success
99 */
100int
101stack_snapshot_with_config(struct proc *p, struct stack_snapshot_with_config_args *uap, __unused int *retval)
102{
103 int error = 0;
104 kern_return_t kr;
105
106 if ((error = suser(kauth_cred_get(), &p->p_acflag)))
107 return(error);
108
109 if((void*)uap->stackshot_config == NULL) {
110 return EINVAL;
111 }
112
113 switch (uap->stackshot_config_version) {
114 case STACKSHOT_CONFIG_TYPE:
115 if (uap->stackshot_config_size != sizeof(stackshot_config_t)) {
116 return EINVAL;
117 }
118 stackshot_config_t config;
119 error = copyin(uap->stackshot_config, &config, sizeof(stackshot_config_t));
120 if (error != KERN_SUCCESS)
121 {
122 return EFAULT;
123 }
124 kr = kern_stack_snapshot_internal(uap->stackshot_config_version, &config, sizeof(stackshot_config_t), TRUE);
125 return stackshot_kern_return_to_bsd_error(kr);
126 default:
127 return ENOTSUP;
128 }
129}
130
131#if CONFIG_TELEMETRY
132/*
133 * microstackshot: Catch all system call for microstackshot related operations, including
134 * enabling/disabling both global and windowed microstackshots as well
135 * as retrieving windowed or global stackshots and the boot profile.
136 * Inputs: uap->tracebuf - address of the user space destination
137 * buffer
138 * uap->tracebuf_size - size of the user space trace buffer
139 * uap->flags - various flags
140 * Outputs: EPERM if the caller is not privileged
141 * EINVAL if the supplied mss_args is NULL, mss_args.tracebuf is NULL or mss_args.tracebuf_size is not sane
142 * ENOMEM if we don't have enough memory to satisfy the request
143 * *retval contains the number of bytes traced, if successful
144 * and -1 otherwise.
145 */
146int
147microstackshot(struct proc *p, struct microstackshot_args *uap, int32_t *retval)
148{
149 int error = 0;
150 kern_return_t kr;
151
152 if ((error = suser(kauth_cred_get(), &p->p_acflag)))
153 return(error);
154
155 kr = stack_microstackshot(uap->tracebuf, uap->tracebuf_size, uap->flags, retval);
156 return stackshot_kern_return_to_bsd_error(kr);
157}
158#endif /* CONFIG_TELEMETRY */
159
160/*
161 * kern_stack_snapshot_with_reason: Obtains a coherent set of stack traces for specified threads on the sysem,
162 * tracing both kernel and user stacks where available. Allocates a buffer from the
163 * kernel and stores the address of this buffer.
164 *
165 * Inputs: reason - the reason for triggering a stackshot (unused at the moment, but in the
166 * future will be saved in the stackshot)
167 * Outputs: EINVAL/ENOTSUP if there is a problem with the arguments
168 * EPERM if the caller doesn't pass at least one KERNEL stackshot flag
169 * ENOMEM if the kernel is unable to allocate enough memory to serve the request
170 * ESRCH if the target PID isn't found
171 * returns KERN_SUCCESS on success
172 */
173int
174kern_stack_snapshot_with_reason(__unused char *reason)
175{
176 stackshot_config_t config;
177 kern_return_t kr;
178
179 config.sc_pid = -1;
180 config.sc_flags = (STACKSHOT_SAVE_LOADINFO | STACKSHOT_GET_GLOBAL_MEM_STATS | STACKSHOT_SAVE_IN_KERNEL_BUFFER |
181 STACKSHOT_KCDATA_FORMAT | STACKSHOT_ENABLE_UUID_FAULTING | STACKSHOT_THREAD_WAITINFO |
182 STACKSHOT_NO_IO_STATS);
183 config.sc_delta_timestamp = 0;
184 config.sc_out_buffer_addr = 0;
185 config.sc_out_size_addr = 0;
186
187 kr = kern_stack_snapshot_internal(STACKSHOT_CONFIG_TYPE, &config, sizeof(stackshot_config_t), FALSE);
188 return stackshot_kern_return_to_bsd_error(kr);
189}
190