当前位置: 首页 > 工具软件 > Breakpad > 使用案例 >

linux设备端breakpad程序崩溃日志的捕获与分析

奚无尘
2023-12-01

linux设备端breakpad程序崩溃日志的捕获与分析

说明

本文介绍的linux 嵌入式设备端程序崩溃问题的处理与分析。文章的编写时间与代码的实现时间相隔很多,有些细节文章中不再补充。本文只记录笔记中记载的和能从代码中查到的相关部分内容。

平台介绍

硬件平台:imx6solo
系统:linux

breakpad 的编译

编译breakpad 需要修改源码,怎么修改这里不做说明。

PC端的编译与安装

交叉编译

编译设备端breakpad运行的库时,交叉编译如果按正常的操作方式,是会编译出错的。
正常编译时会source 环境变量

source /opt/fsl-imx-x11/3.14.52-1.1.0/environment-setup-cortexa9hf-vfp-neon-poky-linux-gnueabi

environment-setup-cortexa9hf-vfp-neon-poky-linux-gnueabi 脚本的大致内容如下

export SDKTARGETSYSROOT=/opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/cortexa9hf-vfp-neon-poky-linux-gnueabi
export PATH=/opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/x86_64-pokysdk-linux/usr/bin:/opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi:$PATH
export CCACHE_PATH=/opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/x86_64-pokysdk-linux/usr/bin:/opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi:$CCACHE_PATH
export PKG_CONFIG_SYSROOT_DIR=$SDKTARGETSYSROOT
export PKG_CONFIG_PATH=$SDKTARGETSYSROOT/usr/lib/pkgconfig
export CONFIG_SITE=/opt/fsl-imx-x11/3.14.52-1.1.0/site-config-cortexa9hf-vfp-neon-poky-linux-gnueabi
export OECORE_NATIVE_SYSROOT="/opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/x86_64-pokysdk-linux"
export OECORE_TARGET_SYSROOT="$SDKTARGETSYSROOT"
export OECORE_ACLOCAL_OPTS="-I /opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/x86_64-pokysdk-linux/usr/share/aclocal"
export PYTHONHOME=/opt/fsl-imx-x11/3.14.52-1.1.0/sysroots/x86_64-pokysdk-linux/usr
export CC="arm-poky-linux-gnueabi-gcc  -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 --sysroot=$SDKTARGETSYSROOT"
export CXX="arm-poky-linux-gnueabi-g++  -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 --sysroot=$SDKTARGETSYSROOT"
export CPP="arm-poky-linux-gnueabi-gcc -E  -march=armv7-a -mfloat-abi=hard -mfpu=neon -mtune=cortex-a9 --sysroot=$SDKTARGETSYSROOT"
export AS="arm-poky-linux-gnueabi-as "
export LD="arm-poky-linux-gnueabi-ld  --sysroot=$SDKTARGETSYSROOT"
export GDB=arm-poky-linux-gnueabi-gdb
export STRIP=arm-poky-linux-gnueabi-strip
export RANLIB=arm-poky-linux-gnueabi-ranlib
export OBJCOPY=arm-poky-linux-gnueabi-objcopy
export OBJDUMP=arm-poky-linux-gnueabi-objdump
export AR=arm-poky-linux-gnueabi-ar
export NM=arm-poky-linux-gnueabi-nm
export M4=m4
export TARGET_PREFIX=arm-poky-linux-gnueabi-
export CONFIGURE_FLAGS="--target=arm-poky-linux-gnueabi --host=arm-poky-linux-gnueabi --build=x86_64-linux --with-libtool-sysroot=$SDKTARGETSYSROOT"
export CFLAGS=" -O2 -pipe -g -feliminate-unused-debug-types"
export CXXFLAGS=" -O2 -pipe -g -feliminate-unused-debug-types"
export LDFLAGS="-Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed"
export CPPFLAGS=""
export KCFLAGS="--sysroot=$SDKTARGETSYSROOT"
export OECORE_DISTRO_VERSION="3.14.52-1.1.0"
export OECORE_SDK_VERSION="3.14.52-1.1.0"
export ARCH=arm
export CROSS_COMPILE=arm-poky-linux-gnueabi-

