#!/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