WEBKT

性能工程师的eBPF实战指南:如何用eBPF定位应用瓶颈?

77 0 0 0

什么是eBPF?为什么它如此强大?

eBPF 实战:定位应用性能瓶颈

1. 函数调用跟踪:火焰图(Flame Graph)

2. 延迟分析:跟踪点(Tracepoint)

3. 内存分析:slabtop & eBPF

4. 网络分析:tcpdump & eBPF

5. IO 分析:iotop & eBPF

eBPF 的未来

总结

作为一名性能工程师,我深知应用性能优化是场没有硝烟的战争。面对日益复杂的应用架构,传统的性能分析工具往往显得力不从心。这时,eBPF(extended Berkeley Packet Filter)就像一把瑞士军刀,为我们提供了前所未有的洞察力,能够深入内核,精准定位性能瓶颈。

什么是eBPF?为什么它如此强大?

简单来说,eBPF 是一种内核技术,允许我们在内核中安全地运行用户自定义的代码,而无需修改内核源代码或加载内核模块。这意味着我们可以动态地向内核添加新的功能,例如监控、跟踪、安全策略等等。

eBPF 的强大之处在于:

  • 高性能:eBPF 代码在内核中运行,避免了用户态和内核态之间频繁的切换,大大降低了性能开销。
  • 安全性:eBPF 代码在运行前会经过内核的验证器(verifier)检查,确保其不会崩溃或危害系统安全。
  • 灵活性:eBPF 可以用于各种各样的性能分析场景,例如跟踪函数调用、分析内存使用、监控网络流量等等。
  • 可编程性:eBPF 提供了丰富的 API 和工具链,方便我们编写和部署 eBPF 程序。

eBPF 实战:定位应用性能瓶颈

接下来,我将分享一些使用 eBPF 定位应用性能瓶颈的实战技巧,并结合具体的代码示例,帮助你快速上手。

1. 函数调用跟踪:火焰图(Flame Graph)

火焰图是一种非常直观的性能分析工具,它可以将 CPU 的调用栈信息可视化,帮助我们快速找到 CPU 密集型的函数。

原理:火焰图的横轴表示 CPU 的时间,纵轴表示调用栈的深度。每个矩形代表一个函数,矩形的宽度表示该函数占用的 CPU 时间比例。通过观察火焰图,我们可以快速找到占用 CPU 时间最多的函数,从而定位性能瓶颈。

工具perfbpftrace

示例

假设我们有一个名为 my_app 的应用程序,我们想了解它的 CPU 使用情况,可以使用以下命令生成火焰图:

sudo perf record -F 99 -p $(pidof my_app) -g -- sleep 30
sudo perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > flamegraph.svg
  • perf record:用于收集 CPU 的调用栈信息。
    • -F 99:指定采样频率为 99Hz。
    • -p $(pidof my_app):指定要分析的进程 ID。
    • -g:启用调用栈跟踪。
    • -- sleep 30:指定采样时间为 30 秒。
  • perf script:用于将 perf record 生成的数据转换为文本格式。
  • stackcollapse-perf.pl:用于将文本格式的调用栈信息折叠成火焰图所需的格式。
  • flamegraph.pl:用于生成火焰图。

分析:打开 flamegraph.svg 文件,我们可以看到 my_app 的火焰图。通过观察火焰图,我们可以找到占用 CPU 时间最多的函数,例如 my_func。然后,我们可以深入分析 my_func 的代码,找出性能瓶颈。

bpftrace版本

# bpftrace -e 'profile:hz:99 { @[ustack()] = count(); }' > out.stacks
# ./stackcollapse.pl out.stacks | ./flamegraph.pl > flamegraph.svg

2. 延迟分析:跟踪点(Tracepoint)

跟踪点是内核中预先定义好的事件点,我们可以使用 eBPF 程序来监控这些事件点,例如系统调用、网络事件等等。通过分析这些事件的延迟,我们可以找到导致应用性能下降的原因。

原理:通过在内核的跟踪点上附加 eBPF 程序,我们可以捕获事件发生时的上下文信息,例如时间戳、进程 ID、函数参数等等。然后,我们可以使用这些信息来计算事件的延迟,并进行统计分析。

工具trace-cmdbpftrace

示例

假设我们想监控 read 系统调用的延迟,可以使用以下命令:

