负载分析和排查七

明白了 👍
你的需求是 全系统范围统计 syscall 的耗时占比,而不仅仅是单个进程或 perf trace 默认输出的那种“分进程 + syscall”的 summary。

我给你梳理一下思路和可行方案:


1. 为什么你现在的 perf trace -a --summary 不行

  • perf trace 默认 summary 是 按进程分组,然后在每个进程下列 syscall。

  • 所以你看到的输出是:

    • containerd-shim 的 syscall summary

    • runc 的 syscall summary

    • …每个进程单独统计。

  • 你后面 awk 只是取了单个 summary 区块的数值,并没有把所有进程的 syscall 聚合。


2. 全系统聚合统计 syscall 耗时占比的几种方法

方法一:perf stat -e syscalls:sys_enter_*

sudo perf stat -a -e 'syscalls:sys_enter_*' sleep 10
  • 它能统计所有 syscall 的调用次数(event count),全系统范围。

  • 但缺点:只统计调用次数,没法直接得出 syscall 耗时。


方法二:perf record + perf script 聚合

  1. 记录一段时间的 syscall:

    sudo perf record -a -e syscalls:sys_enter_* -e syscalls:sys_exit_* sleep 10
    

    sys_enter + sys_exit 配对,才能得到耗时。

  2. perf script 导出原始事件:

    sudo perf script > out.perf
    
  3. 用 bcc 工具 / 自己写脚本做聚合:

    • 把 enter/exit 配对求耗时

    • 按 syscall 汇总调用次数 & 总耗时

    • 最后算百分比。


方法三:直接用 bcc/eBPF(推荐)

比如 bcc 自带的 syscount / syslatency

sudo /usr/share/bcc/tools/syscount -p      # 统计所有进程所有 syscalls 次数
sudo /usr/share/bcc/tools/syslatency       # 统计每个 syscall 的耗时分布

要做 耗时占比 可以直接改 syscount.py,让它统计:

  • 调用次数

  • 总耗时

  • 占比 = 总耗时 / 所有 syscall 耗时

这样就能得到类似:

SYSCALL         CALLS      TOTAL(ms)   AVG(us)   PERCENT
futex           12345      1256.3      101.7     34.5%
read            6789       980.1       144.4     26.9%
write           4567       876.2       191.8     23.9%
...

3. 总结对比

方法是否全系统是否有耗时是否需要额外工具
perf stat sys_enter
perf record + script 需要自己写脚本聚合
bcc syscount/syslatency 需要安装 bcc/eBPF

👉 所以结论是:
如果你只要调用次数,用 perf stat -a -e syscalls:sys_enter_* 就够了。
如果你要调用耗时占比,最稳妥的方法是 用 bcc 的 syscount/syslatency 脚本,直接能拿到全系统聚合数据。


要不要我帮你写一个 基于 bcc 的 Python 脚本,直接输出“全系统 syscalls 总耗时占比表”?

#!/usr/bin/env python3
import sys
import re
from collections import defaultdict

if len(sys.argv) != 2:
    print(f"Usage: {sys.argv[0]} out.perf")
    sys.exit(1)

filename = sys.argv[1]

# syscall 统计
calls = defaultdict(int)
time_us = defaultdict(float)
# 存储每个 pid 的 enter 时间戳
stack = dict()  # key = (pid, syscall), value = timestamp

# 匹配 enter/exit
enter_re = re.compile(r'\s*(\S+)\s+(\d+)\s+\[\d+\]\s+([\d.]+):\s+syscalls:sys_enter_(\w+):')
exit_re  = re.compile(r'\s*(\S+)\s+(\d+)\s+\[\d+\]\s+([\d.]+):\s+syscalls:sys_exit_(\w+):')

with open(filename) as f:
    for line in f:
        m_enter = enter_re.match(line)
        if m_enter:
            comm, pid, ts, syscall = m_enter.groups()
            stack[(pid, syscall)] = float(ts) * 1e6  # 秒->微秒
            continue
        m_exit = exit_re.match(line)
        if m_exit:
            comm, pid, ts, syscall = m_exit.groups()
            key = (pid, syscall)
            if key in stack:
                elapsed = float(ts)*1e6 - stack.pop(key)
                calls[syscall] += 1
                time_us[syscall] += elapsed

total = sum(time_us.values())
if total == 0:
    print("No syscall data")
    sys.exit(0)

print(f"{'syscall':20} {'calls':>8} {'time(us)':>12} {'%':>6}")
for s, t in sorted(time_us.items(), key=lambda x: x[1], reverse=True):
    print(f"{s:20} {calls[s]:8d} {t:12.3f} {t/total*100:6.2f}%")

  

posted on 2025-09-24 10:49  吃草的青蛙  阅读(20)  评论(0)    收藏  举报

导航