Linux下查看函数被那些函数调用过?

时间:2021-05-02

一、问题

有个打印log的函数,想知道该函数执行的时候,之前执行了哪些函数?

二、分析

在应用程序打印函数栈需要通过函数backtrace(),该函数对应头文件如下:

  • #include<execinfo.h>
  • 1、三个与打印调用栈相关的函数

    打印函数栈需要使用到以下3个函数

  • intbacktrace(void**buffer,intsize);
  • 函数功能:用于获取当前线程的调用堆栈。参数:buffer:它是一个指针数组,函数获取的当前线程的调用堆栈将会被存放在buffer中。在buffer中的指针实际是从堆栈中获取的返回地址,每一个堆栈 框架有一个返回地址。size:用来指定buffer中可以保存多少个void*元素。返回值:实际获取的指针个数,最大不超过size大小。

    char** backtrace_symbols (void *const *buffer, int size);

    函数功能:将从backtrace函数获取的信息转化为一个字符串数组。参数:buffer:从backtrace函数获取的数组指针。size:是该数组中的元素个数(backtrace函数的返回值)。返回值:是一个指向字符串数组的指针,它的大小同buffer相同。每个字符串包含了一个相对于buffer中对应元素的 可打印信息。它包括函数名,函数的偏移地址,和实际的返回地址。

    注:

    • 1、只有使用ELF二进制格式的程序才能获取函数名称和偏移地址。在其他系统,只有16进制的返回地址能被获取。另外,需要传递相应的标志给链接器,以能支持函数名功能即编译选项-rdynamic。
    • 2、backtrace_symbols生成的字符串都是malloc出来的,最后需要free该块内存。
  • voidbacktrace_symbols_fd(void*const*buffer,intsize,intfd)
  • 功能:backtrace_symbols_fd与backtrace_symbols函数具有相同的功能,不同的是它不会给调用者返回字符串数组,而是将结果写入文件描述符为fd的文件中,每个函数对应一行.它不需要调用malloc函数,因此适用于有可能调用该函数会失败的情况。参数:fd:通常填写STDOUT_FILENO

    2. 链接库

    在编译的时候需要加上**-rdynamic**选项。

  • -rdynamic
  • Passtheflag-export-dynamictotheELFlinker,ontargetsthatsupportit.Thisinstructsthelinkertoaddallsymbols,notonlyusedones,tothedynamicsymboltable.Thisoptionisneededforsomeusesof"dlopen"ortoallowobtainingbacktracesfromwithinaprogram.
  • 该选项让链接器将所有符号添加到动态符号表中,这样才能将函数地址翻译成函数名,否则打印的结果是不会打印函数名的。

    另外,这个选项不会处理static函数,所以,static函数的符号无法得到。

    3. 举例

  • #include<execinfo.h>
  • #include<stdio.h>
  • #include<stdlib.h>
  • #include<unistd.h>
  • voidfun1();
  • voidfun2();
  • voidfun3();
  • voidprint_stacktrace();
  • voidprint_stacktrace()
  • {
  • intsize=16;
  • void*array[100];
  • intstack_num=backtrace(array,size);
  • char**stacktrace=backtrace_symbols(array,stack_num);
  • backtrace_symbols_fd(array,size,STDOUT_FILENO);
  • #if0
  • char**stacktrace=backtrace_symbols(array,stack_num);
  • for(inti=0;i<stack_num;++i)
  • {
  • printf("%s\n",stacktrace[i]);
  • }
  • free(stacktrace);
  • #endif
  • }
  • voidfun1()
  • {
  • printf("stackstracebegin:\n");
  • print_stacktrace();
  • }
  • voidfun2()
  • {
  • fun1();
  • }
  • voidfun3()
  • {
  • fun2();
  • }
  • intmain()
  • {
  • fun3();
  • }
  • 编译运行gcc编译时加上-rdynamic参数,通知链接器支持函数名功能(不加-rdynamic参数则无函数名打印):

  • gcc123.c-orun-rdynamic-g
  • 执行结果:

    4. 补充 address2line

    同一个函数可以在代码中多个地方调用,如果我们只是知道函数,要想知道在哪里调用了该函数,可以通过address2line命令来完成,我们用第2步中编译出来的test2来做实验(address2line的-f选项可以打出函数名, -C选项也可以demangle):

    address2line

    三、内核代码中如何打印函数栈?

    在Linux内核中提供了一个可以打印出内核调用堆栈的函数 dump_stack()。

    该函数在我们调试内核的过程中可以打印出函数调用关系,该函数可以帮助我们进行内核调试,以及让我们了解内核的调用关系。

    1. 头文件

    该函数头文件为:

  • #include<asm/ptrace.h>
  • 使用方式:

    直接在想要查看的函数中添加

  • dump_stack();
  • 2. 举例

    测试代码如下:hello.c

  • 1#include<linux/init.h>
  • 2#include<linux/module.h>
  • 3#include<asm/ptrace.h>
  • 4
  • 6MODULE_LICENSE("GPL");
  • 7MODULE_AUTHOR("PD");
  • 8voidaaa(inta);
  • 9voidbbb(intb);
  • 10voidccc(intc);
  • 11
  • 14voidccc(intc)
  • 15{
  • 16printk(KERN_SOH"cccc\n");
  • 17dump_stack();
  • 18printk("cis%d\n",c);
  • 19}
  • 20voidbbb(intb)
  • 21{
  • 22intc=b+10;
  • 23printk(KERN_SOH"bbbb\n");
  • 24ccc(c);
  • 25}
  • 26voidaaa(inta)
  • 27{
  • 28intb=a+10;
  • 29printk(KERN_SOH"aaaa\n");
  • 30bbb(b);
  • 31}
  • 32
  • 34staticinthello_init(void)
  • 35{
  • 36inta=10;
  • 37
  • 38aaa(a);
  • 39printk(KERN_SOH"hello_init\n");
  • 40
  • 41return0;
  • 42}
  • 43staticvoidhello_exit(void)
  • 44{
  • 45printk("hello_exit\n");
  • 46return;
  • 47}
  • 48
  • 49module_init(hello_init);//insmod
  • 50module_exit(hello_exit);//rmmod
  • Makefile

  • ifneq($(KERNELRELEASE),)
  • obj-m:=hello.o
  • else
  • KDIR:=/lib/modules/$(shelluname-r)/build
  • PWD:=$(shellpwd)
  • all:
  • make-C$(KDIR)M=$(PWD)modules
  • clean:
  • rm-f*.ko*.o*.mod.o*.symvers*.cmd*.mod.c*.order
  • endif
  • 编译安装模块

  • dmesg-c
  • make
  • insmodhello.ko
  • 【注意】 都在root权限下操作

    结果

    可以看到在函数ccc中使用dump_stack()打印出了ccc的函数调用栈。

    在内核开发中,我们可以使用dump_stack()来打印相关信息,同时在内核源码学习中也可以用来了解函数调用关系。

    原文地址:https://mp.weixin.qq.com/s/odyYVeDpO_1NcS8U_QE0AQ

    声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

    相关文章