OSNOISE 追踪器

在高性能计算(HPC)的语境下,操作系统噪声(osnoise)是指应用程序因操作系统内部活动而受到的干扰。在 Linux 环境中,NMI、IRQ、SoftIRQ 以及任何其他系统线程都可能为系统引入噪声。此外,与硬件相关的任务也可能导致噪声,例如通过 SMI。

hwlat_detector 是用于识别最复杂的噪声来源的工具之一:硬件噪声

简而言之,hwlat_detector 会创建一个按给定周期定期运行的线程。在周期的开始,该线程会禁用中断并开始采样。在运行期间,hwlatd 线程会在循环中读取时间。由于中断已被禁用,线程、IRQ 和 SoftIRQ 无法干扰 hwlatd 线程。因此,两次不同时间读取之间产生的任何时间间隔,其根源要么在于 NMI,要么在于硬件本身。在周期结束时,hwlatd 会启用中断并报告读取之间观察到的最大间隔。它还会打印一个 NMI 发生计数器。如果输出没有报告 NMI 的执行情况,用户就可以得出结论:硬件是导致延迟的罪魁祸首。hwlat 通过观察 NMI 的进入和退出点来检测 NMI 的执行。

osnoise 追踪器利用了 hwlat_detector 的原理,通过运行一个类似的循环,但启用了抢占、SoftIRQ 和 IRQ,从而在其执行过程中允许所有的 osnoise 来源。采用与 hwlat 相同的方法,osnoise 会记录任何干扰源的进入和退出点,并递增每个 CPU(per-cpu)的干扰计数器。osnoise 追踪器还为每个干扰源保存一个干扰计数器。每当工具观察到 NMI、IRQ、SoftIRQ 和线程的进入事件时,相应的干扰计数器就会增加。当发生噪声且没有任何操作系统级别的干扰时,硬件噪声计数器会增加,这指向了与硬件相关的噪声。通过这种方式,osnoise 可以对任何干扰源进行统计。在周期结束时,osnoise 追踪器会打印所有噪声的总和、最大单次噪声、线程可用的 CPU 百分比以及各噪声源的计数器。

用法

将 ASCII 文本 “osnoise” 写入追踪系统的 current_tracer 文件中(通常挂载在 /sys/kernel/tracing)。

例如

[root@f32 ~]# cd /sys/kernel/tracing/
[root@f32 tracing]# echo osnoise > current_tracer

可以通过读取 trace 文件来跟踪追踪信息:

[root@f32 tracing]# 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
#              | |         |   ||||      |           |             |    |            |      |      |      |      |      |
           <...>-859     [000] ....    81.637220: 1000000        190  99.98100       9     18      0   1007     18      1
           <...>-860     [001] ....    81.638154: 1000000        656  99.93440      74     23      0   1006     16      3
           <...>-861     [002] ....    81.638193: 1000000       5675  99.43250     202      6      0   1013     25     21
           <...>-862     [003] ....    81.638242: 1000000        125  99.98750      45      1      0   1011     23      0
           <...>-863     [004] ....    81.638260: 1000000       1721  99.82790     168      7      0   1002     49     41
           <...>-864     [005] ....    81.638286: 1000000        263  99.97370      57      6      0   1006     26      2
           <...>-865     [006] ....    81.638302: 1000000        109  99.98910      21      3      0   1006     18      1
           <...>-866     [007] ....    81.638326: 1000000       7816  99.21840     107      8      0   1016     39     19

除了常规的追踪字段(从 TASK-PID 到 TIMESTAMP)外,追踪器会在每个运行 osnoise/ 线程的 CPU 的每个周期结束时打印一条消息。osnoise 特定的字段报告了:

  • RUNTIME IN US 报告了 osnoise 线程持续循环读取时间所花费的时长(以微秒为单位)。

  • NOISE IN US 报告了 osnoise 追踪器在相关运行期间观察到的噪声总和(以微秒为单位)。

  • % OF CPU AVAILABLE 报告了在运行时间窗口内可供 osnoise 线程使用的 CPU 百分比。

  • MAX SINGLE NOISE IN US 报告了在运行时间窗口内观察到的最大单次噪声(以微秒为单位)。

  • 干扰计数器(Interference counters)显示了在运行时间窗口内各自干扰发生的次数。

