目录

ebpf-diag-ecnu

基于 eBPF 的系统异常观测与根因定位工具

简介

ebpf-diag-ecnu 是一款轻量级 Linux 系统异常实时观测与诊断工具,基于 eBPF 技术实现。支持对 CPU 异常占用、I/O 延迟抖动、内存抖动、锁竞争和系统调用热点五类典型场景进行实时观测、指标采集、事件关联分析和结构化诊断结果输出。

赛题: 全国研究生操作系统比赛 — 基于 eBPF 的系统异常观测与根因定位工具

目标平台: openKylin (Kernel 6.6+);x86_64 已动态验证,ARM64/RISC-V 具备构建适配但尚无原生动态证据

当前版本: v0.3.0(竞赛交付收尾版;5/5 观测链路与 M1–M5.5 证据化根因分析已完成)

名称说明: 当前工具名为 ebpf-diag-ecnuEBPF_DIAG_* 环境变量和 ebpf_diag 内部 namespace 为兼容接口,继续保留。

代码仓库: https://gitlink.org.cn/xingxiu/jyedxtycgcygydwgj

当前交付结论:官方 openKylin 2.0 SP2 Live x86_64 已完成 native-full;持久安装的 openKylin 2.0 SP2 已完成最新代码的 clean build、CTest、五类 native-smoke,以及锁、内存、I/O 安全证据和候选竞争专项真机验证。当前无独立慢盘,因此持久环境保持 target_eligible=no,不把专项 PASS 冒充 native-full。

名称变更后已用全新 build-ecnu 目录完成 PASS=15 FAIL=0 SKIP=1 静态验收和严格构建;SKIP 仅表示当前非 root 会话未重新加载五类 BPF。提交前应再执行一次 sudo bash scripts/validate_openkylin.sh --native-smoke,为新二进制名称形成最终动态日志。

特性

  • 低侵入观测: 默认模式基于限频 eBPF 内核探针,无需修改目标代码;显式 PID pthread uprobe 是短时诊断模式,不属于常驻低开销路径
  • 5 类异常链路: CPU 异常、内存压力、I/O 延迟、futex 竞争、系统调用热点
  • 自适应检测: 自适应基线学习 + 相对阈值,自动适配不同性能的硬件设备
  • 证据化根因分析: 结构化证据 + 跨窗口 Incident + 根因排名,显式列出反证与缺失证据
  • 候选竞争可审计: 同一 Evidence 可同时增强替代候选、削弱原假设,并输出实体与 PID 生命周期对齐的方向关系
  • Waiter/Waker 时序链: 关联 futex wait、sched_wakeup 与返回结果,区分真实竞争、超时等待,并明确 waker 不等于 lock owner
  • Owner 模式证据: 对受限 glibc mutex owner 候选聚合身份数量与重复观测跨度,区分高频轮转和稳定热点 owner,同时保留精确持锁时长缺口
  • 定向临界区配对: 仅在显式 --target-pid 下采样成功的 pthread API 配对,输出 lock 返回到 unlock 进入的临界区下界且不对全系统挂载用户态锁探针
  • 长/短临界区辨别: 真机对照已区分微秒级高频重新获取与已知 15ms 长临界区,并把配对时长 Evidence 链接到根因
  • 实时检测: 时间窗口聚合 + 按场景设置的持续窗口防抖
  • 结构化输出: JSON 同时输出 anomalies、evidence_catalog、incidents、root_causes 与系统摘要
  • 可复现测试: 五类异常复现脚本、对象归属断言和非特权 CTest
  • 可移植构建: CO-RE + x86/ARM64/RISC-V 架构宏自动选择(openKylin 2.0 SP2 Live x86_64 native-full 已通过;ARM64/RISC-V 动态验证仍待完成)

快速开始

环境要求

  • Linux Kernel 5.8+ (推荐 6.6+)
  • libbpf 1.x(目标 openKylin 6.6 内核推荐并已动态验证;0.5 可编译,但本机实测无法解析该内核 BTF)
  • clang, cmake
  • root 权限或 CAP_BPF, CAP_PERFMON, CAP_SYS_ADMIN

编译

