如何查找调用函数的名称?

时间:2022-09-09 07:33:20

I have been using PRETTY_FUNCTION to output the current function name, however I have reimplemented some functions and would like to find out which functions are calling them.

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

In C++ how can I get the function name of the calling routine?

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

7 个解决方案

#1


17  

Here are two options:

这里有两个选择:

  1. You can get a full stacktrace (including the name, module, and offset of the calling function) with recent versions of glibc with the GNU backtrace functions. See my answer here for the details. This is probably the easiest thing.

    您可以获得完整的stacktrace(包括调用函数的名称、模块和补偿),使用最新版本的glibc和GNU回溯函数。详见我的答案。这可能是最简单的事情。

  2. If that isn't exactly what you're looking for, then you might try libunwind, but it's going to involve more work.

    如果这并不是你想要的,那么你可以试试libunwind,但是这需要更多的工作。

Keep in mind that this isn't something you can know statically (as with PRETTY_FUNCTION); you actually have to walk the stack to figure out what function called you. So this isn't something that's really worth doing in ordinary debug printfs. If you want to do more serious debugging or analysis, though, then this might be useful for you.

记住,这不是静态可以知道的东西(比如PRETTY_FUNCTION);实际上,你需要遍历堆栈才能找出调用你的函数。在普通的调试printfs中,这并不是真正值得做的事情。如果您希望进行更严格的调试或分析,那么这可能对您有用。

#2


39  

Here is a solution you can often use. It has the advantage of requiring no changes to the actual function code (no adding calls to stackwalk functions, changing parameters to pass in function names, or linking to extra libraries.). To get it working, you simply need to use a bit of preprocessor magic:

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

Simple Example

// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
  // Do Something
}

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

You must rename your function temporarily, but see the note below for more suggestions. This will result in a printf() statement at each point of calling the function. Obviously, you have to make some arrangements if you are calling a member function, or need to capture the return value (Like pass the function call and __FUNCTION__ to a custom function that returns the same type...), but the basic technique is the same. You might want to use __LINE__ and __FILE__ or some other preprocessor macros depending on which compiler you have. (This example is specifically for MS VC++, but probably works in others.)

您必须暂时重命名您的函数,但是请参阅下面的说明以获得更多的建议。这将在调用函数的每个点上产生printf()语句。显然,如果您正在调用一个成员函数,或者需要捕获返回值(比如将函数调用和__FUNCTION__传递给返回相同类型的自定义函数…),那么您必须做一些安排,但是基本的技术是相同的。您可能希望使用__LINE__和__FILE__或其他一些预处理器宏,这取决于您拥有哪个编译器。(这个例子是专门针对MS vc++的,但可能适用于其他的。)

Also, you might want to put something like this in your header surrounded by #ifdef guards to conditionally turn it on, which can handle renaming the actual function for you as well.

另外,您可能想要将类似的东西放在由#ifdef guard包围的header中,以有条件地打开它,它也可以为您处理重命名实际的函数。

UPDATE [2012-06-21]

I got a request to expand my answer. As it turns out, my above example is a bit simplistic. Here are some fully compiling examples of handling this, using C++.

我得到一个扩大我答案的请求。事实证明,我上面的例子有点简单。这里有一些使用c++处理此问题的完整编译示例。

Full Source Example with a return value

Using a class with operator() makes this pretty straight forward. This first technique works for freestanding functions with and without return values. operator() just needs to reflect the same return as the function in question, and have matching arguments.

使用带有操作符()的类可以使这一点非常直接。第一个技术适用于具有和没有返回值的独立函数。操作符()只需反映与问题函数相同的返回值,并具有匹配的参数。

You can compile this with g++ -o test test.cpp for a non-reporting version and g++ -o test test.cpp -DREPORT for a version that displays the caller information.

您可以使用g++ -o测试来编译它。cpp用于非报告版本和g++ -o测试。用于显示调用者信息的版本。

#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;
}

Sample Output (Reporting)

样例输出(报告)

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

Basically, anywhere that FunctionName occurs, it replaces it with Reporter(__FUNCTION__,__FILE__,__LINE__), the net effect of which is the preprocessor writing some object instancing with an immediate call to the operator() function. You can view the result (in gcc) of the preprocessor substitutions with g++ -E -DREPORT test.cpp. Caller2() becomes this:

基本上,函数名发生的任何地方,都会用Reporter(__FUNCTION__,__FILE__,__LINE__)替换它,它的净效果是预处理器通过立即调用operator()函数来编写一些对象实例。您可以使用g++ -E -DREPORT test.cpp来查看预处理器替换的结果(在gcc中)。某乙()就变成了这样:

void Caller2()
{
  std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl;
}

