linux下程序运行日志及崩溃排查

  • 示例脚本(run.sh)
该脚本可作为程序的启动脚本,其使用日志文件记录程序运行时的标准输出(stdout)和标准错误(stderr)信息,使用核心转储文件记录程序崩溃时的各种状态信息。具体内容如下:

#!/bin/bash

# ===== 配置区域 =====
APP_NAME="Widgets"           # 可执行程序名称
LOG_DIR="/opt/auto/logs"     # 日志文件目录
QT_LOG_PATTERN="${LOG_DIR}/*.log"  # Qt 日志文件模式
CORE_PATTERN="${LOG_DIR}/core.${APP_NAME}.*"  # 核心转储文件模式
# ====================

# 创建日志目录
mkdir -p "$LOG_DIR"

# 生成带时间戳的日志文件名
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${LOG_DIR}/${APP_NAME}_${TIMESTAMP}.log"

# 启用核心转储
echo "===== 启用核心转储 =====" | tee -a "$LOG_FILE"
ulimit -c unlimited
echo "/opt/auto/logs/core.${APP_NAME}.%e.%t" | sudo tee /proc/sys/kernel/core_pattern

# 设置设备权限
echo "===== 设置设备权限 =====" | tee -a "$LOG_FILE"
sudo chmod 777 /dev/ttyS0
sudo chmod 777 /dev/ttyS1

# 设置环境变量
export LD_LIBRARY_PATH="/opt/auto/lib/:$LD_LIBRARY_PATH"
cd /opt/auto || exit 1

# 记录启动信息
{
    echo "===== 启动时间: $(date) ====="
    echo "应用程序: $APP_NAME"
    echo "工作目录: $(pwd)"
    echo "环境变量 LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
    echo "核心转储模式: $(cat /proc/sys/kernel/core_pattern)"
    echo "当前用户: $(whoami)"
    echo "显示环境: $DISPLAY"
    echo "系统信息: $(uname -a)"
    echo "内存信息: $(free -h)"
} >> "$LOG_FILE"

# 启动程序并捕获输出
echo "===== 启动应用程序 =====" >> "$LOG_FILE"
./"$APP_NAME" >> "$LOG_FILE" 2>&1
EXIT_CODE=$?

# 记录退出信息
{
    echo "===== 程序退出时间: $(date) ====="
    echo "退出代码: $EXIT_CODE"
} >> "$LOG_FILE"

# 增强崩溃诊断
if [ $EXIT_CODE -ne 0 ]; then
    echo "===== 开始崩溃诊断 =====" >> "$LOG_FILE"
    
    # 1. 记录最近的 Qt 日志
    echo "----- 最近的 Qt 日志 (最多20行) -----" >> "$LOG_FILE"
    LATEST_QT_LOG=$(ls -t $QT_LOG_PATTERN | head -n 1)
    if [ -f "$LATEST_QT_LOG" ]; then
        echo "检测到 Qt 日志文件: $LATEST_QT_LOG" >> "$LOG_FILE"
        tail -n 20 "$LATEST_QT_LOG" >> "$LOG_FILE"
    else
        echo "未找到 Qt 日志文件" >> "$LOG_FILE"
    fi
    
    # 2. 检查核心转储
    echo "----- 核心转储检查 -----" >> "$LOG_FILE"
    if ls $CORE_PATTERN 1> /dev/null 2>&1; then
        echo "发现核心转储文件:" >> "$LOG_FILE"
        ls -lh $CORE_PATTERN >> "$LOG_FILE"
        echo "使用以下命令分析: gdb /opt/auto/$APP_NAME /opt/auto/logs/core.${APP_NAME}.*" >> "$LOG_FILE"
    else
        echo "未发现核心转储文件" >> "$LOG_FILE"
    fi
    
    # 3. 系统资源检查
    echo "----- 系统资源状态 -----" >> "$LOG_FILE"
    echo "内存使用: $(free -h | awk '/Mem/{print $3"/"$2}')" >> "$LOG_FILE"
    echo "CPU负载: $(uptime | awk -F'load average: ' '{print $2}')" >> "$LOG_FILE"
    echo "磁盘空间: $(df -h / | awk 'NR==2{print $4 " free"}')" >> "$LOG_FILE"
    
    # 4. 进程检查
    echo "----- 相关进程检查 -----" >> "$LOG_FILE"
    pgrep -a "$APP_NAME" >> "$LOG_FILE" 2>&1
    
    echo "===== 崩溃诊断结束 =====" >> "$LOG_FILE"
