zoukankan      html  css  js  c++  java
  • Linux下追踪函数调用,打印栈帧

          事情的起因是这样的,之前同事的代码有一个内存池出现了没有回收的情况。也就是是Pop出来的对象没有Push回去,情况很难复现,所以在Pop里的打印日志,跟踪是谁调用了它,我想在GDB调试里可以追踪调用的栈帧,那也一定有方法实现。首先上网搜索了一下,并没有结果!还好代码量不是很多,只能用最笨的方法,在每个调用Pop的地方,传参,把调用的文件,行号作为字符串传进去,在日志里打印!忙活完了,总感觉一定是有方法可以实现查看调用栈帧的,于是在QQ群里的问了下,果然有这方面经验的同学给出了答案!

        主要是通过backtrace返回调用的栈帧,然后通过backtrace_symbols把地址转换为字符串。最后,在Linux下有个工具addr2line可以将地址转换为文件名和行号!通过管道调用addr2line,最后打印调用栈帧。

    #include <execinfo.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
     /*
      * 打印栈帧
      *
      * 通过backtrace,backtrace_symbols获取栈帧信息,然后建立管道,通过addr2line解析
      *
      */

    int32_t myexec(const char *cmd)
    {
        FILE *pp = popen(cmd, "r"); //建立管道
        if (!pp)
        {
            return -1;
        }
        char tmp[1024];
        while (fgets(tmp, sizeof(tmp), pp) != NULL)
        {
            if (tmp[strlen(tmp) - 1] == ' ')
            {
                tmp[strlen(tmp) - 1] = ''; //去除换行符
            }

            printf("%-30s",tmp);
         }
         printf(" ");
         pclose(pp); //关闭管道
         return 0;
     }

    void parseName(char * str,char *exeName,char *addr)
    {
        char *strTemp = str;
        char * addrTemp;
        while (*strTemp)
        {
            if (*strTemp == '(')
                memcpy(exeName, str, strTemp - str);

            if (*strTemp == '[')
                addrTemp = strTemp;

            if (*strTemp == ']')
                memcpy(addr, str + (addrTemp - str) + 1, strTemp - addrTemp - 1);
            strTemp++;
        }
    }

    void print_trace(void)
    {
        void *array[10];
        size_t size;
        char **strings;

        size = backtrace(array,10);
        strings = backtrace_symbols(array,size);

        printf("Obtained %zd stack frames. ",size);
        char cmd[500] = {0};
        char exeName[100] = {0};
        char addr[100] = {0};
        for(size_t i = 0;i < size;i++)
        {
            memset(cmd,0,sizeof(cmd));
            memset(exeName,0,sizeof(exeName));
            memset(addr,0,sizeof(addr));
     
            parseName(strings[i],exeName,addr);
            printf("%-15s",addr);
            sprintf(cmd,"addr2line -C -f -e %s %s",exeName,addr);
            myexec(cmd);
        }
    }

    void dummp_function(void)
    {
        print_trace();
    }

    int main(int argc,char *argv[])
    {
        dummp_function();
        return 0;
    }

    编译:

    g++ -Wall -g backtrace.cpp -o bt

    执行:

    ./bt

    效果:

    
    

    Obtained 5 stack frames.
    0x400a8a       print_trace() /home/ubuntu/project/Test/backtrace.cpp:58
    0x400c47       dummp_function() /home/ubuntu/project/Test/backtrace.cpp:81
    0x400c5d main  /home/ubuntu/project/Test/backtrace.cpp:86
    0x7f5ca6d8ef45 ?? ??:0
    0x4007d9       _start ??:?

     

    大致满足自己的需求效果,函数名称还需要修饰一下!

  • 相关阅读:
    黑鲨2无限重启 把竞技按钮调到最上
    绿联 电池
    阿里云
    Centos 8 搭建时钟服务器
    CentOS8系统时间同步解决方法
    解决问题的人干活快的人
    【海通国际】Joe Lowry(Mr. Lithium)谈全球电池原材料供应危机
    Linux 实验楼
    用 set follow-fork-mode child即可。这是一个 gdb 命令,其目的是告诉 gdb 在目标应用调用fork之后接着调试子进程而不是父进程,因为在 Linux 中fork系统调用成功会返回两次,一次在父进程,一次在子进程
    【随笔】阿里云修改DNS
  • 原文地址:https://www.cnblogs.com/jx-dx/p/4932363.html
Copyright © 2011-2022 走看看