方法论

CPU分析的一般步骤:

  1. 性能监控指标+工具:通过已有指标或者现成的工具进行初步分析和判断
  2. 使用USE方法进行进一步分析:U:CPU使用率,S:满载率:E:错误
  3. 测量分析
  4. 微基准测试
  5. 静态性能优化

本文侧重对工具的使用介绍。

基础分析工具

基础分析主要包含以下工具的使用:uptime、vmstat、mpstat、sar、ps、top、pidstat,time/ptime

1. uptime


通常uptime可以用来查看系统负载信息

  1. $ uptime
  2. 19:10:52 up 1135 days, 7:37, 1 user, load average: 87.87, 77.49, 66.99

最后三列分别是1分钟、5分钟和15分钟系统的平均负载。如果数字是依次减小的,说明现在系统负载在升高,如果是依次增大的,则说明系统负载在降低。
在Linux系统中load值的计算包含系统中正在运行的进行(RUNNING)、可以运行的进程(RUNABLE)和处于不可中断的进程。
相关参考文档:https://brendangregg.com/blog/2017-08-08/linux-load-averages.html

2. vmstat


虚拟内存分析工具第一列输出的是可运行进程数,最后几列输出的是cpu相关指标,可以用来分析cpu相关的问题,该工具可显示CPU正式使用率。

  1. $ vmstat 1
  2. procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
  3. r b swpd free buff cache si so bi bo in cs us sy id wa st
  4. 58 9 0 856644 44052 88905592 0 0 4214 3852 0 0 61 8 26 5 0
  5. 60 7 0 973376 44072 88245416 0 0 324648 333560 265446 247480 62 11 23 3 0
  6. 58 5 0 1349060 44068 86577808 0 0 289808 257000 270242 276081 55 18 24 3 0
  7. 40 24 0 1573068 44080 84997592 0 0 224756 659252 250767 268095 64 13 17 6 0
  8. 120 12 0 1636280 44080 83402944 0 0 307740 283888 249253 253964 56 14 22 8 0
  9. 37 14 0 867316 44080 81521136 0 0 454760 298512 275080 294816 51 16 24 10 0
  10. 52 11 0 2181236 44260 80656688 0 0 445088 560996 298539 249712 56 12 24 9 0
  11. 37 6 0 7423208 44260 81312632 0 0 401356 511956 306425 330183 50 9 33 7 0

跟CPU相关的列分析:

  • r:对于Linux系统该字段表示所有的Runnable+Running进程,不包含处于D状态的进程,如下图所示
  • us:用户态(用户)CPU使用率
  • sy:内核态(系统)CPU使用率
  • id:CPU空闲率
  • wa:CPU等待I/O使用率,当进程阻塞在磁盘IO上时可以用来衡量CPU idle
  • st:在虚拟化环境显示被其它租户占用的CPU使用率

image.png

3. mpstat


多CPU统计工具,可以统计每个CPU的使用情况

~# mpstat -P ALL 1
Linux 5.4.0-99-generic (ambariserver-wangshuaihua-02.dev.kwaidc.com)     02/26/22     _x86_64_    (8 CPU)

15:11:56     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
15:11:57     all    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       0    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       1    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       2    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       3    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       4    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       5    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       6    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:11:57       7    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00

命令输出列说明:

  • CPU:逻辑CPU序号,或者是all
  • %usr:用户态(用户)CPU使用率,不包含%nice
  • %nice:带有nice进程的用户态CPU使用率
  • %sys:内核态(系统)CPU使用率
  • %iowait:I/O wait
  • %irq:硬中断CPU使用率
  • %soft:软中断CPU使用率
  • %steal:虚拟化环境其它租户CPU使用率
  • %guest:客户端虚拟机使用使用率
  • %gnice:客户端虚拟机带有nice标记的进程CPU使用率
  • %idle:CPU剩余率

需要关注的列是 %usr,%sys和%idle,注意查看每个CPU的使用率,看是否存在CPU使用不均衡或者热点。

mpstat默认只显示CPU的整体使用情况,如下图所示:

~# mpstat 1
Linux 5.4.0-99-generic (ambariserver-wangshuaihua-02.dev.kwaidc.com)     02/26/22     _x86_64_    (8 CPU)