sudo trace-cmd record -e syscalls:sys_enter_read -e syscalls:sys_exit_read -p $(pidof my_app)
sudo trace-cmd report > trace.txt
  • trace-cmd record:用于记录内核事件。
    • -e syscalls:sys_enter_read:指定要监控的事件为 sys_enter_read,即 read 系统调用的入口。
    • -e syscalls:sys_exit_read:指定要监控的事件为 sys_exit_read,即 read 系统调用的出口。
    • -p $(pidof my_app):指定要分析的进程 ID。
  • trace-cmd report:用于将 trace-cmd record 生成的数据转换为文本格式。

分析:打开 trace.txt 文件,我们可以看到 read 系统调用的详细信息,包括时间戳、进程 ID、返回值等等。通过计算 sys_exit_readsys_enter_read 之间的时间差,我们可以得到 read 系统调用的延迟。然后,我们可以统计分析这些延迟,找出延迟较高的 read 调用,并深入分析其原因。

bpftrace版本

#!/usr/bin/env bpftrace
BEGIN
{
printf("Tracing read() syscalls... Hit Ctrl-C to end.\n");
}
tracepoint:syscalls:sys_enter_read
{
@start[tid] = nsecs;
}
tracepoint:syscalls:sys_exit_read / @start[tid] /
{
@latency = hist(nsecs - @start[tid]);
delete(@start[tid]);
}
END
{
clear();
printf("\nRead() latency:\n");
print(@latency);
}

3. 内存分析:slabtop & eBPF

内存泄漏和内存碎片是导致应用性能下降的常见原因。使用 eBPF,我们可以深入分析内存的使用情况,例如内存分配、释放、缓存等等,从而找到内存泄漏和内存碎片的原因。

原理:eBPF 可以用于跟踪内存分配和释放的函数调用,例如 mallocfree 等等。通过分析这些函数调用的参数和返回值,我们可以了解内存的使用情况,例如分配的大小、分配的地址、释放的时间等等。此外,eBPF 还可以用于监控内核的内存缓存(slab),了解缓存的使用情况。

工具slabtopbpftrace

示例

  • slabtop

slabtop 是一个用于监控内核 slab 缓存的工具。它可以显示各个 slab 缓存的大小、使用率、碎片率等等。通过观察 slabtop 的输出,我们可以找到占用内存较多的 slab 缓存,并深入分析其原因。

  • eBPF 跟踪 malloc/free

假设我们想跟踪 mallocfree 函数的调用,可以使用以下 bpftrace 脚本:

#!/usr/bin/env bpftrace
#include <linux/mm_types.h>
BEGIN
{
printf("Tracing malloc() and free()... Hit Ctrl-C to end.\n");
}
// malloc
uprobe:libc:malloc
{
@bytes[pid] = sum(arg0);
printf("malloc(%d) by PID %d\n", arg0, pid);
}
// free
uprobe:libc:free
{
printf("free(%lx) by PID %d\n", arg0, pid);
}
END
{
clear();
printf("\nTotal bytes allocated per process:\n");
print(@bytes);
}

这个脚本使用 uprobe 附加到 mallocfree 函数上,并打印出分配和释放的内存大小和进程 ID。通过分析这些信息,我们可以找到内存泄漏的原因。

分析

  1. 运行 slabtop 命令,观察 slab 缓存的使用情况。
  2. 运行 bpftrace 脚本,跟踪 mallocfree 函数的调用。
  3. 结合 slabtop 和 bpftrace 的输出,分析内存的使用情况,找出内存泄漏和内存碎片的原因。

4. 网络分析:tcpdump & eBPF

网络延迟和丢包是导致应用性能下降的另一个常见原因。使用 eBPF,我们可以深入分析网络流量,例如 TCP 连接、HTTP 请求等等,从而找到网络瓶颈。

原理:eBPF 可以用于监控网络数据包的收发,例如捕获 TCP 连接的建立、关闭、数据传输等等。通过分析这些数据包的信息,我们可以了解网络的延迟、丢包、拥塞等等。此外,eBPF 还可以用于过滤网络流量,只捕获我们感兴趣的数据包。

工具tcpdumpbpftracetc

示例

  • tcpdump

tcpdump 是一个常用的网络抓包工具。我们可以使用 tcpdump 捕获网络数据包,并分析其内容。

  • eBPF 跟踪 TCP 连接

