在C/C++程序里打印调用栈信息

我们知道,GDB的backtrace命令可以查看堆栈信息。但很多时候,GDB根本用不上。比如说,在线上环境中可能没有GDB,即使有,也不太可能让我们直接在上面调试。如果能让程序自己输出调用栈,那是最好不过了。本文介绍和调用椎栈相关的几个函数。

 


NAME
       backtrace, backtrace_symbols, backtrace_symbols_fd - support for application self-debugging

SYNOPSIS
       #include <execinfo.h>

       int backtrace(void **buffer, int size);

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

       void backtrace_symbols_fd(void *const *buffer, int size, int fd);

 

以上内容源自这几个函数的man手册。

 

先简单介绍一下这几个函数的功能:

l backtrace:获取当前的调用栈信息,结果存储在buffer中,返回值为栈的深度,参数size限制栈的最大深度,即最大取size步的栈信息。

l backtrace_symbols:把backtrace获取的栈信息转化为字符串,以字符指针数组的形式返回,参数size限定转换的深度,一般用backtrace调用的返回值。

l backtrace_symbols_fd:它的功能和backtrace_symbols差不多,只不过它不把转换结果返回给调用方,而是写入fd指定的文件描述符。

 

 

Man手册里,给出了一个简单的实例,我们看一下:


#include<execinfo.h>

#include<stdio.h>

#include<stdlib.h>

#include<unistd.h>

void

myfunc3(void)

{

   int j, nptrs;

   #define SIZE 100

   void *buffer[100];

   char **strings;

   nptrs = backtrace(buffer, SIZE);

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

   /* The call backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO)

    *  would produce similar output to the following: */

 

   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);

}

 

staticvoid  /* "static" means don't export the symbol... */

myfunc2(void)

{

   myfunc3();

}

 

void

myfunc(int ncalls)

{

   if (ncalls > 1)

       myfunc(ncalls - 1);

   else

       myfunc2();

}

 

int

main(int argc,char *argv[])

{

   if (argc != 2) {

       fprintf(stderr,"%s num-calls\n", argv[0]);

       exit(EXIT_FAILURE);

   }

   myfunc(atoi(argv[1]));

   exit(EXIT_SUCCESS);

}

 

编译:


# cc prog.c -o prog

 

运行:


# ./prog 0
backtrace() returned 6 addresses
./prog() [0x80485a3]
./prog() [0x8048630]
./prog() [0x8048653]
./prog() [0x80486a7]

 

这样,是输出了调用栈,不过只是以十六进制输出函数地址而已,可读性很差。仔细看下man手册,原来很简单,编译时加上个参数:

 

重新编译:


# cc -rdynamic  prog.c -o prog

通过gcc手册,我们可以也解下参数的说明:


-rdynamic
           Pass the flag -export-dynamic to the ELF linker, on targets that support it. This instructs the linker to add all symbols, not only used ones, to the dynamic symbol table. This option is needed for some uses of "dlopen" or to allow obtaining backtraces from within a program.

 

再执行:


# ./prog 0

backtrace() returned 6 addresses
./prog(myfunc3+0x1f) [0x8048763]
./prog() [0x80487f0]
./prog(myfunc+0x21) [0x8048813]
./prog(main+0x52) [0x8048867]
/lib/libc.so.6(__libc_start_main+0xe6) [0xaf9cc6]
./prog() [0x80486b1]

 

这回,可以看到函数名了。是不是很酷呢?把它封装到你的调试代码中吧。

时间: 2024-09-25 22:17:36

在C/C++程序里打印调用栈信息的相关文章

pb程序里如何调用vs2012编译的dll ,该dll是封装的第三方的webservice

问题描述 pb程序里如何调用vs2012编译的dll ,该dll是封装的第三方的webservice 第三方给了一个webservice,我用vs的命令,编译成了dll,可惜pb程序里无法正常调用该dll 解决方案 .net的dll,需要包装成com对象,才能被pb调用,不是一般的dll.关于如何包装com组件,以及pb如何调用com组件,请google. 解决方案二: pb程序里如何调用vs2012编译的dll ,该dll是封装的第三方的webservice 第三方给了一个webservice

想问问我建立了一个webservice应用程序客户端,直接在程序里运行调用方法是对的,但进debug或release直接运行后就报错,这是为什么啊?