# 安装依赖 (Ubuntu/openKylin)
sudo apt install -y build-essential cmake clang llvm libbpf-dev libelf-dev zlib1g-dev

# 不要让 libbpf.so.0 指向 libbpf.so.1;这会破坏 iproute2 等系统程序的 ABI。
# 两个 SONAME 可并存,CMake 会优先选择 libbpf.so.1;也可显式指定:
# cmake -S . -B build -DLIBBPF_LIBRARY=/path/to/libbpf.so.1

# 生成 vmlinux.h (基于当前内核)
bpftool btf dump file /sys/kernel/btf/vmlinux format c > ebpf/vmlinux.h

# 编译
cmake -S . -B build -DBUILD_TESTING=ON
cmake --build build -j$(nproc)
ctest --test-dir build --output-on-failure

运行

# 监控 CPU 和内存异常,60秒,JSON 输出
sudo ./build/ebpf-diag-ecnu --monitor cpu,mem --duration 60 --output json

# 全场景监控(5 类异常 + 交叉关联分析)
sudo ./build/ebpf-diag-ecnu --monitor all --duration 60 \
    --output json --output-file output/report.json --verbose

# 一键测试
sudo bash scripts/test_all.sh

# 阶段进展验证(普通用户执行静态验证,root 会增加动态 smoke)
bash scripts/validate_progress.sh

# 静态验证通过后,显式执行五类动态 smoke / 完整压力回归
sudo bash scripts/validate_progress.sh --dynamic
sudo env EBPF_DIAG_TEST_DISK=/path/to/independent-test-disk \
  bash scripts/validate_progress.sh --scenarios

# M5.5 候选竞争真机验收;无独立慢盘时 CPU—I/O 分支会安全跳过
sudo bash scripts/test_hypothesis_competition.sh

# 列出可用探针
sudo ./build/ebpf-diag-ecnu --list-probes

openKylin 用户态容器验证

官方 openKylin 2.0 SP2 用户态镜像已经完成可复核 build/run。容器共享宿主内核,因此该结果只证明目标用户态工具链兼容,不能代替 openKylin Kernel 6.6+ 真机验收。

docker build -f docker/Dockerfile -t ebpf-diag-ecnu:openkylin-2.0 .
docker run --rm ebpf-diag-ecnu:openkylin-2.0

# 在 openKylin 2.0 / Kernel 6.6+ 真机上
sudo bash scripts/validate_openkylin.sh --native-smoke

完整分层验收流程见 openKylin 验收指南

项目结构

ebpf-diag-ecnu/
├── ebpf/                # eBPF 内核态程序 (.bpf.c)
│   ├── cpu.bpf.c        # CPU 异常观测探针 (sched_switch + sched_wakeup)
│   ├── memory.bpf.c     # 内存异常观测探针 (page_alloc + kswapd + OOM)
│   ├── io.bpf.c         # I/O 延迟观测探针 (block_rq_issue + block_rq_complete)
│   ├── lock.bpf.c       # 锁竞争观测探针 (futex_wait kprobe + kretprobe)
│   ├── syscall.bpf.c    # 系统调用热点探针 (sys_enter + sys_exit)
│   └── common.bpf.h     # 共享 eBPF 数据结构
├── include/             # 用户态头文件
│   ├── types.h          # 公共数据类型 (Event, Metrics, AnomalyReport)
│   ├── collector.h      # 采集器接口 (BaseCollector, 5 类 Collector)
│   ├── detector.h       # 检测器接口 (自适应基线检测)
│   ├── analyzer.h       # 症状分析、关联与最终摘要
│   ├── root_cause.h     # Evidence/Incident/RootCause/Action 模型
│   └── reporter.h       # 报告输出 (JsonReporter + SummaryData)
├── src/                 # 用户态 C++ 实现
│   ├── main.cpp         # CLI 入口 + 主循环
│   ├── core/            # EbpfLoader, EventReader, Logger, Symbols
│   ├── collector/       # 5 类指标采集器 (主导进程追踪)
│   ├── detector/        # 5 类异常检测器 (自适应基线)
│   ├── analyzer/        # 根因分析 + 多维交叉关联
│   └── reporter/        # JSON 结构化输出 + 系统摘要
├── scripts/             # 场景回归与性能脚本
├── tests/               # 不需要 root 的 Detector/Reporter 单元测试
├── docker/              # Dockerfile + docker-compose.yml
├── docs/                # 设计文档、测试方法、阶段总结
└── output/              # 诊断输出目录