假设我们想跟踪 TCP 连接的建立和关闭,可以使用以下 bpftrace 脚本:

#!/usr/bin/env bpftrace
kprobe:tcp_v4_connect
{
printf("%s connect src=%lx dst=%lx sport=%d dport=%d\n",
comm, args[0]->saddr, args[0]->daddr, args[0]->sport, args[0]->dport);
}
kprobe:tcp_close
{
printf("%s close src=%lx dst=%lx sport=%d dport=%d state=%d\n",
comm, args[0]->saddr, args[0]->daddr, args[0]->sport, args[0]->dport, args[0]->state);
}

这个脚本使用 kprobe 附加到 tcp_v4_connecttcp_close 函数上,并打印出 TCP 连接的源地址、目的地址、端口号等信息。通过分析这些信息,我们可以了解 TCP 连接的建立和关闭情况,从而找到网络瓶颈。

分析

  1. 使用 tcpdump 捕获网络数据包,并分析其内容。
  2. 运行 bpftrace 脚本,跟踪 TCP 连接的建立和关闭。
  3. 结合 tcpdump 和 bpftrace 的输出,分析网络流量,找出网络瓶颈。

5. IO 分析:iotop & eBPF

磁盘 IO 延迟是导致应用性能下降的另一个常见原因。使用 eBPF,我们可以深入分析磁盘 IO 的情况,例如读写延迟、IOPS 等等,从而找到 IO 瓶颈。

原理:eBPF 可以用于跟踪磁盘 IO 的请求,例如捕获 readwrite 系统调用的参数和返回值。通过分析这些信息,我们可以了解磁盘 IO 的延迟、IOPS、吞吐量等等。此外,eBPF 还可以用于监控内核的 IO 调度器,了解 IO 调度的策略。

工具iotopbpftrace

示例

  • iotop

iotop 是一个用于监控磁盘 IO 的工具。它可以显示各个进程的 IOPS、吞吐量、延迟等等。通过观察 iotop 的输出,我们可以找到 IO 密集型的进程,并深入分析其原因。

  • eBPF 跟踪 IO 延迟

假设我们想跟踪 IO 延迟,可以使用以下 bpftrace 脚本:

#!/usr/bin/env bpftrace
#include <linux/fs.h>
BEGIN
{
printf("Tracing block I/O... Hit Ctrl-C to end.\n");
}
// trace block I/O start
kprobe:blk_start_request
{
@start[args[0]->pid] = nsecs;
}
// trace block I/O completion
kprobe:blk_mq_end_request
/ @start[args[1]->pid] /
{
@lat = hist(nsecs - @start[args[1]->pid]);
delete(@start[args[1]->pid]);
}
END
{
clear();
printf("\nBlock I/O latency:\n");
print(@lat);
}

这个脚本使用 kprobe 附加到 blk_start_requestblk_mq_end_request 函数上,并计算 IO 请求的延迟。通过分析这些信息,我们可以了解 IO 延迟的情况,从而找到 IO 瓶颈。

分析

  1. 运行 iotop 命令,观察磁盘 IO 的情况。
  2. 运行 bpftrace 脚本,跟踪 IO 延迟。
  3. 结合 iotop 和 bpftrace 的输出,分析磁盘 IO 的情况,找出 IO 瓶颈。

eBPF 的未来

eBPF 正在迅速发展,越来越多的工具和框架正在涌现,例如:

  • bcc (BPF Compiler Collection):提供了一组用于编写和部署 eBPF 程序的工具。
  • bpftrace:一种高级的 eBPF 跟踪语言,可以方便地编写 eBPF 程序。
  • cilium:一个基于 eBPF 的网络和安全解决方案。

我相信,随着 eBPF 的不断发展,它将在性能分析、安全、网络等领域发挥越来越重要的作用。

总结

eBPF 是一种强大的内核技术,可以帮助我们深入分析应用性能瓶颈。通过本文的介绍,希望你能够掌握使用 eBPF 进行性能分析的基本技巧,并将其应用到实际工作中。记住,性能优化是一个持续的过程,需要不断学习和实践。希望这篇文章能帮助你更好地利用 eBPF 这把瑞士军刀,在性能优化的道路上披荆斩棘,最终交付卓越的应用体验!

性能调优侠 eBPF性能分析火焰图

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/9228