问题描述 想问问我建立了一个webservice应用程序客户端,直接在程序里运行调用方法是对的,但进debug或release直接运行后就报错,这是为什么啊?感觉就是不用程序直接运行的话,其他方法运行就报错误.里面什么其他的都没做,就是直接加了web引用,然后调了一个方法而已.谁能解答一下? 解决方案 解决方案二:up

小览CallStack(调用栈)(三)-用调试器脚本查看调用栈信息

在这一系列之前的两篇文章中,我介绍了如何在windbg中查看调用栈的相关 信息(详见小览call stack(调用栈)(一)),以及调用约定(详见小览call stack(调用栈) (二)--调用约定).今天的这篇博客在二者的基础 之上,介绍如何使用调式器脚本程序来观察调用栈.对CallStack感兴趣的朋友 可以在此基础上开发更加详尽的脚本来观察CallStack的信息:对调试感兴趣的 朋友则可以看一下DScript的用处. 我们先来看一个例子,下面的程序并不是一个优美的程序片段,但是它能够

Tomcat里打印的异常信息,为什么在程序里捕捉不到?

问题描述 前段时间出现了个问题,有点郁闷.为什么别人在访问WebServices的时候会出现这样的情况?如果是访问者那边报异常的时候,Tomcat里就会抛异常. 解决方案 解决方案二:他只是把异常输出了下并没有抛出解决方案三:引用1楼bearkin的回复: 他只是把异常输出了下并没有抛出 哦,那在程序里的日志里无法打印又是怎么回事啊?解决方案四:引用2楼k010010001的回复: 引用1楼bearkin的回复:他只是把异常输出了下并没有抛出哦,那在程序里的日志里无法打印又是怎么回事啊? ...

PostgreSQL 如何打印函数调用栈信息

增加了调用堆信息的输出. 可以用于plpgsql debug等. 测试 : pg94@db-192-168-100-216-> psql psql (9.4devel) Type "help" for help. digoal=# -- access to call stack digoal=# create or replace function inner_func(int) digoal-# returns int as $$ digoal$# declare _conte

前端魔法堂——调用栈,异常实例中的宝藏

前言  在上一篇<前端魔法堂--异常不仅仅是try/catch>中我们描述出一副异常及如何捕获异常的画像,但仅仅如此而已.试想一下,我们穷尽一切捕获异常实例,然后仅仅为告诉用户,运维和开发人员页面报了一个哪个哪个类型的错误吗?答案是否定的.我们的目的是收集刚刚足够的现场证据,好让我们能马上重现问题,快速修复,提供更优质的用户体验.那么问题就落在"收集足够的现场证据",那么我们又需要哪些现场证据呢?那就是异常信息,调用栈和栈帧局部状态.(异常信息我们已经获取了) 本文将围绕上

GDB查看栈信息

当程序被停住了,你需要做的第一件事就是查看程序是在哪里停住的.当你的程序调用了一个函数,函数的地址,函数参数,函数内的局部变量都会被压入"栈"(Stack)中.你可以用GDB命令来查看当前的栈中的信息. 下面是一些查看函数调用栈信息的GDB命令: backtracebt 打印当前的函数调用栈的所有信息.如:&http://www.aliyun.com/zixun/aggregation/37954.html">nbsp;(gdb) bt#0  func (n=2

r语言-在C# 程序里 调用R语言 调用完R语言后C#程序也跟着结束了,怎么让他不跟着结束呢?

问题描述 在C# 程序里 调用R语言 调用完R语言后C#程序也跟着结束了,怎么让他不跟着结束呢? 我程序里调用R语言的代码是这样的 REngine.SetEnvironmentVariables(); REngine engine = REngine.GetInstance(); engine.Evaluate(R_cmd); engine.Evaluate("q()"); engine.Dispose(); 这样有什么问题吗? 解决方案 你C#怎么写的?控制台程序可以加上一个Cons

大哥大姐救救我吧,如何让webservices里引用的dll程序调用应用程序里的方法

问题描述 如何让webservices里引用的dll程序调用应用程序里的方法 解决方案 解决方案二:你直接引用了,实例化后就提示它里面的方法了啊解决方案三:实例化之后不久可以调用了,解决方案四:同意楼上解决方案五:ding解决方案六:ding解决方案七:实在不行就regsvr32注册下dll