# eBPF 诊断工具 本目录包含用于监控和诊断 `agent_runner` 及其子进程性能的 eBPF 工具。 ## 📊 监控架构总览 ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ 进程性能监控完整方案 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ CPU 性能监控(持续) │ │ │ │ │ │ │ │ Grafana Alloy (eBPF) → Pyroscope Server → Web UI (4040) │ │ │ │ - 97 Hz 采样率 │ │ │ │ - 每 15 秒发送数据 │ │ │ │ - 自动发现进程 │ │ │ │ - 支持历史查询 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 进程指标监控(持续) │ │ │ │ │ │ │ │ Alloy Process Exporter → Prometheus → Grafana Dashboard │ │ │ │ - CPU、内存、I/O、FD、线程数 │ │ │ │ - 15 秒采集间隔 │ │ │ │ - 时序数据存储 │ │ │ │ - Dashboard 可视化 (3000) │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ Off-CPU 阻塞监控(定期) │ │ │ │ │ │ │ │ offcputime-bpfcc → SVG 火焰图文件 │ │ │ │ - 每 60 秒生成一次 │ │ │ │ - 显示阻塞堆栈 │ │ │ │ - 识别 I/O、锁、等待等阻塞 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 系统调用监控(持续) │ │ │ │ │ │ │ │ syscount-bpfcc → 统计文件 │ │ │ │ execsnoop-bpfcc → 进程创建日志 │ │ │ │ opensnoop-bpfcc → 文件访问日志 │ │ │ │ - 每 60 秒统计一次 │ │ │ │ - 持续追踪进程和文件访问 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ ↓ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 手动诊断工具(按需使用) │ │ │ │ │ │ │ │ diag-tool.sh - 综合诊断工具 │ │ │ │ auto-flamegraph.sh - 自动火焰图生成 │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` ## 目录结构 ``` ebpf-tools/ ├── README.md # 本文档 ├── alloy-config.alloy # Grafana Alloy 配置(CPU 监控 + 进程指标导出) ├── diag-tool.sh # 手动诊断工具 ├── auto-flamegraph.sh # 自动火焰图生成 ├── offcpu-monitor.sh # Off-CPU 阻塞监控 └── syscall-monitor.sh # 系统调用监控 ``` --- ## 🔧 工具详解 ### 1. alloy-config.alloy - Grafana Alloy 配置 **用途**: 持续 CPU 性能监控 + 进程指标导出 **工作原理**: - 使用 eBPF 自动发现 `agent_runner` 及其子进程 - 以 97 Hz 频率采样 CPU 性能数据,发送到 Pyroscope - 采集进程指标(CPU、内存、I/O、FD、线程数),发送到 Prometheus - 在 Web UI 中实时查看:Pyroscope (4040) + Grafana (3000) **采集的指标**: | 指标类别 | 指标名称 | 说明 | |---------|---------|------| | **CPU** | `process_cpu_seconds_total` | CPU 时间(累计) | | **内存** | `process_resident_memory_bytes` | 常驻内存(RSS) | | **内存** | `process_virtual_memory_bytes` | 虚拟内存(VSZ) | | **FD** | `process_open_fds` | 打开的文件描述符 | | **I/O** | `process_read_bytes_total` | 读取字节数 | | **I/O** | `process_write_bytes_total` | 写入字节数 | | **线程** | `process_num_threads` | 线程数量 | | **上下文切换** | `process_context_switches_total` | 上下文切换次数 | **进程标签**: - `process_pid`: 进程 PID - `process_name`: 进程名称 - `process_exe`: 完整执行路径 - `parent_pid`: 父进程 PID - `project_id`: 项目 ID - `container_id`: 容器 ID **环境变量**: | 变量 | 默认值 | 说明 | |------|--------|------| | `ENABLE_ALLOY` | - | 是否启用(由 ebpf-debug feature 控制) | | `PYROSCOPE_URL` | http://pyroscope:4040 | Pyroscope Server 地址 | | `PROMETHEUS_URL` | http://prometheus:9090/api/v1/write | Prometheus 写入地址 | **查看数据**: ```bash # 1. Pyroscope Web UI (CPU 性能) open http://localhost:4040 # 2. Grafana Dashboard (进程指标) open http://localhost:3000 # 登录: admin / admin # 查找: "Agent Runner 进程监控" ``` --- ### 2. offcpu-monitor.sh - Off-CPU 阻塞监控 **用途**: 定期生成 Off-CPU 阻塞火焰图,分析进程阻塞原因 **工作原理**: - 每 60 秒自动运行一次(可配置) - 对 `agent_runner` 及其所有子进程进行采样 - 使用 `offcputime-bpfcc` 捕获阻塞堆栈 - 生成 SVG 火焰图文件,自动清理旧文件(最多保留 50 个) **环境变量**: | 变量 | 默认值 | 说明 | |------|--------|------| | `ENABLE_OFFCPUTIME` | - | 是否启用(由 ebpf-debug feature 控制) | | `OFFCPU_DURATION` | 30 | 每次采样时长(秒) | | `OFFCPU_INTERVAL` | 60 | 生成间隔(秒) | | `MAX_OFFCPU_FILES` | 50 | 最多保留文件数量 | **输出文件**: ``` /app/container-logs/diag/ ├── offcpu-monitor.log # 监控日志 ├── offcpu-agent_runner-1-20250111_143025.svg # 主进程阻塞火焰图 └── offcpu-claude-code-acp-123-*.svg # 子进程阻塞火焰图 ``` --- ### 3. syscall-monitor.sh - 系统调用监控 **用途**: 监控进程的系统调用活动,包括进程创建、文件访问和系统调用统计 **工作原理**: - 每 60 秒统计一次系统调用(可配置) - 后台持续追踪进程创建 (`execsnoop-bpfcc`) - 后台持续追踪文件访问 (`opensnoop-bpfcc`) - 定期生成系统调用统计报告 **采集的数据**: | 工具 | 数据 | 说明 | |------|------|------| | `syscount-bpfcc` | 系统调用统计 | 每个系统调用的次数和耗时 | | `execsnoop-bpfcc` | 进程创建日志 | 新进程的创建时间和命令行 | | `opensnoop-bpfcc` | 文件访问日志 | 文件打开/关闭操作 | **环境变量**: | 变量 | 默认值 | 说明 | |------|--------|------| | `ENABLE_SYSCALL_MONITOR` | - | 是否启用(由 ebpf-debug feature 控制) | | `SAMPLE_DURATION` | 30 | 每次采样时长(秒) | | `GENERATE_INTERVAL` | 60 | 生成间隔(秒) | **输出文件**: ``` /app/container-logs/diag/ ├── syscall-monitor.log # 监控日志 ├── syscall-count-agent_runner-1-*.txt # 系统调用统计 ├── execsnoop-*.log # 进程创建日志 └── opensnoop-*.log # 文件访问日志 ``` **日志说明**: - **控制台输出**: 每次采样只输出一行汇总日志 - **文件日志**: 详细日志写入 `syscall-monitor.log` --- ### 4. diag-tool.sh - 手动诊断工具 手动触发的 eBPF 诊断工具,用于在怀疑有性能问题时主动采集数据。 **用法**: ```bash diag-tool.sh {offcpu|flame|profile|all} [duration] ``` **命令**: | 命令 | 说明 | |------|------| | `offcpu [duration]` | 分析 off-cpu 堆栈,默认 30 秒 | | `flame [duration]` | 生成火焰图,默认 30 秒 | | `profile [duration]` | CPU 性能分析,默认 30 秒 | | `all ` | 综合诊断(包含所有分析) | **快捷命令**: - `e-offcpu` - 等同于 `diag-tool.sh offcpu` - `e-flame` - 等同于 `diag-tool.sh flame` - `e-profile` - 等同于 `diag-tool.sh profile` - `e-all` - 等同于 `diag-tool.sh all` --- ### 5. auto-flamegraph.sh - 自动火焰图生成 持续在后台运行的火焰图生成工具,自动监控 `agent_runner` 及其所有子进程。 **工作原理**: 1. 自动检测 `agent_runner` 进程 PID 2. 递归获取所有子进程 PID 3. 使用 bpftrace 采样性能数据(默认 30 秒) 4. 生成火焰图 SVG 文件 5. 每 60 秒重复一次 --- ## 🎯 使用场景 ### 场景 1: 持续监控 CPU 性能 **目标**: 在 Web UI 中实时查看 agent_runner 性能数据 **步骤**: 1. 确保容器已启动(Alloy 自动运行) 2. 打开 http://localhost:4040 (Pyroscope) 3. 选择应用 `agent_runner` 4. 按需过滤标签(如 `process_name="agent_runner"`) 5. 查看火焰图、时序数据、Top 函数 **适用问题**: - CPU 使用率异常 - 函数调用热点分析 - 性能回归检测 --- ### 场景 2: 查看进程指标趋势 **目标**: 监控内存、I/O、FD 等进程指标的趋势 **步骤**: 1. 打开 http://localhost:3000 (Grafana) 2. 登录(admin / admin) 3. 查找 "Agent Runner 进程监控" Dashboard 4. 选择 `project_id`、`instance`、`process_name` 过滤 5. 查看各面板数据 **可用面板**: - 概览: RSS/VSZ 内存、CPU 使用率、文件描述符 - 内存趋势: RSS 和 VSZ 的时间序列图 - I/O 监控: 读取/写入速率 - 上下文切换: 自愿/非自愿切换速率 - 线程详情: 线程数量、FD 使用率 - 缺页错误: 次要/主要缺页错误速率 --- ### 场景 3: 分析进程阻塞问题 **目标**: 找出进程为什么被阻塞(等待 I/O、锁等) **步骤**: 1. 等待 offcpu-monitor 自动生成火焰图(或手动触发) 2. 导出 SVG 文件到本地 3. 在浏览器中打开火焰图 4. 查找宽的阻塞堆栈 ```bash # 导出最新的 Off-CPU 火焰图 docker cp :/app/container-logs/diag/offcpu-*.svg ./ open offcpu-*.svg ``` **适用问题**: - `new_session` 超时 - 进程响应缓慢 - I/O 阻塞 - 锁竞争 --- ### 场景 4: 分析系统调用模式 **目标**: 了解进程的系统调用行为,找出系统调用热点 **步骤**: 1. 查看系统调用统计日志 2. 分析哪些系统调用最频繁 3. 查看 execsnoop/opensnoop 日志了解进程和文件访问 ```bash # 查看系统调用统计 docker exec cat /app/container-logs/diag/syscall-count-*.txt | head -20 # 查看进程创建日志 docker exec tail -f /app/container-logs/diag/execsnoop-*.log ``` --- ### 场景 5: 手动诊断已知问题 ```bash # 进入容器 docker exec -it bash # 诊断 agent_runner e-all $(pgrep agent_runner) # 导出结果 docker cp :/app/container-logs/diag ./diag-results ``` --- ## 📈 火焰图分析 ### CPU 火焰图(Alloy + Pyroscope) **如何阅读**: - **横轴**: CPU 时间占比(越宽表示占用越多) - **纵轴**: 调用堆栈(从上到下是调用关系) - **颜色**: 暖色调表示热点函数 **典型问题识别**: | 现象 | 可能原因 | |------|----------| | 某个函数占据大部分宽度 | CPU 密集型计算 | | 深层函数很宽 | 递归调用或深层嵌套 | | 出现 `syscall` 大量时间 | 系统调用开销 | | 出现 `sleep`/`usleep` | 主动休眠或等待 | ### Off-CPU 火焰图(offcputime-bpfcc) **如何阅读**: - **横轴**: 阻塞时间占比(越宽表示阻塞越久) - **纵轴**: 阻塞时的调用堆栈 - **颜色**: 暖色调表示阻塞热点 **典型问题识别**: | 现象 | 可能原因 | |------|----------| | `schedule()` 占据大量时间 | 进程被调度出去(CPU 竞争) | | `do_wait()`/`wait_event()` | 等待事件或信号 | | `__sock_sendmsg()`/`__sock_recvmsg()` | 网络 I/O 阻塞 | | `blk_mq_submit_bio()` | 磁盘 I/O 阻塞 | | `futex_wait()` | 锁等待(互斥锁) | --- ## 🔍 故障排查 ### Grafana Dashboard 显示 "No Data" ```bash # 1. 检查 Prometheus 中是否有数据 curl -s 'http://localhost:9091/api/v1/query?query=process_resident_memory_bytes' # 2. 检查标签值 curl -s 'http://localhost:9091/api/v1/label/project_id/values' # 3. 确认 agent_runner 容器正在运行 docker ps | grep agent_runner ``` ### 变量下拉框为空 ```bash # 检查标签值是否存在 curl -s 'http://localhost:9091/api/v1/label/project_id/values' # 如果没有数据,说明没有 agent_runner 容器在运行 # 启动一个 agent_runner 容器后再检查 ``` ### Pyroscope Web UI 无数据 ```bash # 1. 检查 Pyroscope Server docker ps | grep pyroscope docker logs rcoder-pyroscope # 2. 检查 Alloy 是否发送数据 docker exec grep "collected profiles" /app/container-logs/diag/alloy.log # 3. 检查网络连接 docker exec curl http://pyroscope:4040 ``` ### Off-CPU 火焰图未生成 ```bash # 1. 检查 offcputime-bpfcc 是否可用 docker exec which offcputime-bpfcc # 2. 检查监控进程 docker exec ps aux | grep offcpu-monitor # 3. 查看监控日志 docker exec tail -f /app/container-logs/diag/offcpu-monitor.log ``` ### 系统调用监控无输出 ```bash # 1. 检查 syscount-bpfcc 是否可用 docker exec which syscount-bpfcc # 2. 检查监控脚本是否运行 docker exec ps aux | grep syscall-monitor # 3. 查看监控日志 docker exec tail -f /app/container-logs/diag/syscall-monitor.log ``` --- ## ⚠️ 安全注意事项 ⚠️ **eBPF 工具需要容器特权模式运行**,仅在受信任的调试环境使用! 生产环境请使用 `make docker-build-agent-production` 构建无 eBPF 工具的镜像。 --- ## 📚 相关文档 - [Grafana Alloy Documentation](https://grafana.com/docs/alloy/latest/) - [Pyroscope Documentation](https://pyroscope.io/docs/) - [Prometheus Documentation](https://prometheus.io/docs/) - [Grafana Documentation](https://grafana.com/docs/) - [Brendan Gregg's FlameGraph](https://github.com/brendangregg/FlameGraph) - [bpftrace 参考指南](https://bpftrace.dev/) - 主项目文档: `/docker/README.md`