GrabBag/App/DroneScrewbolt/DEBUG_V3_ATEXIT.md

295 lines
7.3 KiB
Markdown
Raw Normal View History

2026-06-26 17:55:15 +08:00
# 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"
```
---
## 🎯 最可能的原因(基于当前信息)
### 可能性 1Qt 信号槽机制问题60%
`detectionResult` 信号通过 Qt::AutoConnection 从**检测线程**发送到**主线程**的槽函数。可能:
1. Qt 的事件队列满了导致崩溃
2. 跨线程信号槽的内部问题
3. 槽函数中有问题的代码
**验证方法**:改用 Qt::QueuedConnection 明确指定队列连接
### 可能性 2publishDetectionResult 内部问题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 也不会触发,那时我们就知道要往那个方向调查了。
**请尽快测试并提供新日志!** 🎯