我一直在使用PRETTY_FUNCTION来输出当前的函数名,但是我已经重新实现了一些函数,并希望找出哪些函数正在调用它们.
在C++中如何获取调用例程的函数名?
这是您经常使用的解决方案.它的优点是不需要更改实际的功能代码(不添加对stackwalk函数的调用,更改参数以传递函数名称,或链接到额外的库).要使其正常工作,您只需使用一些预处理器魔法:
// orignal function name was 'FunctionName' void FunctionNameReal(...) { // Do Something } #undef FunctionName #define FunctionName printf("Calling FunctionName from %s\n",__FUNCTION__);FunctionNameReal
您必须暂时重命名您的功能,但请参阅下面的注释以获取更多建议.这将在printf()
调用函数的每个点处产生一个语句.显然,你必须做一些安排,如果你正在调用一个成员函数,或者需要捕获返回值(比如传递函数调用和 __FUNCTION__
返回相同类型的自定义函数......),但基本技术是相同.您可能希望使用__LINE__
和/ __FILE__
或其他一些预处理器宏,具体取决于您拥有的编译器.(此示例专门用于MS VC++,但可能适用于其他人.)
此外,您可能希望将这样的内容放在由#ifdef
警卫包围的标题中,以有条件地将其打开,这也可以处理为您重命名实际功能.
我收到了扩大答案的请求.事实证明,我上面的例子有点简单.以下是使用C++处理此问题的一些完整编译示例.
使用class
with operator()
使得这非常简单.第一种技术适用于具有和不具有返回值的独立功能. operator()
只需要反映与相关函数相同的返回值,并具有匹配的参数.
您可以g++ -o test test.cpp
使用非报告版本和g++ -o test test.cpp -DREPORT
显示调用者信息的版本进行编译.
#includeint 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
基本上,在任何FunctionName
发生的地方,它都会替换它Reporter(__FUNCTION__,__FILE__,__LINE__)
,其净效果是预处理器通过立即调用operator()
函数来编写一些对象实例.您可以使用查看预处理器替换的结果(以gcc为单位)g++ -E -DREPORT test.cpp
.Caller2()成为:
void Caller2() { std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl; }
你可以看到,__LINE__
并__FILE__
已取代.(我不确定为什么__FUNCTION__
仍然在输出中显示为了诚实,但编译版本报告正确的功能,因此它可能与多次传递预处理或gcc错误有关.)
这有点复杂,但与前面的例子非常相似.我们也在替换类,而不是仅仅替换对函数的调用.
与上面的示例类似,您可以g++ -o test test.cpp
使用非报告版本和g++ -o test test.cpp -DREPORT
显示调用者信息的版本进行编译.
#includeclass 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()
执行实际的函数调用.
希望有人帮助!
这有两个选择:
您可以使用GNU回溯函数获得最新版本的glibc的完整堆栈跟踪(包括调用函数的名称,模块和偏移量).有关详细信息,请参阅我的答案.这可能是最简单的事情.
如果这不是您正在寻找的,那么您可以尝试libunwind,但它将涉及更多的工作.
请记住,这不是您可以静态知道的事情(与PRETTY_FUNCTION一样); 你实际上必须走堆栈来找出你的功能.所以这不是普通调试printfs真正值得做的事情.但是,如果您想进行更严格的调试或分析,那么这可能对您有用.
GCC版本≥4.8你可以使用__builtin_FUNCTION
- 不要混淆__FUNCTION__
和类似 - 它似乎有点模糊.
例:
#includevoid foobar(const char* str = __builtin_FUNCTION()){ std::printf("called by %s\n", str); } int main(){ foobar(); return 0; }
输出:
called by main
WandBox上的示例