【debug】程序调用栈记录profile-backtrace和backtrace|分析瓶颈|分析bug所在

news/2024/12/2 14:41:41/

目录

简介

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使用时需要在编译时开启调试信息,不同编译器的开启方式略有不同。


 


http://www.ppmy.cn/news/410947.html

相关文章

Vue 3.3 有哪些更新

此版本专注于开发人员体验改进-特别是SFC<script setup>与TypeScript的使用。与Vue语言工具[1]&#xff08;以前称为Volar&#xff09;的1.6版本一起&#xff0c;我们在将Vue与TypeScript一起使用时解决了许多长期存在的痛点。这篇文章概述了3.3中突出显示的功能。有关更…

【计算机网络】第二章应用层-电子科技大学2023期末考试

第二章 应用层 应用层协议原理 网络应用程序体系结构 客户机/服务器体系结构&#xff1a;至少有一个服务器&#xff0c;一个客户机&#xff0c;其中服务器总是打开的&#xff0c;具有固定的众所周知的IP地址&#xff0c;主机群集常被用于创建强大的虚拟服务器&#xff0c;而客…

使用 Node.js、K8s 和分布式 SQL 构建世界上最具弹性的待办事项列表应用程序

本文演示了如何使用 Kubernetes (K8s) 和分布式 SQL 构建云原生 Node.js 应用程序。 开发可扩展且可靠的应用程序是一项热爱的工作。一个云原生系统可能包括单元测试、集成测试、构建测试&#xff0c;以及用于构建和部署应用程序的完整管道&#xff0c;只需单击一个按钮即可。 …

有道免费翻译接口

1.有道翻译接口 https://fanyi.youdao.com/translate?&doctypejson&typeAUTO&ienglish

esxi 6.7 已经注入 瑞昱RTL8111 网卡

前两天用火神主板想安装esxi 结果提示无网卡&#xff0c;搜索结果是 esxi7.0不支持 瑞昱RTL网卡 也无法自己加入网卡包。 后来不得已用esxi 6.7 esxi 6.7 也是没有 瑞昱 的网卡驱动得 自己加入 回来按照网上教程成功加入且已成功安装esxi6.7 我自己打包的 https://clou…

安装网卡驱动Realtek 8125

安装网卡驱动Realtek 8125 首先输入lspci查看网卡型号 Ethernet controller显示的就是网卡型号 安装方法网址 https://github.com/heri16/r8125 官网地址https://www.realtek.com/en/component/zoo/category/network-interface-controllers-10-100-1000m-gigabit-ethernet-pc…

爬虫破解qq翻译接口自制一个翻译工具

输入要翻译的内容时 &#xff0c; 地址并未发生变化&#xff0c;因此可断定是向接口发送的ajax请求 清空一下审查窗口&#xff0c;再点击一次翻译可以发现实际发送请求的url为https://fanyi.qq.com/api/translate 并且可以看到发送的是post请求&#xff0c;请求表单数据和响应数…

【Realtek sdk-3.4.14b】RTL8197G增加YT8531 Switch

问题描述 Realtek sdk-3.4.14b增加YT8531 Switch芯片支持 代码修改 修改rtl819x/linux-3.10/drivers/net/phy/Kconfig --- a/rtl819x/linux-3.10/drivers/net/phy/Kconfig +++ b/rtl819x/linux-3.10/drivers/net/phy/KconfigCurrently, only 8-bit registers are supported.…