260 lines
6.7 KiB
Markdown
260 lines
6.7 KiB
Markdown
# DroneScrewServer 崩溃根因最终分析
|
||
|
||
## 🎯 最新发现(2026-06-04)
|
||
|
||
### 崩溃模式确认
|
||
|
||
经过多次测试和日志分析,发现了以下关键信息:
|
||
|
||
#### 测试 1:PNG 编码崩溃(已解决)
|
||
- **崩溃点**:`[PUB-raw] before PNG encode`
|
||
- **原因**:PNG 编码库段错误
|
||
- **解决方案**:禁用 PNG 压缩
|
||
- **状态**:✅ 已修复
|
||
|
||
#### 测试 2:禁用 PNG 后仍崩溃(新问题)
|
||
|
||
**第一次崩溃(01:44:59)**:
|
||
```
|
||
运行时长:约 20 分钟
|
||
模式:检测模式
|
||
最后日志:[DETECT] frame #2041 after algo Detect, boxes=0
|
||
退出码:255
|
||
```
|
||
|
||
**第二次崩溃(02:05:05)**:
|
||
```
|
||
运行时长:约 20 分钟(1140秒)
|
||
模式:Idle(完全空载)
|
||
最后日志:[HEARTBEAT] uptime=1140s mode=idle
|
||
退出码:255
|
||
```
|
||
|
||
### 🔍 关键线索
|
||
|
||
1. **时间规律性**:
|
||
- 两次崩溃都在启动后约 **19-20 分钟**
|
||
- 与是否运行检测**无关**
|
||
- 与处理的帧数**无关**
|
||
|
||
2. **Idle 模式也崩溃**:
|
||
- 程序完全空载(没有检测、没有图像处理)
|
||
- 仅运行心跳日志
|
||
- 说明**不是业务逻辑代码的问题**
|
||
|
||
3. **没有异常日志**:
|
||
- 所有 try-catch 都没有捕获到异常
|
||
- 所有业务流程日志都完整
|
||
- 崩溃发生在**代码逻辑之外**
|
||
|
||
## 💡 根本原因推断
|
||
|
||
### 最可能的原因:systemd 看门狗或超时配置
|
||
|
||
#### 证据:
|
||
1. **精确的 20 分钟间隔**:不像随机崩溃,更像超时强制终止
|
||
2. **退出码 255**:异常退出,但非段错误(段错误通常是 139)
|
||
3. **systemd 日志**:`Main process exited, code=exited, status=255/EXCEPTION`
|
||
4. **Idle 也崩溃**:排除了所有业务代码问题
|
||
|
||
#### systemd 可能的配置:
|
||
```ini
|
||
[Service]
|
||
RuntimeMaxSec=20m # 运行时间上限 20 分钟
|
||
# 或
|
||
WatchdogSec=20m # 看门狗超时 20 分钟
|
||
# 或
|
||
TimeoutStopSec=20m # 停止超时
|
||
```
|
||
|
||
### 其他可能性(概率较低)
|
||
|
||
#### 1. Qt 事件循环死锁
|
||
- Qt 主事件循环卡死
|
||
- 某个线程持有锁不释放
|
||
- 但这通常不会有精确的 20 分钟间隔
|
||
|
||
#### 2. 内存泄漏导致 OOM-Killer
|
||
- 但 htop 显示内存 < 1GB
|
||
- 而且 OOM-Killer 的退出码通常是 9 (SIGKILL)
|
||
|
||
#### 3. 相机 SDK 内部问题
|
||
- MVS SDK 的后台线程崩溃
|
||
- 但为什么会是精确的 20 分钟?
|
||
|
||
## 🛠️ 解决方案
|
||
|
||
### 方案 1:检查 systemd 服务配置(优先)
|
||
|
||
**步骤**:
|
||
```bash
|
||
# 查看当前服务配置
|
||
sudo systemctl cat dronescrewserver.service
|
||
|
||
# 查看完整状态
|
||
sudo systemctl status dronescrewserver.service
|
||
|
||
# 查看服务属性
|
||
sudo systemctl show dronescrewserver.service | grep -i "timeout\|watchdog\|runtime"
|
||
```
|
||
|
||
**如果发现超时配置,修改服务文件**:
|
||
```ini
|
||
[Service]
|
||
# 移除或增加运行时间限制
|
||
# RuntimeMaxSec=infinity # 或设置更长时间
|
||
# WatchdogSec=0 # 禁用看门狗
|
||
|
||
# 增加停止超时(避免强制杀死)
|
||
TimeoutStopSec=30s
|
||
|
||
# 重启策略
|
||
Restart=always
|
||
RestartSec=5s
|
||
```
|
||
|
||
**应用配置**:
|
||
```bash
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl restart dronescrewserver
|
||
```
|
||
|
||
### 方案 2:启用 coredump 定位段错误(如果是段错误)
|
||
|
||
```bash
|
||
# 启用 coredump
|
||
ulimit -c unlimited
|
||
sudo mkdir -p /var/coredumps
|
||
echo '/var/coredumps/core.%e.%p.%t' | sudo tee /proc/sys/kernel/core_pattern
|
||
|
||
# 修改 systemd 服务
|
||
sudo systemctl edit dronescrewserver.service
|
||
|
||
# 添加:
|
||
[Service]
|
||
LimitCORE=infinity
|
||
|
||
# 重启
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl restart dronescrewserver
|
||
|
||
# 崩溃后查看 coredump
|
||
ls -lh /var/coredumps/
|
||
# 使用 gdb 分析
|
||
gdb /path/to/DroneScrewServer /var/coredumps/core.xxx
|
||
```
|
||
|
||
### 方案 3:增加详细的系统调用跟踪
|
||
|
||
```bash
|
||
# 使用 strace 跟踪(会影响性能)
|
||
sudo systemctl stop dronescrewserver
|
||
sudo strace -f -o /tmp/dronescrew_strace.log /path/to/DroneScrewServer
|
||
|
||
# 或修改 systemd 服务
|
||
[Service]
|
||
ExecStart=/usr/bin/strace -f -o /tmp/dronescrew_strace.log /path/to/DroneScrewServer
|
||
```
|
||
|
||
### 方案 4:修改代码增加信号处理器
|
||
|
||
在 `main.cpp` 中增加信号处理,捕获崩溃信号:
|
||
|
||
```cpp
|
||
#include <signal.h>
|
||
#include <execinfo.h>
|
||
|
||
void signalHandler(int sig)
|
||
{
|
||
void* array[20];
|
||
size_t size = backtrace(array, 20);
|
||
|
||
LOG_ERROR("========== SIGNAL %d RECEIVED ==========\n", sig);
|
||
LOG_ERROR("Stack trace:\n");
|
||
|
||
char** messages = backtrace_symbols(array, size);
|
||
for (size_t i = 0; i < size; i++)
|
||
LOG_ERROR(" [%zu] %s\n", i, messages[i]);
|
||
free(messages);
|
||
|
||
exit(sig);
|
||
}
|
||
|
||
int main(int argc, char* argv[])
|
||
{
|
||
// 注册信号处理器
|
||
signal(SIGSEGV, signalHandler); // 段错误
|
||
signal(SIGABRT, signalHandler); // 异常终止
|
||
signal(SIGTERM, signalHandler); // 终止信号
|
||
signal(SIGKILL, signalHandler); // 强制终止(无法捕获,但尝试)
|
||
|
||
// ... 原有代码 ...
|
||
}
|
||
```
|
||
|
||
## 📊 优先级和推荐
|
||
|
||
### 立即执行(优先级 P0)
|
||
|
||
1. **检查 systemd 服务配置**
|
||
```bash
|
||
sudo systemctl show dronescrewserver.service | grep -i "timeout\|watchdog\|runtime"
|
||
```
|
||
|
||
2. **查看完整的 systemd 日志**
|
||
```bash
|
||
sudo journalctl -u dronescrewserver.service --since "1 hour ago" | grep -i "timeout\|killed\|signal"
|
||
```
|
||
|
||
### 短期(优先级 P1)
|
||
|
||
3. **启用 coredump**(如果是段错误)
|
||
4. **增加信号处理器**(捕获终止信号)
|
||
|
||
### 中期(优先级 P2)
|
||
|
||
5. **代码审查**:
|
||
- 检查是否有死锁可能
|
||
- 检查线程退出逻辑
|
||
- 检查资源清理
|
||
|
||
## 🧪 测试验证
|
||
|
||
### 验证步骤
|
||
|
||
1. **查看 systemd 配置**后,运行测试:
|
||
- 如果有 20 分钟限制 → 移除后测试能否运行超过 20 分钟
|
||
- 如果没有限制 → 继续其他方案
|
||
|
||
2. **启用 coredump** 后,等待下次崩溃:
|
||
- 查看是否生成了 coredump 文件
|
||
- 使用 gdb 分析崩溃点
|
||
|
||
3. **增加信号处理器** 后,查看日志:
|
||
- 如果看到 "SIGNAL XX RECEIVED",说明是信号导致
|
||
- 根据信号类型判断原因
|
||
|
||
## 📈 下一步行动
|
||
|
||
**请先执行以下命令并提供输出**:
|
||
|
||
```bash
|
||
# 1. 查看 systemd 服务完整配置
|
||
sudo systemctl cat dronescrewserver.service
|
||
|
||
# 2. 查看服务超时相关属性
|
||
sudo systemctl show dronescrewserver.service | grep -E "RuntimeMaxSec|WatchdogSec|TimeoutStopSec|TimeoutStartSec"
|
||
|
||
# 3. 查看最近的 systemd 日志(包含信号信息)
|
||
sudo journalctl -u dronescrewserver.service --since "2 hours ago" | tail -100
|
||
```
|
||
|
||
根据这些信息,我们就能确定是否是 systemd 配置问题,然后针对性解决。
|
||
|
||
## 📝 修改记录
|
||
|
||
| 日期 | 问题 | 解决方案 | 状态 |
|
||
|------|------|---------|------|
|
||
| 2026-06-04 | PNG 编码崩溃 | 禁用 PNG 压缩 | ✅ 已修复 |
|
||
| 2026-06-04 | 20 分钟定时崩溃 | 待确认 systemd 配置 | 🔍 调查中 |
|