本文介绍的linux 嵌入式设备端程序崩溃问题的处理与分析。文章的编写时间与代码的实现时间相隔很多,有些细节文章中不再补充。本文只记录笔记中记载的和能从代码中查到的相关部分内容。
硬件平台:imx6solo
系统:linux
编译breakpad 需要修改源码,怎么修改这里不做说明。
略
编译设备端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
以在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发现程序崩在数据接收线程,因为多线程,有一个变量在操作时未加锁。当时就是找到了定位点,也花了一些时间想为什么会出现崩溃。因为问题隐藏的太深了。