关于c ++:如何找到调用函数的名称?

How do I find the name of the calling function?

我一直在使用pretty_函数输出当前的函数名,但是我重新实现了一些函数,并想知道哪些函数正在调用它们。

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


这里有一个您经常使用的解决方案。它的优点是不需要更改实际的函数代码(不需要添加对stackwalk函数的调用、更改参数以传入函数名或链接到其他库)。要使其正常工作,只需使用一些预处理器魔术:

简单实例

1
2
3
4
5
6
7
8
9
// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
  // Do Something
}

#undef FunctionName
#define FunctionName printf("Calling FunctionName from %s
",__FUNCTION__);FunctionNameReal

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

另外,您可能希望在头部放置类似这样的东西,由#ifdef保护包围,以便有条件地打开它,它也可以为您处理对实际函数的重命名。

更新[2012-06-21]

我收到一个扩大我答案的请求。事实证明,我上面的例子有点简单。这里有一些完整的例子,用C++来处理。

带返回值的完整源代码示例

使用classoperator()使这一点非常直接。第一种技术适用于带有或不带返回值的独立函数。operator()只需要反映与所讨论的函数相同的返回,并具有匹配的参数。

对于非报告版本,您可以使用g++ -o test test.cpp进行编译,对于显示调用者信息的版本,可以使用g++ -o test test.cpp -DREPORT进行编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#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;
}

样本输出(报告)

1
2
3
4
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()变为:

1
2
3
4
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编译显示调用者信息的版本。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#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;
}

以下是示例输出:

1
2
3
4
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 backtrace函数的最新版本的glibc获得完整的stacktrace(包括调用函数的名称、模块和偏移量)。详情请参阅我的回答。这可能是最简单的事情。

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

  • 请记住,这不是您静态地可以知道的东西(比如漂亮的_函数);实际上,您必须遍历堆栈来找出调用您的函数。所以这在普通的调试打印文件中是不值得做的。不过,如果您想进行更为严肃的调试或分析,那么这可能对您有用。


    对于GCC版本≥4.8,您可以使用__builtin_FUNCTION,不要与__FUNCTION__和类似的版本混淆,它看起来有点模糊。

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    #include <cstdio>

    void foobar(const char* str = __builtin_FUNCTION()){
        std::printf("called by %s
    "
    , str);
    }

    int main(){
        foobar();
        return 0;
    }

    输出:

    1
    called by main

    魔杖盒示例


    除非问题比您明确要求的更多,否则只需重命名函数,让编译器/链接器告诉您调用它的位置。


    亚伦回答的变化。我不确定这个答案是否有这个问题,但是当你做一个#define function时,它会变成一个全局变量,那么,如果你的项目有几个具有相同成员类函数名的类,那么所有的类都会将它们的函数名重定义为相同的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #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;
    }

    结果:

    1
    2
    3
    4
    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

    您可以使用此代码跟踪程序中最后n个点的控制位置。用法:见下面的主要功能。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    // 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
    "
    ,
            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;
    }


    在FIRS近似中,只需对函数名的代码基进行grep。然后是doxygen,然后是动态日志(这两个都由其他人讨论)。


    您可能需要所有可能调用它们的函数的名称。这基本上是调用图中的一组边。Doxygen可以生成调用图,然后只需查看函数节点的传入边缘即可。