]> git.saurik.com Git - apple/xnu.git/blob - bsd/pgo/profile_runtime.c
xnu-3789.51.2.tar.gz
[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 #ifdef PROFILE
39
40 /* These __llvm functions are defined in InstrProfiling.h in compiler_rt. That
41 * is a internal header, so we need to re-prototype them here. */
42
43 uint64_t __llvm_profile_get_size_for_buffer(void);
44 int __llvm_profile_write_buffer(char *Buffer);
45 uint64_t __llvm_profile_get_size_for_buffer_internal(const char *DataBegin,
46 const char *DataEnd,
47 const char *CountersBegin,
48 const char *CountersEnd ,
49 const char *NamesBegin,
50 const char *NamesEnd);
51 int __llvm_profile_write_buffer_internal(char *Buffer,
52 const char *DataBegin,
53 const char *DataEnd,
54 const char *CountersBegin,
55 const char *CountersEnd ,
56 const char *NamesBegin,
57 const char *NamesEnd);
58
59 extern char __pgo_hib_DataStart __asm("section$start$__HIB$__llvm_prf_data");
60 extern char __pgo_hib_DataEnd __asm("section$end$__HIB$__llvm_prf_data");
61 extern char __pgo_hib_NamesStart __asm("section$start$__HIB$__llvm_prf_names");
62 extern char __pgo_hib_NamesEnd __asm("section$end$__HIB$__llvm_prf_names");
63 extern char __pgo_hib_CountersStart __asm("section$start$__HIB$__llvm_prf_cnts");
64 extern char __pgo_hib_CountersEnd __asm("section$end$__HIB$__llvm_prf_cnts");
65
66
67 static uint64_t get_size_for_buffer(int flags)
68 {
69 if (flags & PGO_HIB) {
70 return __llvm_profile_get_size_for_buffer_internal(
71 &__pgo_hib_DataStart, &__pgo_hib_DataEnd,
72 &__pgo_hib_CountersStart, &__pgo_hib_CountersEnd,
73 &__pgo_hib_NamesStart, &__pgo_hib_NamesEnd);
74 } else {
75 return __llvm_profile_get_size_for_buffer();
76 }
77 }
78
79
80 static int write_buffer(int flags, char *buffer)
81 {
82 if (flags & PGO_HIB) {
83 return __llvm_profile_write_buffer_internal(
84 buffer,
85 &__pgo_hib_DataStart, &__pgo_hib_DataEnd,
86 &__pgo_hib_CountersStart, &__pgo_hib_CountersEnd,
87 &__pgo_hib_NamesStart, &__pgo_hib_NamesEnd);
88 } else {
89 return __llvm_profile_write_buffer(buffer);
90 }
91 }
92
93
94 #endif
95
96 /* this variable is used to signal to the debugger that we'd like it to reset
97 * the counters */
98 int kdp_pgo_reset_counters = 0;
99
100 /* called in debugger context */
101 static kern_return_t do_pgo_reset_counters(void *context)
102 {
103 #pragma unused(context)
104 #ifdef PROFILE
105 memset(&__pgo_hib_CountersStart, 0,
106 ((uintptr_t)(&__pgo_hib_CountersEnd)) - ((uintptr_t)(&__pgo_hib_CountersStart)));
107 #endif
108 OSKextResetPgoCounters();
109 kdp_pgo_reset_counters = 0;
110 return KERN_SUCCESS;
111 }
112
113 static kern_return_t
114 pgo_reset_counters()
115 {
116 kern_return_t r;
117 OSKextResetPgoCountersLock();
118 kdp_pgo_reset_counters = 1;
119 r = DebuggerWithCallback(do_pgo_reset_counters, NULL, FALSE);
120 OSKextResetPgoCountersUnlock();
121 return r;
122 }
123
124
125 /*
126 * returns:
127 * EPERM unless you are root
128 * EINVAL for invalid args.
129 * ENOSYS for not implemented
130 * ERANGE for integer overflow
131 * ENOENT if kext not found
132 * ENOTSUP kext does not support PGO
133 * EIO llvm returned an error. shouldn't ever happen.
134 */
135
136 int grab_pgo_data(struct proc *p,
137 struct grab_pgo_data_args *uap,
138 register_t *retval)
139 {
140 char *buffer = NULL;
141 int err = 0;
142
143 (void) p;
144
145 if (!kauth_cred_issuser(kauth_cred_get())) {
146 err = EPERM;
147 goto out;
148 }
149
150 #if CONFIG_MACF
151 err = mac_system_check_info(kauth_cred_get(), "kern.profiling_data");
152 if (err) {
153 goto out;
154 }
155 #endif
156
157 if ( uap->flags & ~PGO_ALL_FLAGS ||
158 uap->size < 0 ||
159 (uap->size > 0 && uap->buffer == 0))
160 {
161 err = EINVAL;
162 goto out;
163 }
164
165 if ( uap->flags & PGO_RESET_ALL ) {
166 if (uap->flags != PGO_RESET_ALL || uap->uuid || uap->buffer || uap->size ) {
167 err = EINVAL;
168 } else {
169 kern_return_t r = pgo_reset_counters();
170 switch (r) {
171 case KERN_SUCCESS:
172 err = 0;
173 break;
174 case KERN_OPERATION_TIMED_OUT:
175 err = ETIMEDOUT;
176 break;
177 default:
178 err = EIO;
179 break;
180 }
181 }
182 goto out;
183 }
184
185 *retval = 0;
186
187 if (uap->uuid) {
188 uuid_t uuid;
189 err = copyin(uap->uuid, &uuid, sizeof(uuid));
190 if (err) {
191 goto out;
192 }
193
194 if (uap->buffer == 0 && uap->size == 0) {
195 uint64_t size64;
196
197 if (uap->flags & PGO_WAIT_FOR_UNLOAD) {
198 err = EINVAL;
199 goto out;
200 }
201
202 err = OSKextGrabPgoData(uuid, &size64, NULL, 0, 0, !!(uap->flags & PGO_METADATA));
203 if (size64 == 0 && err == 0) {
204 err = EIO;
205 }
206 if (err) {
207 goto out;
208 }
209
210 ssize_t size = size64;
211 if ( ((uint64_t) size) != size64 ||
212 size < 0 )
213 {
214 err = ERANGE;
215 goto out;
216 }
217
218 *retval = size;
219 err = 0;
220 goto out;
221
222 } else if (!uap->buffer || uap->size <= 0) {
223
224 err = EINVAL;
225 goto out;
226
227 } else {
228
229 uint64_t size64 = 0 ;
230
231 err = OSKextGrabPgoData(uuid, &size64, NULL, 0,
232 false,
233 !!(uap->flags & PGO_METADATA));
234
235 if (size64 == 0 && err == 0) {
236 err = EIO;
237 }
238 if (err) {
239 goto out;
240 }
241
242 if (uap->size < 0 || (uint64_t)uap->size < size64) {
243 err = EINVAL;
244 goto out;
245 }
246
247 MALLOC(buffer, char *, size64, M_TEMP, M_WAITOK);
248 if (!buffer) {
249 err = ENOMEM;
250 goto out;
251 }
252
253 err = OSKextGrabPgoData(uuid, &size64, buffer, size64,
254 !!(uap->flags & PGO_WAIT_FOR_UNLOAD),
255 !!(uap->flags & PGO_METADATA));
256 if (err) {
257 goto out;
258 }
259
260 ssize_t size = size64;
261 if ( ((uint64_t) size) != size64 ||
262 size < 0 )
263 {
264 err = ERANGE;
265 goto out;
266 }
267
268 err = copyout(buffer, uap->buffer, size);
269 if (err) {
270 goto out;
271 }
272
273 *retval = size;
274 goto out;
275 }
276 }
277
278
279 #ifdef PROFILE
280
281 uint64_t size64 = get_size_for_buffer(uap->flags);
282 ssize_t size = size64;
283
284 if (uap->flags & (PGO_WAIT_FOR_UNLOAD | PGO_METADATA)) {
285 err = EINVAL;
286 goto out;
287 }
288
289 if ( ((uint64_t) size) != size64 ||
290 size < 0 )
291 {
292 err = ERANGE;
293 goto out;
294 }
295
296
297 if (uap->buffer == 0 && uap->size == 0) {
298 *retval = size;
299 err = 0;
300 goto out;
301 } else if (uap->size < size) {
302 err = EINVAL;
303 goto out;
304 } else {
305 MALLOC(buffer, char *, size, M_TEMP, M_WAITOK);
306 if (!buffer) {
307 err = ENOMEM;
308 goto out;
309 }
310
311 err = write_buffer(uap->flags, buffer);
312 if (err)
313 {
314 err = EIO;
315 goto out;
316 }
317
318 err = copyout(buffer, uap->buffer, size);
319 if (err) {
320 goto out;
321 }
322
323 *retval = size;
324 goto out;
325 }
326
327 #else
328
329 *retval = -1;
330 err = ENOSYS;
331 goto out;
332
333 #endif
334
335 out:
336 if (buffer) {
337 FREE(buffer, M_TEMP);
338 }
339 if (err) {
340 *retval = -1;
341 }
342 return err;
343 }