# 进程测试
# perf
:一个监控工具,能统计进程内各函数占用 CPU 时长的比例。
# top
perf top # 监控当前主机执行的 CPU 事件(属于离散采样)
-a # 监控 CPU 的所有核。默认只监控第 0 个核
-c <int> # 监控 CPU 的第几个核
-p <pid> # 不监控 CPU 中所有进程的 event ,而是监控一个指定进程的 event
-e <str> # 采样的 event 类型,默认为 cpu-clock 。执行 perf list 查看可选的类型
-F <int> # 采样频率,每秒采样多少次 CPU 正在执行的 event 。默认为 4000 ,降低该值可以减少采样的开销,但可能遗漏 event
-g # 监控 event 的调用链
-n # 显示每个 event 采样的 samples 数量
-v # 显示详细内容,比如 Shared Object 的文件路径、Symbol 的内存地址
例:
[root@CentOS ~]# perf top -n Samples: 167K of event 'cpu-clock', 4000 Hz, Event count (approx.): 29827031201 lost: 0/0 drop: 0/0 Overhead Samples Shared Object Symbol 3.53% 4206 [kernel] [k] _raw_spin_unlock_irqrestore 1.99% 2369 [kernel] [k] __do_softirq 1.85% 2206 libcoreclr.so [.] 0x000000000030f332 0.70% 836 perf [.] rb_next ...
- 默认会在一个窗口中显示监控结果,可按方向键上下选择 event ,按回车键进入子菜单。
- 第一行表示累计采样了 167K 个 samples ,event 类型为 cpu-clock ,采样频率为 4000 Hz 。Event count 表示这段时间产生的 event 总数,只有部分被采样了。
- 从第三行开始,每行表示一种名称的 event 。各列的含义如下:
- Overhead :该 event 被采样的数量在全部样本中的比例,可用于估算该 event 占用 CPU 时长的比例。
- Samples :该 event 被采样的数量。
- Shared Object :产生该 event 的对象名称,通常是内核、动态链接库名、进程名。
- Symbol :该 event 的名称,通常是正在执行的函数名称,或者是内存地址。前缀
[.]
表示其内存地址位于用户空间,[k]
表示位于内核空间。
例:
perf top -F 1000 -e block:block_rq_insert -n -a # 监控块设备的 request queue 中新增的操作请求,横轴表示请求数 perf top -F 1000 -e context-switches -gn -p <pid> # 监控进程的 context-switches 次数
# record
perf record [command] # 执行一条命令,并监控其中各个 event 的 CPU 使用率
-o <file> # --output ,将监控数据保存到一个文件中,默认为 $PWD/perf.data
-- sleep 10 # 最多监控 10 秒。默认会一直监控,直到目标进程停止
perf report # 读取 $PWD/perf.data 文件,显示 perf record 记录的信息
- 例:
[root@CentOS ~]# perf record -g -a -- sleep 10 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 1.940 MB perf.data (16068 samples) ] [root@CentOS ~]# perf report -n Samples: 16K of event 'cpu-clock', Event count (approx.): 4017000000 Children Self Samples Command Shared Object Symbol + 99.58% 0.00% 0 swapper [kernel.kallsyms] [k] start_cpu + 99.38% 99.38% 15969 swapper [kernel.kallsyms] [k] native_safe_halt + 3.46% 0.00% 0 swapper [kernel.kallsyms] [k] x86_64_start_kernel 0.24% 0.00% 0 rcu_sched [kernel.kallsyms] [k] kthread ...
- 如果某行的左侧显示 + ,则可按 Enter 键,显示子进程。
- 每行表示一种名称的 event ,通常是函数名。
- Self 列表示该函数被采样的数量在全部样本中的比例。
- Children 列表示子孙函数被采样的数量,在该函数样本中的比例。
# stat
perf stat <command> # 执行一条命令,并统计各种 event 的数量。等执行完命令,才显示统计结果
-I 1000 # 每隔 1000ms 显示一次统计结果
perf stat -I 1000 -a # 监控 CPU 所有核
- 例:各字段的含义:
[root@CentOS ~]# perf stat uname Linux Performance counter stats for 'uname': 0.89 msec task-clock # 0.393 CPUs utilized 1 context-switches # 0.001 M/sec 0 cpu-migrations # 0.000 K/sec 165 page-faults # 0.186 M/sec <not supported> cycles <not supported> instructions <not supported> branches <not supported> branch-misses 0.002252426 seconds time elapsed 0.000000000 seconds user 0.001507000 seconds sys
task-clock (msec) # 该命令使用 CPU 的毫秒数。备注的 CPUs utilized 表示使用了 CPU 的几个核 context-switches # 上下文切换的次数 cache-misses # CPU 在 cache 中找不到需要读取的数据的次数 cpu-migrations # 进程被迁移到其它 CPU 上运行的次数 page-faults # CPU 抛出 page fault 异常的次数 cycles # CPU 的时钟周期数。一条指令的执行可能需要多个 cycles instructions # 指令数。Instructions/Cycles 的比值越大越好 seconds time elapsed # 执行该命令消耗的秒数
# 火焰图
perf report 命令会显示一个很长的列表,需要翻页。为了更高效率地分析 CPU 开销,Brendan Gregg 于 2011 年发明了火焰图(Flame Graph)。
火焰图是根据 perf record 记录的数据,显示一张 svg 格式的图像。
- 图像采用暖色调,形状带有很多尖刺,看起来像火焰。
- 横轴表示 samples 数量。
- 每格通常显示一个函数名。如果是匿名函数,则显示内存地址。
- 一个函数占据横轴宽,说明抽样的 samples 数量多,即占用 CPU 时长的比例大,可能是性能瓶颈。
- 纵轴表示调用链。
- 火焰每升高一层,表示调用一层子函数。火焰越高,表示调用链越深。
- 火焰的顶层是调用链的末端,即当前正在执行的函数。火焰的底层是调用链的源头。
- 火焰的同一层,相同的函数会合并,不同的函数会按字母顺序从左到右排序。
- svg 图像可以互动。
- 鼠标点击图中某个函数,会放大图像,隐藏不在该函数调用链上的其它函数。
- 按 Ctrl+F 可进行搜索,匹配搜索条件的函数会显示成紫色。
例:生成火焰图
perf record -F 100 -g -p <pid> -- sleep 10 perf script -i perf.data > perf.script git clone https://github.com/brendangregg/FlameGraph.git FlameGraph/stackcollapse-perf.pl perf.script > perf.folded FlameGraph/flamegraph.pl perf.folded > perf.svg
火焰图最初用于分析 CPU 开销,后来出现了其它类型的火焰图:
- off-cpu 火焰图:横轴表示各函数阻塞(即未被 CPU 执行)的时长比例,适合分析 IO 密集型进程、分布式锁。
- memory 火焰图:横轴表示各函数申请分配的内存量,适合分析内存密集型进程。
# strace
strace <command> # 执行一条命令,并打印它调用的所有系统接口
-c # 统计每个系统调用的次数、用时
-e read,write,open # 只跟踪指定名称的系统调用
-e trace=file # 只跟踪指定类型的系统调用,比如 all、file、process、network、memory、signal
-tt # 在每行的开头显示时间
-T # 在每行的末尾显示这次系统调用的耗时,比如<0.000007>
strace -p <pid> # 跟踪一个进程
-e trace=signal # 只跟踪该进程收到的信号
- 例:
[root@CentOS ~]# strace -e trace=file echo hello execve("/usr/bin/echo", ["echo", "hello"], [/* 22 vars */]) = 0 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3 hello +++ exited with 0 +++
# time
time <command> # 执行一条命令,并统计其占用的 CPU 时长
- 例:
[root@CentOS ~]# time sleep 1 real 0m1.001s user 0m0.001s sys 0m0.000s
- real time :该命令的持续时长。持续期间,不一定占用 CPU 。
- user time :该命令占用的用户态 CPU 时长。
- sys time :该命令占用的内核态 CPU 时长。
- 该命令可能创建多个进程,占用多个 CPU 核心,可计算出
平均每秒占用 CPU 核数 = ( user + sys ) / real
。
# timeout
timeout <duration> <command> # 以子进程的形式执行一条命令,并限制其运行时长,超时之后则终止命令
-s <signal> # --signal ,超时时发送的信号,默认为 SIGTERM
-k <duration> # --kill-after ,发送 signal 信号之后,最多等待 duration 时长,如果进程依然运行,则发送 SIGKILL 信号
--preserve-status # 如果超时,timeout 命令的退出码默认为 124 。启用该参数,则会让退出码与目标命令一致
- duration 是浮点数,单位可以是 s、m、h、d ,默认为 s 即秒。
- 如果设置为 0 ,则不限制。
- 如果目标命令在 duration 之前结束,则 timeout 命令的退出码与该命令一致。
- 例:
timeout 3.5s ping localhost
# watch
watch <command> # 周期性地执行一条命令,显示其 stdout 和 stderr
-d # 如果输出与上一次执行不同,则将差异部分高亮显示
-e # 如果命令的返回码不为 0 ,则停止执行
-n 2 # 每隔几秒执行一次(默认是 2 秒)
-t # 不显示 watch 窗口的标题
- 例:
watch -d -n 1 ping localhost