# Append environment subscripts
if [ -d "$OECORE_TARGET_SYSROOT/environment-setup.d" ]; then
    for envfile in $OECORE_TARGET_SYSROOT/environment-setup.d/*.sh; do
	    source $envfile
    done
fi
if [ -d "$OECORE_NATIVE_SYSROOT/environment-setup.d" ]; then
    for envfile in $OECORE_NATIVE_SYSROOT/environment-setup.d/*.sh; do
	    source $envfile
    done
fi

然后再make
但这里如果这样交叉编译会有问题,可以交叉编译,但是安装时出问题,提示找不到ranlib

问题原因分析:

因为source 后会暂时修改某些环境变量,导致交叉编译时出问题。

解决方法:

引起这个现象的原因是LDFLAGS 变量发生更改。 source 上述脚本后,执行 unset LDFLAGS

breakpad 的使用

代码的使用

以在qt中的使用为例,在pro中增加 breakpad 编译后成的库 breakpad_client
LIBS +=-L…/lib/breakpad/lib -lbreakpad -lbreakpad_client

在main 函数中增加如下代码

        // 初始化
        google_breakpad::MinidumpDescriptor descriptor(DUMP_FILE_PNAME);
        // minidump文件写入到的目录
        google_breakpad::ExceptionHandler *pHandler =
        new google_breakpad::ExceptionHandler (descriptor, NULL, dumpCallback, NULL, true, -1);
        Q_UNUSED(pHandler);

其中

#define DUMP_FILE_PNAME "../dumpfile"

static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
                         void* context, bool succeeded)
{
    //程序崩溃时重启
    Q_UNUSED(context);
    QString crashReporter ;
    crashReporter = QCoreApplication::applicationDirPath() +
                "/../lib/crashReport/armRelease/crashReport";
    qDebug()<<descriptor.path();
    QStringList arg;
    arg<< QCoreApplication::applicationDirPath()+"/" + descriptor.path();
    QProcess::startDetached(crashReporter, arg); //这里另外起动一个进程用于弹窗提示程序出错。arg为崩溃时的文件名。
    return succeeded;
}

需要使用的文件

按上面的操作编译程序后,发布程序时把中间文件 *sym 文件也要保存下来。在程序崩溃时用到。
另外在项目主目录创建 dumpfile文件夹,并在文件夹下创建 symbols 文件夹和外为breakpad.sh的脚本 ,脚本内容如下

#!/bin/bash

# 使用示例:
#第一步,从SVN releaseSymbol 下载对应版本的pft.sym 文件,然后copy到dumpfile下
#第2步 ./breakpad.sh 2020-10-19_15-10-55.dmp project.sym 

# $1 dump 名

symbol_file=$2  # sym 文件

#for symbol_file in *.sym
#do
    file_info=$(head -n1 $symbol_file)
    IFS=' ' read -a splitlist <<< "${file_info}"
    basefilename=${symbol_file:0:${#symbol_file} - 4}
    dest_dir=$basefilename/${splitlist[3]}
    echo "$dest_dir"
    mkdir -p "symbols/"$dest_dir
    mv $symbol_file ./symbols/$dest_dir
    echo "$symbol_file -> ./symbol/$dest_dir/$symbol_file"
    minidump_stackwalk $1 ./symbols > $basefilename.txt
#done

exit 0;

程序崩溃后代码的定位

按上述脚本中注释,执行脚本,会在当前目录下生成project.txt的文件,这个txt文件会详细描述当前崩溃时各个线程的调用情况,排在最前面的线程为出问题的线程。

实际解决问题

实际中用来解决的问题,设备在长时间运行时,接收串口数据绘制波形,在退出运行界面时出现概率性死机的问题,这个出问题概率比较小,当时处理起来非常棘手。 当时用了传统的方法(注册信号,然后触发信号时获取堆栈信息),要不无法感知到程序崩溃,要么崩溃了看不到详细信息。

后面用breakpad发现程序崩在数据接收线程,因为多线程,有一个变量在操作时未加锁。当时就是找到了定位点,也花了一些时间想为什么会出现崩溃。因为问题隐藏的太深了。

 类似资料: