目录
简介
backtrace
profile-backtrace
区别
示例
profile-backtrace
backtrace
简介
backtrace
backtrace是一个用于生成函数调用栈的工具,在程序崩溃或者出现异常时,可以通过backtrace来获取函数调用栈信息,这些信息可以帮助我们了解到程序的执行流程,定位问题发生的位置。
backtrace的使用一般需要以下步骤:
1.在需要使用backtrace的代码中引入`#include <execinfo.h>`头文件。
2.使用backtrace函数获取函数调用栈信息:`void backtrace(void **buffer, int size)`
- **buffer**: 用于存储函数调用栈信息的buffer,类型为void*的数组。
- **size**: 表示buffer中可存储的指针个数。
3.使用backtrace_symbols函数将函数指针转换为函数名称:`char **backtrace_symbols(void *const *buffer, int size)`
- **buffer**: backtrace函数获取到的函数指针数组。
- **size**: backtrace函数获取到的函数指针数组的长度。
这样,我们就可以利用backtrace函数获取函数调用栈信息,然后使用backtrace_symbols函数将指针转化为函数名称,从而获取到函数调用栈。在获取函数调用栈信息的同时,我们还可以将函数调用栈信息输出到日志中,方便我们查找和定位程序崩溃或者出现异常的位置。
profile-backtrace
`profile-backtrace`是Linux系统中一个用于性能分析的工具,它可以帮助开发者找出程序中消耗CPU时间较多的函数,从而定位程序性能问题。
`profile-backtrace`的工作原理是:通过对程序进行采样,在采样的间隔时间内获取程序堆栈信息,生成函数调用栈,然后对这些调用栈进行统计和分析。最终输出调用次数、调用时间等统计信息,从而找出程序中会占用大量时间的函数,帮助我们进行优化。
在使用`profile-backtrace`进行性能分析时,一般需要以下步骤:
1.在编译时添加`-g`选项,生成调试信息,方便获取函数名称。
2.编写性能测试程序,并在其中需要进行性能分析的地方插入"标记点"。
3.编译性能测试程序。
4.运行`profile-backtrace`进行性能分析,如:`perf record -e cycles ./test`
5.对采样数据进行处理和分析,如:`perf report`
利用`profile-backtrace`可以在系统级别对程序进行性能分析,快速地找出程序中性能瓶颈所在的函数,并使用分析结果进行性能优化。
区别
profile-backtrace 和 backtrace的区别
`profile-backtrace`和`backtrace`都是用于获取函数调用栈信息的工具,但它们的设计目的和使用场景有所不同:
1.设计目的:`profile-backtrace`是用于性能分析的工具,可以帮助开发者找出程序中消耗CPU时间较多的函数,从而定位性能问题;`backtrace`是用于调试的工具,可以帮助开发者在程序崩溃或出现异常时,获取函数调用栈信息,从而定位问题所在。
2.使用场景:`profile-backtrace`一般用于优化程序运行性能,对于性能开销较小的程序可能效果不明显;`backtrace`一般用于调试程序,对于希望获取堆栈跟踪信息的开发人员非常有用。
3.实现方式:`profile-backtrace`是通过对程序进行采样,在采样的间隔时间内获取程序堆栈信息,生成函数调用栈,然后对这些调用栈进行统计和分析;`backtrace`是通过在程序崩溃或出现异常时,获取函数调用栈信息,从而定位问题所在。
因此,`profile-backtrace`和`backtrace`是两个不同的工具,虽然都是获取函数调用栈信息,但是适用的场景和目的不同。
`profile-backtrace`和`backtrace`的使用方法略有不同,下面分别介绍两个工具的使用方法。
1. `profile-backtrace`的使用方法
(1) 编译时添加`-g`选项,以生成调试信息:
```
gcc -o program -g program.c
```
(2) 运行命令行工具`perf`进行性能分析:
```
perf record -g -a -e cycles ./program
```
(3) 处理并查看采样数据:
```
perf report
```
其中,`-g`选项用于生成函数调用栈信息;`-a`选项用于记录所有的进程或线程;`-e cycles`选项用于指定采集的事件类型,此处为CPU周期计数器。
2. `backtrace`的使用方法
(1) 引入头文件`<execinfo.h>`。
```
#include <execinfo.h>
```
(2) 添加函数`backtrace()`,以获取函数调用栈信息:
```
void *buffer[10];
int nptrs = backtrace(buffer, 10);
```
(3) 使用`backtrace_symbols()`函数将函数指针转换为函数名称:
```
char **strings;
strings = backtrace_symbols(buffer, nptrs);
```
(4) 打印函数调用栈信息:
```
for (int i = 0; i < nptrs; i++)
printf("%s\n", strings[i]);
```
在执行完上述步骤后,即可输出函数调用栈信息,并帮助我们定位程序崩溃或异常的来源。
需要注意的是,`backtrace`使用时需要在编译时开启调试信息,因此对于发布版程序,不能使用`backtrace`进行调试。
示例
profile-backtrace
以下是一个简单的`profile-backtrace`使用实例,演示如何使用`profile-backtrace`进行性能分析,找出程序中消耗CPU时间较多的函数。
假设有以下测试程序test.c:
```c
#include <stdio.h>int func1()
{
int a = 1, b = 2, c = 3, d = 4, e = 5;
return a + b + c + d + e;
}int func2()
{
int i, sum = 0;
for (i = 0; i < 10000; i++)
sum += i;
return sum;
}int main()
{
int i;
for (i = 0; i < 100000; i++)
func1();
for (i = 0; i < 100000; i++)
func2();
return 0;
}
```
该程序包括两个函数`func1()`和`func2()`,分别计算一些数值的和,在`main()`函数中依次调用了100000次这两个函数。
现在我们使用`profile-backtrace`对该程序进行性能分析:
1.编译并链接程序,并添加`-g`选项:
```
gcc -g -o test test.c
```
2.运行`profile-backtrace`进行性能分析:
```
perf record -g -a -e cycles ./test
```
其中,`-g`选项用于生成函数调用栈信息;`-a`选项用于记录所有的进程或线程;`-e cycles`选项用于指定采集的事件类型,此处为CPU周期计数器。
3.对采样数据进行处理和分析:
```
perf report
```
运行上述命令后,会打开`perf report`工具,该工具会生成分析结果,并列出程序中各函数的运行次数、消耗CPU时间等信息。在本例中,`func2()`运行次数较多,且消耗CPU时间较长,是程序中耗时的主要函数。
这样,我们就可以使用`profile-backtrace`帮助我们快速定位程序中的性能问题,进而进行优化。
backtrace
以下是一个简单的backtrace使用实例,演示如何使用backtrace获取函数调用栈信息。
#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>#define SIZE 10
void func1(void);
void func2(void);
void func3(void);int main()
{
func1();
return 0;
}void func1()
{
void *buffer[SIZE];
int nptrs = backtrace(buffer, SIZE); //获取函数调用栈信息
char **strings;printf("func1() function call stack:\n");
strings = backtrace_symbols(buffer, nptrs); //将函数指针转换为函数名称
if (strings == NULL)
{
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}for (int i = 0; i < nptrs; ++i)
printf("%s\n", strings[i]);free(strings);
func2();
}void func2()
{
func3();
}void func3()
{
void *buffer[SIZE];
int nptrs = backtrace(buffer, SIZE);
char **strings;printf("func3() function call stack:\n");
strings = backtrace_symbols(buffer, nptrs);
if (strings == NULL)
{
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}for (int i = 0; i < nptrs; ++i)
printf("%s\n", strings[i]);free(strings);
}
该程序定义了三个函数func1()、func2()和func3(),main()函数中调用了func1()。每个函数中都调用了backtrace()函数,获取函数调用栈信息并输出。
运行上述程序,输出结果如下:
func1() function call stack:
./a.out(func3+0x15) [0x4006df]
./a.out(func2+0x9) [0x4006f6]
./a.out(func1+0x1d) [0x40070d]
./a.out(main+0xb) [0x40072c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1) [0x7f37cb87b291]
./a.out() [0x4005da]
func3() function call stack:
./a.out(func3+0x25) [0x4006e9]
./a.out(func2+0x9) [0x4006f6]
./a.out(func1+0x23) [0x400713]
./a.out(main+0xb) [0x40072c]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf1) [0x7f37cb87b291]
./a.out() [0x4005da]
上述输出便是程序中的函数调用栈信息,可以帮助我们快速定位程序崩溃或异常的原因。需要注意的是,backtrace使用时需要在编译时开启调试信息,不同编译器的开启方式略有不同。