perf record 不能直接抓到丢包动作,需配合 skb:kfree_skb 等 tracepoint 定位丢包环节;bpftrace 可动态过滤丢包条件;ping 与业务包路径分离,RPS 不均是常见原因;perf 与 bpftrace 联动须时间对齐,避免错失瞬时丢包。

perf record 能抓到丢包时的内核路径吗
不能直接抓到“丢包”这个动作本身,因为丢包往往发生在没有完整 skb 构建、或被 dev_kfree_skb / kfree_skb 立即释放的路径上,perf record 默认采样的是有栈回溯的用户态或内核函数入口,而很多丢包点(比如 drop_monitor 未启用时的 consume_skb、netif_receive_skb_core 中的 early drop)不触发常规采样点。
真正有用的是配合 perf record -e 'skb:consume_skb' -e 'skb:kfree_skb' -e 'net:net_dev_queue' -e 'net:netif_receive_skb' 这类 tracepoint 事件,它们在 skb 生命周期关键节点打点,能定位到哪个环节释放了本该转发的包。
- 必须用 root 权限运行,否则 tracepoint 不可见
-
skb:kfree_skb的location字段能指出丢包位置,比如net/ipv4/ip_input.c:230表示 IP 层校验失败丢弃 - 避免用
-g(调用图),它会大幅降低采样精度,丢包分析重在事件频次和上下文,不在深度栈
bpftrace 怎么定位随机丢包的触发条件
靠静态打点不够,得用 bpftrace 动态过滤——重点不是“看到丢包”,而是“在丢包前一刻,哪些字段异常”。比如对 skb:kfree_skb 加条件:只打印那些 dst_ip 是目标服务器、且 reason == SKB_DROP_REASON_NOT_SPECIFIED 或 SKB_DROP_REASON_IP_INHDR 的样本。
bpftrace -e '
tracepoint:skb:kfree_skb /args->reason == 17 || args->reason == 5/
{
printf("DROP @ %s:%d, reason=%d, len=%d, proto=%d\n",
str(args->location), args->location_line,
args->reason, args->len, args->protocol);
}'
其中 reason == 17 是 SKB_DROP_REASON_IP_INHDR(IP 头错误),reason == 5 是 SKB_DROP_REASON_NOT_SPECIFIED(泛用型丢弃,常出现在驱动层)。注意:args->location 是地址,需用 str() 解析为符号名,否则输出一串数字。
- 别依赖
pid或comm过滤,丢包多发生在软中断上下文,comm常是ksoftirqd/0 - 加
count()聚合后用printf打印,避免高频输出冲垮终端 - 如果
location全是0x...,说明内核调试符号没加载,需安装kernel-debuginfo包
为什么 ping 延迟正常但业务包大量丢失
因为 ping(ICMP Echo)走的是 icmp_rcv → ping_lookup 路径,而业务流量(如 TCP SYN)走 tcp_v4_rcv → tcp_v4_do_rcv,两者在连接状态检查、early_demux、RPS 队列分发等环节完全独立。一个常见原因是 RPS 配置不均导致某 CPU 队列溢出,而 ICMP 包被 hash 到空闲队列,TCP 包却被持续打到已满队列,触发 netdev_max_backlog 丢弃。
- 查
/proc/net/snmp中Udp:和Tcp:段的InCsumErrors、ListenOverflows、EstabResets,比看Drop:更准 - 用
cat /sys/class/net/eth0/queues/rx-*/rps_cpus看 RPS 是否只启用了部分 CPU,再用cat /proc/interrupts | grep eth0看硬中断是否集中 -
perf record -e 'syscalls:sys_enter_sendto' -e 'syscalls:sys_exit_sendto'可确认用户态是否真发出了包,排除应用层写入阻塞
perf + bpftrace 联动分析的关键陷阱
最常踩的坑是时间窗口不同步:perf record 默认按时间采样,bpftrace 是事件驱动,两个工具各自运行时,看似同时抓包,实则事件可能错开几毫秒——丢包是瞬时行为,错过就无法关联。
- 必须用
perf script导出带时间戳的原始事件,再用 bpftrace 输出也加strftime("%H:%M:%S", nsecs),最后用脚本按毫秒级对齐 - 别信
timestamp字段的绝对值,不同 CPU 的 TSC 可能漂移,优先用nsecs(单调递增纳秒计数)做排序 - 如果发现 bpftrace 抓到大量
SKB_DROP_REASON_SOCKET_FILTER,先检查有没有其他 eBPF 程序(如 Cilium、Falco)在 filter 链上误删包,而不是立刻怀疑内核
丢包点越靠近硬件(如网卡驱动 igb_clean_rx_irq 中的 rx_desc->status & E1000_RXD_STAT_DD 未置位),越难用高层工具捕获;这时候得切到 ethtool -S 看 rx_discards 或驱动私有统计,再决定是否要抓 PCIe TLP 层日志。









