sudo apt updatesudo apt install bpfcc-tools
sudo apt updatesudo apt install -y libdw-dev systemtap-sdt-dev libslang2-dev libperl-dev libcap-dev libbabeltrace-dev libpfm4-dev libtraceevent-dev# 进入内核源码中的perf目录,编译perf工具cd linux-6.8.2/tools/perfmake -j 10sudo make installsudo cp perf /usr/bin/perf
sudo stackcount-bpfcc tcp_sendmsg -D 10
含义:用 eBPF kprobe 钩住内核函数 tcp_sendmsg,统计 10 秒钟内,都是哪些调用路径走到了 tcp_sendmsg,统计每条调用栈出现的次数。
如果看到一堆b'[unknown]'的堆栈,是因为用户栈符号解析失败导致的,这一般是因为用户程序编译时没有保留帧指针-fomit‑frame‑pointer(这也是gcc默认编译选项),省略 rbp 栈基址寄存器,eBPF 无法做栈回溯。
可以:
1、不看用户栈,只看内核栈(设置成-K表示内核栈,设置为-U表示用户栈)
sudo stackcount-bpfcc tcp_sendmsg -K -D 10
2、bpftrace 使用 dwarf 基于调试信息做用户栈(不需要帧指针)
stackcount‑bpfcc 底层用 bpf_get_stackid(),依赖帧指针;bpftrace 可以开启 dwarf 栈展开,代价 CPU 开销更大。
sudo bpftrace -e 'kprobe:tcp_sendmsg { @[kstack + ustack(dwarf)] = count(); } interval:s:10 { exit(); }'
3、虽然拿不到完整用户栈,但可以看到哪个进程触发 tcp_sendmsg:
sudo bpftrace -e 'kprobe:tcp_sendmsg { printf("pid=%-6d comm=%s\n", pid, comm); @[kstack] = count(); } interval:s:10 { exit(); }'
统计调用__kmalloc的次数,并打印内核栈
sudo bpftrace -e 'kprobe:__kmalloc{$size = arg1;printf("__kmalloc size=%u\n", $size);@bytes[kstack] = sum($size);@cnt[kstack] = count();}interval:s:10 {print(@cnt);print(@bytes);exit();}'
统计调用__kmalloc的次数,并打印用户栈
sudo bpftrace -e 'kprobe:__kmalloc{$size = arg1;printf("__kmalloc size=%u\n", $size);@bytes[ustack] = sum($size);@cnt[ustack] = count();}interval:s:10 {print(@cnt);print(@bytes);exit();}'
先获取要追踪的符号在动态库的偏移:
如要追踪内存分配函数malloc(符号在/usr/lib/x86_64-linux-gnu/libc.so.6):
objdump -T `realpath /usr/lib/x86_64-linux-gnu/libc.so.6` | awk -v s="malloc" '$7==s {print "0x"$1}';
#!/bin/bashset -eTRACEFS=/sys/kernel/tracingLIBC=$(realpath /usr/lib/x86_64-linux-gnu/libc.so.6)# 这个是偏移OFFSET=0x000a50a0echo "LIBC=$LIBC"echo "OFFSET=$OFFSET"# ==========关键清理顺序==========# 1. 关闭全部已经enable的事件组for e in $TRACEFS/events/mygrp/*/enable; do[ -f "$e" ] && echo 0 > "$e"done# 2. 关闭全局traceecho 0 > $TRACEFS/tracing_on# 3. 清空uprobe_events,释放资源echo > $TRACEFS/uprobe_eventssleep 0.1# ==============================# 拼装探针命令cmd1="p:mygrp/malloc_entry ${LIBC}:${OFFSET} %di"cmd2="r:mygrp/malloc_ret ${LIBC}:${OFFSET} %ax"echo "write cmd1: $cmd1"echo "$cmd1" > $TRACEFS/uprobe_eventsecho "write cmd2: $cmd2"echo "$cmd2" >> $TRACEFS/uprobe_eventsecho -e "\n==== uprobe_events content ===="cat $TRACEFS/uprobe_eventsif [ -z "$(cat $TRACEFS/uprobe_events)" ];thenecho "ERROR: uprobe create failed! check dmesg"dmesg -T | grep -i uprobeexit 1fi# enable 事件echo 1 > $TRACEFS/events/mygrp/malloc_entry/enableecho 1 > $TRACEFS/events/mygrp/malloc_ret/enableecho 1 > $TRACEFS/tracing_onecho -e "\n正在采集,10秒..."sleep 10cat $TRACEFS/trace# 退出前再次完整清理for e in $TRACEFS/events/mygrp/*/enable; do[ -f "$e" ] && echo 0 > "$e"doneecho 0 > $TRACEFS/tracing_onecho > $TRACEFS/uprobe_eventsecho "cleanup done"