15:14:04     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
15:14:05     all    0.00    0.00    0.12    0.00    0.00    0.00    0.00    0.00    0.00   99.88
15:14:06     all    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00
15:14:07     all    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00    0.00  100.00

4. sar


sar工具可以用来观测多项系统指标,针对CPU的参数有:

  • -P ALL:效果与mpstat -P ALL一样,只是输出的列稍微少一些
  • -u:与mpstat默认输出结果一样,系统平均指标
  • -q:输出run-queue大小,RUNNABLE+RUNNING与vmstat的r列内容一致
    初次使用sar工具可能会报错,如果没有sar工具则需要安装,安装后可能还会遇到以下错误
    ~# sar -P ALL
    Cannot open /var/log/sysstat/sa26: No such file or directory
    Please check if data collecting is enabled
    
    解决方法:
    ~# sar  -o 2 3
    
~# sar -P ALL 1 -u -q
Linux 5.4.0-99-generic (ambariserver-wangshuaihua-02.dev.kwaidc.com)     02/26/22     _x86_64_    (8 CPU)

15:27:19        CPU     %user     %nice   %system   %iowait    %steal     %idle
15:27:20        all      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          0      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          1      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          2      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          3      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          4      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          5      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          6      0.00      0.00      0.00      0.00      0.00    100.00
15:27:20          7      0.00      0.00      0.00      0.00      0.00    100.00

5. ps

ps方法是非常常用的工具,可以列出所有进程的相关信息,命令格式有两种:

  • BSD格式,参数不带’-‘字符
  • SVR4测试,参数带’-‘字符

BSD格式命令输出如下所示:

~# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.0  0.1 171060 13240 ?        Ss   Feb15   0:27 /sbin/init
root           2  0.0  0.0      0     0 ?        S    Feb15   0:00 [kthreadd]
root           3  0.0  0.0      0     0 ?        I<   Feb15   0:00 [rcu_gp]
root           4  0.0  0.0      0     0 ?        I<   Feb15   0:00 [rcu_par_gp]
root           6  0.0  0.0      0     0 ?        I<   Feb15   0:00 [kworker/0:0H-kblockd]
root           9  0.0  0.0      0     0 ?        I<   Feb15   0:00 [mm_percpu_wq]
root          10  0.0  0.0      0     0 ?        S    Feb15   0:00 [ksoftirqd/0]
root          11  0.0  0.0      0     0 ?        I    Feb15   8:49 [rcu_sched]

SVR4格式命令输出如下所示:

~# ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 Feb15 ?        00:00:27 /sbin/init
root           2       0  0 Feb15 ?        00:00:00 [kthreadd]
root           3       2  0 Feb15 ?        00:00:00 [rcu_gp]
root           4       2  0 Feb15 ?        00:00:00 [rcu_par_gp]
root           6       2  0 Feb15 ?        00:00:00 [kworker/0:0H-kblockd]
root           9       2  0 Feb15 ?        00:00:00 [mm_percpu_wq]
root          10       2  0 Feb15 ?        00:00:00 [ksoftirqd/0]
root          11       2  0 Feb15 ?        00:08:49 [rcu_sched]
root          12       2  0 Feb15 ?        00:00:06 [migration/0]

从命令的输出结果上面,看起来BSD格式的输出信息更全一些,但是ps命令可以通过-o参数定制输出列。针对CPU的使用情况,我们按照BSD格式的输出来说,需要关注的是%CPU和TIME列:

  • %CPU:进程从启动以来的平均CPU使用率
  • TIME:进程启动以来总消耗的CPU时间用户态+内核态(usr+sys)

6. top

top命令可以以指定的时间间隔实时展示进程的CPU使用情况。

