blob: 14b6a9619aa9342059a955839e03853b57d1fa6f [file] [log] [blame]
Demonstrations of criticalstat: Find long atomic critical sections in the kernel.
criticalstat traces and reports occurrences of atomic critical sections in the
kernel with useful stacktraces showing the origin of them. Such critical
sections frequently occur due to use of spinlocks, or if interrupts or
preemption were explicitly disabled by a driver. IRQ routines in Linux are also
executed with interrupts disabled. There are many reasons. Such critical
sections are a source of long latency/responsive issues for real-time systems.
This works by probing the preempt/irq and cpuidle tracepoints in the kernel.
Since this uses BPF, only the root user can use this tool. Further, the kernel
has to be built with certain CONFIG options enabled inorder for it to work:
CONFIG_PREEMPTIRQ_EVENTS before kernel 4.19
CONFIG_PREEMPTIRQ_TRACEPOINTS in kernel 4.19 and later
CONFIG_DEBUG_PREEMPT
CONFIG_PREEMPT_TRACER
Additionally, the following options should be turned off on older kernels:
CONFIG_PROVE_LOCKING
CONFIG_LOCKDEP
USAGE:
# ./criticalstat -h
usage: criticalstat [-h] [-p] [-i] [-d DURATION]
Trace long critical sections
optional arguments:
-h, --help Show this help message and exit
-p, --preemptoff Find long sections where preemption was off
-i, --irqoff Find long sections where IRQ was off
-d DURATION, --duration DURATION
Duration in uS (microseconds) below which we filter
examples:
./criticalstat # run with default options: irq off for more than 100 uS
./criticalstat -p # find sections with preemption disabled for more than 100 uS
./criticalstat -d 500 # find sections with IRQs disabled for more than 500 uS
./criticalstat -p -d 500 # find sections with preemption disabled for more than 500 uS
The tool runs continuously until interrupted by Ctrl-C
By default, criticalstat finds IRQ disable sections for > 100us.
# ./criticalstat
Finding critical section with IRQ disabled for > 100us
===================================
TASK: kworker/u16:5 (pid 5903 tid 5903) Total Time: 194.427 us
Section start: __schedule -> schedule
Section end: _raw_spin_unlock_irq -> finish_task_switch
trace_hardirqs_on+0xdc
trace_hardirqs_on+0xdc
_raw_spin_unlock_irq+0x18
finish_task_switch+0xf0
__schedule+0x8c8
preempt_schedule_irq+0x38
el1_preempt+0x8
===================================
If too many sections are showing up, the user can raise the threshold to only
show critical sections that are > 500us by passing "-d" option:
# ./criticalstat -d 500
Finding critical section with IRQ disabled for > 500us
===================================
TASK: crtc_commit:111 (pid 246 tid 246) Total Time: 580.730 us
Section start: clk_enable_lock -> clk_enable
Section end: _raw_spin_unlock_irqrestore -> clk_enable
trace_hardirqs_on+0xdc
trace_hardirqs_on+0xdc
_raw_spin_unlock_irqrestore+0x24
clk_enable+0x80
msm_dss_enable_clk+0x7c
sde_power_resource_enable+0x578
_sde_crtc_vblank_enable_no_lock+0x68
sde_crtc_vblank+0x8c
sde_kms_enable_vblank+0x18
vblank_ctrl_worker+0xd0
kthread_worker_fn+0xf8
kthread+0x114
ret_from_fork+0x10
===================================
If instead of irq disabled sections, we want to see preempt disabled sections,
then pass the "-p" option. Below we try to find preempt-disabled critical
sections that are > 500us.
# ./criticalstat -p -d 500
Finding critical section with preempt disabled for > 500us
===================================
TASK: swapper/1 (pid 0 tid 0) Total Time: 618.437 us
Section start: preempt_count_add -> preempt_count_add
Section end: preempt_count_sub -> preempt_count_sub
trace_preempt_on+0x98
trace_preempt_on+0x98
preempt_latency_stop+0x164
preempt_count_sub+0x50
schedule+0x74
schedule_preempt_disabled+0x14
cpu_startup_entry+0x84
secondary_start_kernel+0x1c8
[unknown]
===================================
criticalstat -p can also reflect kernel scheduler issues sometimes. These may
show up as long preempt-off sections if the functions in the scheduler take a
long time to run (such as pick_next_task_fair which selects the CPU for a task
Follow is a report showing a preempt-off latency of 700us during the schedule
loop's execution:
===================================
TASK: irq/296-cs35l36 (pid 666 tid 666) Total Time: 732.657 us
Section start: schedule -> schedule
Section end: schedule -> schedule
trace_preempt_on+0x98
trace_preempt_on+0x98
preempt_count_sub+0xa4
schedule+0x78
schedule_timeout+0x80
wait_for_common+0xb4
wait_for_completion_timeout+0x28
geni_i2c_xfer+0x298
__i2c_transfer+0x4e0
i2c_transfer+0x8
irq_thread_fn+0x2c
irq_thread+0x160
kthread+0x118
ret_from_fork+0x10
===================================
See Also: Linux kernel's preemptoff and irqoff tracers which provide similar
tracing but with some limitations.