【调试】打印函数栈,以及由函数指针输出函数名的方法

Before All

以下皆在linux环境下。windows上用vs可随时查看函数栈。

用户态

打印函数栈

使用backtrace()相关函数来达到输出函数栈的目的,man backtrace查看详细的参数,返回值等信息。
以下测试例,编译时需加上-rdynamic选项:

#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>

#define SIZE 100

void myfunc3(void)
{
    int nptrs;
    void *buffer[100];
    char **strings;

    nptrs = backtrace(buffer, SIZE);
    printf("backtrace() returned %d addresses\n", nptrs);

    /* 1 means standard output */
    backtrace_symbols_fd(buffer, nptrs, 1); 

    /* or use backtrace_symbols() */
    /*  
    strings = backtrace_symbols(buffer, nptrs);
    if (strings == NULL) {
        perror("backtrace_symbols");
        exit(EXIT_FAILURE);
    }

    for (j = 0; j < nptrs; j++)
        printf("%s\n", strings[j]);

    free(strings);
//  */

}

static void /* "static" means don't export the symbol... */
myfunc2()
{
    myfunc3();
}

void myfunc()
{
    myfunc2();
}

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

简单说下这3个函数的用法:

int backtrace(void **buffer, int size);
backtrace()把函数栈中的函数地址写到buffer数组,buffer数组的成员即void *类型。size表示把栈顶size个函数地址搞出来,如果想把函数栈完全搞出来,确保buffer数组和size足够大。

char **backtrace_symbols(void *const *buffer, int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
backtrace_symbols()或者backtrace_symbols_fd()与上边的backtrace()配合使用,起一个翻译作用,即把函数地址翻译为函数名(还有函数的偏移和返回地址)。backtrace_symbols()把buffer数组中的size个元素翻译为函数名,把字符串数组保存在返回值char **中,自带了malloc功能,用完需把返回值free,见测试例。backtrace_symbols_fd()前两个参数和前者一样,但是把结果字符串数组输出到fd中,如fd=1,即输出到屏幕。

由函数指针得到函数名

根据以上可以看出,backtrace_symbols和backtrace_symbols_fd可以用来将函数指针转换为函数名,函数指针的讲解可以参考这里,以下是测试例:

#include<stdio.h>
#include<execinfo.h>

void hello_world()
{
    printf("hello world!\n");
}

int main()
{
    void *func = hello_world; //注意这里必须转化为一个void *指针
    backtrace_symbols_fd(&func, 1, 1); 
    return 0;
}

这里就把函数指针func的函数名输出了出来。

内核态

打印函数栈

使用dump_stack()函数。直接在需要查看函数栈的位置加入这一句,然后dmesg中就可以看到函数栈了。

由函数指针得到函数名

内核中可以直接使用printk的%pf,%pF选项,%pF选项多打印偏移地址。测试例:

void testname()
{
    return;
}
void test()
{
    void (*funcptr) = testname;
    printk("%pf\n", funcptr);
}

小结

以上就是在用户态,内核态输出函数栈,以及由函数指针输出函数名的方法,在调试的过程中有时能帮助加深对代码的理解。

猜你喜欢

转载自blog.csdn.net/snow168rain/article/details/52043719