]> git.saurik.com Git - apple/xnu.git/blob - bsd/pgo/profile_runtime.c
c90fd77617c1ac593acc32222a50d244e5f54f40
[apple/xnu.git] / bsd / pgo / profile_runtime.c
1 /*
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <sys/sysproto.h>
30 #include <sys/malloc.h>
31 #include <sys/systm.h>
32 #include <sys/pgo.h>
33 #include <sys/kauth.h>
34 #include <security/mac_framework.h>
35 #include <libkern/OSKextLib.h>
36
37
38 /*
39 * This tells compiler_rt not to include userspace-specific stuff writing
40 * profile data to a file.
41 */
42 int __llvm_profile_runtime = 0;
43
44
45 #ifdef PROFILE
46
47 /* These __llvm functions are defined in InstrProfiling.h in compiler_rt. That
48 * is a internal header, so we need to re-prototype them here. */
49
50 uint64_t __llvm_profile_get_size_for_buffer(void);
51 int __llvm_profile_write_buffer(char *Buffer);
52 uint64_t __llvm_profile_get_size_for_buffer_internal(const char *DataBegin,
53 const char *DataEnd,
54 const char *CountersBegin,
55 const char *CountersEnd ,
56 const char *NamesBegin,
57 const char *NamesEnd);
58 int __llvm_profile_write_buffer_internal(char *Buffer,
59 const char *DataBegin,
60 const char *DataEnd,
61 const char *CountersBegin,
62 const char *CountersEnd ,
63 const char *NamesBegin,
64 const char *NamesEnd);
65
66 extern char __pgo_hib_DataStart __asm("section$start$__HIB$__llvm_prf_data");
67 extern char __pgo_hib_DataEnd __asm("section$end$__HIB$__llvm_prf_data");
68 extern char __pgo_hib_NamesStart __asm("section$start$__HIB$__llvm_prf_names");
69 extern char __pgo_hib_NamesEnd __asm("section$end$__HIB$__llvm_prf_names");
70 extern char __pgo_hib_CountersStart __asm("section$start$__HIB$__llvm_prf_cnts");
71 extern char __pgo_hib_CountersEnd __asm("section$end$__HIB$__llvm_prf_cnts");
72
73
74 static uint64_t get_size_for_buffer(int flags)
75 {
76 if (flags & PGO_HIB) {
77 return __llvm_profile_get_size_for_buffer_internal(
78 &__pgo_hib_DataStart, &__pgo_hib_DataEnd,
79 &__pgo_hib_CountersStart, &__pgo_hib_CountersEnd,
80 &__pgo_hib_NamesStart, &__pgo_hib_NamesEnd);
81 } else {
82 return __llvm_profile_get_size_for_buffer();
83 }
84 }
85
86
87 static int write_buffer(int flags, char *buffer)
88 {
89 if (flags & PGO_HIB) {
90 return __llvm_profile_write_buffer_internal(
91 buffer,
92 &__pgo_hib_DataStart, &__pgo_hib_DataEnd,
93 &__pgo_hib_CountersStart, &__pgo_hib_CountersEnd,
94 &__pgo_hib_NamesStart, &__pgo_hib_NamesEnd);
95 } else {
96 return __llvm_profile_write_buffer(buffer);
97 }
98 }
99
100
101 #endif
102
103 /* this variable is used to signal to the debugger that we'd like it to reset
104 * the counters */
105 int kdp_pgo_reset_counters = 0;
106
107 /* called in debugger context */
108 static kern_return_t do_pgo_reset_counters(void *context)
109 {
110 #pragma unused(context)
111 #ifdef PROFILE
112 memset(&__pgo_hib_CountersStart, 0,
113 ((uintptr_t)(&__pgo_hib_CountersEnd)) - ((uintptr_t)(&__pgo_hib_CountersStart)));
114 #endif
115 OSKextResetPgoCounters();
116 kdp_pgo_reset_counters = 0;
117 return KERN_SUCCESS;
118 }
119
120 static kern_return_t
121 pgo_reset_counters()
122 {
123 kern_return_t r;
124 OSKextResetPgoCountersLock();
125 kdp_pgo_reset_counters = 1;
126 r = DebuggerWithCallback(do_pgo_reset_counters, NULL, FALSE);
127 OSKextResetPgoCountersUnlock();
128 return r;
129 }
130
131
132 /*
133 * returns:
134 * EPERM unless you are root
135 * EINVAL for invalid args.
136 * ENOSYS for not implemented
137 * ERANGE for integer overflow
138 * ENOENT if kext not found
139 * ENOTSUP kext does not support PGO
140 * EIO llvm returned an error. shouldn't ever happen.
141 */
142
143 int grab_pgo_data(struct proc *p,
144 struct grab_pgo_data_args *uap,
145 register_t *retval)
146 {
147 char *buffer = NULL;
148 int err = 0;
149
150 (void) p;
151
152 if (!kauth_cred_issuser(kauth_cred_get())) {
153 err = EPERM;
154 goto out;
155 }
156
157 #if CONFIG_MACF
158 err = mac_system_check_info(kauth_cred_get(), "kern.profiling_data");
159 if (err) {
160 goto out;
161 }
162 #endif
163
164 if ( uap->flags & ~PGO_ALL_FLAGS ||
165 uap->size < 0 ||
166 (uap->size > 0 && uap->buffer == 0))
167 {
168 err = EINVAL;
169 goto out;
170 }
171
172 if ( uap->flags & PGO_RESET_ALL ) {
173 if (uap->flags != PGO_RESET_ALL || uap->uuid || uap->buffer || uap->size ) {
174 err = EINVAL;
175 } else {
176 kern_return_t r = pgo_reset_counters();
177 switch (r) {
178 case KERN_SUCCESS:
179 err = 0;
180 break;
181 case KERN_OPERATION_TIMED_OUT:
182 err = ETIMEDOUT;
183 break;
184 default:
185 err = EIO;
186 break;
187 }
188 }
189 goto out;
190 }
191
192 *retval = 0;
193
194 if (uap->uuid) {
195 uuid_t uuid;
196 err = copyin(uap->uuid, &uuid, sizeof(uuid));
197 if (err) {
198 goto out;
199 }
200
201 if (uap->buffer == 0 && uap->size == 0) {
202 uint64_t size64;
203
204 if (uap->flags & PGO_WAIT_FOR_UNLOAD) {
205 err = EINVAL;
206 goto out;
207 }
208
209 err = OSKextGrabPgoData(uuid, &size64, NULL, 0, 0, !!(uap->flags & PGO_METADATA));
210 if (err) {
211 goto out;
212 }
213
214 ssize_t size = size64;
215 if ( ((uint64_t) size) != size64 ||
216 size < 0 )
217 {
218 err = ERANGE;
219 goto out;
220 }
221
222 *retval = size;
223 err = 0;
224 goto out;
225
226 } else if (!uap->buffer || uap->size <= 0) {
227
228 err = EINVAL;
229 goto out;
230
231 } else {
232
233 MALLOC(buffer, char *, uap->size, M_TEMP, M_WAITOK);
234 if (!buffer) {
235 err = ENOMEM;
236 goto out;
237 }
238
239 uint64_t size64;
240
241 err = OSKextGrabPgoData(uuid, &size64, buffer, uap->size,
242 !!(uap->flags & PGO_WAIT_FOR_UNLOAD),
243 !!(uap->flags & PGO_METADATA));
244 if (err) {
245 goto out;
246 }
247
248 ssize_t size = size64;
249 if ( ((uint64_t) size) != size64 ||
250 size < 0 )
251 {
252 err = ERANGE;
253 goto out;
254 }
255
256 err = copyout(buffer, uap->buffer, size);
257 if (err) {
258 goto out;
259 }
260
261 *retval = size;
262 goto out;
263 }
264 }
265
266
267 #ifdef PROFILE
268
269 uint64_t size64 = get_size_for_buffer(uap->flags);
270 ssize_t size = size64;
271
272 if (uap->flags & (PGO_WAIT_FOR_UNLOAD | PGO_METADATA)) {
273 err = EINVAL;
274 goto out;
275 }
276
277 if ( ((uint64_t) size) != size64 ||
278 size < 0 )
279 {
280 err = ERANGE;
281 goto out;
282 }
283
284
285 if (uap->buffer == 0 && uap->size == 0) {
286 *retval = size;
287 err = 0;
288 goto out;
289 } else if (uap->size < size) {
290 err = EINVAL;
291 goto out;
292 } else {
293 MALLOC(buffer, char *, size, M_TEMP, M_WAITOK);
294 if (!buffer) {
295 err = ENOMEM;
296 goto out;
297 }
298
299 err = write_buffer(uap->flags, buffer);
300 if (err)
301 {
302 err = EIO;
303 goto out;
304 }
305
306 err = copyout(buffer, uap->buffer, size);
307 if (err) {
308 goto out;
309 }
310
311 *retval = size;
312 goto out;
313 }
314
315 #else
316
317 *retval = -1;
318 err = ENOSYS;
319 goto out;
320
321 #endif
322
323 out:
324 if (buffer) {
325 FREE(buffer, M_TEMP);
326 }
327 if (err) {
328 *retval = -1;
329 }
330 return err;
331 }