一般情况下,编译的时候可能需要加 -g 选项,对于android ndk的-g选项添加请参见android类目下的另一篇文章。

以下文章中的__builtin_return_address() 宏,若要确认它是否在编译器源码中存在,可以在编译器源码中搜索 return_address, 若找到则可以直接使用(不同架构的编译器调用方法可能不同),而且同时也搜索到了需要包含哪些头文件。

__builtin_return_address() 宏简单好用,如果只支持层级0,则只有不断的循环用0来找调用者的调用者了。

http://codingrelic.geekhold.com/2009/05/pre-mortem-backtracing.html

A backtrace is often the first step in debugging a problem. Generating a backtrace is generally thought of as a function of the debugger, on a core file after a process has died. However it is sometimes quite useful to generate a live backtrace while a process runs. For example, crashing the process in the field may not be acceptable if a problem is survivable. Logging a backtrace and other information can provide enough to locate the root cause, without having to trigger any customer downtime.

gcc backtrace support

The simplest way to get a crude backtrace is the __builtin_return_address() macro supplied by gcc. You provide the frame number you want to retrieve, and get the return address for that stack frame:

void do_backtrace2()
{
void *pc0 = __builtin_return_address(0);
void *pc1 = __builtin_return_address(1);
void *pc2 = __builtin_return_address(2);
void *pc3 = __builtin_return_address(3); printf("Frame 0: PC=%p\n", pc0);
printf("Frame 1: PC=%p\n", pc1);
printf("Frame 2: PC=%p\n", pc2);
printf("Frame 3: PC=%p\n", pc3);
}

This code will produce the following output:

Frame 0: PC=0x80483ca
Frame 1: PC=0x80483e1
Frame 2: PC=0x62079d
Frame 3: PC=0x80482b9

__builtin_return_address() has significant limitations. It constructs code at compile time to walk back through the stack frames. That means you cannot use a variable in a loop, you can only use integer constants like the 0,1,2,3 shown above. Also on some architectures, including my beloved MIPS, only __builtin_return_address(0) works. MIPS has no frame pointer, making it difficult to walk back up the stack. Frame 0 can use the return address register directly.

glibc's backtrace()

glibc contains a simple backtrace function, which is somewhat more powerful than __builtin_return_address(). The backtrace() call populates an array with the program counter of each calling function, while a separate backtrace_symbols() call can look up the symbolic names for each address:

#include <execinfo.h>

#define BACKTRACE_SIZ   64
void do_backtrace()
{
void *array[BACKTRACE_SIZ];
size_t size, i;
char **strings; size = backtrace(array, BACKTRACE_SIZ);
strings = backtrace_symbols(array, size); for (i = 0; i < size; i++) {
printf("%p : %s\n", array[i], strings[i]);
} free(strings); // malloced by backtrace_symbols
}

The output shows the backtrace with the address of each function call site:

# gcc -g -o backtrace ./backtrace.c
# ./backtrace
0x8048422 : ./backtrace(backtrace_symbols+0xd6) [0x8048422]
0x80484be : ./backtrace(backtrace_symbols+0x172) [0x80484be]
0x80484d5 : ./backtrace(backtrace_symbols+0x189) [0x80484d5]
0x071479d : /lib/tls/libc.so.6(__libc_start_main+0xed) [0x71479d]
0x804837d : ./backtrace(backtrace_symbols+0x31) [0x804837d]

To get useful symbolic names, the -rdynamic option must be passed to the linker:

# gcc -g -rdynamic -o backtrace ./backtrace.c
# ./backtrace
0x804874a : ./backtrace(do_backtrace+0x1a) [0x804874a]
0x80487e6 : ./backtrace(foo1+0xb) [0x80487e6]
0x80487fd : ./backtrace(main+0x15) [0x80487fd]
0x012679d : /lib/tls/libc.so.6(__libc_start_main+0xed) [0x12679d]
0x80486a5 : ./backtrace(backtrace_symbols+0x31) [0x80486a5]

There is also a backtrace_symbols_fd() function, which nicely prints the output to a file descriptor without having to malloc a table of strings. If thats all you're trying to do, it is a simpler API.