fi

# 添加日志文件权限设置(确保所有用户可读)
chmod 644 "$LOG_FILE" 2>/dev/null

exit $EXIT_CODE
  • 核心转储文件分析
程序崩溃后,可使用 GDB 指令对核心转储文件进行分析,步骤示例如下:
(1)查看核心转储功能
    ulimit -c    # 查看核心转储功能是否被启用,如果返回 0 代表未未启用,返回 unlimited 代表已启用
    ulimit -c unlimited   # 启用核心转储功能
(2)如果没有安装gdb,则使用以下指令安装:
    sudo apt-get install gdb
(3)gdb /opt/auto/Widgets /opt/auto/logs/xxx    # Widgets为可执行程序名称,xxx 为核心转储文件名称
(4)bt      # 查看堆栈跟踪,显示程序崩溃时的调用层次
    输出示例:
        #0  0x0000555555555169 in crashFunction() ()
        #1  0x0000555555555191 in main ()
(5)frame 0    # 切换到栈帧0,即 crashFunction() 函数
    输出示例:
        #0  0x0000555555555169 in crashFunction() ()
(6)list     # 显示当前栈帧位置附近的源代码
(7)分析代码

注意:分析核心转储时如果需要更多详细的代码信息,则需要在 debug 模式下运行程序,如果是使用命令行编译链接程序,则需要加上"-g"参数,如:
    gcc -g -o myapp myapp.c
    g++ -g -o myapp myapp.cpp
  • 核心转储文件的管理
1、使用 systemd-coredump 服务来处理核心转储
    (1)列出所有的核心转储:
        coredumpctl list
    (2)查找所需的核心转储:
        coredumpctl list Widgets   # 按程序名称查找
        coredumpctl list --since "5 minutes ago"  # 按时间查找
    (3)查看核心转储信息:
        coredumpctl info   # 查看最新生成的转储信息
        coredumpctl info Widgets  # 查看指定程序的转储信息
        coredumpctl info 1234  # 查看指定PID的转储信息
    (4)使用 GDB 调试核心转储
        coredumpctl debug   # 调试最新生成的转储
        coredumpctl debug Widgets  # 调试指定程序的转储
        coredumpctl debug --since "2025-07-09 10:20:00"    # 调试指定时间的转储
    (5)导出转储文件
        coredumpctl dump > core.Widgets  # 导出最新生成的转储文件
        coredumpctl dump Widgets > core.Widgets  # 导出指定程序的转储文件
    (6)文件导出后的 GDB 调试
        gdb /opt/auto/Widgets -c core.Widgets

2、修改核心转储文件生成路径
    方法一:echo "/opt/auto/logs/core.${APP_NAME}.%e.%t" | sudo tee /proc/sys/kernel/core_pattern
    其中,core_pattern 文件是一个虚拟文件,用于设置核心转储文件的命名模式和存储位置,而以上指令通过管道("|")将具体的路径和命名格式传递给了 core_pattern 文件,以此来实现将核心转储文件存储在指定位置的功能
    方法二:sudo sysctl -w kernel.core_pattern=/opt/auto/logs/core.%e.%t
    该指令通过 sysctl 系统调用修改内核参数,最终也会将路径和命名格式写入到 core_pattern 文件中,和方法一功能类似。但不同的是,方法二不支持使用变量名(如:${APP_NAME})来动态构造命名字符串

3、核心转储文件命名占位符
    %e     # 可执行文件名称
    %t     # Unix时间戳
    %p     # 进程ID(PID)
    %s     # 导致崩溃的信号编号
    %u     # 用户ID(UID)
  • 日志输出与 qDebug()
示例:./Widgets >> "$LOG_FILE" 2>&1
(1)./Widgets      # 执行可执行程序 Widgets
(2)>> "$LOG_FILE"  # 将标准输出重定向到日志文件(">>"代表追加模式,而">"代表覆盖模式)
(3)2>&1    # "2>"表示重定向标准错误,"&1"表示标准输出当前指向的位置,"2>&1"表示将标准错误也重定向到标准输出所在的位置,即日志文件中

注意,在未调用 qInstallMessageHandler() 的情况下,程序中的 qDebug() 会默认输出到标准错误(stderr),此时会被 ./Widgets >> "$LOG_FILE" 2>&1 语句重定向捕获到日志文件中。而如果调用了 qInstallMessageHandler(outputMessage),则 qDebug() 的输出不再发送到标准错误(stderr),而是由 outputMessage 函数处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

enyp80

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值