扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
可用于CPU分析的BPF工具,见下图标注的这些命令
成都创新互联主要从事网站建设、做网站、网页设计、企业做网站、公司建网站等业务。立足成都服务绥宁,10多年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:18980820575
下表的这些工具有些是属于BCC或者bpftrace,或为这本书创建的。一些工具同时出现在BCC和bpftrace中。下表出了本节介绍的工具的来源(BT是bpftrace的缩写。)
Tool 工具名称 | Source 来源 | Target 功效/目标 | Description 描述 |
execsnoop | BCC/BT | Sched | Lists new process execution 列出新进程的执行 |
exitsnoop | BCC | Sched | Shows process lifespan and exit reason 显示进程寿命和退出原因 |
runqlat | BCC/BT | Sched | Summarizes CPU run queue latency 总结CPU运行队列延迟情况 |
runqlen | BCC/BT | Sched | Summarizes CPU run queue length 总结CPU运行队列长度 |
runqslower | BCC | Sched | Prints run queue waits slower than a threshold 打印等待时间慢于阈值的运行队列(单位us) |
cpudist | BCC | Sched | Summarizes on-CPU time 汇总on-CPU的时间 |
cpufreq | Book | CPUs | Samples CPU frequency by process 按进程采样CPU频率 |
profile | BCC | CPUs | Samples CPU stack traces 采样CPU堆栈跟踪 |
offcputime | BCC/book | Sched | Summarizes off-CPU stack traces and times 汇总off-CPU堆栈跟踪和时间 |
syscount | BCC/BT | Syscalls | Counts system calls by type and process 按类型和过程统计syscall系统调用 |
argdist | BCC | Syscalls | Can be used for syscall analysis 可以用于系统调用分析 |
trace | BCC | Syscalls | Can be used for syscall analysis 可以用于系统调用分析 |
funccount | BCC | Software | Counts function calls 计算函数调用 |
softirqs | BCC | Interrupts | Summarizes soft interrupt time 汇总软中断时间 |
hardirqs | BCC | Interrupts | Summarizes hard interrupt time 汇总硬中断时间 |
smpcalls | Book | Kernel | Times SMP remote CPU calls 统计SMP远程CPU调用的时间 |
llcstat | BCC | PMCs | Summarizes LLC hit ratio by process 按进程汇总LLC命中率 |
通过exec() syscall系统调用来跟踪新进程。
可以发现消耗CPU资源的短暂进程的问题,还可以用于调试软件的执行,包括应用程序启动脚本。
该工具捕获了用户使用SSH登录系统并启动了包括sshd、groups、mesg在内的进程的瞬间。它还显示了来自系统活动记录器sar的过程,包括将指标写入其日志,包括sa1和sadc。
我们可以使用execsnoop查找消耗资源的高速率的短期进程,通常它们可能运行时间很短很难通过top之类的来发现,但是使用execsnoop很容易发现它。
execsnoop已用于调试许多生产问题,例如:来自后台作业的干扰,应用程序启动缓慢或失败,容器启动缓慢或失败,等等。
execsnoop的工作原理:
跟踪execve系统调用(常用的exec(2)变体),并显示execve(2)参数和返回值的详细信息。这将捕获遵循fork(2)/clone(2)-> exec(2)顺序的新进程,以及 re-exec(2)的进程。
有个特殊情况:一些应用程序在不调用exec(2)的情况下创建新进程,例如,在使用fork(2)或单独克隆(2)创建工作进程池时。因为它们不调用execve(2),所以execsnoop输出中不包括这些。这种情况应该不常见:应用程序应该创建工作线程池,而不是进程。 【举例子: 我们登录到redis-cli后再执行的其它的命令,execsnoop就无法抓取到】
由于进程执行速率预计相对较低(<1000/秒),因此该工具的开销可以忽略不计。
这里再介绍一个来自Netflix的一个真实问题,我使用execnoop进行了调试。这发生在一个用于微基准测试的服务器上,但是基准测试结果显示出太多的差异是不可信任的。当系统被认为是空闲的时候,我运行了execnoop,发现它不是!这些程序每启动一秒钟,就会扰乱我们的基准。结果发现,这是一个配置错误的服务,它试图每秒钟启动一次,失败,然后重新启动。一旦服务被停用,这些进程就停止了(正如使用execnoop所确认的那样),然后基准数就变得一致了。
⇒ execsnoop --help
usage: execsnoop [-h] [-T] [-t] [-x] [-q] [-n NAME] [-l LINE]
[--max-args MAX_ARGS]
Trace exec() syscalls
optional arguments:
-h, --help show this help message and exit
-T, --time include time column on output (HH:MM:SS)
-t, --timestamp include timestamp on output
-x, --fails include failed exec()s
-q, --quote Add quotemarks (") around arguments.
-n NAME, --name NAME only print commands matching this name (regex), any arg
-l LINE, --line LINE only print commands where arg contains this line (regex)
--max-args MAX_ARGS maximum number of arguments parsed and displayed,defaults to 20
examples:
./execsnoop # trace all exec() syscalls
./execsnoop -x # include failed exec()s
./execsnoop -T # include time (HH:MM:SS)
./execsnoop -t # include timestamps
./execsnoop -q # add "quotemarks" around arguments
./execsnoop -n main # only print command lines containing "main"
./execsnoop -l tpkg # only print command where arguments contains "tpkg"
exitsnoop是一个BCC工具,可跟踪进程退出的时间,显示进程的使用期限和退出原因。期限是从进程创建到终止的时间,并且包括CPU的开启和关闭时间。
像execsnoop一样,exitsnoop可以帮助调试短期进程的问题,并提供不同的信息来帮助理解此类工作负载。例如:
此输出显示退出了许多短期进程,例如cmake,sh和make,这表明一个软件版本正在运行。1.00秒后,sleep进程成功退出(退出代码0),而由于KILL信号,另一个sleep进程在7.31秒后退出。这也捕获了221.25秒后退出的“ DOM Worker”线程。
该工具通过检测 sched:sched_process_exit 跟踪点及其参数来工作,并且还使用bpf_get_current_task(),以便可以从任务结构中读取开始时间(不稳定的接口详细信息)。由于此跟踪点很少触发,因此该工具的开销可以忽略不计。
本人的实战截图:
runqlat是一个BCC和bpftrace工具,用于测量CPU调度程序延迟,通常称为运行队列延迟(即使不再使用运行队列实现)。这对于识别和量化CPU饱和问题很有用,在CPU饱和问题中,对CPU资源的需求超出了服务能力。runqlat度量的指标是每个线程(任务)等待其打开CPU所花费的时间。
下图显示了在48核CPU生产API实例上运行的BCC runqlat在系统范围内的CPU利用率约为42%(可以用top命令查看到CPU的利用率)。runqlat的参数为10 1,表示以设置10秒间隔,仅输出一次:
此输出表明,在大多数情况下,线程等待的时间少于15微秒,直方图中的模式介于2到15微秒之间。这是相对较快的(健康系统的一个示例),并且对于运行在42%CPU利用率上的系统来说是预期的。在此示例中,有时运行队列延迟高达8到16毫秒存储桶,但这些异常值是异常的。
runqlat的原理:
通过检测调度程序唤醒和上下文切换事件来确定从唤醒到运行的时间。注意: 这些事件在繁忙的生产系统上可能非常频繁,每秒超过一百万个事件。即使对BPF进行了优化,以这些速率,即使每个事件增加一微秒也会导致明显的开销。需要小心使用runqlat这个工具。
示例:如果您在一个10核的系统上的上下文切换速率为1M/sec,则每个上下文切换增加1微秒将消耗10%的CPU资源(计算方法: 100%×(1×1000000/10×1000000) )。有关BPF开销的一些实际测量值,请参见第18章,每个事件通常小于一微秒。
一个使用runqlat诊断案例
在一个36核CPU的构建服务器进行软件构建操作,其中并行作业的数量被错误地设置为72,从而导致CPU超载。观测到的截图如下:
现在的分布是三模态,最慢的模式以8到16毫秒的桶为中心.这显示了线程的大量等待(小于15微秒内算健康指标)。
可以直接从其他工具和指标中识别出此特定问题。例如,sar可以显示CPU利用率(-u)和运行队列指标(-q)
可以看到,当前系统 CPU idle空闲为0, 全负荷工作。 另外, runq-sz平均运行队列大小为72(包括正在运行和可运行),也大幅超过了可用的36个CPU。
runqlen是一个BCC和bpftrace工具,用于对CPU运行队列的长度进行采样,计算有多少任务在等待轮到他们,并将其显示为线性直方图。这可以用来进一步描述运行队列延迟的问题或作为更便宜的近似值。
下图显示了在48个CPU生产API实例上运行的BCC的runqlet,该实例在系统范围内的CPU利用率约为42%(与先前在runqlat中显示的实例相同)。runqlen的参数为 10 1,以设置10秒间隔,仅输出一次:
这表明在大多数情况下,运行队列长度为零,这意味着线程无需等待轮到他们。
我将运行队列长度描述为辅助性能指标,将运行队列延迟描述为主要性能。与长度不同,延迟会直接和成比例地影响性能。想象一下在杂货店加入结帐行。对您来说更重要的是:线路的长度或您实际花费的等待时间?runqlat更重要。那么,为什么要使用runqlen?
首先,可以使用runqlen进一步描述在runqlat中发现的问题,并解释延迟如何变高。其次,runqlen采用99赫兹的定时采样,而runqlat跟踪调度程序事件。与runqlat的调度程序跟踪相比,此定时采样的开销可忽略不计。对于24x7全天候监控,最好先使用runqlen来识别问题(因为运行起来更便宜),然后再使用runqlat临时量化延迟。
四线程,一个CPU的一个示例:
在此示例中,将四个繁忙线程的CPU工作负载绑定到CPU0。执行runqlen -C以显示每个CPU的直方图:
CPU 0上的运行队列长度为三个:一个线程在CPU上等待三个线程。此每个CPU的输出对于检查调度程序平衡非常有用。
小实验:
我们执行5次这个单行命令taskset -c 0 sh -c 'while :; do :; done' &,这个循环操作绑定在cpu0上运行,然后执行 runqlen可以看到如下结果,明显可以看到cpu0上有很多运行队列的堆积,而cpu1上堆积基本都是0。
runqslower是一个BCC工具,它列出了运行队列等待时间超过可配置阈值的实例,并显示了遭受等待时间及其持续时间的过程。
以下示例来自当前在系统范围内以45%CPU利用率运行的48 CPU生产API实例:
此输出表明,在13秒的时间内, 有10个运行队列等待时间超过默认阈值10000微秒(10毫秒)的情况。对于具有55%空闲CPU余量的服务器来说,这似乎令人惊讶,但这是一个繁忙的多线程应用程序,在调度程序可以将线程迁移到空闲CPU之前,运行队列可能不平衡。该工具可以确认受影响的应用程序。
该工具当前通过将kprobes用于内核函数ttwu_do_wakeup(),wake_up_new_task()和finish_task_switch()来工作。将来的版本应使用类似于runqlat的bpftrace版本的代码切换到调度程序跟踪点。开销与runqlat相似;由于kprobes的成本,即使在runqslower不输出任何输出的情况下,它也会在繁忙的系统上引起明显的开销。
cpudist是一个BCC工具,用于显示每个线程唤醒的CPU时间分布。这可用于帮助表征CPU工作负载,为以后的调整和设计决策提供详细信息。
例如,从一个48核CPU生产实例中:
此输出表明生产应用程序通常仅在CPU上花费很短的时间:从0到127微秒。
下面的图,这是一个CPU繁重的工作负载,具有比可用CPU更多的繁忙线程,并且具有以毫秒(-m)为单位的直方图:
现在有一种CPU持续时间从4到15毫秒的模式:这很可能是线程耗尽了调度程序的时间量,然后遇到了非自愿的上下文切换。
该工具用于帮助了解Netflix的生产变化,其中机器学习应用程序开始运行的速度快了三倍。perf命令用于显示上下文切换速率已降低,而cpudist用于解释其影响:应用程序现在通常在上下文切换之间运行两到四毫秒,而更早的时候只能在0到3微秒之间运行,然后再被上下文切换中断。
注意:
cpudist通过跟踪调度程序上下文切换事件来工作,该事件在繁忙的生产工作负载上非常频繁(超过一百万个事件/秒)。与runqlat一样,此工具的开销可能很大,因此请谨慎使用。
root@dba-test:~|⇒ cpudist --help
usage: cpudist [-h] [-O] [-T] [-m] [-P] [-L] [-p PID] [interval] [count]
Summarize on-CPU time per task as a histogram.
positional arguments:
interval output interval, in seconds
count number of outputs
optional arguments:
-h, --help show this help message and exit
-O, --offcpu measure off-CPU time #只显示CPU以外的时间,而不是CPU上的时间
-T, --timestamp include timestamp on output
-m, --milliseconds millisecond histogram
-P, --pids print a histogram per process ID #每个进程打印一个直方图
-L, --tids print a histogram per thread ID
-p PID, --pid PID trace this PID only
examples:
cpudist # summarize on-CPU time as a histogram
cpudist -O # summarize off-CPU time as a histogram
cpudist 1 10 # print 1 second summaries, 10 times
cpudist -mT 1 # 1s summaries, milliseconds, and timestamps
cpudist -P # show each PID separately
cpudist -p 185 # trace PID 185 only
这是个bpftrace脚本文件,不在BCC工具集内。 个人认为只要大致了解下这个命令即可。
cpufreq对CPU频率进行采样,并将其显示为系统范围的直方图,并带有每个进程的名称直方图。这仅适用于更改频率的CPU缩放调节器,例如节电,并可用于确定应用程序运行的时钟速度。
profile是一个BCC工具,它以一定的时间间隔对堆栈跟踪进行采样,并报告堆栈跟踪的频率计数。这是BCC中了解CPU消耗的最有用的工具,因为它总结了消耗CPU资源的几乎所有代码路径。由于事件率固定为可以调整的采样率,因此它也可以以相对可忽略的开销使用。
根据profile的结果,我们可以绘制火焰图
$ profile -af 30 > out.stacks01
$ git clone https://github.com/brendangregg/FlameGraph
$ cd FlameGraph
$ ./flamegraph.pl --color=java < ../out.stacks01 > out.svg
然后即可输出一幅火焰图。
使profile与其他CPU探查器不同的原因在于,为了提高效率,此频率计数是在内核空间中计算的。其他基于内核的探查器,例如perf,会将每个采样的堆栈跟踪发送到用户空间,在该用户跟踪中将其后处理为摘要。这可能会占用大量CPU资源,并且取决于调用,它还可能涉及文件系统和磁盘I/O来记录样本。profile避免了这些费用。
root@dba-test:~|⇒ profile --help
usage: profile [-h] [-p PID | -L TID] [-U | -K] [-F FREQUENCY | -c COUNT] [-d]
[-a] [-I] [-f] [--stack-storage-size STACK_STORAGE_SIZE]
[-C CPU]
[duration]
Profile CPU stack traces at a timed interval
positional arguments:
duration duration of trace, in seconds
optional arguments:
-h, --help show this help message and exit
-p PID, --pid PID profile process with this PID only
-L TID, --tid TID profile thread with this TID only
-U, --user-stacks-only show stacks from user space only (no kernel space stacks)
-K, --kernel-stacks-only show stacks from kernel space only (no user space stacks)
-F FREQUENCY, --frequency FREQUENCY sample frequency, Hertz
-c COUNT, --count COUNT sample period, number of events
-d, --delimited insert delimiter between kernel/user stacks
-a, --annotations add _[k] annotations to kernel frames
-I, --include-idle include CPU idle stacks
-f, --folded output folded format, one line per stack (for flame graphs)
--stack-storage-size STACK_STORAGE_SIZE
the number of unique stack traces that can be stored and displayed (default 16384)
-C CPU, --cpu CPU cpu number to run profile on
examples:
./profile # profile stack traces at 49 Hertz until Ctrl-C
./profile -F 99 # profile stack traces at 99 Hertz
./profile -c 1000000 # profile stack traces every 1 in a million events
./profile 5 # profile at 49 Hertz for 5 seconds only
./profile -f 5 # output in folded format for flame graphs
./profile -p 185 # only profile process with PID 185
./profile -L 185 # only profile thread with TID 185
./profile -U # only show user space stacks (no kernel)
./profile -K # only show kernel space stacks (no user)
offcputime是一个BCC和bpftrace工具,用于总结阻塞线程和关闭CPU所花费的时间,并显示堆栈跟踪信息以说明原因。
对于CPU分析,此工具说明了为什么线程未在CPU上运行。
与profile对应;在它们之间,它们显示了线程在系统上花费的全部时间:使用profile的CPU时间和使用offcputime的CPU时间。
offcputime已用于查找各种生产问题,包括查找在锁获取中阻塞的意外时间以及负责的堆栈跟踪。
注意:
offcputime通过检测上下文切换并记录从线程离开CPU到返回CPU的时间以及堆栈跟踪来工作。为了提高效率,在内核上下文中对时间和堆栈跟踪进行了频率计数。但是,上下文切换事件可能会非常频繁,并且对于繁忙的生产工作负载,此工具的开销可能会变得非常大(例如,> 10%)。该工具最好只在短时间内运行,以最大程度地减少生产影响。
offCPU time火焰图
与profile一样,offcputime的输出可能非常冗长,以至于您可以发现最好将它作为火焰图进行检查。可以将offcputime可视化为非CPU时间火焰图。
相关绘图的命令如下:
# offcputime -fKu 5 > out.offcputime01.txt
$ flamegraph.pl --hash --bgcolors=blue --title="Off-CPU Time Flame Graph" < out.offcputime01.txt > out.offcputime01.svg
funccount是一种BCC工具,可以对函数和其他事件进行频率计数。
它可用于为软件CPU使用情况提供更多上下文,显示调用哪些函数以及调用频率。profile可能能够显示某个功能在CPU上很热,但无法解释为什么:该功能是否运行缓慢,或者每秒是否被简单调用了数百万次。
TIPS:profile不能轻易解释这一点。包括profile在内的探查器对CPU指令指针进行采样,因此与该函数的反汇编进行比较可能会显示该函数是卡在循环中还是被调用多次。
例如,通过匹配以 tcp_开头的功能,可以对繁忙的生产实例上的内核TCP功能进行频率计数:
可以使用-i 1使其每1秒输出一次。例如,较早的profile输出显示函数get_page_from_freelist()在CPU上很热。是因为它经常被调用还是因为它运行缓慢?可以用如下命令来测量其每秒速率:
这通过使用函数的动态跟踪来进行:内核函数使用kprobes,用户级函数使用uprob。此工具的开销与功能的速率有关。某些函数(例如malloc()和get_page_from_freelist() )往往会频繁发生,因此对其进行跟踪可能会大大降低目标应用程序的速度,超过10%,请谨慎使用。
softirqs是一个BCC工具,它显示了花费在soft IRQ(软中断)所花费的时间。
可以通过不同的工具轻松获得系统范围的软中断时间。例如,mpstat将其显示为%soft。也有 /proc/softirqs显示软IRQ事件的计数。
BCC softirqs工具的不同之处在于,它可以显示每个软IRQ的时间,而不是事件计数。
例如,从一个48 CPU生产实例和一个10秒的跟踪中:
此输出显示,花费在net_rx花费的时间最多,总计1358毫秒。这很重要,因为在该48 CPU系统上,它占CPU时间的3%。
softirqs通过使用irq:softirq_enter和irq:softirq_exit跟踪点来工作。该工具的开销与事件发生率有关,这对于繁忙的生产系统和较高的网络数据包发生率可能会很高。谨慎使用并检查开销。
hardirqs是一个BCC工具,它显示了花费在hard IRQ(硬中断)所花费的时间。
硬中断中的系统级时间可以从其他工具轻松获得。例如,mpstat将其显示为%irq。也有 /proc/interrupts显示hard IRQ事件的计数。
BCC hardirqs工具的不同之处在于,它可以显示每个硬IRQ的时间,而不是事件计数。
下面是mpstat -P ALL 1的部分截图(这台ecs的负载很低)
hardirqs可以提供CPU分析器不可见的CPU使用情况信息。有关缺少硬件PMU的云实例的性能分析,请参见第6.2.4节的内部部分。
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流