xref: /xnu-8020.140.41/bsd/kern/stackshot.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
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