请注意,上面的示例显示了大量的硬件(HW)噪声样本。原因在于此样本是在虚拟机上采集的,宿主机的干扰被检测为了硬件干扰。

追踪器配置

追踪器在 osnoise 目录下有一组选项,它们是:

  • osnoise/cpus:osnoise 线程将在其上执行的 CPU。

  • osnoise/period_us:osnoise 线程的周期。

  • osnoise/runtime_us:osnoise 线程寻找噪声的时长。

  • osnoise/stop_tracing_us:如果发生高于配置值的单次噪声,则停止系统追踪。写入 0 可禁用此选项。

  • osnoise/stop_tracing_total_us:如果总噪声高于配置值,则停止系统追踪。写入 0 可禁用此选项。

  • tracing_threshold:两次 time() 读取之间被视为噪声的最小时间差(以微秒为单位)。设置为 0 时将使用默认值,当前为 1 微秒。

  • osnoise/options:一组开关选项,可以通过将选项名称写入文件来启用,或者通过写入带有 ‘NO_’ 前缀的选项名称来禁用。例如,写入 NO_OSNOISE_WORKLOAD 会禁用 OSNOISE_WORKLOAD 选项。特殊的 DEFAULTS 选项会将所有选项重置为默认值。

追踪器选项

osnoise/options 文件为 osnoise 追踪器提供了一组开关配置选项。这些选项是:

  • DEFAULTS:将选项重置为默认值。

  • OSNOISE_WORKLOAD:不分发 osnoise 工作负载(请参见下面的专门章节)。

  • PANIC_ON_STOP:如果追踪器停止,则调用 panic()。此选项用于捕获 vmcore。

  • OSNOISE_PREEMPT_DISABLE:在运行 osnoise 工作负载时禁用抢占,仅允许 IRQ 和硬件相关的噪声。

  • OSNOISE_IRQ_DISABLE:在运行 osnoise 工作负载时禁用 IRQ,仅允许 NMI 和硬件相关的噪声(类似于 hwlat 追踪器)。

附加追踪

除了追踪器之外,还添加了一组追踪点(tracepoints)以方便识别 osnoise 的来源。

  • osnoise:sample_threshold:每当噪声高于可配置的 tolerance_ns 时打印。

  • osnoise:nmi_noise:来自 NMI 的噪声,包括持续时间。

  • osnoise:irq_noise:来自 IRQ 的噪声,包括持续时间。

  • osnoise:softirq_noise:来自 SoftIRQ 的噪声,包括持续时间。

  • osnoise:thread_noise:来自线程的噪声,包括持续时间。

请注意,所有这些值都是净值。例如,如果在 osnoise 运行期间,另一个线程抢占了 osnoise 线程,它将在开始时启动一个 thread_noise 持续时间。然后,发生了一个 IRQ,抢占了 thread_noise,从而启动了 irq_noise。当 IRQ 执行结束时,它会计算其持续时间,并将该持续时间从 thread_noise 中减去,从而避免对 IRQ 执行进行重复计算。此逻辑对所有噪声来源都适用。

以下是这些追踪点用法的一个示例:

  osnoise/8-961     [008] d.h.  5789.857532: irq_noise: local_timer:236 start 5789.857529929 duration 1845 ns
  osnoise/8-961     [008] dNh.  5789.858408: irq_noise: local_timer:236 start 5789.858404871 duration 2848 ns
migration/8-54      [008] d...  5789.858413: thread_noise: migration/8:54 start 5789.858409300 duration 3068 ns
  osnoise/8-961     [008] ....  5789.858413: sample_threshold: start 5789.858404555 duration 8812 ns interferences 2

在此示例中,最后一行报告了一个 8 微秒的噪声样本,指向了两次干扰。回看追踪信息,前两个条目是关于定时器 IRQ 执行后运行的迁移线程。第一个事件不属于噪声,因为它发生在一毫秒之前。

值得注意的是,追踪点中报告的持续时间总和小于 sample_threshold 中报告的 8 微秒。其原因在于任何干扰执行前后所发生的进入和退出代码的开销。这就证明了采用双重方法的合理性:测量线程和追踪。

在没有工作负载的情况下运行 osnoise 追踪器

通过在设置了 NO_OSNOISE_WORKLOAD 选项的情况下启用 osnoise 追踪器,osnoise: 追踪点可用于测量任何类型 Linux 任务的执行时间,且不受其他任务的干扰。