Chinaunix首页 | 论坛 | 博客
  • 博客访问: 565147
  • 博文数量: 127
  • 博客积分: 1169
  • 博客等级: 少尉
  • 技术积分: 1298
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-16 14:29
个人简介

空白

文章分类

全部博文(127)

分类: C/C++

2014-11-01 04:52:30

原文地址:

遇到的问题: (题意请描述) :

程序执行一段时间(不固定, 一分钟以下) 会出现Alignment trap: xxx(pid) PC=0x0001645 .....

来自kernel的错误讯息, 现在已经把成是尽量精简.. 但还是不出错误, 也用 addr2line 这个程序将PC所指出的 function address印出, 但也是没有帮助... 请问要这类bug? 谢谢!

希望得到的正确结果:

程序出来的错误结果:

开发平台: (: VC++ or gcc/g++ or Dev-C++, Windows or Linux) : linux, gcc4

有问题的code (请善用置底文标色功能) :

补充说明:

PC 只是一个 Program Counter, 无法告诉问题发生时, 执行到一个 function. 如果 gdb 的话, 只要问题发生时, 使用 bt (backtrace) 这个命令, 即可知道问题发生的地方在. 但是, 所描述的这个问题, 只有 ARM 系列的 CPU 2.6.x kernel 才会看得到. 通常在 embedded device , 要用 gdb remote debug 也不是一件简单的事.

在下提供一个方法, 可以不需要 gdb, 使程序发生问题时印出 backtrace 的 内容, 配合 addr2line 找出问题发生的地方(档案,行数,function). 但在这 之前, 可以先试着使用 gcc 的检功能,

1. 编译时, 增加 -Wcast-align -Wpadded -Wpacked

2. 修正所有编译时出现的 Warning 如果程序代码不小, 那这可能会需要相当多的时间修正, 及重新编译, 有些地方 也可能修了之后, 打坏了原本的架构, 而且, 这功能只能告诉, "这样的程 式码, 有可能会发生这个问题", 不保证能指出正发生问题的地方. 如果上述这个方法仍不能解决的问题, , 后述的这个方法或许可以试试看. (后面的内容十分冗长)

 

[原理]

我们要在程序有问题的地方, 中断, 然后印出 backtrace 来让 addr2line . 因此, 我们分成以下三个步骤说明之.

1. signal 让程序在有问题的地方中断

2. 印出 backtrace

3. 使用 addr2line 来解析其内容

 

[实作方法]

1.  signal 让程序在有问题的地方中断:

kernel 的文件 -- Documentation/arm/mem_align 里有提到, 当有 memory alignment 的问题发生时, kernel 会做出一些处置, 所看 到的讯息, 即是 kernel 印出 warning, 这个行是可以透过 /proc 变更的. 以下的指令可以检视目前的设定,

# cat /proc/cpu/alignment

User: 0

System: 577

Skipped: 0

Half: 9076

Word: 4479

Multi: 0

User faults: 4 (signal) <----- 目前设定

关于 User faults, 有以下 5 种设定,

0 - ignore (默认)

1 – warn

2 - fixup

3 - fixup+warn

4 - signal

5 - signal+warn (我们需要这个)

我们需要将行模式设成 4 5, 理由是 4 5 都会在发生问题之时 送出 SIGBUS, 如果程序里没有 signal handler, process 就会被 kill . 我们要利用这个 signal 来中断有问题的地方, 并且印出 backtrace. 因此我们先更改模式 5,

# echo 5 > /proc/cpu/alignment

 

2.  印出 backtrace

这个部, 需要利用 #include 里的 backtrace(). 如果 source code 分成很多 .c .cpp 的话, 请找出 main() 所在的那个 档案来增加以下的 code, -----------------------------------------------------------------

#include

#include

#include

/* signal handler */

static void catch_sig(int sig) {

void *trace[128];

int n = backtrace(trace, sizeof(trace) / sizeof(trace[0]));

backtrace_symbols_fd(trace, n, 1);

exit(0);

}

/* 此函式指定 SIGBUS handler catch_sig() */

static void set_signals(void) {

struct sigaction act;

sigemptyset(&act.sa_mask);

act.sa_flags = 0;

act.sa_handler = catch_sig;

sigaction(SIGBUS, &act, NULL);

}

/* main() 的前头, 呼叫 set_signals() */

int main() {

set_signals();

 :

:

}

 -------------------------------------------------------------------

编译时, 务必增加 -g -rdynamic 选项.

: gcc -o prog -g -rdynamic prog.c

 

重新编译及执行后, 若发生 Alignment trap, kernel 会送给这个 process 一个 SIGBUS signal, 而我们在 set_signal() , 设定了收到 SIGBUS 时要执行 catch_sig(), 而在 catch_sig() , 我们利用 backtrace() 来 取得 stack frame 的地址之后, backtrace_symbols_fd() 来印出比较 看得的信息 (但还是得配合 addr2line 来解读), 印出的内容看起来像是 以下这样:

# ./prog
./prog[0x8048743] --+--->
前面这两行必指向 catch_sig(), 可以忽略 [0xffffe420] -------+

./prog(func+0x1d)[0x804878c] --> 问题发生在这里

./prog(main+0xa3)[0x804883d] --> main() 呼叫了 func() /lib/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7e21455]

./prog[0x8048691]

 

从这儿, 我们可以看到问题出现的地方在 func() 0x1d, 接下来, 下面 将介绍 addr2line 来更进一步解析问题出现的位置.

 

3.  addr2line

addr2line 的用法很简单, 如下所示:

用法: addr2line -e 执行文件 -f 地址

承上例:

# addr2line -e prog -f 0x804878c

func ---------------------> 函式名称

/home/haha/prog.c:312 ----> 档案及行数

 

最后, 只要再注意一件事即可, 就是 312 这个行数, 还不是问题发生的正确位置, 要如何取得确切的位置呢? 假设以下内容 prog.c 的一部,

309 void func() {

310    unsigned char *data = (unsigned char *)malloc(16);

-> 311     struct st_bug *ptr = (struct st_bug *)data;

* 312 printf("Hi, I am here\n");

:

387 }

 

我们发现到, 刚才addr2line解析出来的行数是312, 但是问题其实不在312, 而是 311, 那是因呼叫 function , 要把返回的地址记录在 Stack 里的缘故, 因此当程序执行到有问题的 311 行时, process 会接到 kernel 送来的 SIGBUS, 然而, 在呼叫 signal handler -- catch_sig() , catch_sig() 执行完后, 要回到的地址就是 312, 所以会先把 312 的地址 先存在 Stack , 再呼叫 catch_sig(), 而我们在 catch_sig() 里呼叫 backtrace() , 只是忠实的告诉我们每一个 function 被呼叫时, Stack 里所预存的返回地址是什而已. (亦即每一个 stack frame 当时所纪录的 地址). 因此, addr2line 看到的行数, 需要上移一行, 才是正问题发生的 地方. (在此例也就是指 311 )

 

[此方法的其他应用]

利用"实作方法" 2 3, 也可以用来捕捉 SIGSEGV (Segmentation Fault, 内存区段错误) 所发生的地方. 甚至是其他的 signal 发生时, 程序执行的位置.

 

[后记]

本来是睡不着, 才写这篇, 结果, 写完也不用睡了.... XD
阅读(4215) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~