我如何找到调用函数的名称?

我一直在使用PRETTY_FUNCTION来输出当前的函数名称,但是我已经重新实现了一些函数,并想找出哪些函数正在调用它们。

在C ++中,我如何获得调用例程的函数名称?


这里有两个选项:

  • 您可以使用GNU backtrace函数获取最新版本的glibc的完整堆栈跟踪(包括调用函数的名称,模块和偏移量)。 有关详细信息,请参阅我的答案。 这可能是最简单的事情。

  • 如果这不正是你想要的,那么你可能会尝试libunwind,但它会涉及更多的工作。

  • 请记住,这不是静态可以知道的事情(与PRETTY_FUNCTION一样); 你实际上必须走栈来找出你叫什么功能。 所以这在普通的调试printf中并不值得。 但是,如果您想进行更严肃的调试或分析,那么这可能对您有用。


    这是您经常可以使用的解决方案。 它具有不需要修改实际功能代码的优点(不需要添加对栈函数的调用,改变参数以传递函数名称或链接到额外的库)。 为了得到它的工作,你只需要使用一些预处理器魔法:

    简单的例子

    // orignal function name was 'FunctionName'
    void FunctionNameReal(...)
    {
      // Do Something
    }
    
    #undef FunctionName
    #define FunctionName printf("Calling FunctionName from %sn",__FUNCTION__);FunctionNameReal
    

    您必须暂时重命名您的功能,但请参阅下面的注释获取更多建议。 这将导致在调用该函数的每个点都有一个printf()语句。 显然,如果你正在调用一个成员函数,或者需要捕获返回值(比如将函数调用和__FUNCTION__传递给返回相同类型的自定义函数...),则必须做出一些安排,但基本技巧是一样。 您可能需要使用__LINE____FILE__或其他一些预处理器宏,具体取决于您使用的编译器。 (这个例子专门用于MS VC ++,但可能适用于其他。)

    此外,您可能希望在#ifdef卫兵包围的标题中放置类似这样的内容,以便有条件地打开它,这也可以为您重命名实际功能。

    更新[2012-06-21]

    我收到了一个请求来扩展我的答案。 事实证明,我上面的例子有点简单。 以下是使用C ++处理这个问题的一些完整编译示例。

    具有返回值的完整源代码示例

    使用operator()class使得这非常简单。 这第一种技术适用于具有和不具有返回值的独立函数。 operator()只需要反映与正在讨论的函数相同的返回值,并且具有匹配的参数。

    您可以使用g++ -o test test.cpp编译此g++ -o test test.cpp为非报告版本g++ -o test test.cpp -DREPORT ,在代码中显示调用者信息的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
    

    基本上,在FunctionName出现的任何地方,它会用Reporter(__FUNCTION__,__FILE__,__LINE__)替换它,其效果是预处理器通过立即调用operator()函数编写一些对象实例。 您可以使用g++ -E -DREPORT test.cpp查看预处理器替换的结果(在gcc中)。 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用于非报告版本,用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 %sn", str);
    }
    
    int main(){
        foobar();
        return 0;
    }
    

    输出:

    called by main
    

    在WandBox上的例子

    链接地址: http://www.djcxy.com/p/86049.html

    上一篇: How do I find the name of the calling function?

    下一篇: How do you read a segfault kernel log message