Linux 的 function 追踪

在学习开源代码或者接触到老项目的时候,对于复杂的函数调用流程,往往会感觉晕头转向。这个时候如果能打印出函数的执行流程,对于我们了解项目会很有帮助,下面介绍一下在Linux下打印函数调用流程的技术。

1. 使用gcc和g++的 -finstrument-functions 选项

-finstrument-functions 选项的意思是在函数的入口处自动的插入 __cyg_profile_func_enter 在函数的出口自动加入 __cyg_profile_func_exit ,通过自己实现这两个函数,就可以打印出调用流程了。

2. function_trace.c

实现的一个模板如下:

//function_trace.c
#include  <stdio.h>
#define __USE_GNU
#include  <dlfcn.h>
static FILE *fp_trace=NULL;
static int nDepth = 0;
void  __cyg_profile_func_enter(void *func, void *caller) __attribute__((no_instrument_function));
void  __cyg_profile_func_exit(void *func, void *caller) __attribute__((no_instrument_function));

void __attribute__((constructor)) traceBegin(void) {
  fp_trace = fopen("func_trace.out", "w");
}

void __attribute__((destructor)) traceEnd(void) {
  if (fp_trace != NULL) {
    fclose(fp_trace);
    fp_trace = NULL;
  }
}


void __cyg_profile_func_enter(void *func, void *caller) 
{
    nDepth ++;
    for(int i = 0 ; i   nDepth ; i++)
    {
        if(fp_trace)
        {
            fprintf(fp_trace,"\t");
        }
        printf("\t");
    }
    Dl_info info;
    if (dladdr(func, &info) != 0) 
    {
        if(fp_trace)
        {
            fprintf(fp_trace,"Enter:[%s,%s] \n",info.dli_fname,info.dli_sname);
        }
        printf("Enter:[%s,%s] \n",info.dli_fname,info.dli_sname);
    }
}

void __cyg_profile_func_exit(void *func, void *caller) {

    for(int i = 0 ; i   nDepth ; i++)
    {
        if(fp_trace)
        {
            fprintf(fp_trace,"\t");
        }
        printf("\t");
    }
    nDepth --;

    Dl_info info;
    if (dladdr(func, &info) != 0) 
    {        
        if(fp_trace)
        {
            fprintf(fp_trace,"Enter:[%s,%s] \n",info.dli_fname,info.dli_sname);
        }
        printf("Leave:[%s,%s] \n",info.dli_fname,info.dli_sname);
    }
}

编译命令:

  gcc function_trace.c -c

3 主文件示例

functrace_main.cpp

//functrace_main.cpp 
#include  <stdio.h>

class CBase
{
public:
    explicit CBase()
    {

    }

    virtual ~CBase()
    {

    }

    virtual void Print()
    {
       printf("%s\n",__PRETTY_FUNCTION__);
    }
};

class CDriveOne:public CBase
{
public:
  CDriveOne()
  {

  } 
  virtual ~CDriveOne()
  {

  } 

  virtual void Print() override 
  {
     printf("%s\n",__PRETTY_FUNCTION__);
  }
};

class CDriveTwo:public CBase
{
public:
  CDriveTwo()
  {

  } 
  virtual ~CDriveTwo()
  {

  } 

  virtual void Print() override 
  {
     printf("%s\n",__PRETTY_FUNCTION__);
  }
};


void Overload()
{
  printf("%s\n",__PRETTY_FUNCTION__);
}

void Overload(int x)
{
  printf("%s\n",__PRETTY_FUNCTION__);
}

void FuncOuter()
{

}

void FuncMedium()
{
    FuncOuter();
}

void FuncInner()
{
    FuncOuter();
    FuncMedium();
}

int main(int argc, char **argv) { 

  {
    printf("----------------------------Drive override Test Begin\n");
    CBase * pFirst = new CDriveOne();
    pFirst->Print();

    CBase * pSecond = new CDriveTwo();
    pSecond->Print();

    delete pFirst;
    delete pSecond;
    printf("----------------------------Drive override Test End\n");
  }

  {
    printf("----------------------------Override  Test Begin\n");
    Overload();
    Overload(2);
    printf("----------------------------Override  Test End\n");
  }  
  {
    printf("----------------------------Simple  Test Begin\n");
    FuncOuter();
    FuncMedium();
    FuncInner();
    printf("----------------------------Simple  Test End\n");
  }
  return 0;
}