~# top
top - 23:46:46 up 1135 days, 12:13,  1 user,  load average: 46.75, 60.82, 66.77
Tasks: 1660 total,   2 running, 1657 sleeping,   0 stopped,   1 zombie
%Cpu(s): 28.5 us,  2.3 sy,  0.0 ni, 63.9 id,  4.7 wa,  0.0 hi,  0.5 si,  0.0 st
KiB Mem : 19780201+total,   606332 free, 70851200 used, 12634449+buff/cache
KiB Swap:        0 total,        0 free,        0 used. 12021227+avail Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
46652 yarn      20   0 8807788 3.459g  40876 S 428.9  1.8   7:49.67 java
69808 yarn      20   0 8643232 2.257g  41064 S 412.5  1.2   2:12.97 java
6608 yarn      20   0 9153288 3.615g  54376 S 404.9  1.9  17:39.61 java
8852 yarn      20   0 4104884 1.796g  47476 S 104.9  1.0   4:58.38 java
7359 hdfs      20   0 8616144 5.152g   3900 S  90.1  2.7 424070:32 java
46763 yarn      20   0 10.562g 7.527g 108768 S  66.1  4.0   1:09.90 java
28082 yarn      20   0 5371224 1.766g  34608 S  59.9  0.9   3:20.35 java
8030 yarn      20   0 4093672 1.656g  61588 S  52.0  0.9   5:33.91 java
 51830 yarn      20   0 14.877g 0.010t  15560 S  33.2  5.3  64477:55 java

top命令的输出结果中包含了系统整体CPU使用情况(第3行),和每个进程的CPU使用情况。对于每个进程CPU使用情况我们需要关注的是%CPU和TIME列:

  • %CPU:两次屏幕刷新期间进程的CPU使用率
  • TIME:进程启动以来的总CPU使用时间,单位为秒,比如上面输出的7359号进程已经消耗的CPU时间是424070分32秒

虽然top是一个非常基础的分析工具,但是它对性能的损耗不可小觑,在负载重的机器上表现尤为明显,需要谨慎使用。top对系统性能有较大的影响主要是因为要读取/proc下面所有进程的信息。因为top是对/proc做快照,所以很容易漏掉执行时间比较短的进程。

7. pidstat


pidstat可以用来分析单个进程或线程的CPU使用情况,默认情况下会滚动输出。

# pidstat 1
Linux 3.10.0-693.5.2.el7.x86_64 (bjlt-h10366.sy)     02/27/2022     _x86_64_    (56 CPU)

12:00:34 AM   UID       PID    %usr %system  %guest    %CPU   CPU  Command
12:00:35 AM  1107     24628  100.00    4.59    0.00  100.00    51  java
12:00:35 AM  1107     29868  100.00    3.67    0.00  100.00    38  java
12:00:35 AM  1107     37421  100.00    2.75    0.00  100.00    45  java
12:00:35 AM  1107     38523    0.92    0.00    0.00    0.92    38  java
12:00:35 AM  1107     44470  100.00   26.61    0.00  100.00    40  java
12:00:35 AM  1107     48401  100.00   17.43    0.00  100.00    40  java
12:00:35 AM  1107     51584   27.52    2.75    0.00   30.28    29  java
12:00:35 AM  1107     51830    0.00  100.00    0.00  100.00    45  java

CPU分析常用参数:

  • -p ALL:显示所有进程或线程CPU使用情况
  • -t:显示线程粒度统计

    8. time


    time有两个版本,一个是shell内建的,另外一个是/usr/bin/下的,这两个有不同的使用效果
    shell内建time命令
    两次对同一个文件求MD5值 ```bash

    time md5sum ambari-agent-2.6.2.0-0.x86_64.rpm

    06e883c97b88079b0cc71a763fa2a99e ambari-agent-2.6.2.0-0.x86_64.rpm

real 0m0.417s user 0m0.175s sys 0m0.056s

time md5sum ambari-agent-2.6.2.0-0.x86_64.rpm

06e883c97b88079b0cc71a763fa2a99e ambari-agent-2.6.2.0-0.x86_64.rpm

real 0m0.242s user 0m0.177s sys 0m0.027s

从结果上可以看出第一次使用了0.417秒,用户态使用了0.175秒,内核态使用了0.056秒,其中有0.186(0.417-0.175-0.056)秒的差距,猜测可能是花等磁盘io上了,第二次计算后时间差值缩小到了0.038秒,估计是这个文件已经通过文件系统缓存到了内存中。<br />**/usr/bin/time**使用
```bash
# /usr/bin/time -v md5sum ambari-agent-2.6.2.0-0.x86_64.rpm
06e883c97b88079b0cc71a763fa2a99e  ambari-agent-2.6.2.0-0.x86_64.rpm
Command being timed: "md5sum ambari-agent-2.6.2.0-0.x86_64.rpm"
User time (seconds): 0.16
System time (seconds): 0.03
Percent of CPU this job got: 98%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.20
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 724
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 227
Voluntary context switches: 0
Involuntary context switches: 22
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
    Exit status: 0

