]>
git.saurik.com Git - apple/xnu.git/blob - bsd/pgo/profile_runtime.c
2 * Copyright (c) 2014 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <machine/machine_routines.h>
30 #include <sys/sysproto.h>
31 #include <sys/malloc.h>
32 #include <sys/systm.h>
34 #include <sys/kauth.h>
35 #include <security/mac_framework.h>
36 #include <libkern/OSKextLib.h>
41 /* These __llvm functions are defined in InstrProfiling.h in compiler_rt. That
42 * is a internal header, so we need to re-prototype them here. */
44 uint64_t __llvm_profile_get_size_for_buffer(void);
45 int __llvm_profile_write_buffer(char *Buffer
);
46 uint64_t __llvm_profile_get_size_for_buffer_internal(const char *DataBegin
,
48 const char *CountersBegin
,
49 const char *CountersEnd
,
50 const char *NamesBegin
,
51 const char *NamesEnd
);
52 int __llvm_profile_write_buffer_internal(char *Buffer
,
53 const char *DataBegin
,
55 const char *CountersBegin
,
56 const char *CountersEnd
,
57 const char *NamesBegin
,
58 const char *NamesEnd
);
60 extern char __pgo_hib_DataStart
__asm("section$start$__HIB$__llvm_prf_data");
61 extern char __pgo_hib_DataEnd
__asm("section$end$__HIB$__llvm_prf_data");
62 extern char __pgo_hib_NamesStart
__asm("section$start$__HIB$__llvm_prf_names");
63 extern char __pgo_hib_NamesEnd
__asm("section$end$__HIB$__llvm_prf_names");
64 extern char __pgo_hib_CountersStart
__asm("section$start$__HIB$__llvm_prf_cnts");
65 extern char __pgo_hib_CountersEnd
__asm("section$end$__HIB$__llvm_prf_cnts");
68 static uint64_t get_size_for_buffer(int flags
)
70 if (flags
& PGO_HIB
) {
71 return __llvm_profile_get_size_for_buffer_internal(
72 &__pgo_hib_DataStart
, &__pgo_hib_DataEnd
,
73 &__pgo_hib_CountersStart
, &__pgo_hib_CountersEnd
,
74 &__pgo_hib_NamesStart
, &__pgo_hib_NamesEnd
);
76 return __llvm_profile_get_size_for_buffer();
81 static int write_buffer(int flags
, char *buffer
)
83 if (flags
& PGO_HIB
) {
84 return __llvm_profile_write_buffer_internal(
86 &__pgo_hib_DataStart
, &__pgo_hib_DataEnd
,
87 &__pgo_hib_CountersStart
, &__pgo_hib_CountersEnd
,
88 &__pgo_hib_NamesStart
, &__pgo_hib_NamesEnd
);
90 return __llvm_profile_write_buffer(buffer
);
97 /* this variable is used to signal to the debugger that we'd like it to reset
99 int kdp_pgo_reset_counters
= 0;
101 /* called in debugger context */
102 kern_return_t
do_pgo_reset_counters()
105 memset(&__pgo_hib_CountersStart
, 0,
106 ((uintptr_t)(&__pgo_hib_CountersEnd
)) - ((uintptr_t)(&__pgo_hib_CountersStart
)));
108 OSKextResetPgoCounters();
109 kdp_pgo_reset_counters
= 0;
116 return DebuggerTrapWithState(DBOP_RESET_PGO_COUNTERS
, NULL
, NULL
, NULL
, 0, NULL
, FALSE
, 0);
125 OSKextResetPgoCountersLock();
127 istate
= ml_set_interrupts_enabled(FALSE
);
129 kdp_pgo_reset_counters
= 1;
132 ml_set_interrupts_enabled(istate
);
134 OSKextResetPgoCountersUnlock();
141 * EPERM unless you are root
142 * EINVAL for invalid args.
143 * ENOSYS for not implemented
144 * ERANGE for integer overflow
145 * ENOENT if kext not found
146 * ENOTSUP kext does not support PGO
147 * EIO llvm returned an error. shouldn't ever happen.
150 int grab_pgo_data(struct proc
*p
,
151 struct grab_pgo_data_args
*uap
,
159 if (!kauth_cred_issuser(kauth_cred_get())) {
165 err
= mac_system_check_info(kauth_cred_get(), "kern.profiling_data");
171 if ( uap
->flags
& ~PGO_ALL_FLAGS
||
173 (uap
->size
> 0 && uap
->buffer
== 0))
179 if ( uap
->flags
& PGO_RESET_ALL
) {
180 if (uap
->flags
!= PGO_RESET_ALL
|| uap
->uuid
|| uap
->buffer
|| uap
->size
) {
183 kern_return_t r
= pgo_reset_counters();
188 case KERN_OPERATION_TIMED_OUT
:
203 err
= copyin(uap
->uuid
, &uuid
, sizeof(uuid
));
208 if (uap
->buffer
== 0 && uap
->size
== 0) {
211 if (uap
->flags
& PGO_WAIT_FOR_UNLOAD
) {
216 err
= OSKextGrabPgoData(uuid
, &size64
, NULL
, 0, 0, !!(uap
->flags
& PGO_METADATA
));
217 if (size64
== 0 && err
== 0) {
224 ssize_t size
= size64
;
225 if ( ((uint64_t) size
) != size64
||
236 } else if (!uap
->buffer
|| uap
->size
<= 0) {
243 uint64_t size64
= 0 ;
245 err
= OSKextGrabPgoData(uuid
, &size64
, NULL
, 0,
247 !!(uap
->flags
& PGO_METADATA
));
249 if (size64
== 0 && err
== 0) {
256 if (uap
->size
< 0 || (uint64_t)uap
->size
< size64
) {
261 MALLOC(buffer
, char *, size64
, M_TEMP
, M_WAITOK
| M_ZERO
);
267 err
= OSKextGrabPgoData(uuid
, &size64
, buffer
, size64
,
268 !!(uap
->flags
& PGO_WAIT_FOR_UNLOAD
),
269 !!(uap
->flags
& PGO_METADATA
));
274 ssize_t size
= size64
;
275 if ( ((uint64_t) size
) != size64
||
282 err
= copyout(buffer
, uap
->buffer
, size
);
295 uint64_t size64
= get_size_for_buffer(uap
->flags
);
296 ssize_t size
= size64
;
298 if (uap
->flags
& (PGO_WAIT_FOR_UNLOAD
| PGO_METADATA
)) {
303 if ( ((uint64_t) size
) != size64
||
311 if (uap
->buffer
== 0 && uap
->size
== 0) {
315 } else if (uap
->size
< size
) {
319 MALLOC(buffer
, char *, size
, M_TEMP
, M_WAITOK
| M_ZERO
);
325 err
= write_buffer(uap
->flags
, buffer
);
332 err
= copyout(buffer
, uap
->buffer
, size
);
351 FREE(buffer
, M_TEMP
);