在Golang中利用BPF进行动态追踪

Part 1 背景

Golang作为云原生领域中使用最广泛的编程技术,是我们MatrixOrigin数据库主力开发语言。Golang本身提供了pprof性能剖析工具,可以让我们快速,粗略的分析性能瓶颈,在日常开发中广泛使用。然而,随着性能调优要求不断升高,我们需要更精准的性能指标,比如某个特定golang函数的执行时间,此时pprof就无能为力了,我们必须另想办法。

第一反应,我们可以考虑人工加入计时函数,然后重新编译代码执行。该方案当然可行,但是缺点也很明显,对每个需要分析的函数都要修改代码,重新编译执行。该方案工作量比较大,也不易维护。如果是线上系统,往往无法修改代码。

那么,是否有更好的方法来测量特定函数的时延?最好是不用修改代码并重新编译。答案是:Yes。


Part 2 历史

01 Uprobe

如果想在不修改代码的前提下,实现修改代码测量的功能,那么自然而然的选择,则在程序运行时动态的修改程序的代码段,加入我们自定义逻辑。

Linux开发者们,提供了在程序运行时,动态修改程序代码的功能——uprobe。它可以修改指定汇编代码为int3指令,并把原指令保存起来。当应用程序运行到int3指令时,便会触发异常,切换到Linux内核态,这里可以执行提前好的测量逻辑。随后返回用户空间,执行最开始被int3替换并保存的指令。在应用程序看来,一切都毫无感觉。而我们的测量逻辑,记录了当前时间。如果我们在函数开始和结束,各记录一次,则可以通过计算差值,得到函数的执行时间。

02 BPF

Uprobe机制,提供了动态追踪应用程序的基础框架。然而,它依旧很难使用,测量逻辑需要写Linux内核模块,这对大多数应用程序开发者来说,使用门槛依旧很高。

随着BPF技术横空出世,Linux内核开发者们,将其整合进入了uprobe框架。从而可以使用类似C的代码,编写动态追踪逻辑。这比Linux内核模块实现,要简单多了。

03 Bpftrace

为了进一步降低uprobe + BPF 的使用门槛,开发者们设计并实现了bpftrace工具,它作为BPF的前端,利用类似于脚本语法的简单方式,编译生成BPF程序并自动加载。这在我们分析应用程序时,可以非常快速的实现动态测量程序。也可以提前准备好脚本,直接拿来使用。

Bpftrace原本设计主要是针对C语言开发的应用场景,那么它可以用在golang环境中吗?答案是可以,但是需要调整。接下来的篇幅,将介绍如何让bpftrace在golang环境中正确的使用。


Part 3 当BPF遇上Golang

01 问题

Golang作为一种编译型语言,理论上可以直接使用uprobe + BPF进行动态追踪。然而,golang与C语言相比,又有微妙的不同之处,这里用go 1.19 + x86环境为例子:

  1. Golang ABI与C语言相比,C语言函数只使用寄存器传递参数。然而golang不确定使用寄存器还是使用栈来传递函数参数(要么用寄存器,要么用栈传递,不会混合),编译器有相关算法来决定,具体算法规则可以参考golang源码的src/cmd/compile/internal-abi.md文档。golang函数传递参数的方式的不同,
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值