148 lines
4.8 KiB
Bash
Executable File
148 lines
4.8 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
# 核心配置
|
||
CRASH_LOG_DIR="./crash_logs"
|
||
RUN_DURATION=2
|
||
GDB_TIMEOUT=$((RUN_DURATION + 2)) # 延长超时,确保GDB完全初始化
|
||
MAX_LOOP=0
|
||
# 强制捕获所有致命信号
|
||
CRASH_SIGNALS=("SIGSEGV" "SIGABRT" "SIGILL" "SIGFPE" "SIGBUS")
|
||
|
||
# 创建日志目录
|
||
mkdir -p ${CRASH_LOG_DIR}
|
||
rm -f ${CRASH_LOG_DIR}/* # 清空旧日志(可选)
|
||
|
||
# 清理进程函数
|
||
cleanup() {
|
||
pkill -9 gdb >/dev/null 2>&1
|
||
pkill -9 pPowerManger >/dev/null 2>&1
|
||
sleep 0.2
|
||
}
|
||
|
||
# 检查是否是真崩溃(从日志+系统日志双重验证)
|
||
is_real_crash() {
|
||
local log_file=$1
|
||
local loop=$2
|
||
# 1. 检查GDB日志中的崩溃关键字
|
||
if grep -qiE "segmentation fault|sigsegv|crash detected|backtrace" "${log_file}"; then
|
||
return 0
|
||
fi
|
||
# 2. 检查系统dmesg中的段错误记录(终极验证)
|
||
if dmesg | grep -i "pPowerManger.*segfault" | grep -i "loop_${loop}"; then
|
||
return 0
|
||
fi
|
||
return 1
|
||
}
|
||
|
||
# 打印系统级崩溃日志(dmesg)
|
||
print_system_crash_log() {
|
||
local loop=$1
|
||
echo -e "\033[33m===== 系统级崩溃日志(dmesg)=====\033[0m"
|
||
dmesg | grep -A 5 -B 2 "pPowerManger.*segfault" | tail -10
|
||
echo -e "\033[33m===================================\033[0m"
|
||
}
|
||
|
||
echo -e "🔍 启动pPowerManger稳定性测试(强制捕获段错误)\n"
|
||
echo "📂 崩溃日志目录: ${CRASH_LOG_DIR}"
|
||
echo "⏱ 程序运行时长: ${RUN_DURATION}秒"
|
||
echo "📌 捕获信号: ${CRASH_SIGNALS[*]}"
|
||
echo -e "========================================\n"
|
||
|
||
cleanup # 初始清理
|
||
LOOP_COUNT=0
|
||
|
||
while true; do
|
||
LOOP_COUNT=$((LOOP_COUNT + 1))
|
||
if [ ${MAX_LOOP} -ne 0 ] && [ ${LOOP_COUNT} -gt ${MAX_LOOP} ]; then
|
||
echo -e "\n✅ 测试完成(${MAX_LOOP}次循环无崩溃)"
|
||
cleanup
|
||
exit 0
|
||
fi
|
||
|
||
echo -e "===== 第 ${LOOP_COUNT} 次循环 ====="
|
||
CRASH_LOG="${CRASH_LOG_DIR}/crash_loop_${LOOP_COUNT}.log"
|
||
GDB_CMD="${CRASH_LOG_DIR}/gdb_cmds_${LOOP_COUNT}.gdb"
|
||
|
||
# 生成强化版GDB脚本:强制捕获所有信号,优先初始化信号处理
|
||
cat > ${GDB_CMD} << EOF
|
||
# 核心:GDB启动后先设置信号捕获,再启动程序
|
||
set args pPowerManger.moos
|
||
set timeout ${GDB_TIMEOUT}
|
||
set print pretty on
|
||
set print frame-arguments all
|
||
set logging file ${CRASH_LOG}.gdb_raw
|
||
set logging on # 开启GDB原始日志(双重保障)
|
||
|
||
# 强制捕获所有致命信号
|
||
EOF
|
||
|
||
# 为每个崩溃信号添加捕获规则(追加到GDB脚本)
|
||
for sig in "${CRASH_SIGNALS[@]}"; do
|
||
cat >> ${GDB_CMD} << EOF
|
||
handle ${sig} stop print pass nostop # 即使子线程崩溃也捕获
|
||
EOF
|
||
done
|
||
|
||
# 追加程序启动和崩溃处理逻辑
|
||
cat >> ${GDB_CMD} << EOF
|
||
# 延迟10ms启动程序,确保GDB完全初始化
|
||
sleep 0.01
|
||
run
|
||
|
||
# 崩溃后强制打印所有信息(无论是否主线程)
|
||
if \$exitcode != 0 || \$signal_number != 0
|
||
echo "\n====================================="
|
||
echo " CRASH DETECTED"
|
||
echo "====================================="
|
||
echo "Loop: ${LOOP_COUNT}"
|
||
echo "Exit code: \$exitcode"
|
||
echo "Signal: \$signal_name (\$signal_number)"
|
||
echo "====================================="
|
||
thread apply all bt full # 打印所有线程的调用栈(关键!)
|
||
info threads
|
||
info proc mappings # 打印内存映射(排查内存越界)
|
||
info locals
|
||
info registers
|
||
endif
|
||
|
||
set logging off
|
||
quit
|
||
EOF
|
||
|
||
# 核心:用nohup+GDB启动,避免输出丢失;同时记录进程ID
|
||
echo "启动程序(GDB强化模式)..."
|
||
nohup timeout ${GDB_TIMEOUT} gdb -batch -x ${GDB_CMD} ./pPowerManger > ${CRASH_LOG} 2>&1 &
|
||
GDB_PID=$!
|
||
wait ${GDB_PID} # 等待GDB执行完成
|
||
|
||
# 双重验证:检查是否真崩溃
|
||
if is_real_crash ${CRASH_LOG} ${LOOP_COUNT}; then
|
||
# 捕获到真崩溃
|
||
echo -e "\033[31m❌ 第 ${LOOP_COUNT} 次循环捕获到段错误!\033[0m"
|
||
echo -e "\033[31m📄 崩溃日志: ${CRASH_LOG}\033[0m"
|
||
print_system_crash_log ${LOOP_COUNT}
|
||
|
||
# 打印最关键的调用栈(所有线程)
|
||
echo -e "\033[31m===== 所有线程崩溃调用栈 =====\033[0m"
|
||
grep -A 50 "thread apply all bt full" ${CRASH_LOG} | grep -v "No stack frame"
|
||
echo -e "\033[31m==============================\033[0m"
|
||
|
||
cleanup
|
||
echo -e "\n🛑 测试终止,完整日志在 ${CRASH_LOG}"
|
||
exit 1
|
||
else
|
||
# 无崩溃(正常退出/启动失败)
|
||
if grep -qi "Program exited normally" ${CRASH_LOG}; then
|
||
echo "✅ 程序正常运行${RUN_DURATION}秒后退出"
|
||
else
|
||
echo "⚠️ 程序启动失败(非段错误),继续循环"
|
||
# 保留启动失败日志(方便排查)
|
||
mv ${CRASH_LOG} ${CRASH_LOG}.start_fail
|
||
fi
|
||
rm -f ${GDB_CMD} ${CRASH_LOG}.gdb_raw
|
||
fi
|
||
|
||
cleanup
|
||
echo -e "第 ${LOOP_COUNT} 次循环完成\n"
|
||
sleep 0.5
|
||
done |