As an aside: notice how the address of __libc_start_main varies in the examples above, 0x62079d versus 0x71479d versus 0x12679d? That is address space randomization in action. libc is mapped at a randomized base address every time a binary is started. The offset of __libc_start_main within the page is a constant 0x79d, but the upper bits of the address will vary from one run to the next. This helps prevent buffer overflow and other code injection attacks, by randomizing the address of library routines.

libunwind

libunwind is a library for extracting call chain information. It supports many different CPU architectures. Here is an example of using libunwind to accomplish a similar result as glibc's backtrace() function:

#include <libunwind.h>

void do_backtrace2()
{
unw_cursor_t cursor;
unw_context_t context; unw_getcontext(&context);
unw_init_local(&cursor, &context); while (unw_step(&cursor) > 0) {
unw_word_t offset, pc;
char fname[64]; unw_get_reg(&cursor, UNW_REG_IP, &pc); fname[0] = '\0';
(void) unw_get_proc_name(&cursor, fname, sizeof(fname), &offset); printf ("%p : (%s+0x%x) [%p]\n", pc, fname, offset, pc);
}
}

The output:

0x80486b3 : (foo+0xb) [0x80486b3]
0x80486ca : (main+0x15) [0x80486ca]
0x016379d : (__libc_start_main+0xed) [0x16379d]
0x80484c9 : (_start+0x21) [0x80484c9]

That is quite a bit more code to get a simple backtrace, but libunwind offers more capability to examine the program state at each frame. For example, one can print the saved register values:

#include <libunwind.h>

void do_backtrace2()
{
unw_cursor_t cursor;
unw_context_t context; unw_getcontext(&context);
unw_init_local(&cursor, &context);
while (unw_step(&cursor) > 0) {
unw_word_t offset;
unw_word_t pc, eax, ebx, ecx, edx;
char fname[64]; unw_get_reg(&cursor, UNW_REG_IP, &pc);
unw_get_reg(&cursor, UNW_X86_EAX, &eax);
unw_get_reg(&cursor, UNW_X86_EDX, &edx);
unw_get_reg(&cursor, UNW_X86_ECX, &ecx);
unw_get_reg(&cursor, UNW_X86_EBX, &ebx); fname[0] = '\0';
unw_get_proc_name(&cursor, fname, sizeof(fname), &offset);
printf ("%p : (%s+0x%x) [%p]\n", pc, fname, offset, pc);
printf("\tEAX=0x%08x EDX=0x%08x ECX=0x%08x EBX=0x%08x\n",
eax, edx, ecx, ebx);
}
}

The output:

0x80486b3 : (foo1+0xb) [0x80486b3]
EAX=0x00000000 EDX=0x00b548b0 ECX=0x00000000 EBX=0x00000000
0x80486ca : (main+0x15) [0x80486ca]
EAX=0x00000000 EDX=0x00b548b0 ECX=0x00000000 EBX=0x00000000
0x044879d : (__libc_start_main+0xed) [0x44879d]
EAX=0x00000000 EDX=0x003368b0 ECX=0x00000000 EBX=0x00000000
0x80484c9 : (_start+0x21) [0x80484c9]
EAX=0x00000000 EDX=0x00b548b0 ECX=0x00000000 EBX=0x00000000

When would this be useful? Given the relative costs, it is not unusual to have an embedded CPU with considerably more RAM than flash. In the event of a crash there may not be enough flash to save a full core file. Having the process deliberately catch SIGSEGV and dump its own backtrace with register values means you'd at least have something to work with even if there is no core file.

libunwind with Android ARM support:        https://github.com/ehsan/libunwind

Conclusion

Over time I think I have used __builtin_return_address(0) more often than any of the other techniques. Whether constructing simple performance instrumentation or logging problems from the field, knowing the caller has often been sufficient. For more extensive backtrace functionality I end up using libunwind. The backtrace() function in glibc always seems to be too heavy for the simple stuff yet not sufficient for the complex stuff.

