`
字符串
  • 浏览: 36345 次
文章分类
社区版块
存档分类
最新评论

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

 
阅读更多

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

NAME

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

 

SYNOPSIS

       #include

 

       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

#include

#include

#include

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]

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

分享到:
评论

相关推荐

    C/C++程序员面试指南.杨国祥(带详细书签).pdf

    面试题18:简述C、C++程序编译的内存分配情况 面试题19:以下四段代码中哪段没有错误 第6章 字符串 6.1 数字字符串 面试题1:编码实现数字转化为字符串 面试题2:编码实现字符串转化为数字 6.2 字符串函数 面试题3:...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

     ·分享c++程序的错误查找技术,并介绍通用的调试原则讨论每一个windows应用程序的结构和基本元素  ·举例说明如何使用mfc开发本地windows应用程序  ·指导读者用c++和c++/cli设计和创建大量的windows应用程序 ...

    华为C++笔试题全部汇总

    5.下面这段程序,打印结果是_A_: A. 1 B. 2 C. 3 D. 以上都不对 int g_iCount = 0; class CParent { public: CParent() {} ~CParent() {g_iCount += 1;} }; class CSon : public CParent { public: CSon() {} ...

    C 语言编程常见问题解答.chm

    15.1 编译程序中的C++扩充功能可以用在C程序中吗? 15.2 C++和C有什么区别? 15.3 在C程序中可以用“∥”作注释符吗? 15.4 char,short,int和long类型分别有多长? 15.5 高位优先(big-endian)与低位优先...

    C语言编程要点

    15.1. 编译程序中的C++扩充功能可以用在C程序中吗? 210 15.2. C++和C有什么区别? 210 15.3. 在c程序中可以用“∥”作注释吗? 211 15.4. char,short,int和long类型分别有多长? 212 15.5. 高位优先(big—endian)与...

    Visual.C#.编程精彩百例

    实例14 调用栈记录异常点 实例15 使用C#异常的栈跟踪 实例16 运行期间检测变量类型 实例17 常用值类型的原型定义 实例18 打印杨辉三角形 实例19 比较学生信息 实例20 获取车辆信息 实例21 简单角色类游戏的...

    数据结构(C++)有关练习题

    在计算机科学发展过程中,早期数据结构教材大都采用PASCAL语言为描述工具,后来出现了采用C语言为描述工具的教材版本、至今又出现了采用C++语言为描述工具的多种教材版本。本教实验指导书是为已经学习过C++语言的...

    若干源程序资料12.rar

    2012-06-11 21:35 50,176 关于c语言和汇编语言相互嵌套调用的学习总结.doc 2012-06-11 21:11 190,993 典型算法包.rar 2012-06-11 21:16 264,555 内存管理和设备调试中文.pdf 2012-06-11 21:30 267,989 冈萨雷斯数字...

    windows驱动开发技术详解-part2

    这两个驱动程序没有操作具体的硬件设备,只是在系统里创建了虚拟设备。在随后的章节中,它们会作 为基本驱动程序框架,被本书其他章节的驱动程序开发所复用。笔者将带领读者编写代码、编译、安装 和调试程序。  ...

    Windows驱动开发技术详解的光盘-part1

    这两个驱动程序没有操作具体的硬件设备,只是在系统里创建了虚拟设备。在随后的章节中,它们会作为基本驱动程序框架,被本书其他章节的驱动程序开发所复用。笔者将带领读者编写代码、编译、安装和调试程序。  1.1 ...

    LuaBind 源码 (Lua增强库)

    错误,程序挂起在产生二义性调用的地方.一个简单的例子是,注册两个函数,一个函数 接受一个int参数,另外一个函数接受一个float参数. 因为Lua将不区别浮点数和整形数, 所以他们都是匹配的. 因为所有的重载是被测试过的...

    trace call stack demo

    一个简单的打印程序调用栈的例子 .... &lt;br&gt;http://topic.csdn.net/u/20080804/15/623a4355-cfeb-4241-8fba-022fff8facf4.html

    《Visual.C#.编程精彩百例》配套光盘part1

    实例14 调用栈记录异常点 实例15 使用C#异常的栈跟踪 实例16 运行期间检测变量类型 实例17 常用值类型的原型定义 实例18 打印杨辉三角形 实例19 比较学生信息 实例20 获取车辆信息 ...

    《Visual.C#.编程精彩百例》配套光盘.part2

    实例14 调用栈记录异常点 实例15 使用C#异常的栈跟踪 实例16 运行期间检测变量类型 实例17 常用值类型的原型定义 实例18 打印杨辉三角形 实例19 比较学生信息 实例20 获取车辆信息 ...

    传智播客扫地僧视频讲义源码

    02_简单的C++程序helloworld 03_用面向过程和面向对象方法求解圆形面积_类和对象的关系思考_传智扫地僧 04_类中不写成员函数易犯错误模型 05-易犯错误模型-为什么需要成员函数(补充资料)-传智扫地僧 06_课堂答疑类中...

    Delphi5开发人员指南

    7.5 在应用程序中使用ActiveX控件 153 7.6 发布带有ActiveX控件的应用程序 154 7.7 注册ActiveX控件 155 7.8 BlackJack:一个OCX示范程序 155 7.8.1 纸牌 155 7.8.2 游戏 157 7.8.3 调用ActiveX控件的方法 165 7.9 ...

    达内 coreJava 习题答案

    1,编写程序,判断给定的某个年份是否是闰年。 闰年的判断规则如下: (1)若某个年份能被4整除但不能被100整除,则是闰年。 (2)若某个年份能被400整除,则也是闰年。 import java.util.Scanner; class ...

    新版Android开发教程.rar

    NTT DoCoMo, Inc. 日本多科莫电信 SOFTBANK MOBILE Corp. 日本软银移动 Sprint Nextel( 美国 ) T-Mobile( 德国 ) Telecom Italia( 意大利 ) Telef ó nica( 西班牙 ) Vodafone 沃达丰电信 China Unicom 中国联通 ...

    JAVA面试题最全集

    给定一个C语言函数,要求实现在java类中进行调用。 45.如何获得数组的长度? 46.访问修饰符“public/private/protected/缺省的修饰符”的使用 47.用关键字final修饰一个类或者方法时,有何意义? 48.掌握类和...

Global site tag (gtag.js) - Google Analytics