]>
Commit | Line | Data |
---|---|---|
316670eb | 1 | /* |
f427ee49 | 2 | * Copyright (c) 2011-2018 Apple Computer, Inc. All rights reserved. |
316670eb A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
0a7de745 | 5 | * |
316670eb A |
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. | |
0a7de745 | 14 | * |
316670eb A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
316670eb A |
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. | |
0a7de745 | 25 | * |
316670eb A |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ | |
f427ee49 | 28 | |
39037602 | 29 | #include <kern/ipc_tt.h> /* port_name_to_task */ |
316670eb A |
30 | #include <kern/thread.h> |
31 | #include <kern/machine.h> | |
32 | #include <kern/kalloc.h> | |
39037602 | 33 | #include <mach/mach_types.h> |
316670eb | 34 | #include <sys/errno.h> |
39037602 | 35 | #include <sys/ktrace.h> |
316670eb | 36 | |
316670eb | 37 | #include <kperf/action.h> |
39037602 A |
38 | #include <kperf/buffer.h> |
39 | #include <kperf/kdebug_trigger.h> | |
316670eb | 40 | #include <kperf/kperf.h> |
f427ee49 | 41 | #include <kperf/kptimer.h> |
d9a64523 | 42 | #include <kperf/lazy.h> |
39037602 A |
43 | #include <kperf/pet.h> |
44 | #include <kperf/sample.h> | |
39236c6e | 45 | |
5ba3f43e A |
46 | /* from libkern/libkern.h */ |
47 | extern uint64_t strtouq(const char *, char **, int); | |
48 | ||
f427ee49 | 49 | LCK_GRP_DECLARE(kperf_lck_grp, "kperf"); |
39236c6e | 50 | |
39037602 A |
51 | /* one wired sample buffer per CPU */ |
52 | static struct kperf_sample *intr_samplev; | |
53 | static unsigned int intr_samplec = 0; | |
39236c6e | 54 | |
39037602 | 55 | /* current sampling status */ |
f427ee49 | 56 | enum kperf_sampling kperf_status = KPERF_SAMPLING_OFF; |
316670eb | 57 | |
f427ee49 A |
58 | /* |
59 | * Only set up kperf once. | |
60 | */ | |
61 | static bool kperf_is_setup = false; | |
316670eb | 62 | |
39037602 A |
63 | /* whether or not to callback to kperf on context switch */ |
64 | boolean_t kperf_on_cpu_active = FALSE; | |
316670eb | 65 | |
d9a64523 A |
66 | unsigned int kperf_thread_blocked_action; |
67 | unsigned int kperf_cpu_sample_action; | |
68 | ||
39037602 | 69 | struct kperf_sample * |
316670eb A |
70 | kperf_intr_sample_buffer(void) |
71 | { | |
39037602 | 72 | unsigned ncpu = cpu_number(); |
316670eb | 73 | |
39037602 A |
74 | assert(ml_get_interrupts_enabled() == FALSE); |
75 | assert(ncpu < intr_samplec); | |
316670eb | 76 | |
39037602 | 77 | return &(intr_samplev[ncpu]); |
39236c6e A |
78 | } |
79 | ||
f427ee49 A |
80 | void |
81 | kperf_init_early(void) | |
316670eb | 82 | { |
f427ee49 A |
83 | /* |
84 | * kperf allocates based on the number of CPUs and requires them to all be | |
85 | * accounted for. | |
86 | */ | |
87 | ml_wait_max_cpus(); | |
88 | ||
89 | boolean_t found_kperf = FALSE; | |
90 | char kperf_config_str[64]; | |
91 | found_kperf = PE_parse_boot_arg_str("kperf", kperf_config_str, sizeof(kperf_config_str)); | |
92 | if (found_kperf && kperf_config_str[0] != '\0') { | |
93 | kperf_kernel_configure(kperf_config_str); | |
39037602 | 94 | } |
f427ee49 | 95 | } |
39037602 | 96 | |
f427ee49 A |
97 | void |
98 | kperf_init(void) | |
99 | { | |
100 | kptimer_init(); | |
101 | } | |
316670eb | 102 | |
f427ee49 A |
103 | void |
104 | kperf_setup(void) | |
105 | { | |
106 | if (kperf_is_setup) { | |
107 | return; | |
39037602 | 108 | } |
39236c6e | 109 | |
f427ee49 A |
110 | intr_samplec = machine_info.logical_cpu_max; |
111 | size_t intr_samplev_size = intr_samplec * sizeof(*intr_samplev); | |
112 | intr_samplev = kalloc_tag(intr_samplev_size, VM_KERN_MEMORY_DIAG); | |
113 | memset(intr_samplev, 0, intr_samplev_size); | |
39236c6e | 114 | |
f427ee49 | 115 | kperf_kdebug_setup(); |
316670eb | 116 | |
f427ee49 | 117 | kperf_is_setup = true; |
316670eb A |
118 | } |
119 | ||
39037602 A |
120 | void |
121 | kperf_reset(void) | |
316670eb | 122 | { |
f427ee49 A |
123 | /* |
124 | * Make sure samples aren't being taken before tearing everything down. | |
125 | */ | |
126 | (void)kperf_disable_sampling(); | |
39037602 | 127 | |
d9a64523 | 128 | kperf_lazy_reset(); |
39037602 | 129 | (void)kperf_kdbg_cswitch_set(0); |
39037602 | 130 | kperf_kdebug_reset(); |
f427ee49 A |
131 | kptimer_reset(); |
132 | kppet_reset(); | |
39037602 | 133 | |
f427ee49 A |
134 | /* |
135 | * Most of the other systems call into actions, so reset them last. | |
136 | */ | |
39037602 | 137 | kperf_action_reset(); |
316670eb A |
138 | } |
139 | ||
5ba3f43e A |
140 | void |
141 | kperf_kernel_configure(const char *config) | |
142 | { | |
143 | int pairs = 0; | |
144 | char *end; | |
145 | bool pet = false; | |
146 | ||
147 | assert(config != NULL); | |
148 | ||
149 | ktrace_start_single_threaded(); | |
150 | ||
151 | ktrace_kernel_configure(KTRACE_KPERF); | |
152 | ||
153 | if (config[0] == 'p') { | |
154 | pet = true; | |
155 | config++; | |
156 | } | |
157 | ||
158 | do { | |
159 | uint32_t action_samplers; | |
cc8bc92a | 160 | uint64_t timer_period_ns; |
5ba3f43e A |
161 | uint64_t timer_period; |
162 | ||
163 | pairs += 1; | |
164 | kperf_action_set_count(pairs); | |
f427ee49 | 165 | kptimer_set_count(pairs); |
5ba3f43e A |
166 | |
167 | action_samplers = (uint32_t)strtouq(config, &end, 0); | |
168 | if (config == end) { | |
169 | kprintf("kperf: unable to parse '%s' as action sampler\n", config); | |
170 | goto out; | |
171 | } | |
172 | config = end; | |
173 | ||
174 | kperf_action_set_samplers(pairs, action_samplers); | |
175 | ||
176 | if (config[0] == '\0') { | |
177 | kprintf("kperf: missing timer period in config\n"); | |
178 | goto out; | |
179 | } | |
180 | config++; | |
181 | ||
cc8bc92a | 182 | timer_period_ns = strtouq(config, &end, 0); |
5ba3f43e A |
183 | if (config == end) { |
184 | kprintf("kperf: unable to parse '%s' as timer period\n", config); | |
185 | goto out; | |
186 | } | |
cc8bc92a | 187 | nanoseconds_to_absolutetime(timer_period_ns, &timer_period); |
5ba3f43e A |
188 | config = end; |
189 | ||
f427ee49 A |
190 | kptimer_set_period(pairs - 1, timer_period); |
191 | kptimer_set_action(pairs - 1, pairs); | |
5ba3f43e A |
192 | |
193 | if (pet) { | |
f427ee49 A |
194 | kptimer_set_pet_timerid(pairs - 1); |
195 | kppet_set_lightweight_pet(1); | |
5ba3f43e A |
196 | pet = false; |
197 | } | |
198 | } while (*(config++) == ','); | |
199 | ||
f427ee49 | 200 | int error = kperf_enable_sampling(); |
d9a64523 | 201 | if (error) { |
f427ee49 | 202 | printf("kperf: cannot enable sampling at boot: %d\n", error); |
d9a64523 | 203 | } |
5ba3f43e A |
204 | |
205 | out: | |
206 | ktrace_end_single_threaded(); | |
207 | } | |
208 | ||
d9a64523 | 209 | void kperf_on_cpu_internal(thread_t thread, thread_continue_t continuation, |
0a7de745 | 210 | uintptr_t *starting_fp); |
316670eb | 211 | void |
39037602 | 212 | kperf_on_cpu_internal(thread_t thread, thread_continue_t continuation, |
0a7de745 | 213 | uintptr_t *starting_fp) |
316670eb | 214 | { |
39037602 A |
215 | if (kperf_kdebug_cswitch) { |
216 | /* trace the new thread's PID for Instruments */ | |
217 | int pid = task_pid(get_threadtask(thread)); | |
39037602 A |
218 | BUF_DATA(PERF_TI_CSWITCH, thread_tid(thread), pid); |
219 | } | |
f427ee49 A |
220 | if (kppet_lightweight_active) { |
221 | kppet_on_cpu(thread, continuation, starting_fp); | |
39037602 | 222 | } |
d9a64523 A |
223 | if (kperf_lazy_wait_action != 0) { |
224 | kperf_lazy_wait_sample(thread, continuation, starting_fp); | |
225 | } | |
316670eb A |
226 | } |
227 | ||
316670eb | 228 | void |
39037602 | 229 | kperf_on_cpu_update(void) |
316670eb | 230 | { |
39037602 | 231 | kperf_on_cpu_active = kperf_kdebug_cswitch || |
f427ee49 | 232 | kppet_lightweight_active || |
0a7de745 | 233 | kperf_lazy_wait_action != 0; |
39037602 A |
234 | } |
235 | ||
f427ee49 A |
236 | bool |
237 | kperf_is_sampling(void) | |
316670eb | 238 | { |
f427ee49 | 239 | return kperf_status == KPERF_SAMPLING_ON; |
316670eb A |
240 | } |
241 | ||
242 | int | |
f427ee49 | 243 | kperf_enable_sampling(void) |
316670eb | 244 | { |
f427ee49 | 245 | if (kperf_status == KPERF_SAMPLING_ON) { |
316670eb | 246 | return 0; |
39037602 | 247 | } |
316670eb | 248 | |
f427ee49 A |
249 | if (kperf_status != KPERF_SAMPLING_OFF) { |
250 | panic("kperf: sampling was %d when asked to enable", kperf_status); | |
39037602 | 251 | } |
316670eb A |
252 | |
253 | /* make sure interrupt tables and actions are initted */ | |
f427ee49 | 254 | if (!kperf_is_setup || (kperf_action_get_count() == 0)) { |
316670eb | 255 | return ECANCELED; |
39037602 | 256 | } |
316670eb | 257 | |
f427ee49 A |
258 | kperf_status = KPERF_SAMPLING_ON; |
259 | kppet_lightweight_active_update(); | |
260 | kptimer_start(); | |
316670eb A |
261 | |
262 | return 0; | |
263 | } | |
264 | ||
265 | int | |
f427ee49 | 266 | kperf_disable_sampling(void) |
316670eb | 267 | { |
f427ee49 | 268 | if (kperf_status != KPERF_SAMPLING_ON) { |
316670eb | 269 | return 0; |
39037602 | 270 | } |
316670eb A |
271 | |
272 | /* mark a shutting down */ | |
f427ee49 | 273 | kperf_status = KPERF_SAMPLING_SHUTDOWN; |
316670eb A |
274 | |
275 | /* tell timers to disable */ | |
f427ee49 | 276 | kptimer_stop(); |
316670eb A |
277 | |
278 | /* mark as off */ | |
f427ee49 A |
279 | kperf_status = KPERF_SAMPLING_OFF; |
280 | kppet_lightweight_active_update(); | |
316670eb A |
281 | |
282 | return 0; | |
283 | } | |
39236c6e | 284 | |
f427ee49 A |
285 | void |
286 | kperf_timer_expire(void *param0, void * __unused param1) | |
287 | { | |
288 | processor_t processor = param0; | |
289 | int cpuid = processor->cpu_id; | |
290 | ||
291 | kptimer_expire(processor, cpuid, mach_absolute_time()); | |
292 | } | |
293 | ||
39037602 A |
294 | boolean_t |
295 | kperf_thread_get_dirty(thread_t thread) | |
296 | { | |
0a7de745 | 297 | return thread->c_switch != thread->kperf_c_switch; |
39037602 A |
298 | } |
299 | ||
300 | void | |
301 | kperf_thread_set_dirty(thread_t thread, boolean_t dirty) | |
302 | { | |
303 | if (dirty) { | |
304 | thread->kperf_c_switch = thread->c_switch - 1; | |
305 | } else { | |
306 | thread->kperf_c_switch = thread->c_switch; | |
307 | } | |
308 | } | |
309 | ||
39236c6e A |
310 | int |
311 | kperf_port_to_pid(mach_port_name_t portname) | |
312 | { | |
39037602 | 313 | if (!MACH_PORT_VALID(portname)) { |
39236c6e | 314 | return -1; |
39037602 | 315 | } |
39236c6e | 316 | |
d9a64523 | 317 | task_t task = port_name_to_task(portname); |
39037602 A |
318 | if (task == TASK_NULL) { |
319 | return -1; | |
320 | } | |
cb323159 | 321 | |
d9a64523 | 322 | pid_t pid = task_pid(task); |
cb323159 A |
323 | |
324 | os_ref_count_t __assert_only count = task_deallocate_internal(task); | |
325 | assert(count != 0); | |
39236c6e A |
326 | |
327 | return pid; | |
328 | } |