linux c 及 c++打印调用者函数caller function的方法,包括arm c平台的更多相关文章

  1. 【Linux开发】为qt-embedded添加jpeg库的交叉编译方法for arm

    看了一个文章: =====================================谢论坛内各位的帮助,我的qt/e2.3.10和qtopia2.1.1终于全部编译通过. 下面是jpeg和uui ...

  2. Linux 内核启动信息的打印 --- dev_driver_string函数/dev_name函数

    内核启动时,常会打印出一些信息:开头是 "驱动模块的名字: + 具体的信息" 如:在运行的linux系统设备上,插入鼠标,就会打印出鼠标的相关信息; [ 402.134068] i ...

  3. 使用linux backtrace打印出错函数堆栈信息

    一般察看函数运行时堆栈的方法是使用GDB(bt命令)之类的外部调试器,但是,有些时候为了分析程序的BUG,(主要针对长时间运行程序的分析),在程序出错时打印出函数的调用堆栈是非常有用的. 在glibc ...

  4. linux 内核(驱动)常用函数

    2.4.1 内存申请和释放 include/linux/kernel.h里声明了kmalloc()和kfree().用于在内核模式下申请和释放内存.    void *kmalloc(unsigned ...

  5. PHP 打印调用函数入口地址(堆栈)

    今天网站出现一个BUG,然后直接在数据库类里面写日志,看是哪条SQL出了问题,SQL语句到是找到了,但是不知道这条SQL语句来自何处,于是就想啊,如果能有一个办法,查看当前正在运行的这个方法是被哪个方 ...

  6. linux下c程序调用reboot函数实现直接重启【转】

    转自:http://www.blog.chinaunix.net/uid-20564848-id-73878.html linux下c程序调用reboot函数实现直接重启 当然你也可以直接调用syst ...

  7. PHP 打印调用函数入口地址(堆栈),方便调式

    今天网站出现一个BUG,然后直接在数据库类里面写日志,看是哪条SQL出了问题,SQL语句到是找到了,但是不知道这条SQL语句来自何处,于是就想啊,如果能有一个办法,查看当前正在运行的这个方法是被哪个方 ...

  8. linux网络编程九:splice函数,高效的零拷贝

    from:http://blog.csdn.net/jasonliuvip/article/details/22600569 linux网络编程九:splice函数,高效的零拷贝 最近在看<Li ...

  9. Linux pwn入门教程(10)——针对函数重定位流程的几种攻击

    作者:Tangerine@SAINTSEC 本系列的最后一篇 感谢各位看客的支持 感谢原作者的付出一直以来都有读者向笔者咨询教程系列问题,奈何该系列并非笔者所写[笔者仅为代发]且笔者功底薄弱,故无法解 ...

随机推荐

  1. 在Ubuntu Linux下怎样安装QQ

    最近好多人在吐槽Linux下上QQ简直就是煎熬,网页版的不方便,网上各种版本的QQ要么是功能不全.要么是界面丑到爆,要么是运行不稳定.那么这次为大家带来一个功能完整.运行稳定的wineQQ安装过程. ...

  2. unity3d 使用背景贴图

    使用贴图代替天空盒作为背景,参照:http://www.narkii.com/club/thread-261840-1.html 看看我做的:

  3. Bootstrap简易使用指南

    1.框架 1.1全局样式 使用HTML5的doctype,scaffolding.less中定义全局样式,从2开始使用normalize.css,并使用reset.less进行简化 1.2默认栅格系统 ...

  4. 上下切换js

    <div class="wview"> <span class="prevs" id="prevs-j"></ ...

  5. 关于hibernate的实体类中有集合类型转化成JSON的工具类 - 怀念今天的专栏 - 博客频道

    Json 来源:http://blog.csdn.net/zczzsq/article/details/18697045#1536434-hi-1-4387-42d97150898b1af15ddaa ...

  6. Codeforces Round #210

    A:简单题: #include<cstdio> using namespace std; int n,k; int main() { scanf("%d%d",& ...

  7. Codeforces Round #239 (Div. 2) C. Triangle

    time limit per test:1 secondmemory limit per test:256 megabytesinput:standard inputoutput:standard o ...

  8. javaweb学习总结(三十七)——获得MySQL数据库自动生成的主键

    测试脚本如下: 1 create table test1 2 ( 3 id int primary key auto_increment, 4 name varchar(20) 5 ); 测试代码: ...

  9. SPRING IN ACTION 第4版笔记-第四章ASPECT-ORIENTED SPRING-009-带参数的ADVICE2 配置文件为XML

    一. 1.配置文件为xml时则切面类不用写aop的anotation package com.springinaction.springidol; public class Magician impl ...

  10. Java调用存储过程时报 The user specified as a definer ('root'@'%') does not exist 解决方法

    Caused by: java.sql.SQLException: The user specified as a definer (''@'') does not exist        at c ...