支持的异常场景

场景 状态 验证方式 说明
CPU 异常占用 ✅ 双轮通过 stress-ng --cpu 4 两条报告均归属 stress-ng,保留 TGID/TID
内存压力 ✅ 双轮通过 stress-ng --vm 4 80% 两条报告均归属 stress-ng,报告关联进程 RSS
I/O 延迟抖动 ✅ 双轮通过 fio on 独立 USB 慢盘 预热临时文件后两条报告均归属 fio,脚本自动清理文件
Futex 竞争 ✅ 双轮通过 pthread mutex ×16 两条报告均归属 mutex_test,热点 futex 地址非零
系统调用热点 ✅ 双轮通过 futex 锁竞争 两条报告均归属 mutex_test/futex;低频长等待被抑制
多类型关联 ⚠️ 兼容字段 多压力并发 只表示同 PID 共现;正式根因方向由 causal_edges/hypothesis_relations 表达

验证环境

项目 配置
目标真机 openKylin 2.0 SP2,6.6.0-19-generic,x86_64
历史内核兼容 openKylin 6.6.0-24-generic #0ok1-KYLINOS 内核层回归
用户态兼容 openKylin 2.0 SP2 容器构建/CTest;容器不替代真机内核验收
存储边界 Live 历史 native-full 使用独立 USB/exFAT;持久环境当前仅 NVMe,无独立慢盘
libbpf 目标环境使用 libbpf 1.x;构建优先链接 libbpf.so.1,不修改系统 SONAME

输出格式

以下展示最终报告的主要层次,省略了部分 Evidence 和 Action 字段:

{
  "tool": "ebpf-diag-ecnu",
  "version": "0.3.0",
  "timestamp": "2026-07-30T...",
  "duration_seconds": 30,
  "total_anomalies": 2,
  "anomalies": [
    {
      "type": "CPU异常占用",
      "severity": "critical",
      "object": { "pid": 234016, "tid": 234032, "comm": "m5_mix_lock" }
    }
  ],
  "evidence_catalog": [
    {
      "id": "ev-000069",
      "source": "futex",
      "metric": "avg_wait_time_ms",
      "value": 0.014283,
      "quality": "sampled",
      "entity": {
        "pid": 234016,
        "process_start_time_ticks": 4473502,
        "resource": "futex@0x404138"
      }
    }
  ],
  "causal_edges": [
    {
      "id": "edge-000001",
      "relation": "waits_on_futex",
      "evidence_ids": ["ev-000069"],
      "confidence": 0.90
    }
  ],
  "hypothesis_relations": [
    {
      "from_root_cause_id": "rc-0001",
      "relation": "explains_competing_evidence",
      "to_root_cause_id": "rc-0002",
      "evidence_ids": ["ev-000069"],
      "confidence": 0.75
    }
  ],
  "incidents": [
    { "id": "inc-0001", "root_cause_ids": ["rc-0001"] }
  ],
  "root_causes": [
    {
      "rank": 1,
      "cause_code": "LOCK_CONTENTION_FUTEX",
      "status": "likely",
      "confidence": 0.97,
      "confidence_score": {
        "base": 0.80,
        "ceiling": 0.97,
        "factors": [
          { "name": "observed_futex_wait_latency", "contribution": 0.05, "evidence_ids": ["ev-000069"] },
          { "name": "multiple_waiters", "contribution": 0.03, "evidence_ids": ["ev-000068"] },
          { "name": "waker_identity", "contribution": 0.04, "evidence_ids": ["ev-000073"] },
          { "name": "ordered_wakeup", "contribution": 0.02, "evidence_ids": ["ev-000072"] },
          { "name": "successful_wake_dominance", "contribution": 0.03, "evidence_ids": ["ev-000075"] },
          { "name": "validated_owner_candidate", "contribution": 0.02, "evidence_ids": ["ev-000083"] }
        ],
        "final": 0.97
      }
    },
    {
      "rank": 2,
      "cause_code": "CPU_COMPUTE_HOTSPOT_UNCONFIRMED",
      "status": "hypothesis",
      "confidence": 0.45
    }
  ],
  "summary": {
    "system_health_score": 87.0,
    "dominant_issue": "多个 owner 高频轮转的 glibc mutex 竞争",
    "dominant_issue_source": "ranked_root_cause",
    "dominant_root_cause": {
      "cause_code": "LOCK_CONTENTION_FUTEX",
      "status": "likely",
      "confidence": 0.97
    },
    "breakdown": { "cpu": 1, "memory": 0, "io": 0, "lock": 0, "syscall": 1, "correlations": 0 }
  }
}