进阶分级工具

进阶分析主要包含以下工具的使用:perf、profile

perf


perf是一个Linux的官方分析器,是一个强大的工具,可以做很多分析。这里只会介绍涉及到CPU部分的使用。
perf常见用法:

  1. 使用perf对指定命令以99Hz进行运行时采样:

perf record -F 99 xxxx
比如:

~# perf record -F 99 md5sum test.txt
7da6d500f2ebc693c9219ea53e5ccb3a  test.txt
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.008 MB perf.data (9 samples) ]

~# perf report -n --stdio
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 9  of event 'cycles'
# Event count (approx.): 43611121
#
# Overhead       Samples  Command  Shared Object      Symbol
# ........  ............  .......  .................  ................................
#
30.92%             1  md5sum   md5sum             [.] 0x0000000000004709
22.97%             1  md5sum   md5sum             [.] 0x0000000000004314
22.97%             1  md5sum   md5sum             [.] 0x0000000000004800
8.74%             1  md5sum   md5sum             [.] 0x00000000000043b7
8.74%             1  md5sum   md5sum             [.] 0x0000000000004755
5.02%             1  md5sum   md5sum             [.] 0x00000000000046c9
0.64%             1  md5sum   libc-2.31.so       [.] realloc
0.00%             1  md5sum   [kernel.kallsyms]  [k] filemap_fault
     0.00%             1  perf     [kernel.kallsyms]  [k] perf_event_addr_filters_exec

image.png

  1. 对CPU调用栈进行采样,99Hz,持续10s

perf record -a -g -F 99 -- sleep 10

  1. 对特定进程进行采样,使用dwarf (dbg信息)展开栈结构,99 Hz,持续10s

perf record -F 99 -p PID --call-graph dwarf -- sleep 10

  1. 通过exec调用统计新进程创建信息

perf record -e sched:sched_process_exec -a

  1. 记录上下文切换事件,包含栈信息,持续10s

perf record -e sched:sched_switch -a -g -- sleep 10

  1. 合并查看perf.data文件中的内容

perf report -n --stdio

  1. 列出perf.data文件中记录的所有事件,并列出数据头信息(推荐使用)

perf report --header

  1. 统计PMC信息,持续10s ```bash ~# perf stat -a — sleep 10

Performance counter stats for ‘system wide’:

80008.95 msec cpu-clock # 7.996 CPUs utilized 884 context-switches # 0.011 K/sec 0 cpu-migrations # 0.000 K/sec 71 page-faults # 0.001 K/sec 95619070 cycles # 0.001 GHz 22556744 stalled-cycles-frontend # 23.59% frontend cycles idle 13343963 stalled-cycles-backend # 13.96% backend cycles idle 21832747 instructions # 0.23 insn per cycle

1.03 stalled cycles per insn

4608307 branches # 0.058 M/sec 2187 branch-misses # 0.05% of all branches

  10.005943705 seconds time elapsed

9. 查看上下文切换事件统计,持续10s
```bash
~# perf stat -e sched:sched_switch -a -- sleep 10

Performance counter stats for 'system wide':

1031      sched:sched_switch

      10.001918573 seconds time elapsed
  1. 查看xx命令的LLC缓存(last level cache)统计 ```bash

    perf stat -e LLC-loads,LLC-load-misses,LLC-stores,LLC-store-misses md5sum ambari-agent-2.6.2.0-0.x86_64.rpm

    06e883c97b88079b0cc71a763fa2a99e ambari-agent-2.6.2.0-0.x86_64.rpm

Performance counter stats for ‘md5sum ambari-agent-2.6.2.0-0.x86_64.rpm’:

1,383,647 LLC-loads (50.96%) 603,408 LLC-load-misses # 43.61% of all LL-cache hits (52.58%) 576,738 LLC-stores (49.12%) 40,283 LLC-store-misses (47.45%)

   0.491177402 seconds time elapsed

11. 生成火焰图<br />对于高于5.8的内核版本可以直接使用perf命令自动生成

perf script report flamegraph


对于低于5.8内核版本,可以使用工具生成
```bash
# perf script --header > out.stacks
# git clone https://github.com/brendangregg/FlameGraph; cd FlameGraph
# ./stackcollapse-perf.pl < ../out.stacks | ./flamegraph.pl --hash > out.svg
# python3 -m http.server 1235

