0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

基于ebpf的性能工具应用

科技绿洲 来源:Linux开发架构之路 作者:Linux开发架构之路 2023-11-08 16:19 次阅读

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

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

构建样例

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

#include
#include

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:可执行文件:函数名:

图片

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

图片

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

图片

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 calln")}'

我们需要进行过滤,增加filter只保留我们关心的应用程序的调用探测。bpftrace提供了系统变量comm表示可执行文件名 (进程名),只需要在上述指令中增加 filter,只处理comm=="mem_check"的malloc调用事件。左边终端执行探测,右边终端执行可执行文件。每调用一次malloc函数,就能探测到一次:

图片

使用bpftrace脚本进一步探测

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

BEGIN {
printf("start proben");
}

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

END {
printf("end proben");
}

探测mem_check中malloc的内存空间大小。

malloc的原型:

void *malloc(size_t size);

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

BEGIN {
printf("start proben");
}

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

END {
printf("end proben");
}

如下图可以看到mem_check中申请内存的情况,最后一个malloc size 1024是mem_check自动创建输出缓冲区申请的内存,不用理会。

图片

探测mem_check中malloc的返回值

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

BEGIN {
printf("start proben");
}

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

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

END {
printf("end proben");
}

运行结果:

图片

探测mem_check中free

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

BEGIN {
printf("start proben");
}

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

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

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

END {
printf("end proben");
}

运行结果:

图片

探测内存泄露

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

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

  1. 定义一个map变量@mem:保存malloc返回的内存地址。
  2. 当探测到free调用时,将@mem对应地址删除。
  3. 最后@mem剩下的就是内存泄露的地址。

内存泄露检测脚本如下:

BEGIN {
printf("start proben");
}

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

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

}

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

END {
printf("end proben");
}

运行结果:

图片

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

总结

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 内存
    +关注

    关注

    8

    文章

    3034

    浏览量

    74137
  • 代码
    +关注

    关注

    30

    文章

    4801

    浏览量

    68735
  • Valgrind
    +关注

    关注

    0

    文章

    9

    浏览量

    6816
