如何获取调用函数的名称?

时间:2011-06-06 12:10:21

标签: c gcc function-pointers

我正在使用gnu工具链。我怎样才能在运行时找到函数的调用者?例如,函数B()被许多函数使用函数指针调用。现在,每当B被调用时,我想打印调用者名称。我需要这个来调试某个问题。

3 个答案:

答案 0 :(得分:18)

如果您使用的是GNU,则可以使用backtrace功能。在该手册页上有一个使用示例。

答案 1 :(得分:9)

对您的函数的调用的代码位置__builtin_return_address()内在函数中的gcc保存。要检索名称,您必须解析程序的符号表;虽然可以做到,但是通过dladdr(),这是有限制的:

  1. 在所有上下文中调用backtrace() / dladdr()(例如,来自信号处理程序,或同时在多线程程序中,或从无法调用的上下文{{1})可能不安全}})。
  2. 操作不是即时的,但可能需要将调用线程置于休眠状态;如果在通话时保持任何锁定,这是一件坏事。
  3. 返回地址在名称中的“可解析”程度有限;例如如果内联函数调用了您的函数,那么调用者的调用者将会显示(malloc()联机帮助页也会显示此内容,而backtrace()中的dladdr()也是如此。 BUGS“部分)。
  4. 在C ++中,通过con / destructors / initializers进行调用还存在额外的挑战,其中函数的“调用者”可能最终成为编译器生成的元代码,而您感兴趣的实际位置可能在其他地方(调用者的调用者,甚至更深的调用栈。)
  5. 通常是解耦跟踪和功能名称解析的更好方法;即只输出返回地址(作为十六进制/二进制),然后根据程序运行时检索到的符号表对结果日志进行后处理。

答案 2 :(得分:5)

Vasil Dimov在回答类似问题时指出的另一种方法是用报告或传递调用函数名称的包装宏替换函数调用。这将适用于内联函数,其中backtrace不会。另一方面,如果您通过引用调用函数或以其他方式获取其地址,它将无法工作。

例如:

int B(int x){
    ...
}

可能会成为:

int _B(int x, char *caller){
    printf("caller is %s\n", caller);
    ...
}

#define B(x) _B((x), __func__)

并且每次调用B()都会打印调用者名称。 Vasil Dimov以不同的方式构造它,直接在宏中打印名称并保持函数不变。