295 lines
7.3 KiB
Markdown
295 lines
7.3 KiB
Markdown
# DroneScrewServer 20分钟崩溃 - 最终调试版本
|
||
|
||
## 🔍 当前状态
|
||
|
||
### 已确认的信息
|
||
|
||
1. ✅ **PNG 崩溃已解决** - 禁用 PNG 压缩后不再有 PNG 相关崩溃
|
||
2. ✅ **信号处理器已注册** - 但崩溃时**没有触发**
|
||
3. ❌ **程序仍然在 20 分钟后崩溃** - 退出码 255
|
||
4. ❌ **没有任何异常日志** - 所有 try-catch 未触发
|
||
5. ❌ **没有信号日志** - signalHandler 未被调用
|
||
|
||
### 最新发现
|
||
|
||
**崩溃时的最后日志**:
|
||
```
|
||
[DETECT] frame #2163 after rawImageReady emit
|
||
[DETECT] frame #2163 before algo Detect, left=2242 right=19
|
||
[DETECT] frame #2163 after algo Detect, boxes=0
|
||
Main process exited, status=255/EXCEPTION
|
||
```
|
||
|
||
**关键发现**:
|
||
- ✅ `after algo Detect` 日志完成
|
||
- ❌ **没有 `after detectionResult emit` 日志**
|
||
- ❌ 没有信号处理器日志
|
||
|
||
**结论**:崩溃发生在 `emit detectionResult(result)` 这一行,但**不是通过信号触发的**!
|
||
|
||
---
|
||
|
||
## 🎯 新增的调试功能(v3)
|
||
|
||
### 1. atexit 处理器
|
||
|
||
**目的**:捕获正常退出(通过 `exit()` 或 `return`)
|
||
|
||
**功能**:
|
||
- 程序退出时自动调用
|
||
- 打印完整的退出调用栈
|
||
- 告诉我们是谁调用了 exit()
|
||
|
||
**预期日志**:
|
||
```
|
||
[ATEXIT] Program is exiting normally!
|
||
[ATEXIT-BACKTRACE] Exit call stack (N frames):
|
||
[0] atexitHandler+0x...
|
||
[1] exit+0x...
|
||
[2] 某个函数+0x... ← 这就是罪魁祸首!
|
||
```
|
||
|
||
### 2. 检测线程详细日志
|
||
|
||
**新增日志点**:
|
||
```cpp
|
||
emit detectionResult(result);
|
||
LOG_DEBUG("[DETECT] frame #%d after detectionResult emit\n", ...); ← 新增
|
||
...
|
||
LOG_DEBUG("[DETECT] frame #%d loop end, checking exit flag=%d\n", ...); ← 新增
|
||
```
|
||
|
||
**目的**:
|
||
- 确认是否是 `emit detectionResult` 导致崩溃
|
||
- 检查循环是否正常继续
|
||
- 监控线程退出标志
|
||
|
||
---
|
||
|
||
## 🧪 测试步骤
|
||
|
||
### 1. 编译部署新版本
|
||
|
||
```bash
|
||
cd GrabBagPrj
|
||
qmake GrabBagPrj.pro
|
||
make
|
||
|
||
# 部署到设备
|
||
scp App/DroneScrewbolt/DroneScrewServer/DroneScrewServer user@device:/path/to/
|
||
```
|
||
|
||
### 2. 运行测试
|
||
|
||
```bash
|
||
# 重启服务
|
||
sudo systemctl restart dronescrewserver
|
||
|
||
# 实时查看日志
|
||
sudo journalctl -u dronescrewserver.service -f | tee /tmp/dronescrew_test.log
|
||
```
|
||
|
||
### 3. 等待崩溃(约 20 分钟)
|
||
|
||
### 4. 分析日志
|
||
|
||
查找关键日志:
|
||
|
||
```bash
|
||
# 1. 查找 atexit 日志
|
||
grep -A 30 "\[ATEXIT\]" /tmp/dronescrew_test.log
|
||
|
||
# 2. 查找信号日志
|
||
grep -A 30 "\[SIGNAL\]" /tmp/dronescrew_test.log
|
||
|
||
# 3. 查找崩溃前的最后日志
|
||
grep -B 20 "Main process exited" /tmp/dronescrew_test.log
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 预期结果分析
|
||
|
||
### 场景 A:看到 atexit 日志
|
||
|
||
```
|
||
[DETECT] frame #2163 after algo Detect, boxes=0
|
||
[ATEXIT] Program is exiting normally!
|
||
[ATEXIT-BACKTRACE] Exit call stack (15 frames):
|
||
[0] atexitHandler+0x123
|
||
[1] __run_exit_handlers+0x456
|
||
[2] exit+0x789
|
||
[3] QCoreApplication::exit+0xabc ← Qt 事件循环退出
|
||
[4] 某个槽函数+0xdef
|
||
```
|
||
|
||
**说明**:
|
||
- 有代码主动调用了 `exit()` 或 `QCoreApplication::exit()`
|
||
- 调用栈会告诉我们是哪个函数
|
||
|
||
**可能的原因**:
|
||
1. `publishDetectionResult` 中某处调用了 exit
|
||
2. Qt 信号槽机制触发了某个会退出的代码
|
||
3. 第三方库内部调用了 exit
|
||
|
||
### 场景 B:看到信号日志
|
||
|
||
```
|
||
[DETECT] frame #2163 after algo Detect, boxes=0
|
||
[SIGNAL] Caught signal 11: SIGSEGV (Segmentation Fault)
|
||
[BACKTRACE] Stack trace (20 frames):
|
||
[0] signalHandler+0x123
|
||
[1] /lib/libc.so+0x456
|
||
[2] 某个函数+0x789 ← 崩溃点
|
||
```
|
||
|
||
**说明**:
|
||
- 是信号导致的崩溃(段错误、总线错误等)
|
||
- 但之前的测试没有捕获到,可能之前版本没有正确注册
|
||
|
||
### 场景 C:既没有 atexit 也没有 signal
|
||
|
||
```
|
||
[DETECT] frame #2163 after algo Detect, boxes=0
|
||
Main process exited, status=255/EXCEPTION
|
||
```
|
||
|
||
**说明**:
|
||
- 程序被外部强制终止(`_exit()` 或 `kill`)
|
||
- `_exit()` 不会触发 atexit 处理器
|
||
- 需要使用其他方法调试
|
||
|
||
**下一步**:
|
||
1. 使用 `strace` 跟踪系统调用
|
||
2. 检查是否有其他进程杀死了这个进程
|
||
3. 检查 systemd 的 cgroup 限制
|
||
|
||
### 场景 D:看到 "after detectionResult emit"
|
||
|
||
```
|
||
[DETECT] frame #2163 after algo Detect, boxes=0
|
||
[DETECT] frame #2163 after detectionResult emit ← 新日志
|
||
[DETECT] frame #2163 loop end, checking exit flag=0
|
||
[DETECT] frame #2164 before rawImageReady emit
|
||
(崩溃在其他地方)
|
||
```
|
||
|
||
**说明**:
|
||
- `emit detectionResult` 不是直接原因
|
||
- 崩溃发生在后续的某个地方
|
||
- 需要继续增加日志
|
||
|
||
---
|
||
|
||
## 🔬 根据测试结果的下一步方案
|
||
|
||
### 如果有 atexit 日志
|
||
|
||
→ **根据调用栈定位调用 exit 的代码**
|
||
→ 修复或注释掉那个 exit 调用
|
||
→ 问题解决
|
||
|
||
### 如果有 signal 日志
|
||
|
||
→ **根据调用栈定位崩溃的函数**
|
||
→ 修复段错误或内存问题
|
||
→ 问题解决
|
||
|
||
### 如果都没有
|
||
|
||
→ **使用 strace 跟踪系统调用**:
|
||
```bash
|
||
sudo strace -f -tt -T -o /tmp/strace.log \
|
||
-e trace=exit,exit_group,kill,tkill,tgkill \
|
||
/path/to/DroneScrewServer
|
||
```
|
||
|
||
→ **查看是否有外部进程杀死**:
|
||
```bash
|
||
# 查看系统日志
|
||
dmesg | grep -i "killed\|oom"
|
||
|
||
# 查看 audit 日志
|
||
sudo ausearch -m ANOM_ABEND -ts recent
|
||
```
|
||
|
||
→ **检查 cgroup 限制**:
|
||
```bash
|
||
systemctl show dronescrewserver.service | grep -i "memory\|cpu\|tasks"
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 最可能的原因(基于当前信息)
|
||
|
||
### 可能性 1:Qt 信号槽机制问题(60%)
|
||
|
||
`detectionResult` 信号通过 Qt::AutoConnection 从**检测线程**发送到**主线程**的槽函数。可能:
|
||
1. Qt 的事件队列满了导致崩溃
|
||
2. 跨线程信号槽的内部问题
|
||
3. 槽函数中有问题的代码
|
||
|
||
**验证方法**:改用 Qt::QueuedConnection 明确指定队列连接
|
||
|
||
### 可能性 2:publishDetectionResult 内部问题(30%)
|
||
|
||
虽然有异常保护,但可能:
|
||
1. ZeroMQ 内部调用了 `_exit()`
|
||
2. QJsonDocument 序列化时崩溃
|
||
3. 互斥锁死锁导致问题
|
||
|
||
**验证方法**:临时注释掉整个函数体,看是否还崩溃
|
||
|
||
### 可能性 3:第三方库的定时任务(10%)
|
||
|
||
MVS SDK 或其他库的后台线程在 20 分钟后执行某个操作导致崩溃。
|
||
|
||
**验证方法**:使用 strace 或 gdb 附加到进程
|
||
|
||
---
|
||
|
||
## 📝 修改文件清单
|
||
|
||
### 本次修改(v3)
|
||
|
||
1. `main.cpp`
|
||
- 增加 `atexitHandler()` - 捕获正常退出
|
||
- 注册 `atexit()` 处理器
|
||
|
||
2. `DroneScrewServerPresenter.cpp`
|
||
- 增加 `after detectionResult emit` 日志
|
||
- 增加 `loop end` 日志
|
||
|
||
### 累计修改
|
||
|
||
| 文件 | 修改内容 | 版本 |
|
||
|------|---------|------|
|
||
| `DroneScrewZmqProtocol.cpp` | 禁用 PNG 压缩 | v1 |
|
||
| `DroneScrewServerPresenter.cpp` | 详细检测日志 | v1 |
|
||
| `DroneScrewZmqProtocol.cpp` | PNG/ZMQ 日志 | v1 |
|
||
| `main.cpp` | 信号处理器 | v2 |
|
||
| `main.cpp` | atexit 处理器 | v3 ⭐ |
|
||
| `DroneScrewServerPresenter.cpp` | emit 后日志 | v3 ⭐ |
|
||
|
||
---
|
||
|
||
## 🚀 立即行动
|
||
|
||
1. ✅ **编译部署** v3 版本
|
||
2. ✅ **运行测试** 至少 30 分钟
|
||
3. ✅ **查找关键字**:
|
||
- `[ATEXIT]` - 正常退出
|
||
- `[SIGNAL]` - 信号崩溃
|
||
- `after detectionResult emit` - emit 是否完成
|
||
4. ✅ **提供完整日志**
|
||
|
||
---
|
||
|
||
## 💪 我们越来越接近真相了!
|
||
|
||
通过 atexit 处理器,我们**一定能找到**是谁调用了 exit()!
|
||
|
||
如果程序是被外部强制终止的(`_exit()` 或 `kill`),atexit 也不会触发,那时我们就知道要往那个方向调查了。
|
||
|
||
**请尽快测试并提供新日志!** 🎯
|