当前限制

  • 2026-07-28 已在官方 openKylin 2.0 SP2 Live、6.6.0-19-generic x86_64 物理机完成 native-full;持久安装环境完成最新代码的 clean build、CTest、五类 native-smoke 和 M2–M5 专项真机验证,但因无独立慢盘未重跑 native-full;
  • 内存 fault 是系统级 /proc/vmstat 增量,RSS 增长只用于辅助归属;
  • 锁场景只覆盖进入内核慢路径的 futex wait;
  • syscall map 的窗口读取不是原子快照;
  • I/O 仍有 block 层覆盖盲区,同参数并发请求可能发生 key 冲突,内核写回窗口也可能归属 kworker;场景脚本通过预热文件和充分恢复区分两轮业务压力;
  • PID-scoped pthread uprobe 只适合显式 PID 短时诊断;在 16 线程极端紧密 mutex 微基准上,1/4096 五轮测量的吞吐损失为 83.123%,不得作为常驻跟踪开启;该极端数值也不直接外推到具有业务临界区的应用。
  • 根因实体已用 PID start time 隔离 PID 重用;该值当前在窗口聚合时读取 procfs,已退出的短命进程可能输出 0 并作为缺失证据。
  • 当前精简分支不包含 output/ 历史日志;Live 验收证据可从 Git 提交 ddf5298 恢复。

文档导航

文档 内容
docs/设计文档.md 架构设计、技术选型、eBPF 探针设计
docs/根因分析设计总结.md 最终 Evidence/Incident/因果图/评分/候选竞争设计与边界
docs/项目收尾总结.md 最终成果、验收矩阵、交付入口与可选增强
docs/赛题需求对照与缺口审计.md 对照赛题原 README 的完成项、缺口、评分风险与优先级
docs/测试方法.md 各场景测试步骤、一键脚本、故障排查
docs/openkylin_validation.md openKylin 用户态、Kernel 6.6+ 真机分层验收
docs/当前状态与下一阶段计划.md 当前最终交付状态、历史证据与可选增强
docs/根因分析层设计.md M1–M4 根因分析演进与实现记录
docs/M2.4锁根因分析总结.md M2 waiter/waker、owner、临界区配对、对照样本与性能边界
docs/M3内存根因分析总结.md M3 匿名页、direct reclaim、swap/reclaim 证据链与边界
docs/M4应用文件系统设备根因分析总结.md M4 应用—文件系统—设备根因链与安全验收
docs/M5因果图与责任评分设计.md 有向因果边、可复算评分、反证扣分、候选竞争、跨窗口与 PID 生命周期
docs/M5.5候选竞争真机验证总结.md CPU—Lock 真机候选竞争、Detector 错窗、关系评分与根因主导摘要
docs/工具运行流程.md 以 CPU 为例的完整数据流详解
docs/第一阶段总结.md Phase 1 技术总结
docs/第二阶段总结.md Phase 2 技术总结
docs/第三阶段总结/README.md Phase 3 openKylin Live native-full 总结
docs/第四阶段总结/README.md Phase 4 持久 openKylin 迁移与可靠性加固

许可证

GPL-2.0

关于
142.2 MB
邀请码
    Gitlink(确实开源)
  • 加入我们
  • 官网邮箱:gitlink@ccf.org.cn
  • QQ群
  • QQ群
  • 公众号
  • 公众号

版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9 京公网安备 11010802047560号