通过浏览器可打开火焰图
image.png
也可以通过管道方式将perf script —header结果直接重定向给stackcollapse-perf.pl,不使用out.stacks文件

eBPF工具

基于eBPF的分析工具主要有:cpudist、runqlat、runqlen、softiqrs、hardirqs、bpftrace

1. execsnoop


追踪新进程创建事件,通过exec事件实现,用法如下:
BCC版本:

~# execsnoop-bpfcc
PCOMM            PID    PPID   RET ARGS
bash             129653 129624   0 /bin/bash
locale-check     129654 129653   0 /usr/bin/locale-check C.UTF-8
locale           129655 129653   0 /usr/bin/locale
lesspipe         129657 129653   0 /usr/bin/lesspipe
basename         129658 129657   0 /usr/bin/basename /usr/bin/lesspipe
dirname          129660 129659   0 /usr/bin/dirname /usr/bin/lesspipe

常用参数

  • -x:包含调用exec失败的情况
  • -n pattern:只输出命令行复合模式的结果
  • -l pattern:只输出命令行参数复合模式的结果
  • —max args args:输出命令行参数个数的上限,默认为20个

bpftrace版本

~# execsnoop.bt
Attaching 2 probes...
TIME(ms)   PID   ARGS

实现源码:

BEGIN
{
printf("%-10s %-5s %s\n", "TIME(ms)", "PID", "ARGS");
}

tracepoint:syscalls:sys_enter_execve
{
printf("%-10u %-5d ", elapsed / 1000000, pid);
join(args->argv);
}

2. exitsnoop


一个BCC工具,可以打印出进程运行的时长和退出原因.

~# exitsnoop-bpfcc
PCOMM            PID    PPID   TID    AGE(s)  EXIT_CODE
kworker/dying    128579 2      128579 18302.81 0
systemd-udevd    129785 453    129785 3.94    0
systemd-udevd    129786 453    129786 3.94    0

常用参数:

  • -p PID:指定进程
  • -t:输出时间戳信息
  • -x:仅输出进程退出码不为0的情况

3. cpudist


cpudist是一个BCC工,具用于统计进程在cpu上的运行时间,并输出直方图

~# cpudist-bpfcc
Tracing on-CPU time... Hit Ctrl-C to end.
^C
     usecs               : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 0        |                                        |
         4 -> 7          : 1        |                                        |
         8 -> 15         : 3        |*                                       |
        16 -> 31         : 8        |**                                      |
        32 -> 63         : 5        |*                                       |
        64 -> 127        : 3        |*                                       |
       128 -> 255        : 5        |*                                       |
       256 -> 511        : 1        |                                        |
       512 -> 1023       : 2        |                                        |
      1024 -> 2047       : 3        |*                                       |
      2048 -> 4095       : 21       |*******                                 |
      4096 -> 8191       : 113      |****************************************|
      8192 -> 16383      : 6        |**                                      |
     16384 -> 32767      : 21       |*******                                 |
     32768 -> 65535      : 20       |*******                                 |
     65536 -> 131071     : 16       |*****                                   |
    131072 -> 262143     : 5        |*                                       |

常用参数

  • -m: 时间单位用ms,默认us
  • -O: 展示不在cpu执行时间的分布
  • -P: 每个进程打印一个直方图
  • -p PID: 仅仅追踪指定PID

    4. runqlat


    runqlat同时有bcc版本和bpftrace版本,通常用来衡量CPU的满载度,这个工具获取的指标数据是每一个任务等待cpu消耗的时间。
    bcc版本,运行10s,只运行1次

    ~# runqlat-bpfcc 10 1
    Tracing run queue latency... Hit Ctrl-C to end.
    
       usecs               : count     distribution
           0 -> 1          : 3        |                                        |
           2 -> 3          : 15       |***                                     |
           4 -> 7          : 38       |********                                |
           8 -> 15         : 184      |****************************************|
          16 -> 31         : 179      |**************************************  |
          32 -> 63         : 25       |*****                                   |
          64 -> 127        : 3        |                                        |
    

bpftrace版本,按 {key control c} 结束运行

~# runqlat.bt
Attaching 5 probes...
Tracing CPU scheduler... Hit Ctrl-C to end.
^C


