提问人:Phil Hannent 提问时间:12/9/2008 最后编辑:Phil Hannent 更新时间:11/17/2023 访问量:99520
如何找到调用函数的名称?
How do I find the name of the calling function?
答:
以下是两个选项:
您可以使用带有 GNU 回溯函数的最新版本的 glibc 获得完整的堆栈跟踪(包括调用函数的名称、模块和偏移量)。有关详细信息,请参阅此处的答案。这可能是最简单的事情。
如果这不是您想要的,那么您可以尝试 libunwind,但它将涉及更多的工作。
请记住,这不是您可以静态知道的东西(就像PRETTY_FUNCTION一样);你实际上必须遍历堆栈才能弄清楚是什么函数调用了你。因此,这在普通的调试printfs中并不值得做。但是,如果您想进行更严肃的调试或分析,那么这可能对您有用。
您可能需要所有可能调用它们的函数的名称。这基本上是调用图中的一组边。Doxygen 可以生成调用图,然后只需查看函数节点的传入边缘即可。
评论
在第一个近似值中,只需 grep 函数名称的代码库即可。然后是 Doxygen,然后是动态日志记录(两者都由其他人讨论过)。
这是您经常使用的解决方案。它的优点是不需要更改实际函数代码(无需添加对 stackwalk 函数的调用、更改参数以传入函数名称或链接到额外的库)。要让它工作,你只需要使用一些预处理器的魔力:
简单示例
// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
// Do Something
}
#undef FunctionName
#define FunctionName printf("Calling FunctionName from %s\n",__FUNCTION__);FunctionNameReal
您必须暂时重命名函数,但有关更多建议,请参阅下面的注释。这将在调用函数的每个点生成一个语句。显然,如果你正在调用一个成员函数,或者需要捕获返回值(比如将函数调用传递给返回相同类型的自定义函数...),你必须做一些安排,但基本技术是相同的。您可能需要使用 和/或一些其他预处理器宏,具体取决于您拥有的编译器。(此示例专门用于 MS VC++,但可能适用于其他示例。printf()
__FUNCTION__
__LINE__
__FILE__
此外,您可能希望在被守卫包围的标头中放置这样的东西,以有条件地打开它,这也可以为您重命名实际函数。#ifdef
更新 [2012-06-21]
我收到了扩展我的答案的请求。事实证明,我上面的例子有点简单化。以下是一些使用 C++ 处理此问题的完整编译示例。
具有返回值的完整源代码示例
使用 with 使这变得非常简单。第一种技术适用于有和没有返回值的独立函数。 只需要反映与相关函数相同的返回值,并具有匹配的参数。class
operator()
operator()
您可以为非报告版本和显示调用方信息的版本编译此版本。g++ -o test test.cpp
g++ -o test test.cpp -DREPORT
#include <iostream>
int FunctionName(int one, int two)
{
static int calls=0;
return (++calls+one)*two;
}
#ifdef REPORT
// class to capture the caller and print it.
class Reporter
{
public:
Reporter(std::string Caller, std::string File, int Line)
: caller_(Caller)
, file_(File)
, line_(Line)
{}
int operator()(int one, int two)
{
std::cout
<< "Reporter: FunctionName() is being called by "
<< caller_ << "() in " << file_ << ":" << line_ << std::endl;
// can use the original name here, as it is still defined
return FunctionName(one,two);
}
private:
std::string caller_;
std::string file_;
int line_;
};
// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of Reporter initialized with the caller
# undef FunctionName
# define FunctionName Reporter(__FUNCTION__,__FILE__,__LINE__)
#endif
void Caller1()
{
int val = FunctionName(7,9); // <-- works for captured return value
std::cout << "Mystery Function got " << val << std::endl;
}
void Caller2()
{
// Works for inline as well.
std::cout << "Mystery Function got " << FunctionName(11,13) << std::endl;
}
int main(int argc, char** argv)
{
Caller1();
Caller2();
return 0;
}
示例输出(报告)
Reporter: FunctionName() is being called by Caller1() in test.cpp:44
Mystery Function got 72
Reporter: FunctionName() is being called by Caller2() in test.cpp:51
Mystery Function got 169
基本上,无论发生什么,它都会将其替换为 ,其最终效果是预处理器通过立即调用函数来编写一些对象实例化。您可以使用 查看预处理器替换的结果(以 gcc 为单位)。Caller2() 变为:FunctionName
Reporter(__FUNCTION__,__FILE__,__LINE__)
operator()
g++ -E -DREPORT test.cpp
void Caller2()
{
std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl;
}
你可以看到这一点,并且已经被替换了。(老实说,我不确定为什么仍然显示在输出中,但编译版本报告了正确的函数,因此它可能与多通道预处理或 gcc 错误有关。__LINE__
__FILE__
__FUNCTION__
具有类成员函数的完整源代码示例
这有点复杂,但与前面的示例非常相似。我们不仅替换了对函数的调用,还替换了类。
与上面的示例一样,您可以为非报告版本和显示调用方信息的版本编译此版本。g++ -o test test.cpp
g++ -o test test.cpp -DREPORT
#include <iostream>
class ClassName
{
public:
explicit ClassName(int Member)
: member_(Member)
{}
int FunctionName(int one, int two)
{
return (++member_+one)*two;
}
private:
int member_;
};
#ifdef REPORT
// class to capture the caller and print it.
class ClassNameDecorator
{
public:
ClassNameDecorator( int Member)
: className_(Member)
{}
ClassNameDecorator& FunctionName(std::string Caller, std::string File, int Line)
{
std::cout
<< "Reporter: ClassName::FunctionName() is being called by "
<< Caller << "() in " << File << ":" << Line << std::endl;
return *this;
}
int operator()(int one, int two)
{
return className_.FunctionName(one,two);
}
private:
ClassName className_;
};
// remove the symbol for the function, then define a new version that instead
// creates a stack temporary instance of ClassNameDecorator.
// FunctionName is then replaced with a version that takes the caller information
// and uses Method Chaining to allow operator() to be invoked with the original
// parameters.
# undef ClassName
# define ClassName ClassNameDecorator
# undef FunctionName
# define FunctionName FunctionName(__FUNCTION__,__FILE__,__LINE__)
#endif
void Caller1()
{
ClassName foo(21);
int val = foo.FunctionName(7,9); // <-- works for captured return value
std::cout << "Mystery Function got " << val << std::endl;
}
void Caller2()
{
ClassName foo(42);
// Works for inline as well.
std::cout << "Mystery Function got " << foo.FunctionName(11,13) << std::endl;
}
int main(int argc, char** argv)
{
Caller1();
Caller2();
return 0;
}
下面是示例输出:
Reporter: ClassName::FunctionName() is being called by Caller1() in test.cpp:56
Mystery Function got 261
Reporter: ClassName::FunctionName() is being called by Caller2() in test.cpp:64
Mystery Function got 702
这个版本的亮点是一个装饰原始类的类,以及一个返回对类实例的引用的替换函数,允许执行实际的函数调用。operator()
评论
除非问题比你明确提出的要多,否则只需重命名该函数,并让编译器/链接器告诉你它的调用位置。
评论
使用 GCC 4.8 ≥ 版本,您可以使用——不要混淆和类似——它似乎有点晦涩难懂。__builtin_FUNCTION
__FUNCTION__
例:
#include <cstdio>
void foobar(const char* str = __builtin_FUNCTION()){
std::printf("called by %s\n", str);
}
int main(){
foobar();
return 0;
}
输出:
called by main
评论
()
__builtin_FUNCTION()
__builtin_PRETTY_FUNCTION()
__builtin_LINE()
__builtin_FILE()
您可以使用此代码来跟踪程序中最后 n 个点的控制位点。用法:见下面的main功能。
// What: Track last few lines in loci of control, gpl/moshahmed_at_gmail
// Test: gcc -Wall -g -lm -std=c11 track.c
#include <stdio.h>
#include <string.h>
#define _DEBUG
#ifdef _DEBUG
#define lsize 255 /* const int lsize=255; -- C++ */
struct locs {
int line[lsize];
char *file[lsize];
char *func[lsize];
int cur; /* cur=0; C++ */
} locs;
#define track do {\
locs.line[locs.cur]=__LINE__ ;\
locs.file[locs.cur]=(char*)__FILE__ ;\
locs.func[locs.cur]=(char*) __builtin_FUNCTION() /* __PRETTY_FUNCTION__ -- C++ */ ;\
locs.cur=(locs.cur+1) % lsize;\
} while(0);
void track_start(){
memset(&locs,0, sizeof locs);
}
void track_print(){
int i, k;
for (i=0; i<lsize; i++){
k = (locs.cur+i) % lsize;
if (locs.file[k]){
fprintf(stderr,"%d: %s:%d %s\n",
k, locs.file[k],
locs.line[k], locs.func[k]);
}
}
}
#else
#define track do {} while(0)
#define track_start() (void)0
#define track_print() (void)0
#endif
// Sample usage.
void bar(){ track ; }
void foo(){ track ; bar(); }
int main(){
int k;
track_start();
for (k=0;k<2;k++)
foo();
track;
track_print();
return 0;
}
评论
if (!locs.file[k]) break;
track_print()
locs.file[locs.cur] = NULL;
track
亚伦答案的变体。我不确定这个答案是否有这个问题,但是当你做一个 时,它就变成了一个全局变量,那么,如果你的项目有几个具有相同成员类函数名称的类,那么所有类的函数名称都会被重新定义为同一个函数。#define function
#include <iostream>
struct ClassName {
int member;
ClassName(int member) : member(member) { }
int secretFunctionName(
int one, int two, const char* caller, const char* file, int line)
{
std::cout << "Reporter: ClassName::function_name() is being called by "
<< caller << "() in " << file << ":" << line << std::endl;
return (++member+one)*two;
}
};
#define unique_global_function_name(first, second) \
secretFunctionName(first, second, __FUNCTION__,__FILE__,__LINE__)
void caller1() {
ClassName foo(21);
int val = foo.unique_global_function_name(7, 9);
std::cout << "Mystery Function got " << val << std::endl;
}
void caller2() {
ClassName foo(42);
int val = foo.unique_global_function_name(11, 13);
std::cout << "Mystery Function got " << val << std::endl;
}
int main(int argc, char** argv) {
caller1();
caller2();
return 0;
}
结果:
Reporter: ClassName::function_name() is being called by caller1() in D:\test.cpp:26
Mystery Function got 261
Reporter: ClassName::function_name() is being called by caller2() in D:\test.cpp:33
Mystery Function got 702
Cflow 可用于获取用 C/C++ 编写的源代码的调用图。您可以解析此调用图以获取所需的内容。
将 __builtin_return_address 和 dladdr 结合在 C++、C、Objective-C 和 Objective-C++ 中工作:
#include <dlfcn.h>
Dl_info info;
if (dladdr(__builtin_return_address(0), &info)) {
printf("%s called by %s", __builtin_FUNCTION(), info.dli_sname);
}
请注意,需要动态链接的程序:
若要动态链接程序,可能需要将 or 添加为选项(源)。dladdr
-rdynamic
-Wl,--export-dynamic
标准::source_location
例:
#include <iostream>
#include <source_location>
void some_function(std::source_location fun = std::source_location::current())
{
std::cout << fun.function_name() << "\n";
}
void some_other_function()
{
some_function();
}
int main()
{
some_other_function();
}
输出:
void __cdecl some_other_function(void)
评论