基于ebpf的性能工具-bpftrace实战(内存泄漏)

news2024/11/25 14:55:20

8ddc3306e4414ad4e2e263f84a173e37.jpeg

在之前的篇章中,我们已经详细阐述了bpftrace的操作原理,以及其脚本语法的特点。在本文中,我们将通过实际案例展示bpftrace这一强大工具的实际应用,以便更加深入地理解其在解决问题中的价值。

基于ubuntu22.04-深入浅出 eBPF

基于ebpf的性能工具-bpftrace

基于ebpf的性能工具-bpftrace脚本语法

在实际的软件开发过程中,内存问题常常是耗费大量时间进行分析的挑战之一。为了更有效地定位和解决与内存相关的难题,一系列辅助工具应运而生,其中备受赞誉的Valgrind工具便是其中之一。事实上,笔者本人曾利用Valgrind工具成功地发现并解决了一个隐藏在软件中的bug,这充分体现了工具在开发过程中的重要性。

然而,同样强大的bpftrace工具同样具备简洁而直观的特点,能够协助我们高效地追踪内存泄漏问题。在这方面,bpftrace提供了一种更加精细的、实时的分析方式,帮助开发人员准确地定位代码中可能存在的内存泄漏情况。

构建样例

  • 我们编写一个程序--mem_check.c,代码中包含正确的申请内存和释放内存的逻辑,同时包含存在内存泄露的代码代码。。

#include <stdio.h>
#include <stdlib.h>

int main(){
    char *p1 = NULL;
    char *p2 = NULL;
    
    for(int i = 0; i < 5; i++) 
    {       
        p1 = malloc(16);
    }

    for(int i = 0; i < 5; i++)
    {
        p2 = malloc(32);
        free(p2);
    }
    getchar();
    return 0;
}
  • 上面的代码非常简单,我们申请了5次16个字节的内存,没有释放,存在内存泄露。申请5次32个字节的内存,有释放,没存在内存泄露。那么我们如何通过bpftrace定位呢?

  • 我们通过bpftrace对mem_check.c进行动态的统计内存的申请和释放,定位内存泄露的问题。我们需要对关键的两个接口进行probe--malloc和free,这两个接口的实现在libc中。

  • 编译mem_check.c文件,生成可执行文件:

gcc mem_check.c -o mem_check

探测mem_ckeck可执行文件

  • bpftrace可以对内核态进行探测也可以对用户态进行探测,其中探针如下:

    • 内核态探针:kprobe/kretprobe

    • 用户态探针:uprobe/uretprobe

  • mem_check.c是一个应用程序,显然我们需要使用用户态探针:uprobe/uretprobe

  • 通过uprobe探测mem_check.c中的malloc函数,我们单行指令验证,参数格式是 uprobe:可执行文件:函数名:

a0a214a7bce87b8b45f6266a53e4fc76.png
  • 理论是没有没有问题,但实际发生错误:No probes to attach。原因:可执行文件mem_check中找不到符号:malloc,我们可以通过nm命令确定一下:

ab8b39c02b01f5d9d774b82c1860539f.png
  • 我们发现malloc是一个链接自GLIBC_2.2.5的符号,并不是mem_ckeck自身的符号,所以我们探测的符号修改libc库中malloc符号,系统中可能存在多个c库,我们需要找到mem_ckeck程序使用的C库,通过ldd命令查看:

be5f7ad878837f6f6bcc02fada36755d.png
  • mem_check可执行文件使用的C库为:/lib/x86_64-linux-gnu/libc.so.6,我们将可以执行文件替换为/lib/x86_64-linux-gnu/libc.so.6。再次执行,会出现大量内容,显然是其他进程调用了malloc引起的,而我们的mem_ckeck还没有运行,显然还没有探测我们的可执行程序。

bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc {printf("malloc call\n")}'
  • 我们需要进行过滤,增加filter只保留我们关心的应用程序的调用探测。bpftrace提供了系统变量comm表示可执行文件名 (进程名),只需要在上述指令中增加 filter,只处理comm=="mem_check"的malloc调用事件。左边终端执行探测,右边终端执行可执行文件。每调用一次malloc函数,就能探测到一次:

3c8fd2b7b077335e2fa7e7039bbe8f54.png

使用bpftrace脚本进一步探测

  • 将上面的单行命令变为bpftrace脚本--bpf_test.bt

BEGIN {
    printf("start probe\n");
}

uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
  printf("malloc call\n");
}

END {
    printf("end probe\n");
}
  • 探测mem_check中malloc的内存空间大小。

  1. malloc的原型:

void *malloc(size_t size);
  1. bpftrace的uprobe和kprobe可以通过内置变量arg0、arg1 ··· ··· 访问函数参数,对bpf_test.bt修改就可以打印malloc申请内存的大小:

BEGIN {
    printf("start probe\n");
}

uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
  printf("malloc size: %d\n", arg0);
}

END {
    printf("end probe\n");
}
  1. 如下图可以看到mem_check中申请内存的情况,最后一个malloc size 1024是mem_check自动创建输出缓冲区申请的内存,不用理会。

7f165ef1369df01c3e3341d23dcb76d5.png
  • 探测mem_check中malloc的返回值

  1. malloc的返回值是地址,需借助uretprobe进行探测,函数返回值可通过内置变量retval访问。uretprobe的filter与malloc参数探测时类似,脚本修改为:

BEGIN {
    printf("start probe\n");
}

uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
    printf("malloc size: %d\n", arg0);
}

uretprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
  printf("addr = %p\n", retval);
}

END {
    printf("end probe\n");
}
  1. 运行结果:

6db5ae8ac2d0d8be392fab9951b873ef.png
  • 探测mem_check中free

  1. 我们已经探测到mem_check的malloc的内存大小,内存的地址,我们通过探测free,然后匹配malloc和free的情况就可以查找内存的泄漏点。脚本修改为:

BEGIN {
    printf("start probe\n");
}

uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
    printf("malloc size: %d\n", arg0);
}

uretprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
  printf("addr = %p\n", retval);
}

uprobe:/lib/x86_64-linux-gnu/libc.so.6:free /comm == "mem_check"/{
  printf("free addr = %p\n", arg0);
}

END {
    printf("end probe\n");
}
  1. 运行结果:

190734fb0bdf5ec47e1a564369a43912.png

探测内存泄露

  • 上面我们已经探测到了mem_check中的malloc,free情况。我们可以通过malloc和free的地址集合差,就可以得到内存泄露的地址位置。

  • bpftrace底层使用的是eBPF的map作为存储结构,可以简单的看作K-V存储,我们可以利用map来统计地址集合差,步骤如下:

  1. 定义一个map变量@mem:保存malloc返回的内存地址。

  2. 当探测到free调用时,将@mem对应地址删除。

  3. 最后@mem剩下的就是内存泄露的地址。

内存泄露检测脚本如下:

BEGIN {
    printf("start probe\n");
}

uprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
    printf("malloc size: %d\n", arg0);
    @size = arg0;
}

uretprobe:/lib/x86_64-linux-gnu/libc.so.6:malloc /comm == "mem_check"/{
    printf("addr = %p\n", retval);
    @mem[retval] = @size;

}

uprobe:/lib/x86_64-linux-gnu/libc.so.6:free /comm == "mem_check"/{
    printf("free addr = %p\n", arg0);
    delete(@mem[arg0]);
}

END {
    printf("end probe\n");
}
  • 运行结果:

473ee42fc2ba973dafb3bc31d0fa3f23.png
  • 如上图,红色框中就是没有释放的内存和内存大小。

总结

通过编写一些简单的bpftrace脚本,我们就可以监视应用程序的内存分配和释放事件,捕获内存泄漏的迹象。这种直接的实时监控方式,使得开发者能够在问题出现时即刻获得反馈,从而更加迅速地解决潜在的内存问题,提升软件的稳定性和性能。

