我们知道,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]
这回,可以看到函数名了。是不是很酷呢?把它封装到你的调试代码中吧。
相关推荐
面试题18:简述C、C++程序编译的内存分配情况 面试题19:以下四段代码中哪段没有错误 第6章 字符串 6.1 数字字符串 面试题1:编码实现数字转化为字符串 面试题2:编码实现字符串转化为数字 6.2 字符串函数 面试题3:...
- **标准输入/输出(stdio.h)**:包括`printf`、`scanf`、`puts`、`gets`等,用于处理程序与用户的交互,打印信息或接收用户输入。 - **字符串处理(string.h)**:提供`strcpy`、`strcat`、`strcmp`、`strlen`等...
在C/C++中,数组可以是一维、二维或多维的。 2. **链表**:链表是由节点组成的数据结构,每个节点包含数据和指向下一个节点的指针。单链表、双链表和循环链表是常见的链表类型。 3. **栈**:栈是一种后进先出...
在C/C++编程中,面试或笔试经常涉及到各种技术细节,以下是一些重要的知识点: 1. **入口点**:在Windows程序中,入口点通常是`WinMain()`函数,而非传统的`main()`。这个函数是Windows API的一部分,用于初始化...
### C与C++不同方式实现栈 #### 概述 本文将通过两种不同的编程方法来实现栈这一数据结构,一种是使用C语言的传统指针方式,另一种则是利用C++的面向对象特性。这两种方法各有优势,能够帮助初学者更好地理解和...
- 使用`backtrace`或`bt`命令查看当前调用栈信息。 - **查看源程序**: - `list filename` 显示指定文件的源代码。 - `list line_number` 显示指定行号附近的源代码。 - `search pattern` 搜索源代码中的模式。 ...
本文将深入探讨如何在Android环境下为不同语言(C语言、C++、Java以及内核空间)添加并打印调用堆栈信息。 首先,让我们来看看Java语言的call stack。在Java中,我们可以利用`Thread.currentThread().getStackTrace...
栈在递归、函数调用、表达式求值等场景中有广泛应用。 - **队列**:队列是先进先出(FIFO)的数据结构,分为顺序队列和链式队列。常用操作包括入队(enqueue)、出队(dequeue)和查看队首元素。队列常用于任务调度...
《C/C++帮助文档》是针对蓝桥杯软件类比赛精心编撰的一份参考资料,它涵盖了C和C++编程语言的基础知识、高级特性以及算法与数据结构的应用,旨在为参赛者提供全面的学习指导和问题解决方案。这份文档的重要性在于,...
在数据结构中,栈是一种后进先出(LIFO)的数据结构,常用于实现递归、表达式求值、函数调用等场景。队列则是先进先出(FIFO)的数据结构,常用于任务调度、打印队列等。C语言标准库提供了对这两种数据结构的支持,...
`extern "C"`用于在C++中调用C编译的函数,保持函数名不被C++的名称修饰。 17. switch中不允许的数据类型: switch语句的表达式不能是浮点型。 18. `GetMemory`和`Test`函数的运行结果: Test函数会分配100个...
这些题目主要考察的是C/C++编程语言的基础知识,包括数组、指针、内存管理和函数返回值等核心概念。...以上解析涵盖了C/C++中数组、指针、内存管理和函数操作等关键知识点,它们是理解和编写C/C++程序的基础。
### C/C++编程知识点解析 ...- **主函数与程序控制流**:`main()`函数是C/C++程序的入口点,但并非所有动作都由它直接引发。例如,异常处理、信号处理和线程调度可在不直接调用`main()`的情况下发生。
在IT领域,数据结构是计算机科学的基础,它们是组织和管理数据的方式,直接影响到程序的效率和性能。链式栈是一种特殊的数据结构,它属于线性数据结构,并且利用链表来实现栈的操作。本节将详细介绍如何用C++来实现...
C++ Socket 聊天室程序是网络编程领域的一个经典示例,主要用于教授如何使用C++语言实现基于Socket的通信。Socket是网络编程中的一个重要概念,它提供了进程间通过网络进行通信的接口。在这个程序中,我们将探讨以下...
在Linux环境下进行C/C++程序调试是软件开发过程中的关键环节,这有助于开发者发现并修复程序中的错误,提升软件质量和性能。本文将详细介绍在Linux下使用Valgrind工具进行C/C++程序调试的方法和技巧。 ### Valgrind...
- **`backtrace`**:查看函数调用栈。 - **`delete`**:删除断点。 - **`run`**:重新运行程序。 - **`quit`**:退出GDB。 #### 五、结论 通过本篇文章,我们不仅了解了GDB的基本功能,还深入探讨了如何使用GDB来...
在实际应用中,顺序栈常被用于表达式求值、递归调用的模拟、函数调用堆栈、深度优先搜索(DFS)等多种场景。通过理解并实现顺序栈,开发者可以更好地理解和掌握数据结构与算法,提高解决问题的能力。 总结来说,这...
`std::cin`用于从标准输入(通常是键盘)读取用户输入的值,`std::cout`则用于向标准输出(通常是屏幕)打印信息。 `iostream.h`是C++早期版本中用于输入输出操作的头文件,但在现代C++中,推荐使用`iostream`,...
【谭浩强版C++程序设计语言】是入门学习C++的经典教材,由著名计算机教育专家谭浩强教授编写。这本书以清晰易懂的语言和丰富的实例,为初学者提供了全面的C++编程基础。PPT整理版则将书中的关键知识点以幻灯片的形式...