// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This script analyzes interrupt wakeups caused by the execution of a process
// and all its children.
//
// Execute like this:
// sudo dtrace -s iwakeups.d $(pgrep -x "Google Chrome")
// Note on results produced:
//
// This script will produce a data file suitable to be converted to a pprof by
// export_dtrace.py. Care needs to be taken in the analysis of the data. As
// stated in the probe comments, this script emits the stacks of the code that
// ran because of the interrupt wakeup, not the code that triggered the wakeup.
// The iwakeup probe fires in the Darwin kernel
// (see github.com/apple/darwin-xnu/blob/2ff845c2e033bd0ff64b5b6aa6063a1f8f65aa32/osfmk/kern/sched_prim.c#L709).
// The first user space thread that will come on-cpu as a result of the wakeup
// will be in the state that made the thread go to sleep in the first place.
//
// Example:
//
// libsystem_kernel.dylib`mach_msg_trap+0xa
// Google Chrome Framework`base::WaitableEvent::TimedWait(base::TimeDelta const&)+0x172
// Google Chrome Framework`base::internal::WorkerThread::RunWorker()+0x382
// Google Chrome Framework`base::internal::WorkerThread::RunPooledWorker()+0xd
// Google Chrome Framework`base::(anonymous namespace)::ThreadFunc(void*)+0x75
// libsystem_pthread.dylib`_pthread_start+0x7d
// libsystem_pthread.dylib`thread_start+0xf
//
// In this example the stack was emitted because the cpu was interrupted to
// allow the code to exit the TimedWait() invocation. This kind of data could
// be called "wakee" stacks. In contrast, the "waker" stack could be captured
// using stack() in the iwakeup probe. It cannot be captured using ustack()
// since the first wakeup is not caused by user space code.
// This probe fires when the CPU is interrupted to wake up a thread.
sched:::iwakeup
{
// Make note that the thread's stack should be captured the next time it
// goes on-cpu.
wakees[((thread_t)arg0)->thread_id] = 1;
}
// This probe fires when a thread begins execution on a cpu.
// Additional conditions are used to execute the body only for threads in our
// processes of interest, when they've previously caused an interrupt wake up.
sched:::on-cpu/(pid == $1 || ppid == $1) && wakees[curthread->thread_id] == 1/
{
@wakes[ustack(512)] = count();
// Don't monitor this thread again until the next iwakeup.
wakees[curthread->thread_id] = 0;
}
// This probe fires when a thread begins execution on a cpu.
// Additional conditions are used to execute the body only for threads in
// processes of no interest, when they've previously caused an interrupt wake up.
sched:::on-cpu/pid != $1 && ppid != $1 && wakees[curthread->thread_id] == 1/
{
// This thread does not belong to a process of interest. Remove it from |wakees|
// to avoid capturing stacks if thread id reuse causes a thread with the same
// id to go on-cpu on a process of interest.
wakees[curthread->thread_id] = 0;
}