收藏 人收藏

    评论

    相关推荐

    解构内核源码eBPF样例编译过程

    了解和掌握纯c语言的ebpf编译和使用,有助于我们加深对于eBPF威廉希尔官方网站 原理的进一步掌握,也有助于开发符合自己业务需求的高性能ebpf程序。
    的头像 发表于 04-17 14:05 1585次阅读

    Linux跟踪工具bpftrace的原理和使用

    这篇文章介绍一个基于ebpf威廉希尔官方网站 的强大工具--bpftrace。
    发表于 09-01 15:10 2016次阅读
    Linux跟踪<b class='flag-5'>工具</b>bpftrace的原理和使用

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

    ,并且介绍了如何运行bpftrace脚本,这篇文章将介绍bpftrace脚本的语法。 基于ubuntu22.04-深入浅出 eBPF 基于ebpf性能工具
    的头像 发表于 09-04 16:04 1060次阅读
    基于<b class='flag-5'>ebpf</b>的<b class='flag-5'>性能</b><b class='flag-5'>工具</b>-bpftrace脚本语法

    关于 eBPF 安全可观测性,你需要知道的那些事儿

    非常复杂的话题,牵一发而动全身,防御机制、加固配置、漏洞利用等等挑战性的威廉希尔官方网站 。在进行加固防御的过程中,又会产生性能或者系统稳定性相关的影响。从 eBPF + LSM 的角度可以更加可视化、数据丰富的观测内核
    发表于 09-08 15:31

    openEuler 倡议建立 eBPF 软件发布标准

    eBPF 被广泛应用在云原生、可观测、性能调优、安全、硬件加速等领域,并且其应用场景还在快速扩展,各种场景基于 eBPF 威廉希尔官方网站 的创新 idea 呈现井喷现象,eBPF 的时代已经来临
    发表于 12-23 16:21

    eBPF是什么以及eBPF能干什么

    规则使用基于寄存器的虚拟机来描述包过滤的行为。比较常用的功能是通过过滤来统计流量,tcpdump工具就是基于BPF实现的。而eBPF对它进行了扩展来实现更多的功能。 主要区别如下: 1)允许使用C 语言编写
    的头像 发表于 07-05 15:17 1.2w次阅读
    <b class='flag-5'>eBPF</b>是什么以及<b class='flag-5'>eBPF</b>能干什么

    eBPF深入理解和实现原理

    简单来说,wBPF 是一个在硬件上直接执行 eBPF 程序的系统。
    的头像 发表于 06-14 09:25 3646次阅读

    介绍eBPF针对可观测场景的应用

    随着eBPF推出,由于具有高性能、高扩展、安全性等优势,目前已经在网络、安全、可观察等领域广泛应用,同时也诞生了许多优秀的开源项目,如Cilium、Pixie等,而iLogtail 作为阿里内外千万实例可观测数据的采集器,eBPF
    的头像 发表于 08-11 09:10 1606次阅读

    openEuler倡议建立eBPF软件发布标准

    eBPF 是一个能够在内核运行沙箱程序的威廉希尔官方网站 ,提供了一种在内核事件和用户程序事件发生时安全注入代码的机制,使得非内核开发人员也可以对内核进行控制。随着内核的发展,eBPF 逐步从最初的数据包过滤
    的头像 发表于 12-06 10:29 571次阅读

    Linux 内核:eBPF优势和eBPF潜力总结

    Express Data Path (XDP):网络驱动程序是最早可以附加 XDP BPF 钩子的点。当收到一个数据包时,eBPF 程序就会被触发运行。
    发表于 01-10 11:37 3202次阅读

    什么是eBPFeBPF为何备受追捧?

    用云杉网络 VP 向阳的话来说:“ eBPF 最重要(没有之一)的特点是安全性” 。他表示,以往必须编写内核模块才能做到的工作现在基本都能做到。
    的头像 发表于 05-06 11:41 2299次阅读

    eBPF的前世今生?eBPF在使用中遇到的问题有哪些?

    在介绍eBPF (Extended Berkeley Packet Filter)之前,我们先来了解一下它的前身-BPF (Berkeley Packet Filter)伯克利数据包过滤器。
    的头像 发表于 08-12 15:10 1647次阅读
    <b class='flag-5'>eBPF</b>的前世今生?<b class='flag-5'>eBPF</b>在使用中遇到的问题有哪些?

    基于ebpf性能工具-bpftrace

    运行情况对于诊断问题、优化性能以及进行安全监控至关重要。bpftrace作为一款强大的跟踪工具,为开发人员和系统管理员提供了一种独特的方式来监视和分析Linux系统的内部运行。本文描述bpftrace的原理和使用。 bpftrace 「bpftrace是基于
    的头像 发表于 09-04 16:02 690次阅读
    基于<b class='flag-5'>ebpf</b>的<b class='flag-5'>性能</b><b class='flag-5'>工具</b>-bpftrace

    ebpf的快速开发工具--libbpf-bootstrap

    基于ubuntu22.04-深入浅出 eBPF 基于ebpf性能工具-bpftrace 基于ebpf
    的头像 发表于 09-25 09:04 1034次阅读
    <b class='flag-5'>ebpf</b>的快速开发<b class='flag-5'>工具</b>--libbpf-bootstrap

    eBPF动手实践系列三:基于原生libbpf库的eBPF编程改进方案简析

    在上一篇文章《eBPF动手实践系列二:构建基于纯C语言的eBPF项目》中,我们初步实现了脱离内核源码进行纯C语言eBPF项目的构建。libbpf库在早期和内核源码结合的比较紧密,如今的libbpf库更加成熟,已经完全脱离内核源码
    的头像 发表于 03-19 14:19 869次阅读
    <b class='flag-5'>eBPF</b>动手实践系列三:基于原生libbpf库的<b class='flag-5'>eBPF</b>编程改进方案简析