Files
2026-06-01 13:54:52 +08:00

450 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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} <pid> [duration]
```
**命令**:
| 命令 | 说明 |
|------|------|
| `offcpu <pid> [duration]` | 分析 off-cpu 堆栈,默认 30 秒 |
| `flame <pid> [duration]` | 生成火焰图,默认 30 秒 |
| `profile <pid> [duration]` | CPU 性能分析,默认 30 秒 |
| `all <pid>` | 综合诊断(包含所有分析) |
**快捷命令**:
- `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 <container>:/app/container-logs/diag/offcpu-*.svg ./
open offcpu-*.svg
```
**适用问题**:
- `new_session` 超时
- 进程响应缓慢
- I/O 阻塞
- 锁竞争
---
### 场景 4: 分析系统调用模式
**目标**: 了解进程的系统调用行为,找出系统调用热点
**步骤**:
1. 查看系统调用统计日志
2. 分析哪些系统调用最频繁
3. 查看 execsnoop/opensnoop 日志了解进程和文件访问
```bash
# 查看系统调用统计
docker exec <container> cat /app/container-logs/diag/syscall-count-*.txt | head -20
# 查看进程创建日志
docker exec <container> tail -f /app/container-logs/diag/execsnoop-*.log
```
---
### 场景 5: 手动诊断已知问题
```bash
# 进入容器
docker exec -it <container> bash
# 诊断 agent_runner
e-all $(pgrep agent_runner)
# 导出结果
docker cp <container>:/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 <container> grep "collected profiles" /app/container-logs/diag/alloy.log
# 3. 检查网络连接
docker exec <container> curl http://pyroscope:4040
```
### Off-CPU 火焰图未生成
```bash
# 1. 检查 offcputime-bpfcc 是否可用
docker exec <container> which offcputime-bpfcc
# 2. 检查监控进程
docker exec <container> ps aux | grep offcpu-monitor
# 3. 查看监控日志
docker exec <container> tail -f /app/container-logs/diag/offcpu-monitor.log
```
### 系统调用监控无输出
```bash
# 1. 检查 syscount-bpfcc 是否可用
docker exec <container> which syscount-bpfcc
# 2. 检查监控脚本是否运行
docker exec <container> ps aux | grep syscall-monitor
# 3. 查看监控日志
docker exec <container> 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`