1,kernel -- osnoise
内核5.14-rc1引入了一个新的tracer,从一个线程的角度讲系统的噪声全部罗列出来,供开发者分析系统状况,其中所述的噪声包括:nmi,irq,softirq以及thread。代码位置在kernel/trace/trace_osnoise.c.
tracer osnoise工作原理如下:
- 注册traceevent,包括thread_noise,softirq_noise,irq_noise,nmi_noise,sample_threshold (include/trace/events/osnoise.h)
- 注册一个tracer -- register_tracer(&osnoise_tracer)
- 获取系统的noise
- 注册tracepoint irq_entry irq_exit的钩子函数
register_trace_irq_handler_entry(trace_irqentry_callback, NULL);
register_trace_irq_handler_entry(trace_irqentry_callback, NULL);
(trace point除了trace event定义的函数,如trace_irq_handler_entry之外,还可以 注册其他的钩子函数,当系统行至该trace point之后,hook函数被依次调用,见 include/linux/tracepoint.h中register_trace_##name和int __traceiter_##_name -- ++it_func_ptr)->func)
- 在trace_irqentry_callback和trace_irqentry_callback中分别记录该中断的进出时间和运行时间,得到了系统的一次irq noise duration,同时调用trace_irq_noise将duration放到ftrace的ringbuffer中。
- Thread noise调用register_trace_sched_switch来hook sched_switch
- Softirq调用register_trace_softirq_entry和register_trace_softirq_exit
- Nmi比较特殊,在ftrace_nmi_enter和ftrace_nmi_exit中直接调用trace_osnoise_callback获取nmi noise,并调用调用trace_nmi_noise将duration放到ftrace的ringbuffer中。
- 此外,osnoise还创建了一个内核线程osnoise_main -- run_osnoise,在run_osnoise中做一个循环,根据循环开始和结束的时间,得到osnoise(irq,softirq,nmi,其他thread)的时间(如果没有noise,理论上两个时间基本相等,如果相差较大,说明run_osnoise线程在运行的过程中被打断了),如果osnoise超过设定的threshold,则调用trace_sample_threshold将该信息放到ftrace的ringbuffer中。
- 最后在一次测量的周期结束后,调用latency_fsnotify通知rtla,在用户空间显示。
- 用例:
echo osnoise > current_tracer
cat trace
# tracer: osnoise
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth MAX
# || / SINGLE Interference counters:
# |||| RUNTIME NOISE %% OF CPU NOISE +-----------------------------+
# TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD
# | | | |||| | | | | | | | | | |
<...>-4735 [000] ..... 845.271294: 1000000 3842 99.61580 3306 1 0 360 71 147
<...>-4736 [001] ..... 845.271334: 1000000 2729 99.72710 1339 1 0 384 64 224
<...>-4737 [002] ..... 845.271399: 1000000 1638 99.83620 110 1 0 418 69 205
<...>-4738 [003] ..... 845.271990: 1000000 9167 99.08330 1945 1 0 392 63 258
<...>-4735 [000] ..... 846.271298: 1000000 233 99.97670 25 0 0 360 40 136
<...>-4736 [001] ..... 846.271335: 1000000 838 99.91620 114 0 0 383 37 227
echo osnoise > set_event
cat trace
# tracer: osnoise
#
# _-----=> irqs-off
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth MAX
# || / SINGLE Interference counters:
# |||| RUNTIME NOISE %% OF CPU NOISE +-----------------------------+
# TASK-PID CPU# |||| TIMESTAMP IN US IN US AVAILABLE IN US HW NMI IRQ SIRQ THREAD
# | | | |||| | | | | | | | | | |
osnoise/1-3479 [001] ..... 751.215049: sample_threshold: start 751.214973595 duration 74929 ns interference 3
osnoise/1-3479 [001] d.h.. 751.215103: irq_noise: call_function_single:251 start 751.215102156 duration 423 ns
osnoise/1-3479 [001] d.h.. 751.218970: irq_noise: local_timer:236 start 751.218967753 duration 2237 ns
osnoise/1-3479 [001] dNH.. 751.218972: irq_noise: irq_work:246 start 751.218970522 duration 1122 ns
osnoise/1-3479 [001] .Ns.. 751.218972: softirq_noise: RCU:9 start 751.218971991 duration 263 ns
osnoise/1-3479 [001] .N... 751.218973: sample_threshold: start 751.218967499 duration 5124 ns interference 3
kworker/1:1-50 [001] d.... 751.218975: thread_noise: kworker/1:1:50 start 751.218973223 duration 1673 ns
总体来讲,单次osnoise信息由tracepoint获得,然后通过新定义的tracepoint打印到ftrace的ringbuffer中,统计信息(osnoise的次数)由run_osnoise获取,通过inotify传给应用程序显示。
2,Kernel -- timerlat
与osnoise一起,5.14-rc1同时还引入了一个新的tracer -- timerlat,timerlat在每一个cpu上启动了一个实时任务,像xenomai的测试工具latency一样,使用内核线程来测量系统的延时(cyclictest是在用户空间,相对与内核线程,调度的过程还包括系统调用的返回,work_pending, do_notify_resume等额外的工作)。
与cyclictest和latency不同的是,timerlat在每一次到时记录了两条数据,一个是在irq上下文(preempt rt)的时间信息或在softirq上下文的时间信息,另一个是timerlat实时任务的时间信息,该些信息通过trace_timerlat_sample打印到ftrace的ringbuffer中,同时通过latency_fsnotify发送给rtal。
Timerlat可以设置threshold,如果latency超过threshold,可以设置打印调用栈,供开发者分析。
3, App
Rtla应用是osnoise用户空间的工具程序,主要用于响应osnoise和timerlat通过latency_fsnotify上传的事件并显示信息。Rtla需要借助libtraceevent和libtracefs与osnoise和timerlat两个tracer交换数据。
该程序包含如下内容:
- rtla osnoise top
获取并显示从latency_fsnotify -- osnoise_trace(由trace_osnoise_sample填充信息)得到的消息,所述信息包括sample数量,nmi/irq/softirq/thread的数量。信息显示如下:
duration: 0 07:00:04 | time is in us
CPU Period Runtime Noise % CPU Aval Max Noise Max Single HW NMI IRQ Softirq Thread
0 #4 4000000 178880 95.52800 108642 8184 7 0 2111 646 2725
1 #4 4000000 436602 89.08495 126965 8670 1 0 2178 659 5558
2 #4 4000000 269811 93.25472 125137 6704 4 1 3431 826 4556
3 #4 4000000 414924 89.62690 115139 10989 1 0 2106 656 4039
- rtla osnoise hist
Histogram在cyclictest中表示为发生延时为X毫秒的次数,在osnoise hist中,rtla周期性的调用osnoise_read_trace_hist统计tracefs/event/osnoise/sample_threshold/hist信息,并通过osnoise_print_stats打印到console。同时,osnoise hist还可以设置latency的threshold,latency超过threshold则停止rtla并停止ftrace,这样ringbuffer的底部就保留了问题现场数据,这点与cyclictest -b参数功能相同。
信息显示如下:
RTLA osnoise histogram
# Time unit is microseconds (us)
# Duration: 0 07:00:09
Index CPU-000 CPU-001 CPU-002 CPU-003
5 368 266 484 303
6 274 231 470 251
...
255 0 1 0 1
over: 281 441 299 281
count: 3673 3067 7658 2855
min: 5 5 5 5
avg: 109 202 32 137
max: 15992 8082 8289 10180
- rtla timerlat top
获取并显示从latency_fsnotify -- timerlat_irq/timerlat_main(由trace_timerlat_sample填充信息)得到的消息,所述信息中断上下文和内核线程上下文中产生latency的最大最小和平均值。
信息显示如下:
Timer Latency
0 07:00:03 | IRQ Timer Latency (us) | Thread Timer Latency (us)
CPU COUNT | cur min avg max | cur min avg max
0 #3652 | 2 0 14 83 | 7 1 27 987
1 #3652 | 1 0 17 624 | 6 2 30 639
2 #3652 | 2 0 16 63 | 7 2 28 121
3 #3652 | 2 0 17 74 | 7 2 30 91
- rtla timerlat hist
从timerlat得到timer_latency,并记录最大最小和平均值,周期性显示。
# Time unit is microseconds (us)
# Duration: 0 07:00:09
Index IRQ-000 Thr-000 IRQ-001 Thr-001 IRQ-002 Thr-002 IRQ-003 Thr-003
0 9 0 11 0 32 0 13 0
1 274 0 281 0 773 0 572 0
...
141 0 0 0 0 0 1 0 0
over: 0 1 0 0 0 1 0 0
count: 9569 9569 9570 9570 9569 9569 9570 9570
min: 0 2 0 2 0 2 0 2
avg: 16 29 20 33 4 13 5 14
max: 77 1981 79 115 58 1966 77 84
rtls osnoise/timerlat有很多参数可选,具体信息可以使用rtla --help查阅。