Usage: build/ebpf-sysdiag [OPTIONS]
--scenario LIST cpu,io,memory,lock,syscall,all
--duration SEC collection duration, default 60
--config PATH minimal key:value config file
--output FORMAT json,yaml,markdown
--output-file PATH write report to file
--pid PID target process id
--comm NAME target command name
--sample-rate N keep one event per N events
--sample-interval-ms N default 100
--max-rss-mb N default 200
--max-cpu-percent N default 5
--dry-run validate config only
--list-scenarios print supported scenarios
cd ~/work/ebpf-sysdiag
git pull
make clean
make
build/ebpf-sysdiag --dry-run --config config/default.yaml
sudo build/ebpf-sysdiag --scenario all --duration 3 --output json
通过标准:
make clean && make 无编译错误。
--dry-run 能打印 dry-run ok。
--scenario all 能成功运行到结束。
输出是合法 JSON。
results 可以为空;空结果只表示短窗口内没有事件超过阈值,不代表加载失败。
如果这一步失败,先不要跑压力测试,应优先检查:
tools/check_btf.sh
tools/detect_kernel_features.sh
make check-env
ebpf-sysdiag
ebpf-sysdiag是一个基于 libbpf CO-RE 的轻量级 Linux 系统异常观测与根因定位工具。项目目标是在 openKylin 以及其他 BTF 可用的 Linux 内核上,以尽量低的运行时开销采集关键异常证据,并输出可复核的结构化诊断报告。当前版本覆盖 5 类典型系统异常:
项目不是一个全量 APM agent,也不是一个永久驻留的 metrics exporter。它更适合在异常窗口内按需运行,用最小探针集合保留证据,辅助 SRE、系统工程师、内核开发者和 eBPF 诊断赛题开发者快速判断问题方向。
当前状态
当前代码已经在 zh21 机器上完成一次真实环境验证:
zh21~/work/ebpf-sysdiagLinux 7.0.0-rc3x86_64/sys/kernel/btf/vmlinux可读/usr/sbin/bpftool是 Ubuntu 包装器;项目会自动解析到可用的真实二进制,例如/usr/lib/linux-tools-6.8.0-111/bpftool验证命令:
验证结果:
make clean && make编译成功。BPFTOOL=/path/to/bpftool,Makefile 会自动寻找可用 bpftool。--scenario all能正常加载 eBPF 程序并输出 JSON。cpu、io、memory、lock、syscall五个单独场景均能独立运行并以0退出。设计目标
低侵入
采集侧只在必要位置挂载 tracepoint/raw tracepoint,默认只在 BPF 侧完成过滤、采样和阈值判断后才向用户态提交事件。这样可以避免高频路径把大量无效事件复制到用户态。
可移植
默认构建使用 libbpf skeleton 和 CO-RE。只要目标内核提供
/sys/kernel/btf/vmlinux,BPF 程序就可以基于 BTF 做重定位。项目也保留NO_CORE=1兼容路径,供无 BTF 环境做功能验证。可解释
每条诊断结论都包含:
诊断结果不是只给一个结论字符串,而是保留指标值、阈值和上下文,方便人工复核。
资源自限
默认运行时约束:
100 ms200 MB5%8 MBring buffer--sample-rate降采样分层解耦
采集层、加载层、事件解析层、分析层、输出层分离。每个场景的 BPF object 独立编译、独立加载,便于定位 verifier、attach 或 tracepoint 兼容问题。
总体架构
运行路径如下:
模块分层:
核心目录:
赛题要求与实现文件对应表
这一节用于快速定位“赛题要求”在项目中的实现位置。后续逐项优化时,建议先改 BPF 采集,再改公共事件结构,再改用户态聚合和 detector,最后补配置、测试和文档。
异常场景实现入口
cpu_sched_latency/cpu_intensive_compute/cpu_busy_loop/cpu_thread_contentionbpf/cpu_sched.bpf.c、src/analysis/detectors/cpu_detector.c、tests/reproduce/cpu_runqueue_delay.shblock_io_latencybpf/block_io.bpf.c、src/analysis/detectors/io_detector.c、tests/reproduce/io_latency_fio.shmemory_jitter_oom_riskbpf/memory.bpf.c、src/analysis/detectors/memory_detector.c、tests/reproduce/memory_pressure.shlock_futex_contentionbpf/lock_futex.bpf.c、src/analysis/detectors/lock_detector.c、tests/reproduce/futex_contention.csyscall_latencybpf/syscall.bpf.c、src/analysis/detectors/syscall_detector.c、tests/reproduce/syscall_storm.sh公共链路实现入口
include/diag_types.hdiag_result。新增赛题输出字段或 detector 阈值时必须同步这里。src/loader.c*_bpf__open/load/attach都在这里。新增场景、降级或按场景禁用逻辑时修改这里。src/event_parser.cstruct diag_event。新增事件类型必须补 switch 分支。src/analysis/engine.cdiag_window_snapshot。新增指标、计数、P99 近似或目标选择逻辑时修改这里。src/analysis/detectors/*.croot_cause和建议结论都在对应 detector 中。src/output/json_output.c、src/output/yaml_output.c、src/output/markdown_output.csrc/config.c、config/*.yaml--scenario、--pid、--comm、采样率和各类阈值配置入口。tests/integration/*、tests/reproduce/*按场景修改时的推荐顺序
bpf/<scenario>.bpf.c,确认采集点、过滤和阈值触发逻辑。include/diag_types.h。bpf/nocore_types.bpf.h。src/event_parser.c,保证 ringbuf 事件能解析。src/analysis/engine.c和src/analysis/detectors/detector.h,补窗口聚合字段。src/analysis/detectors/<scenario>_detector.c,实现根因判断、证据链和建议。src/config.c、config/<scenario>.yaml、config/default.yaml,同步阈值。src/output/*。tests/reproduce/和tests/integration/,保证能复现并自动校验。docs/diagnosis_rules.md和TEST.md,保证文档和实际行为一致。五类诊断场景
CPU 异常占用与调度延迟
目标:定位 CPU 密集计算、busy loop、线程过量竞争以及线程已经被唤醒但长时间没有被调度运行的问题。常见原因包括用户态计算热点、线程池过度并发、CPU 饱和、runqueue 过长、cgroup CPU quota 限制、CPU 亲和性配置异常等。
采集点:
tracepoint/sched/sched_wakeuptracepoint/sched/sched_switch核心逻辑:
sched_wakeup记录目标 pid 的唤醒时间戳。sched_switch看到该 pid 真正切入 CPU 时计算now - wake_ts。sched_switch重建任务 on-CPU runtime、上下文切换次数和 voluntary/involuntary switch。cpu_intensive_compute、cpu_busy_loop、cpu_thread_contention和cpu_sched_latency。输出证据:
cpu_runtime_percentcpu_runtime_total_uscpu_runtime_max_uscontext_switch_ratecontext_switch_countinvoluntary_context_switchesvoluntary_context_switchesrunqueue_delay_max_usrunqueue_delay_avg_usrunqueue_delay_event_countaffected_threadsaffected_pid默认阈值:
当前限制:
wake_cpu暂未完整归因,仅保留目标 CPU 和切换上下文信息。块设备 I/O 延迟
目标:定位块设备请求从提交到完成之间的高延迟,常见原因包括设备队列拥塞、磁盘/云盘抖动、I/O throttling、文件系统写回压力等。
采集点:
tracepoint/block/block_rq_issuetracepoint/block/block_rq_complete核心逻辑:
block_rq_issue记录请求时间戳、pid/tid、comm。dev + sector构造请求 key。block_rq_complete计算 issue-to-complete 延迟。io_block_latency_us_p99后输出DIAG_EVT_IO_LAT。major:minor,把提交请求时的 pid/comm 作为辅助证据,避免把写回或完成路径误判为业务根因。block_latency_p99_us近似值、慢 I/O 速率和队列深度估算。fio等真实压力源。输出证据:
block_latency_max_usblock_latency_p99_usblock_latency_avg_usslow_io_countslow_io_rate_per_secqueue_depth_estimateobserved_bytesdevice_majordevice_minorissuer_pid默认阈值:
当前限制:
block_latency_p99_us是基于已上报慢请求的直方图近似,不等价于 fio 对所有完成 I/O 统计出的精确 P99。queue_depth_estimate基于慢请求总耗时和窗口时长估算,用于判断队列拥堵趋势,不替代块层真实 inflight 计数。内存抖动与 OOM 风险
目标:捕获用户态缺页、匿名页增长、可用内存下降、kswapd/direct reclaim 等信号,用于识别内存抖动、持续内存增长和 OOM 风险。该场景按赛题样例重点关注“高内存占用 + 频繁缺页 + 可用内存下降/回收活跃”的组合证据,而不是单独看到 page fault 就报异常。
采集点:
tracepoint/kmem/mm_page_alloctracepoint/exceptions/page_fault_usertracepoint/vmscan/mm_vmscan_kswapd_waketracepoint/vmscan/mm_vmscan_direct_reclaim_begin/proc/meminfo和/proc/<pid>/statm核心逻辑:
mm_page_alloc记录页分配 order、gfp flags 和估算字节数。page_fault_user记录用户态缺页地址和错误码。vmscantracepoint 记录 kswapd 唤醒和 direct reclaim 起点。MemAvailable占比,并扫描/proc选取当前 RSS 最高的候选进程,避免全局 page fault 被最后一个无关进程覆盖。comm再次聚合同名进程 RSS,用于覆盖stress-ng --vm 4这类多 worker 场景。page_fault_rate_per_sec,检测器要求至少出现大额分配、高 RSS、进程组 RSS、回收活动或低可用内存之一;page fault 只作为辅助证据。输出证据:
page_fault_eventspage_fault_rate_per_secallocated_bytesprocess_rss_bytesprocess_group_rss_bytesprocess_group_countsystem_mem_available_percentreclaim_eventsaffected_pid默认阈值:
当前限制:
memory_alloc_rate在 BPF 侧用于过滤较大分配,不等同于完整进程分配速率。futex/锁竞争
目标:用 futex syscall 的等待时间识别用户态锁竞争表象。典型场景包括 pthread mutex 临界区过长、线程池同步热点、条件变量等待异常等。
采集点:
tracepoint/syscalls/sys_enter_futextracepoint/syscalls/sys_exit_futex核心逻辑:
sys_enter_futex记录 pid/tid、futex 地址、op、开始时间。sys_exit_futex计算 futex syscall 持续时间。lock_futex_wait_us_p99后输出DIAG_EVT_FUTEX_WAIT。输出证据:
futex_wait_max_usfutex_wait_countaffected_pid默认阈值:
当前限制:
pthread_mutex_lock/pthread_mutex_unlockuprobe 或业务锁埋点。慢系统调用
目标:定位进入内核后耗时过长的系统调用,常见原因包括 I/O 阻塞、锁等待、网络等待、文件系统慢路径、资源限制等。
采集点:
tracepoint/raw_syscalls/sys_entertracepoint/raw_syscalls/sys_exit核心逻辑:
syscall_latency_us_p99后输出DIAG_EVT_SYSCALL_LAT。pid + syscall id计数,高频调用每达到syscall_rate发出一次聚合事件。输出证据:
syscall_latency_max_ussyscall_latency_avg_ussyscall_event_countsyscall_idaffected_pid默认阈值:
当前限制:
--sample-rate使用。依赖要求
内核要求
默认 CO-RE 构建要求:
/sys/kernel/btf/vmlinux可读。建议内核配置至少包含:
不同发行版配置名称可能略有差异。可以用项目内脚本先做检查:
用户态依赖
需要:
clangllvmmakegcc或兼容 C 编译器pkg-configbpftoollibbpf开发包libelf开发包zlib开发包openKylin/Ubuntu/Debian 类系统可尝试:
该脚本实际安装:
权限要求
加载 BPF 程序通常需要:
CAP_BPF、CAP_PERFMON、CAP_SYS_ADMIN等等价权限组合,具体取决于内核版本和发行版策略。最直接的运行方式是:
bpftool 自动发现
一些 Ubuntu/openKylin 环境中的
/usr/sbin/bpftool不是实际二进制,而是根据当前内核版本寻找linux-tools-$(uname -r)的包装器。自编译内核或者 rc 内核上,经常会出现:为避免这种情况,项目提供:
默认 Makefile 会调用它寻找可用 bpftool,顺序包括:
BPFTOOLcommand -v bpftool/usr/lib/linux-tools-$(uname -r)/bpftool/usr/lib/linux-tools-*/bpftool/usr/local/sbin/bpftool/usr/local/bin/bpftool/usr/sbin/bpftool/usr/bin/bpftool如果自动发现失败,可以手工指定:
构建
默认 CO-RE 构建
构建产物:
说明:
bpf/vmlinux/vmlinux.h由/sys/kernel/btf/vmlinux生成。make clean会删除build/和生成的bpf/vmlinux/vmlinux.h。bpf/vmlinux/vmlinux.h是本机生成物,已经被.gitignore忽略。无 BTF 兼容构建
如果目标机器没有
/sys/kernel/btf/vmlinux,可使用:NO_CORE=1会跳过 CO-RE vmlinux 生成,使用项目内手写 tracepoint ctx 结构。这个模式适合在已知兼容的机器上做功能验证,不建议作为生产默认路径。需要注意:
NO_CORE=1依赖 tracepoint format 布局和当前内核匹配。常见构建问题
找不到
/sys/kernel/btf/vmlinux现象:
处理:
CONFIG_DEBUG_INFO_BTF。make NO_CORE=1。bpftool 包装器失败
现象:
处理:
/usr/lib/linux-tools-*/bpftool。BPFTOOL=/path/to/bpftool。BPF 类型冲突
现象可能包括:
当前版本已经处理:
stdint.h。vmlinux.h再补充项目自定义 tracepoint ctx。diag_trace_event_raw_*项目命名,避免依赖内核 BTF 中的结构名。make clean会清理坏的生成头。快速开始
列出支持的场景
输出:
配置检查
示例输出:
采集全部场景
采集单个场景
CPU:
I/O:
内存:
锁竞争:
慢 syscall:
组合场景
按 PID 过滤
按进程名过滤
注意:
comm是 Linux task comm,长度受TASK_COMM_LEN限制,当前项目按16字节处理。输出到文件
使用配置文件
命令行参数会覆盖配置文件中的值。
CLI 参数
参数说明:
--scenarioall--duration60--config--outputjson、yaml、markdown/mdjson--output-file--pid--comm--sample-rate1--sample-interval-ms100--max-rss-mb200--max-cpu-percent5--dry-run--list-scenarios配置文件
配置文件采用简单的扁平
key: value格式,不是完整 YAML 解析器。支持注释和空行。默认配置见
config/default.yaml:支持的配置键:
duration_secscenariooutputpidcommsample_interval_mssample_ratemax_rss_mbmax_cpu_percentcpu_runq_delay_us_p99io_block_latency_us_p99memory_page_fault_ratememory_alloc_ratememory_alloc_bytesmemory_rss_bytesmemory_reclaim_eventsmemory_available_percentlock_futex_wait_us_p99syscall_latency_us_p99输出格式
支持:
每条诊断结果包含:
typecpu_sched_latency、block_io_latencytargetpid=123 comm=nginxwindow.start_nsCLOCK_MONOTONIC纳秒window.end_nsCLOCK_MONOTONIC纳秒confidencesummaryroot_causeevidencesuggestionsJSON 示例:
置信度规则
当前置信度由证据数量和阈值超限比例综合计算。取值:
confirmedhighly_suspectedpossibleneed_investigation设计原则:
BPF Map 设计
每个场景 BPF object 都包含一组独立 map,避免场景之间互相影响。
events_rbBPF_MAP_TYPE_RINGBUFconfig_mapBPF_MAP_TYPE_ARRAYstruct diag_runtime_configwake_ts_mapBPF_MAP_TYPE_LRU_HASHrequest_start_mapBPF_MAP_TYPE_LRU_HASHfutex_wait_mapBPF_MAP_TYPE_LRU_HASHsyscall_start_mapBPF_MAP_TYPE_LRU_HASHdrop_counter_mapBPF_MAP_TYPE_PERCPU_ARRAYMap 容量默认值:
性能策略
项目默认从以下几个层面控制开销:
--scenario指定的 BPF object。sample_rate降低高频路径输出。建议在压测前后对比:
需要重点观察:
测试与复现
环境检查
编译检查
CPU 调度压力
块 I/O 延迟
tests/reproduce/io_latency_fio.sh默认使用--ioengine=libaio --direct=1 --iodepth=64 --numjobs=4。这样可以让队列深度真实生效;如果使用 fio 默认psync引擎,iodepth会被限制为 1,不适合作为赛题里的队列拥堵复现。内存压力
futex 竞争
syscall storm
集成测试脚本
测试表现分析
测试
ebpf-sysdiag时不要只看“有没有输出”。更合理的目标是同时验证四件事:推荐把测试分成基础可用性、空闲基线、单场景压力、混合压力、资源开销、结果复核六个阶段。
1. 基础可用性测试
先确认构建链路、配置解析和 BPF 加载没有问题:
通过标准:
make clean && make无编译错误。--dry-run能打印dry-run ok。--scenario all能成功运行到结束。results可以为空;空结果只表示短窗口内没有事件超过阈值,不代表加载失败。如果这一步失败,先不要跑压力测试,应优先检查:
常见问题包括 BTF 缺失、bpftool 包装器不可用、权限不足、目标 tracepoint 不存在。
2. 空闲基线测试
空闲基线用于评估误报率。建议在没有主动压测的情况下运行:
理想结果:
results为空;或possible级别结果;或需要警惕的情况:
confirmed。ebpf-sysdiag自己。这些情况通常说明阈值太低、观测对象太宽,或者某类阻塞等待被误当作异常。
3. 单场景压力测试
单场景压力用于验证每个 detector 是否能识别预期异常。建议一次只打开一个场景,避免多个异常互相干扰。
tests/reproduce/cpu_runqueue_delay.shsudo build/ebpf-sysdiag --scenario cpu --duration 20 --output jsoncpu_intensive_compute/cpu_busy_loop/cpu_thread_contention/cpu_sched_latencytests/reproduce/io_latency_fio.shsudo build/ebpf-sysdiag --scenario io --duration 20 --output jsonblock_io_latencytests/reproduce/memory_pressure.shsudo build/ebpf-sysdiag --scenario memory --duration 20 --output jsonmemory_jitter_oom_risk/tmp/futex_contentionsudo build/ebpf-sysdiag --scenario lock --duration 20 --output jsonlock_futex_contentiontests/reproduce/syscall_storm.shsudo build/ebpf-sysdiag --scenario syscall --duration 20 --output jsonsyscall_latencyfutex 测试示例:
syscall 测试示例:
通过标准:
0。type。target能指向压测进程或相关系统对象。evidence.value至少有一个明显超过evidence.threshold。confidence至少为possible;强压力下通常应达到highly_suspected或confirmed。如果没有输出,不一定是 bug,先检查:
--pid或--comm过滤掉。fio、stress-ng等未安装工具。4. 混合压力测试
单场景通过后,再测试
all模式:混合测试关注的是关联能力和噪声控制:
target中。例如 I/O 压力可能同时出现:
block_io_latencysyscall_latencycpu_sched_latency这种情况下,不能简单把所有结果都当作独立根因。通常应按时间窗口和因果链理解:
5. 资源开销测试
工具本身不能成为故障源。建议在压测前后采集基线:
也可以单独观察工具进程:
重点指标:
5%200 MB如果开销过高,优先做三件事:
--scenario all改为单场景。--pid或--comm。--sample-rate 10或更高。6. 结果字段解读
一条结果应按下面顺序读:
typetargetwindowconfidencesummaryroot_causeevidencevalue和threshold,判断超限程度suggestions证据比结论更重要。一个高质量结论通常至少有两类互相支撑的证据:
如果只有一个指标轻微超阈值,通常只能算
possible。7. 示例结果分析
假设输出中有:
解读:
stress-ng-vm是当前 RSS 最高且与缺页风暴同窗口出现的进程。3279717次用户态缺页,折算缺页速率约204982/s,超过默认阈值1000/s。MemAvailable当前为38%,没有低于30%;这条证据表示系统余量,需要和 RSS、缺页速率一起看。4.6 GB,同名进程组 RSS 约18.6 GB,均超过默认阈值。confirmed的内存抖动/OOM 风险。建议复核:
如果输出中有:
解读:
containerd出现最长约10 秒的 futex wait。677高于默认阈值50。confirmed。建议复核:
如果 containerd 没有卡顿、CPU 正常、业务没有受影响,这可能只是正常等待被识别为锁等待热点。
如果输出中有:
解读:
ebpf-sysdiag自己会执行poll、epoll_wait、文件写入、ringbuf 等待等阻塞 syscall。临时规避:
分析时应把
target=ebpf-sysdiag的 syscall 结果视为观测噪声,除非正在专门测试工具自身。8. 误报和漏报分析
误报通常表现为:
confirmed。target指向长期空闲的后台服务。syscall_latency总是指向ebpf-sysdiag。处理方法:
漏报通常表现为:
results为空。target没有指向压测进程。处理方法:
如果仍然没有结果,应检查压测是否经过当前探针覆盖的内核路径。例如缓存命中的文件读取可能不会产生明显块 I/O 延迟,用户态自旋锁也不一定进入 futex。
9. 推荐测试矩阵
make clean && make--dry-rundry-run ok--scenario allcpucpu_sched_latencyioblock_io_latencymemorymemory_jitter_oom_risklocklock_futex_contentionsyscallsyscall_latencyall--pid过滤--comm过滤10. 测试结论模板
建议每次测试后记录一份简短结论:
交叉验证建议
ebpf-sysdiag输出的是异常证据链,不应脱离系统背景单独定性。建议按场景做交叉验证:top、pidstat -w、perf sched、cgroup CPU quota、runqueue 长度iostat -x、fio、blktrace、设备队列深度、cgroup I/O throttlingvmstat、sar -B、/proc/pressure/memory、reclaim/swap/OOM 日志perf lock、pthread uprobe、业务线程栈、火焰图strace -T、perf trace、I/O 和锁事件时间线已知盲区
NO_CORE=1兼容路径依赖 tracepoint 格式,不保证跨内核稳定。开发指南
添加新场景
推荐步骤:
bpf/<scenario>.bpf.c,实现最小探针集合。include/diag_types.h中新增事件类型和事件结构。Makefile的BPF_SRCS中加入新 BPF 源文件。src/loader.c中新增 skeleton open/load/attach 分支。src/event_parser.c中解析新事件。src/analysis/detectors/中新增 detector。src/analysis/engine.c中接入窗口聚合。src/output/中确认输出字段完整。config/<scenario>.yaml。tests/reproduce/和tests/integration/测试脚本。BPF 开发约束
NO_CORE=1下的兼容结构。用户态开发约束
故障定位流程建议
一次典型排查可以这样执行:
如果报告为空:
syscall_latency_us_p99或io_block_latency_us_p99。--duration。如果加载失败:
make check-env。tools/check_btf.sh。sudo权限。/sys/kernel/tracing/events/...。make clean && BPFTOOL=/path/to/bpftool make验证 bpftool 路径。如果输出过多:
--sample-rate N。--pid或--comm。--duration。验收标准
项目当前建议按以下标准验收:
make clean && make在目标机器上成功。--scenario all可以运行并输出合法 JSON。--dry-run可解析默认配置。200 MB。5%。相关文档
DESIGN.md:架构和设计原则。TEST.md:测试计划和复现场景。docs/architecture.md:运行路径概览。docs/diagnosis_rules.md:各场景诊断规则。docs/bpf_maps.md:BPF map 说明。docs/compatibility.md:内核和架构兼容性说明。docs/performance.md:性能基线建议。docs/blind_spots.md:观测盲区。License
项目使用
LICENSE中声明的许可证。BPF 程序声明为Dual BSD/GPL,以满足内核 helper 和加载要求。