@usecs:
[1]                    7 |                                                    |
[2, 4)                32 |@                                                   |
[4, 8)               205 |@@@@@@@@@                                           |
[8, 16)              831 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@              |
[16, 32)            1135 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|
[32, 64)             448 |@@@@@@@@@@@@@@@@@@@@                                |
[64, 128)             55 |@@                                                  |
[128, 256)             5 |                                                    |
[256, 512)             9 |                                                    |
[512, 1K)              6 |                                                    |
[1K, 2K)               1 |                                                    |
[2K, 4K)               1 |                                                    |

因为qunlat是通过调度器唤醒和上下文切换事件进行计算的等待时间,所以对于繁忙的系统可能会带来比较明显的开销,在这种情况下可以尝试使用runqlen工具。

5. runqlen


runqlen工具有BCC和bpftrace版本,其实现原理是对CPU运行队列进行采样,计算还有多少任务等待运行,可以作为一个粗略的分析,命令使用方式如下所示
BCC版本,运行1次,持续10s:

~# runqlen-bpfcc 10 1
Sampling run queue length... Hit Ctrl-C to end.

     runqlen       : count     distribution
        0          : 3859     |****************************************|

bpftrace版本,使用 {key control c} 结束运行:

~# runqlen.bt
Attaching 2 probes...
Sampling run queue length at 99 Hertz... Hit Ctrl-C to end.
^C

@runqlen:
[0, 1)              2498 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@|

6. softiqrs


softirqs 是一个BCC工具,当前还没有bpftrace版本,对于软中断的cpu使用率可以使用mpstat查看,可以参考mpstat命令的输出结果。softirqs与mpstat一个显著的区别是sortirqs可以显示每个软中断的时间消耗。
mpstat 运行1次,持续时间10s

~# softirqs-bpfcc  10 1
Tracing soft irq event time... Hit Ctrl-C to end.

SOFTIRQ          TOTAL_usecs
block                     34
sched                   2436
rcu                     2584
timer                   3861
net_rx                  4305

常用参数:

  • -d: 以直方图方式显示软中断处理时间
  • -T: 打印时间戳
~# softirqs-bpfcc -d -T 10 1
Tracing soft irq event time... Hit Ctrl-C to end.

15:30:27

softirq = net_rx
     usecs               : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 13       |*******                                 |
         4 -> 7          : 40       |***********************                 |
         8 -> 15         : 24       |**************                          |
        16 -> 31         : 28       |****************                        |
        32 -> 63         : 68       |****************************************|
        64 -> 127        : 2        |*                                       |

7. hardirqs


hardirqs也是BCC工具可以用来展示硬中断的处理时间,CPU在处理硬中断的使用率可以参照mpstat命令的结果输出,该命令与sortirqs类似,可以显示每一个中断的消耗时间,使用示例如下:
运行1次,持续10s

~# hardirqs-bpfcc 10 1
Tracing hard irq event time... Hit Ctrl-C to end.

HARDIRQ                    TOTAL_usecs
virtio3-output.0                    20
virtio0-request                     60
virtio3-input.0                   1567

常用参数可参考sortirqs

~# hardirqs-bpfcc -d -T 10 1
Tracing hard irq event time... Hit Ctrl-C to end.

15:35:56

hardirq = b'virtio3-input.0'
     usecs               : count     distribution
         0 -> 1          : 16       |************                            |
         2 -> 3          : 30       |***********************                 |
         4 -> 7          : 23       |******************                      |
         8 -> 15         : 51       |****************************************|
        16 -> 31         : 42       |********************************        |
        32 -> 63         : 2        |*                                       |

hardirq = b'virtio3-output.0'
     usecs               : count     distribution
         0 -> 1          : 0        |                                        |
         2 -> 3          : 0        |                                        |
         4 -> 7          : 0        |                                        |
         8 -> 15         : 1        |****************************************|

8. runqslower


列出运行队列中等待延迟超过阈值的线程名字

~# runqslower-bpfcc
Tracing run queue latency higher than 10000 us
TIME     COMM             PID           LAT(us)

命令语法

runqslower [options] [min_us]

命令行选项

  • -p PID:仅测量给定的进程

默认的阈值为10 000 微秒

9. bpftrace