编译命令:

g++ -g function_trace.o functrace_main.cpp  -ldl -finstrument-functions -export-dynamic

4 程序输出

Enter:[./a.out,main]
----------------------------Drive override Test Begin
                Enter:[./a.out,_ZN9CDriveOneC1Ev]
                        Enter:[./a.out,_ZN5CBaseC1Ev]
                        Leave:[./a.out,_ZN5CBaseC1Ev]
                Leave:[./a.out,_ZN9CDriveOneC1Ev]
                Enter:[./a.out,_ZN9CDriveOne5PrintEv]
virtual void CDriveOne::Print()
                Leave:[./a.out,_ZN9CDriveOne5PrintEv]
                Enter:[./a.out,_ZN9CDriveTwoC2Ev]
                        Enter:[./a.out,_ZN5CBaseC1Ev]
                        Leave:[./a.out,_ZN5CBaseC1Ev]
                Leave:[./a.out,_ZN9CDriveTwoC2Ev]
                Enter:[./a.out,_ZN9CDriveTwo5PrintEv]
virtual void CDriveTwo::Print()
                Leave:[./a.out,_ZN9CDriveTwo5PrintEv]
                Enter:[./a.out,_ZN9CDriveOneD0Ev]
                        Enter:[./a.out,_ZN9CDriveOneD2Ev]
                                Enter:[./a.out,_ZN5CBaseD2Ev]
                                Leave:[./a.out,_ZN5CBaseD2Ev]
                        Leave:[./a.out,_ZN9CDriveOneD2Ev]
                Leave:[./a.out,_ZN9CDriveOneD0Ev]
                Enter:[./a.out,_ZN9CDriveTwoD0Ev]
                        Enter:[./a.out,_ZN9CDriveTwoD1Ev]
                                Enter:[./a.out,_ZN5CBaseD2Ev]
                                Leave:[./a.out,_ZN5CBaseD2Ev]
                        Leave:[./a.out,_ZN9CDriveTwoD1Ev]
                Leave:[./a.out,_ZN9CDriveTwoD0Ev]
----------------------------Drive override Test End
----------------------------Override  Test Begin
                Enter:[./a.out,_Z8Overloadv]
void Overload()
                Leave:[./a.out,_Z8Overloadv]
                Enter:[./a.out,_Z8Overloadi]
void Overload(int)
                Leave:[./a.out,_Z8Overloadi]
----------------------------Override  Test End
----------------------------Simple  Test Begin
                Enter:[./a.out,_Z9FuncOuterv]
                Leave:[./a.out,_Z9FuncOuterv]
                Enter:[./a.out,_Z10FuncMediumv]
                        Enter:[./a.out,_Z9FuncOuterv]
                        Leave:[./a.out,_Z9FuncOuterv]
                Leave:[./a.out,_Z10FuncMediumv]
                Enter:[./a.out,_Z9FuncInnerv]
                        Enter:[./a.out,_Z9FuncOuterv]
                        Leave:[./a.out,_Z9FuncOuterv]
                        Enter:[./a.out,_Z10FuncMediumv]
                                Enter:[./a.out,_Z9FuncOuterv]
                                Leave:[./a.out,_Z9FuncOuterv]
                        Leave:[./a.out,_Z10FuncMediumv]
                Leave:[./a.out,_Z9FuncInnerv]
----------------------------Simple  Test End
        Leave:[./a.out,main]
 ``` /pre>

输出分析:

```console
  Enter:[./a.out,_ZN9CDriveOneC1Ev]   //派生类  ```CDriveOne ```的构造函数 br />
  Enter:[./a.out,_ZN5CBaseC1Ev]   //父类  ```CBase ```的构造函数
  Enter:[./a.out,_Z8Overloadv]    //调用  ```void Overload() ``` br />
  Enter:[./a.out,_Z8Overloadi]    //调用  ```void Overload(int) ```