Alignment trap

    xiaoxiao2021-03-25  35

    遇到的问题: (题意请描述清楚) : 程序执行一段时间(不固定, 一分钟以下) 会出现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

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

    /* 此为 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 来更进一步解析问题出现的位置.

    转载请注明原文地址: https://ju.6miu.com/read-150131.html

    最新回复(0)