当前位置: 代码迷 >> 综合 >> Linux / alignment trap - not handling instruction..
  详细解决方案

Linux / alignment trap - not handling instruction..

热度:47   发布时间:2023-12-12 13:10:03.0

概述

开始编辑日期:2018年12月26号,后重新整理从富文本编辑器转移至Markdown编辑器。
问题描述:Long Long Ago 嵌入式应用程序在现场使用时,小频次出现异常黑屏(进程死,Linux正常运行)。下面是前期好不容易捕获的一次告警信息:…Alignment trap:not handling instruction e1943f9f at [<0002b7b0>] …Unhandled fault: alignment exception(0x001) at 0x00000086.
将此文章更新在博客里头的主要原因是,近几天发现CSDN知识库又TM的不能使用了,公司网络限制比较苛刻,没有其他地方存储查询到的网络资料。声明,本文章,绝不透露任何的涉及公司保密信息的内容,仅记录个人资料与心得,以方便排查解决问题。由于公司保密要求,上网限制等原因,不能进行贴图贴代码等操作,如有问题请直接留言交流。本人菜鸟一只,欢迎批评指正,相互交流。

参考文档

  1. 非gdb, 在程序异常时打印backtrace的一种方案
  2. Linux addr2line 定位so库崩溃位置
  3. 在Linux中如何利用backtrace信息解决问题
  4. 知识补充-GCC常用参数详解

直接backtrace方案

在参考文章<1>中,博主jsxthncn提到,在进行编译时,要加入参数 -g -rdynamic //注意该参数是给链接器而不是编译器设置的。我用的是Qt集成开发环境,Makefile是使用qmake生成的,修改方式如下:
CC = arm-linux-gcc
CXX = arm-linux-g++
LINK = arm-linux-g++ -g -rdynamic ##only modify this line

GDB + CoreDump 方案

问题起源

https://bbs.csdn.net/topics/390852706 //已有论坛问题(同我的问题描述)

arm-linux-gdb安装

搭建交叉调试环境 arm-linux-gdb配合gdbserver
博文中- 编译arm-linux-gdb 在gdb-7.8的解压目录下新建 arm-gdb,用于存放编译生成文件。

./configure --target=arm-linux --prefix=/ -gdb/

这里不要照搬/ -gdb/,第一中间不应该有空格(原谅我是菜鸟,否则执行报错)。–prefix是在指定生成目录。可使用下面的写法:–prefix=/usr/local/arm-gdb

GDB下载 ,arm-GDB一些安装错误解决,<1> <2> <3>//apt-get

arm-linux-gdb编译完成后,可以不用make install,去上边指定的target目录下找就好了。在使用该gdb时,直接带着全路径使用也可以的。

环境配置

新建一个目录用以存放core文件 如:/core_data
Instructiong: ulimit -c size(blocks)
设置core文件上限 但是有时候设置1024块根本不够用 core文件可能高达80M 甚至更高 这里的size不是kb而是block 具体是什么再验证–
echo "ulimit -c 1024 " >> /etc/profile //或者配置成2048 //从现象看跟大小无关
(一定要注意 引号、小破折号 都必须是纯正的英文状态
如echo “ulimit -c 1024” >> /etc/profile
写入文件后是 “ulimit -c 1024” 带着一副引号,使用ulimit -a 进行查看时 发现并未生效

而echo "ulimit -c 1024 " >> /etc/profile 写入ulimit -c 1024 才是正确生效的)
ulimit -c 设置的是block块 而不是kb 如果设置的块不够 在进行gdb追踪时 会有提示–
具体可以使用ulimit -a 进行查看

注意在配置各种参数时 各种指令字符 必须是英文状态的 否则会出错–

如果core文件大小不够 可能会有如下提示
Warning: /root/work/MyProject/bin/core-MyProject-818-946687012 is truncated: expected core file size >= 1695744, found: 352256.

为了支持后台程序也能支持core打印,使得生产的core文件为RLIM_INFINITY(即大小不限制),这样的话,在生产”参数备份处的异常”时,core文件的大小为80多兆。不过gdb该文件时,内容确实更加详细:

GDB调试

将嵌入式系统中运行异常产生的core文件,拷贝回虚拟机(可直接放到交叉编译的target目录),然后在该目录下,执行Eg:
/usr/local/arm-gdb/bin/arm-linux-gdb yourAppName core-AppName-PID

执行完成后,可能并没有出现你要的信息,这时候继续执行GDB调试指令bt(即backtrace),进一步打印程序调用栈信息。到这里,我的问题,已经能追查到具体的行号啦…

解决程序后台运行不能生成core文件

问题描述可参考 ,解决方案可参考,其中提到的方案,确实可以解决后台运行程序崩溃时不生产core的问题,亲测可用,但是楼主代码中,将rlimit设置为RLIM_INFINITY(无限大),让我很头疼,我的嵌入式文件系统本来就不大,如果不限制core大小,一个core文件就高达80多兆,有点小承受不了。截止20181228没有找到其他解决办法,领导在催了,只能先放放,有了手段,但是还没有去真正解决问题呢…

问题处理

coredump函数调用栈显示,程序崩溃在:

 inline QString::QString(const QString &other) : d(other.d){
     Q_ASSERT(&other != this); d->ref.ref(); }//其上一层调用 //伪代码
strList << iter->value().str1 << iter->value().str2..

这是QString的拷贝构造函数,自我复制,导致无限循环,最终致使程序栈溢出。关于拷贝构造函数请参考-百度百科。

机缘巧合,在帮同事处理问题时发现,错误的使用QMap的迭代器(如果iter时map.end() 则执行iter->value() 时,便会发生同样的异常-函数调用栈信息)。经过进一步排查,发现此处存在多线程同时遍历一个map的情况。QMap是可重入的Note: All functions in this class are reentrant.

但是Qt指导手册中并没有提到 iterator Class 是可重入的。在可重入官方文档中,可以看到,如果某个Qt类是可重入或线程安全的,会进行单独标记的。也就是说iterator应该是不可重入的。
在实际问题解决中,还用到了下面这些概念:
Qt-隐式共享、GCC在C语言中内嵌汇编 asm volatile、常用的汇编指令、C++Union的用法(关键信息:使用C++联合,尤其是匿名联合,虽可以节省内存空间,但是也有一定的风险,如果通过一个不适当的数据成员获取当前对象的值!例如上面的ch、i交错访问,就是联合内存上存PartA的时候,却执行了PartB的访问)

  相关解决方案