5b519811455649c21c70d6f7713c7af5.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/973723.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python测试框架 Pytest —— mock使用(pytest-mock)

pytest-mock 安装&#xff1a;pip install pytest-mock 这里的mock和unittest的mock基本上都是一样的&#xff0c;唯一的区别在于pytest.mock需要导入mock对象的详细路径。 # weateher_r.py class Mock_weather():def weather(self):天气接口passdef weather_result(self):模…

内网穿透:FRP(Forwarding Remote Proxy)反向代理

frp 是一个可用于内网穿透的高性能的反向代理应用&#xff0c;支持 tcp, udp 协议&#xff0c;为 http 和 https 应用协议提供了额外的能力&#xff0c;且尝试性支持了点对点穿透 下载地址 https://github.com/fatedier/frp/releases 选择最新的就行&#xff0c;linux和windo…

TCP机制之连接管理(三次握手和四次挥手详解)

TCP的连接管理机制描述了连接如何创建以及如何断开! 建立连接(三次握手) 三次握手的过程 所谓建立连接就是通信双方各自要记录对方的信息,彼此之间要相互认同;这里以A B双方确立男女朋友关系为例: 从图中可以看出,通信双方各自向对方发起一个"建立连接"的请求,同时…

Spring Security 安全框架NOTE

目录 1、什么是 Spring Security 安全框架? 2、关于 SpringSecurity 中的认证 3、关于 SpringSecurity 中的授权 3.1 从数据库中查询用户的权限信息 4、关于自定义失败处理 5、跨域问题 前提引入&#xff1a; 随着科技的完善&#xff0c;现在几乎所有的网站以及软件都需…

为大模型添加记忆体,GBASE南大通用驶入向量赛道

大数据产业创新服务媒体 ——聚焦数据 改变商业 理解、生成、逻辑、记忆是人工智能的四大核心能力。 一段人类的日常对话通常可以分解为引子、记忆、分析三个部分。计算机自然语言处理的解法&#xff0c;AI科学家归纳出一个CPV结构&#xff1a;以ChatGPT为代表的大模型承担“分…

Vmware 网络恢复断网和连接

如果你的 虚拟机无法联网了&#xff0c;比如&#xff1a; vmware 无法将网络更改为桥接状态: 没有未桥接的主机网络适配器 等各种稀奇古怪的问题&#xff1b; 按照下面操作 还远默认设置 包你解决各种问题&#xff01;

Pycharm----将Anaconda建立的环境导入

首先打开项目设置&#xff0c;点击添加 随后点击现有环境&#xff0c;点击三个。。。号进行添加 最后找到你Anaconda安装文件夹&#xff0c;envs找到你建立的环境名称&#xff0c;找到python.exe将它导入即可让现在的python环境为你建立的环境&#xff0c;同时还需要更改终端方…

接口自动化测试系列-接口测试

接口测试工具-postman 利用postman完成接口测试:官网。 接口一般包含&#xff1a; url:请求地址&#xff0c;如:https://www.baidu.com/ method:请求方式&#xff0c;get,post,update,delete等 headers:请求头 body/params:请求体&#xff0c;post一般存在body中。get请求放在…

苹果手机隔空投送怎么用?隔空投送的使用教程来了!

如何实现苹果设备之间快速高效地传输各种数据&#xff1f;相信我&#xff0c;使用【隔空投送】是大家的不二选择。苹果手机的【隔空投送】功能&#xff0c;即大家口中常说的“Airdrop”&#xff0c;能够让苹果用户实现近距离传输照片、视频、文件等。苹果手机隔空投送怎么用&am…

安科瑞电能质量监测与治理系统的解决方案-安科瑞黄安南

01 电能质量问题及现象 02 电能质量标准及选型 03 安科瑞电能质量产品及服务 04 经典案例分析

实现卓越供应链:RFID技术的革命性应用

