]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kperf/pet.c
xnu-3789.1.32.tar.gz
[apple/xnu.git] / osfmk / kperf / pet.c
CommitLineData
316670eb 1/*
39037602 2 * Copyright (c) 2011-2016 Apple Computer, Inc. All rights reserved.
316670eb
A
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
39037602 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.
39037602 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.
39037602 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.
39037602 25 *
316670eb
A
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/* all thread states code */
30#include <mach/mach_types.h>
316670eb
A
31#include <sys/errno.h>
32
39037602 33#include <kperf/kperf.h>
316670eb
A
34#include <kperf/buffer.h>
35#include <kperf/sample.h>
36#include <kperf/context.h>
37#include <kperf/action.h>
316670eb 38#include <kperf/pet.h>
39037602 39#include <kperf/kperf_timer.h>
316670eb 40
3e170ce0 41#include <kern/task.h>
39037602
A
42#include <kern/kalloc.h>
43
44/* action ID to call for each sample
45 *
46 * Address is used as the sync point for waiting.
47 */
48static unsigned int pet_action_id = 0;
49
50static lck_mtx_t *pet_lock;
51static boolean_t pet_initted = FALSE;
52static boolean_t pet_running = FALSE;
fe8ab488 53
39037602
A
54/* number of callstack samples to skip for idle threads */
55static uint32_t pet_idle_rate = KPERF_PET_DEFAULT_IDLE_RATE;
316670eb 56
39037602
A
57/*
58 * Lightweight PET mode samples the system less-intrusively than normal PET
59 * mode. Instead of iterating tasks and threads on each sample, it increments
60 * a global generation count, kperf_pet_gen, which is checked as threads are
61 * context switched on-core. If the thread's local generation count is older
62 * than the global generation, the thread samples itself.
63 *
64 * | |
65 * thread A +--+---------|
66 * | |
67 * thread B |--+---------------|
68 * | |
69 * thread C | | |-------------------------------------
70 * | | |
71 * thread D | | | |-------------------------------
72 * | | | |
73 * +--+---------+-----+--------------------------------> time
74 * | │ |
75 * | +-----+--- threads sampled when they come on-core in
76 * | kperf_pet_switch_context
77 * |
78 * +--- PET timer fire, sample on-core threads A and B,
79 * increment kperf_pet_gen
316670eb 80 */
39037602 81static boolean_t lightweight_pet = FALSE;
316670eb 82
39037602
A
83/*
84 * Whether or not lightweight PET and sampling is active.
85 */
86boolean_t kperf_lightweight_pet_active = FALSE;
316670eb 87
39037602 88uint32_t kperf_pet_gen = 0;
316670eb 89
39037602 90static struct kperf_sample *pet_sample;
316670eb 91
39037602 92/* thread lifecycle */
39236c6e 93
39037602
A
94static kern_return_t pet_init(void);
95static void pet_start(void);
96static void pet_stop(void);
316670eb 97
39037602 98/* PET thread-only */
316670eb 99
39037602
A
100static void pet_thread_loop(void *param, wait_result_t wr);
101static void pet_thread_idle(void);
102static void pet_thread_work_unit(void);
316670eb 103
39037602 104/* listing things to sample */
39236c6e 105
39037602
A
106static task_array_t pet_tasks = NULL;
107static vm_size_t pet_tasks_size = 0;
108static vm_size_t pet_tasks_count = 0;
39236c6e 109
39037602
A
110static thread_array_t pet_threads = NULL;
111static vm_size_t pet_threads_size = 0;
112static vm_size_t pet_threads_count = 0;
39236c6e 113
39037602
A
114static kern_return_t pet_tasks_prepare(void);
115static kern_return_t pet_tasks_prepare_internal(void);
316670eb 116
39037602 117static kern_return_t pet_threads_prepare(task_t task);
316670eb 118
39037602 119/* sampling */
316670eb 120
39037602
A
121static void pet_sample_all_tasks(uint32_t idle_rate);
122static void pet_sample_task(task_t task, uint32_t idle_rate);
123static void pet_sample_thread(int pid, thread_t thread, uint32_t idle_rate);
316670eb 124
39037602 125/* functions called by other areas of kperf */
39236c6e 126
39037602
A
127void
128kperf_pet_fire_before(void)
129{
130 if (!pet_initted || !pet_running) {
131 return;
132 }
133
134 if (lightweight_pet) {
135 BUF_INFO(PERF_PET_SAMPLE);
136 OSIncrementAtomic(&kperf_pet_gen);
316670eb
A
137 }
138}
139
39037602
A
140void
141kperf_pet_fire_after(void)
316670eb 142{
39037602 143 if (!pet_initted || !pet_running) {
316670eb
A
144 return;
145 }
146
39037602
A
147 if (lightweight_pet) {
148 kperf_timer_pet_rearm(0);
149 } else {
150 thread_wakeup(&pet_action_id);
151 }
316670eb
A
152}
153
39037602
A
154void
155kperf_pet_on_cpu(thread_t thread, thread_continue_t continuation,
156 uintptr_t *starting_fp)
316670eb 157{
39037602
A
158 assert(thread != NULL);
159 assert(ml_get_interrupts_enabled() == FALSE);
160
161 if (thread->kperf_pet_gen != kperf_pet_gen) {
162 BUF_VERB(PERF_PET_SAMPLE_THREAD | DBG_FUNC_START, kperf_pet_gen, thread->kperf_pet_gen);
163
164 struct kperf_context ctx = {
165 .cur_thread = thread,
166 .cur_pid = task_pid(get_threadtask(thread)),
167 .starting_fp = starting_fp,
168 };
169 /*
170 * Use a per-CPU interrupt buffer, since this is only called
171 * while interrupts are disabled, from the scheduler.
316670eb 172 */
39037602
A
173 struct kperf_sample *sample = kperf_intr_sample_buffer();
174 if (!sample) {
175 BUF_VERB(PERF_PET_SAMPLE_THREAD | DBG_FUNC_END, 1);
176 return;
316670eb 177 }
39236c6e 178
39037602
A
179 unsigned int flags = SAMPLE_FLAG_NON_INTERRUPT | SAMPLE_FLAG_PEND_USER;
180 if (continuation != NULL) {
181 flags |= SAMPLE_FLAG_CONTINUATION;
316670eb 182 }
39037602 183 kperf_sample(sample, &ctx, pet_action_id, flags);
39236c6e 184
39037602
A
185 BUF_VERB(PERF_PET_SAMPLE_THREAD | DBG_FUNC_END);
186 } else {
187 BUF_VERB(PERF_PET_SAMPLE_THREAD, kperf_pet_gen, thread->kperf_pet_gen);
188 }
189}
316670eb 190
39037602
A
191void
192kperf_pet_config(unsigned int action_id)
193{
194 kern_return_t kr = pet_init();
195 if (kr != KERN_SUCCESS) {
196 return;
316670eb 197 }
39037602
A
198
199 lck_mtx_lock(pet_lock);
200
201 BUF_INFO(PERF_PET_THREAD, 3, action_id);
202
203 if (action_id == 0) {
204 pet_stop();
205 } else {
206 pet_start();
207 }
208
209 pet_action_id = action_id;
210
211 lck_mtx_unlock(pet_lock);
316670eb
A
212}
213
39037602
A
214/* handle resource allocation */
215
216void
217pet_start(void)
316670eb 218{
39037602 219 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
316670eb 220
39037602
A
221 if (pet_running) {
222 return;
223 }
316670eb 224
39037602
A
225 pet_sample = kalloc(sizeof(struct kperf_sample));
226 if (!pet_sample) {
316670eb
A
227 return;
228 }
229
39037602 230 pet_running = TRUE;
316670eb
A
231}
232
39037602
A
233void
234pet_stop(void)
316670eb 235{
39037602
A
236 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
237
238 if (!pet_initted) {
316670eb
A
239 return;
240 }
241
39037602
A
242 if (pet_tasks != NULL) {
243 assert(pet_tasks_size != 0);
244 kfree(pet_tasks, pet_tasks_size);
316670eb 245
39037602
A
246 pet_tasks = NULL;
247 pet_tasks_size = 0;
248 pet_tasks_count = 0;
316670eb
A
249 }
250
39037602
A
251 if (pet_threads != NULL) {
252 assert(pet_threads_size != 0);
253 kfree(pet_threads, pet_threads_size);
254
255 pet_threads = NULL;
256 pet_threads_size = 0;
257 pet_threads_count = 0;
258 }
316670eb 259
39037602
A
260 if (pet_sample != NULL) {
261 kfree(pet_sample, sizeof(struct kperf_sample));
262 pet_sample = NULL;
263 }
316670eb 264
39037602 265 pet_running = FALSE;
316670eb
A
266}
267
39037602
A
268/*
269 * Lazily initialize PET. The PET thread never exits once PET has been used
270 * once.
271 */
272static kern_return_t
273pet_init(void)
316670eb 274{
39037602
A
275 if (pet_initted) {
276 return KERN_SUCCESS;
277 }
316670eb 278
39037602
A
279 /* make the sync point */
280 pet_lock = lck_mtx_alloc_init(&kperf_lck_grp, NULL);
281 assert(pet_lock);
316670eb 282
39037602
A
283 /* create the thread */
284
285 BUF_INFO(PERF_PET_THREAD, 0);
286 thread_t t;
287 kern_return_t kr = kernel_thread_start(pet_thread_loop, NULL, &t);
288 if (kr != KERN_SUCCESS) {
289 lck_mtx_free(pet_lock, &kperf_lck_grp);
290 return kr;
316670eb
A
291 }
292
39037602
A
293 thread_set_thread_name(t, "kperf sampling");
294 /* let the thread hold the only reference */
295 thread_deallocate(t);
296
297 pet_initted = TRUE;
316670eb 298
39037602 299 return KERN_SUCCESS;
316670eb
A
300}
301
39037602
A
302/* called by PET thread only */
303
304static void
305pet_thread_work_unit(void)
316670eb 306{
39037602 307 pet_sample_all_tasks(pet_idle_rate);
316670eb
A
308}
309
316670eb 310static void
39037602 311pet_thread_idle(void)
316670eb 312{
39037602
A
313 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
314
315 (void)lck_mtx_sleep(pet_lock, LCK_SLEEP_DEFAULT, &pet_action_id,
316 THREAD_UNINT);
317}
318
319__attribute__((noreturn))
320static void
321pet_thread_loop(void *param, wait_result_t wr)
322{
323#pragma unused(param, wr)
39236c6e
A
324 uint64_t work_unit_ticks;
325
39037602 326 BUF_INFO(PERF_PET_THREAD, 1);
316670eb 327
39037602
A
328 lck_mtx_lock(pet_lock);
329 for (;;) {
330 BUF_INFO(PERF_PET_IDLE);
331 pet_thread_idle();
316670eb 332
39037602 333 BUF_INFO(PERF_PET_RUN);
39236c6e
A
334
335 /* measure how long the work unit takes */
336 work_unit_ticks = mach_absolute_time();
39037602 337 pet_thread_work_unit();
39236c6e 338 work_unit_ticks = mach_absolute_time() - work_unit_ticks;
316670eb
A
339
340 /* re-program the timer */
39037602
A
341 kperf_timer_pet_rearm(work_unit_ticks);
342 }
343}
316670eb 344
39037602
A
345/* sampling */
346
347static void
348pet_sample_thread(int pid, thread_t thread, uint32_t idle_rate)
349{
350 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
351
352 uint32_t sample_flags = SAMPLE_FLAG_IDLE_THREADS;
353
354 BUF_VERB(PERF_PET_SAMPLE_THREAD | DBG_FUNC_START);
355
356 /* work out the context */
357 struct kperf_context ctx = {
358 .cur_thread = thread,
359 .cur_pid = pid,
360 };
361
362 boolean_t thread_dirty = kperf_thread_get_dirty(thread);
363
364 /*
365 * Clean a dirty thread and skip callstack sample if the thread was not
366 * dirty and thread has skipped less than pet_idle_rate samples.
367 */
368 if (thread_dirty) {
369 kperf_thread_set_dirty(thread, FALSE);
370 } else if ((thread->kperf_pet_cnt % idle_rate) != 0) {
371 sample_flags |= SAMPLE_FLAG_EMPTY_CALLSTACK;
316670eb 372 }
39037602
A
373 thread->kperf_pet_cnt++;
374
375 kperf_sample(pet_sample, &ctx, pet_action_id, sample_flags);
376
377 BUF_VERB(PERF_PET_SAMPLE_THREAD | DBG_FUNC_END);
316670eb
A
378}
379
39037602
A
380static kern_return_t
381pet_threads_prepare(task_t task)
316670eb 382{
39037602
A
383 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
384
385 vm_size_t threads_size_needed;
386
387 if (task == TASK_NULL) {
388 return KERN_INVALID_ARGUMENT;
389 }
390
391 for (;;) {
392 task_lock(task);
393
394 if (!task->active) {
395 task_unlock(task);
39236c6e 396
39037602
A
397 return KERN_FAILURE;
398 }
399
400 /* do we have the memory we need? */
401 threads_size_needed = task->thread_count * sizeof(thread_t);
402 if (threads_size_needed <= pet_threads_size) {
403 break;
404 }
316670eb 405
39037602
A
406 /* not enough memory, unlock the task and increase allocation */
407 task_unlock(task);
316670eb 408
39037602
A
409 if (pet_threads_size != 0) {
410 kfree(pet_threads, pet_threads_size);
411 }
412
413 assert(threads_size_needed > 0);
414 pet_threads_size = threads_size_needed;
316670eb 415
39037602
A
416 pet_threads = kalloc(pet_threads_size);
417 if (pet_threads == NULL) {
418 pet_threads_size = 0;
419 return KERN_RESOURCE_SHORTAGE;
420 }
421 }
422
423 /* have memory and the task is locked and active */
424 thread_t thread;
425 pet_threads_count = 0;
426 queue_iterate(&(task->threads), thread, thread_t, task_threads) {
427 thread_reference_internal(thread);
428 pet_threads[pet_threads_count++] = thread;
429 }
430
431 /* can unlock task now that threads are referenced */
432 task_unlock(task);
433
434 return (pet_threads_count == 0) ? KERN_FAILURE : KERN_SUCCESS;
316670eb
A
435}
436
39037602
A
437static void
438pet_sample_task(task_t task, uint32_t idle_rate)
316670eb 439{
39037602
A
440 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
441
442 BUF_VERB(PERF_PET_SAMPLE_TASK | DBG_FUNC_START);
443
444 kern_return_t kr = pet_threads_prepare(task);
445 if (kr != KERN_SUCCESS) {
446 BUF_INFO(PERF_PET_ERROR, ERR_THREAD, kr);
447 BUF_VERB(PERF_PET_SAMPLE_TASK | DBG_FUNC_END, 1);
39236c6e 448 return;
39037602
A
449 }
450
451 int pid = task_pid(task);
39236c6e 452
39037602
A
453 for (unsigned int i = 0; i < pet_threads_count; i++) {
454 thread_t thread = pet_threads[i];
455 int cpu;
456 assert(thread);
457
458 /* do not sample the thread if it was on a CPU during the IPI. */
459 for (cpu = 0; cpu < machine_info.logical_cpu_max; cpu++) {
460 thread_t candidate = kperf_thread_on_cpus[cpu];
461 if (candidate && (thread_tid(candidate) == thread_tid(thread))) {
462 break;
463 }
464 }
465
466 /* the thread was not on a CPU */
467 if (cpu == machine_info.logical_cpu_max) {
468 pet_sample_thread(pid, thread, idle_rate);
469 }
470
471 thread_deallocate(pet_threads[i]);
472 }
473
474 BUF_VERB(PERF_PET_SAMPLE_TASK | DBG_FUNC_END, pet_threads_count);
316670eb
A
475}
476
39037602
A
477static kern_return_t
478pet_tasks_prepare_internal(void)
479{
480 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
316670eb 481
39037602
A
482 vm_size_t tasks_size_needed = 0;
483
484 for (;;) {
485 lck_mtx_lock(&tasks_threads_lock);
486
487 /* do we have the memory we need? */
488 tasks_size_needed = tasks_count * sizeof(task_t);
489 if (tasks_size_needed <= pet_tasks_size) {
490 break;
491 }
492
493 /* unlock and allocate more memory */
494 lck_mtx_unlock(&tasks_threads_lock);
495
496 /* grow task array */
497 if (tasks_size_needed > pet_tasks_size) {
498 if (pet_tasks_size != 0) {
499 kfree(pet_tasks, pet_tasks_size);
500 }
501
502 assert(tasks_size_needed > 0);
503 pet_tasks_size = tasks_size_needed;
504
505 pet_tasks = (task_array_t)kalloc(pet_tasks_size);
506 if (pet_tasks == NULL) {
507 pet_tasks_size = 0;
508 return KERN_RESOURCE_SHORTAGE;
509 }
510 }
511 }
512
513 return KERN_SUCCESS;
514}
515
516static kern_return_t
517pet_tasks_prepare(void)
316670eb 518{
39037602
A
519 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
520
521 /* allocate space and take the tasks_threads_lock */
522 kern_return_t kr = pet_tasks_prepare_internal();
523 if (KERN_SUCCESS != kr) {
524 return kr;
525 }
526 lck_mtx_assert(&tasks_threads_lock, LCK_MTX_ASSERT_OWNED);
527
528 /* make sure the tasks are not deallocated after dropping the lock */
529 task_t task;
530 pet_tasks_count = 0;
531 queue_iterate(&tasks, task, task_t, tasks) {
532 if (task != kernel_task) {
533 task_reference_internal(task);
534 pet_tasks[pet_tasks_count++] = task;
535 }
536 }
537
538 lck_mtx_unlock(&tasks_threads_lock);
39236c6e 539
39037602 540 return KERN_SUCCESS;
316670eb
A
541}
542
39037602
A
543static void
544pet_sample_all_tasks(uint32_t idle_rate)
316670eb 545{
39037602 546 lck_mtx_assert(pet_lock, LCK_MTX_ASSERT_OWNED);
316670eb 547
39037602 548 BUF_INFO(PERF_PET_SAMPLE | DBG_FUNC_START);
316670eb 549
39037602
A
550 kern_return_t kr = pet_tasks_prepare();
551 if (kr != KERN_SUCCESS) {
552 BUF_INFO(PERF_PET_ERROR, ERR_TASK, kr);
553 BUF_INFO(PERF_PET_SAMPLE | DBG_FUNC_END, 0);
554 return;
555 }
316670eb 556
39037602
A
557 for (unsigned int i = 0; i < pet_tasks_count; i++) {
558 task_t task = pet_tasks[i];
559
560 if (task != kernel_task) {
561 kr = task_suspend_internal(task);
562 if (kr != KERN_SUCCESS) {
563 continue;
564 }
565 }
566
567 pet_sample_task(task, idle_rate);
568
569 if (task != kernel_task) {
570 task_resume_internal(task);
571 }
316670eb
A
572 }
573
39037602
A
574 for(unsigned int i = 0; i < pet_tasks_count; i++) {
575 task_deallocate(pet_tasks[i]);
576 }
577
578 BUF_INFO(PERF_PET_SAMPLE | DBG_FUNC_END, pet_tasks_count);
316670eb 579}
39236c6e 580
39037602
A
581/* support sysctls */
582
39236c6e 583int
39037602 584kperf_get_pet_idle_rate(void)
39236c6e
A
585{
586 return pet_idle_rate;
587}
588
39037602
A
589int
590kperf_set_pet_idle_rate(int val)
39236c6e
A
591{
592 pet_idle_rate = val;
39037602
A
593
594 return 0;
595}
596
597int
598kperf_get_lightweight_pet(void)
599{
600 return lightweight_pet;
601}
602
603int
604kperf_set_lightweight_pet(int val)
605{
606 if (kperf_sampling_status() == KPERF_SAMPLING_ON) {
607 return EBUSY;
608 }
609
610 lightweight_pet = (val == 1);
611 kperf_lightweight_pet_active_update();
612
613 return 0;
614}
615
616void
617kperf_lightweight_pet_active_update(void)
618{
619 kperf_lightweight_pet_active = (kperf_sampling_status() && lightweight_pet);
620 kperf_on_cpu_update();
39236c6e 621}