You can see that __LINE__ and __FILE__ have been substituted. (I'm not sure why __FUNCTION__ still shows in the output to be honest, but the compiled version reports the right function, so it probably has something to do with multi-pass preprocessing or a gcc bug.)

您可以看到__LINE__和__FILE__被替换。(老实说,我不确定__FUNCTION__为什么仍然显示在输出中,但是编译后的版本报告了正确的函数,所以它可能与多遍预处理或gcc bug有关。)

Full Source Example with a Class Member Function

This is a bit more complicated, but very similar to the previous example. Instead of just replacing the call to the function, we are also replacing the class.

这有点复杂,但与前面的例子非常相似。我们不仅要替换对函数的调用,还要替换类。

Like the above example, you can compile this with g++ -o test test.cpp for a non-reporting version and g++ -o test test.cpp -DREPORT for a version that displays the caller information.

像上面的示例一样,您可以使用g++ -o测试来编译它。cpp用于非报告版本和g++ -o测试。用于显示调用者信息的版本。

#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;
}

Here is sample output:

这是示例输出:

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

The high points of this version are a class that decorates the original class, and a replacement function that returns a reference to the class instance, allowing the operator() to do the actual function call.

该版本的高值是一个修饰原始类的类,以及一个替换函数,该函数返回对类实例的引用,允许操作符()执行实际的函数调用。

Hope that helps someone!

希望能帮助一些人!

#3


8  

With GCC version ≥ 4.8 you can use __builtin_FUNCTION — not to be confused with __FUNCTION__ and similar — it seems to be a bit obscure.

的GCC版本≥4.8可以使用__builtin_FUNCTION——而不是与__FUNCTION__混淆和类似的——这似乎有点模糊。

Example:

例子:

#include <cstdio>

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

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

output:

输出:

called by main

example on WandBox

例子WandBox

#4


2  

Unless there is more to the question than you explicitly asked, just rename the function and let the compiler/linker tell you where it is called.

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

#5


0  

You probably want the names of all functions that potentially could call them. This is basically a set of edges in the call graph. Doxygen can generate the call graph, and then it's simply a matter of looking at the incoming edges of your functions node.

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

#6


0  

In the firs approximation, just grep the codebase for the function names. Then comes Doxygen, and then dynamic logging (both discussed by others).

在firs近似中,只需为函数名提取代码基。然后是Doxygen,然后是动态日志记录(其他人都讨论过)。

#7


0  

You can use this code, to track loci of control in last n points in your program. Usage: see main function below.

您可以使用此代码,在程序的最后n个点上跟踪控件的轨迹。使用方法:主要功能如下。

// 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;
} 

#1


17  

Here are two options:

这里有两个选择:

  1. You can get a full stacktrace (including the name, module, and offset of the calling function) with recent versions of glibc with the GNU backtrace functions. See my answer here for the details. This is probably the easiest thing.

    您可以获得完整的stacktrace(包括调用函数的名称、模块和补偿),使用最新版本的glibc和GNU回溯函数。详见我的答案。这可能是最简单的事情。

  2. If that isn't exactly what you're looking for, then you might try libunwind, but it's going to involve more work.

    如果这并不是你想要的,那么你可以试试libunwind,但是这需要更多的工作。

Keep in mind that this isn't something you can know statically (as with PRETTY_FUNCTION); you actually have to walk the stack to figure out what function called you. So this isn't something that's really worth doing in ordinary debug printfs. If you want to do more serious debugging or analysis, though, then this might be useful for you.

记住,这不是静态可以知道的东西(比如PRETTY_FUNCTION);实际上,你需要遍历堆栈才能找出调用你的函数。在普通的调试printfs中,这并不是真正值得做的事情。如果您希望进行更严格的调试或分析,那么这可能对您有用。

#2


39  

Here is a solution you can often use. It has the advantage of requiring no changes to the actual function code (no adding calls to stackwalk functions, changing parameters to pass in function names, or linking to extra libraries.). To get it working, you simply need to use a bit of preprocessor magic:

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

Simple Example

// orignal function name was 'FunctionName'
void FunctionNameReal(...)
{
  // Do Something
}

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

You must rename your function temporarily, but see the note below for more suggestions. This will result in a printf() statement at each point of calling the function. Obviously, you have to make some arrangements if you are calling a member function, or need to capture the return value (Like pass the function call and __FUNCTION__ to a custom function that returns the same type...), but the basic technique is the same. You might want to use __LINE__ and __FILE__ or some other preprocessor macros depending on which compiler you have. (This example is specifically for MS VC++, but probably works in others.)

您必须暂时重命名您的函数,但是请参阅下面的说明以获得更多的建议。这将在调用函数的每个点上产生printf()语句。显然,如果您正在调用一个成员函数,或者需要捕获返回值(比如将函数调用和__FUNCTION__传递给返回相同类型的自定义函数…),那么您必须做一些安排,但是基本的技术是相同的。您可能希望使用__LINE__和__FILE__或其他一些预处理器宏,这取决于您拥有哪个编译器。(这个例子是专门针对MS vc++的,但可能适用于其他的。)