在现代制造业中&#xff0c;供应链和物流的高效运作至关重要&#xff0c;它不仅影响着生产效率&#xff0c;还直接关系到企业的竞争力和客户满意度。为了应对这些挑战&#xff0c;越来越多的企业开始关注智能制造RFID智能设备&#xff0c;将其应用于供应链和物流管理&#xff0…

在OpenStack私有云上安装配置虚拟机

文章目录 零、学习目标一、登录大数据实训云二、创建网络三、创建路由四、添加接口五、创建端口六、添加安全组规则七、创建实例&#xff08;一&#xff09;实例规划&#xff08;二&#xff09;创建实例 - ied&#xff08;三&#xff09;创建实例 - master、slave1与slave2&…

Cesium 根据鼠标点击生成点击点的坐标信息

Cesium 根据鼠标点击生成点击点的坐标信息 一、需求二、分析1. 创建鼠标点击事件2. 点击生成坐标但不是经纬度&#xff0c;而是笛卡尔坐标系下的坐标&#xff0c;这个时候需要做一次转换3. 完整代码 三、数据保存 一、需求 在日常开发中 &#xff0c;会遇到根据鼠标点击生成对应…

MindSponge分子动力学模拟——使用迭代器进行系统演化(2023.09)

技术背景 在前面几篇博客中&#xff0c;我们已经介绍过使用MindSponge去定义一个系统以及使用MindSponge计算一个分子系统的单点能。这篇文章我们将介绍一下在MindSponge中定义迭代器Updater&#xff0c;并使用Sponge对系统进行演化&#xff0c;最后使用CallBack对输出结果进行…

Spring——Spring的控制反转IOC

摘要 IoC 不是一种技术&#xff0c;只是一种思想&#xff0c;一个重要的面向对象编程的法则&#xff0c;它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象&#xff0c;从而导致类与类之间高耦合&#xff0c;难于测试&#xff1b;…

msvcp140.dll是什么东西?msvcp140.dll丢失的5个常用解决方法

今天&#xff0c;我将为大家带来计算机丢失msvcp140.dll修复教程。在我们的日常生活和学习中&#xff0c;计算机问题是无处不在的。有时候&#xff0c;我们可能会遇到一些困扰&#xff0c;比如计算机丢失msvcp140.dll文件。msvcp140.dll是Windows系统中非常重要的动态链接库文件…

代码随想录算法训练营第二十七天| 131.分割回文串

131.分割回文串 本题较难&#xff0c;大家先看视频来理解 分割问题&#xff0c;明天还会有一道分割问题&#xff0c;先打打基础。 代码随想录 视频讲解&#xff1a;带你学透回溯算法-分割回文串&#xff08;对应力扣题目&#xff1a;131.分割回文串&#xff09;| 回溯法精讲…

CS420 课程笔记 P6 - 游戏逆向中的虚拟内存

文章目录 IntroVirtual memoryExample!Static example Intro 在上个视频中&#xff0c;我们知道有些地址在你重进游戏时就会无效&#xff0c;有的有时有效&#xff0c;我们需要了解称为虚拟内存的东西 记住这些信息&#xff1a;当你双击打开 Squally.exe 游戏时&#xff0c;系…

【C++ 学习 ⑲】- 多态(下)

目录 一、虚函数表和多态的原理 1.1 - 虚函数表 1.2 - 多态的原理 二、单继承和多继承关系中的虚函数表 2.1 - 单继承关系中的虚函数表 2.2 - 多继承关系中的虚函数表 三、纯虚函数和抽象类 一、虚函数表和多态的原理 1.1 - 虚函数表 问&#xff1a;sizeof(b) 是多少&a…

使用docker部署db2

1.使用docker部署db2 1.1 拉db2镜像 将db2镜像拉起到本地。 docker pull ibmcom/db21.2启动容器 docker run -d -p 50000:50000 --name db2 --privilegedtrue -e DB2INST1_PASSWORDdbPassword DBNAMEjumpdb -e LICENSEaccept -v /usr/local/db2:/database ibmcom/db2实例化…