bpftrace 是eBPF的另外一个前端,通过bpftrace单行命令可以快速实现一些追踪分析的功能,后端是eBPF。
bpftrace最擅长的是使用一行或几行脚本式的命令进行观测,不适合写比较复杂的程序,单行程序示例如下:
单行脚本式程序的语法:

bpftrace -e 'xxxxx'

-e ‘xxxxx’: 引号中是bpftrace程序,bpftrace的其它使用方式可以使用-h参数查看,具体的bpftrace语言语法解析会有专门介绍的文章。
1) 追踪新创建的进程

bpftrace -e 'tracepoint:syscalls:sys_enter_execve { join(args->argv); }'

2) 追踪每个系统调用,并以pid和进程名进行分组统计

~# bpftrace -e 'tracepoint:raw_syscalls:sys_enter { @[pid, comm] = count(); }'
Attaching 1 probe...
^C

@[813, wpa_supplicant]: 2
@[425, journal-offline]: 4
@[425, systemd-journal]: 8
@[784, gmain]: 10
@[1172, rtkit-daemon]: 16
@[102034, sshd]: 18
@[780, gmain]: 20
@[1690, gsd-color]: 24
@[1597, gmain]: 24
@[788, irqbalance]: 24
@[990, apache2]: 60
@[602, multipathd]: 85
@[102582, bpftrace]: 213
@[884, snmpd]: 911

3) 统计系统调用次数:

~# bpftrace -e 'tracepoint:syscalls:sys_enter_* { @[probe] = count(); }'
Attaching 332 probes...
^C

@[tracepoint:syscalls:sys_enter_bind]: 1
[...]
@[tracepoint:syscalls:sys_enter_futex]: 155
@[tracepoint:syscalls:sys_enter_read]: 200
@[tracepoint:syscalls:sys_enter_dup]: 302
@[tracepoint:syscalls:sys_enter_bpf]: 322
@[tracepoint:syscalls:sys_enter_openat]: 404
@[tracepoint:syscalls:sys_enter_dup2]: 602
@[tracepoint:syscalls:sys_enter_ioctl]: 652
@[tracepoint:syscalls:sys_enter_close]: 1115

4) 对系统中的进程以99Hz进程采样,以进程名分组计算被采样的次数:

~# bpftrace -e 'profile:hz:99 { @[comm] = count(); }'
Attaching 1 probe...
^C

@[rcu_sched]: 1
@[swapper/5]: 48
@[swapper/7]: 48
@[swapper/1]: 513
@[swapper/6]: 567
@[swapper/4]: 959
@[swapper/2]: 1017
@[swapper/0]: 1729
@[swapper/3]: 3115

5) 对全系统范围内的用户和内核空间栈以49Hz进程采样:

~# bpftrace -e 'profile:hz:49 { @[kstack, ustack, comm] = count(); }'
Attaching 1 probe...
^C

@[
group_balance_cpu+5
rebalance_domains+616
run_rebalance_domains+130
__do_softirq+225
irq_exit+174
smp_apic_timer_interrupt+123
apic_timer_interrupt+15
native_safe_halt+14
arch_cpu_idle+21
default_idle_call+35
do_idle+507
cpu_startup_entry+32
start_secondary+359
secondary_startup_64+164
, , swapper/3]: 1

6) 对“sshd”进程用户空间栈以49Hz进行采样:

~# bpftrace -e 'profile:hz:49 /comm == "sshd"/ { @[ustack] = count(); }'
Attaching 1 probe...
^C

7) 计算所有以”vfs_”开头的内核函数的调用,以函数名分别统计:

~# bpftrace -e 'kprobe:vfs_* { @[func] = count(); }'
Attaching 65 probes...
^C

@[vfs_fsync_range]: 2
@[vfs_statfs]: 6
@[vfs_writev]: 6
@[vfs_readlink]: 10
@[vfs_write]: 37
@[vfs_statx]: 54
@[vfs_statx_fd]: 584
@[vfs_getattr_nosec]: 683
@[vfs_getattr]: 684
@[vfs_open]: 759
@[vfs_read]: 991

8) 追踪所有使用pthread_create()创建的线程

~# bpftrace -e 'u:/lib/x86_64-linux-gnu/libpthread-2.31.so:pthread_create { printf("%s by %s (%d)\n", probe, comm, pid); }'
Attaching 1 probe...

9) 列出所有跟sched相关的追踪点(tracepoint)

bpftrace -l 'tracepoint:sched:*'