Also, you might want to put something like this in your header surrounded by #ifdef guards to conditionally turn it on, which can handle renaming the actual function for you as well.

另外,您可能想要将类似的东西放在由#ifdef guard包围的header中,以有条件地打开它,它也可以为您处理重命名实际的函数。

UPDATE [2012-06-21]

I got a request to expand my answer. As it turns out, my above example is a bit simplistic. Here are some fully compiling examples of handling this, using C++.

我得到一个扩大我答案的请求。事实证明,我上面的例子有点简单。这里有一些使用c++处理此问题的完整编译示例。

Full Source Example with a return value

Using a class with operator() makes this pretty straight forward. This first technique works for freestanding functions with and without return values. operator() just needs to reflect the same return as the function in question, and have matching arguments.

使用带有操作符()的类可以使这一点非常直接。第一个技术适用于具有和没有返回值的独立函数。操作符()只需反映与问题函数相同的返回值,并具有匹配的参数。

You can compile this with g++ -o test test.cpp for a non-reporting version and g++ -o test test.cpp -DREPORT for a version that displays the caller information.

您可以使用g++ -o测试来编译它。cpp用于非报告版本和g++ -o测试。用于显示调用者信息的版本。

#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;
}

Sample Output (Reporting)

样例输出(报告)

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

Basically, anywhere that FunctionName occurs, it replaces it with Reporter(__FUNCTION__,__FILE__,__LINE__), the net effect of which is the preprocessor writing some object instancing with an immediate call to the operator() function. You can view the result (in gcc) of the preprocessor substitutions with g++ -E -DREPORT test.cpp. Caller2() becomes this:

基本上,函数名发生的任何地方,都会用Reporter(__FUNCTION__,__FILE__,__LINE__)替换它,它的净效果是预处理器通过立即调用operator()函数来编写一些对象实例。您可以使用g++ -E -DREPORT test.cpp来查看预处理器替换的结果(在gcc中)。某乙()就变成了这样:

void Caller2()
{
  std::cout << "Mystery Function got " << Reporter(__FUNCTION__,"test.cpp",51)(11,13) << std::endl;
}

You can see that __LINE__ and __FILE__ have been substituted. (I'm not sure why __FUNCTION__ still shows in the output to be honest, but the compiled version reports the right function, so it probably has something to do with multi-pass preprocessing or a gcc bug.)

您可以看到__LINE__和__FILE__被替换。(老实说,我不确定__FUNCTION__为什么仍然显示在输出中,但是编译后的版本报告了正确的函数,所以它可能与多遍预处理或gcc bug有关。)

Full Source Example with a Class Member Function

This is a bit more complicated, but very similar to the previous example. Instead of just replacing the call to the function, we are also replacing the class.

这有点复杂,但与前面的例子非常相似。我们不仅要替换对函数的调用,还要替换类。

Like the above example, you can compile this with g++ -o test test.cpp for a non-reporting version and g++ -o test test.cpp -DREPORT for a version that displays the caller information.

像上面的示例一样,您可以使用g++ -o测试来编译它。cpp用于非报告版本和g++ -o测试。用于显示调用者信息的版本。

#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;
}

Here is sample output:

这是示例输出:

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

The high points of this version are a class that decorates the original class, and a replacement function that returns a reference to the class instance, allowing the operator() to do the actual function call.

该版本的高值是一个修饰原始类的类,以及一个替换函数,该函数返回对类实例的引用,允许操作符()执行实际的函数调用。

Hope that helps someone!

希望能帮助一些人!

#3


8  

With GCC version ≥ 4.8 you can use __builtin_FUNCTION — not to be confused with __FUNCTION__ and similar — it seems to be a bit obscure.

的GCC版本≥4.8可以使用__builtin_FUNCTION——而不是与__FUNCTION__混淆和类似的——这似乎有点模糊。

Example:

例子:

#include <cstdio>

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

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

output:

输出:

called by main

example on WandBox

例子WandBox

#4


2  

Unless there is more to the question than you explicitly asked, just rename the function and let the compiler/linker tell you where it is called.

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

#5


0  

You probably want the names of all functions that potentially could call them. This is basically a set of edges in the call graph. Doxygen can generate the call graph, and then it's simply a matter of looking at the incoming edges of your functions node.

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

#6


0  

In the firs approximation, just grep the codebase for the function names. Then comes Doxygen, and then dynamic logging (both discussed by others).

在firs近似中,只需为函数名提取代码基。然后是Doxygen,然后是动态日志记录(其他人都讨论过)。

#7


0  

You can use this code, to track loci of control in last n points in your program. Usage: see main function below.

您可以使用此代码,在程序的最后n个点上跟踪控件的轨迹。使用方法:主要功能如下。

// 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;
}