前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >eBPF 性能之颠 -- 函数执行耗时追踪

eBPF 性能之颠 -- 函数执行耗时追踪

作者头像
初代庄主
发布2023-02-20 10:52:52
1.3K0
发布2023-02-20 10:52:52
举报
文章被收录于专栏:初代庄主

背景

对于算法和数据结构应该大家都不陌生,在这门学科的语境里我们用 O(xxx) 来衡量算法的复杂度。但是实际的工作中性能工程师要回答的常常不是时间复杂度问题,而是 1、程序的哪个部分慢? 2、慢的部分,单次执行的耗时是多少?

对于第 1 个问题我以前的文章 完全掌握火焰图的作图原理&看图技巧 有回答过一些。这里我准备回答一下第 2 个问题,执行慢的函数,它到底有多慢呢?

读完这篇文档,你就能在不改一行代码的情况下,知道给定函数单次执行的耗时,并且是纳秒级别的精度。


原理

进程运行在操作系统(kernel)之上,也就是说进程干了什么,现在在干什么操作系统是清清楚楚的。

这样的话!问题 2 的解决方案就变成了告诉操作系统,让它帮忙盯一下给定函数在什么时候开始执行,并且什么时候执行完成。我们只要拿到这两个时间点就能算出特定函数单次执行的耗时。那我们怎么才能告诉操作系统呢?

Linux 内核里面有一个叫 eBPF 的框架,它是我们与内核对话的接口人。我们使用一门叫 bpftrace 的语言就可以和它对话,把我们想要做的事情告诉它。eBPF 的整体的架构如下。

ebpf 官方图片地址:https://ebpf.io/static/c53dfcbff6ea67a8f00896bd76e4c07c/c5f83/bpftrace.png


用 C++ 写一个 hello world

我以前的职位是 MySQL-DBA ,估计关注我这个号的读者多半也接触过 MySQL,考虑一大家都有 trace MySQL 的需求,我这里就用 C++ 写一段 hello world 程序抛砖引玉一下(MySQL是 C++ 写的)。

代码语言:javascript
复制
#include <unistd.h>
#include <iostream>


using namespace std;


int hello(int i) 
{
    cout<<"hello world " << i << " ." << endl;
    // sleep(0);
    return i;
}

int main(int argc, char *argv[])
{
    for(int i = {0}; i < 3; i++) 
    {
        hello(i);
    }
    return 0;
}

编译并执行

代码语言:javascript
复制
# 编译
mkdir build
cd build
cmake ..
cmake --build .

# 执行
./cpps 
hello world 0 .
hello world 1 .
hello world 2 .

假设我们就是要确认一下 hello 这个函数,每执行一次耗时是多久。以前难于上青天,现在用 eBPF 就是分分钟的事。



eBPF 框架实践

bpftrace 的语法与其它的编程语言有着比较大的差异,但是用多了就习惯了,总的来讲它的模式是这样的。

代码语言:javascript
复制
probe /condition/ { action }

probe 就是我们追踪项, condition 指的过滤条件(只追踪特定 pid 也可以不指定),action 对应捕捉到之后要执行的动作。我默认在座的各位已经懂了(没有懂也没有事,后面会给语言的官方文档),那我们直接上 bpftrace 的代码,我尽可能多写点注释。

代码语言:javascript
复制
#!/usr/bin/env bpftrace 

/*
追踪 /data/repos/cpp-20/build/cpps 程序 的 hello 函数的执行耗时
作者: 蒋乐兴
时间: 2022-02-17
*/

// BEGIN 和 END 是两个特别的 probe , 由于不需要过滤条件,所以 condition 部分就省略了
// 追踪启动时执行的 action 
BEGIN 
{
    printf("**** start trace cpps.hello function time cost ****\n");
}

// 函数开始执行时对应的 action 
uprobe:/data/repos/cpp-20/build/cpps:hello 
{   // 记录一下开始执行的时候到 @start[cmmm] 变量中
    print("enter function hello .");
    @start[comm] = nsecs;
}

// 函数执行完成对应的 action
uretprobe:/data/repos/cpp-20/build/cpps:hello  / @start[comm] /
{   // 取得函数返回时的时间,并计算耗时
    // 清理 @start[comm] 变量为下一次执行做准备
    print("exit  function hello .");
    printf("time-cost %d (ns) \n\n", (nsecs - @start[comm]));
    delete(@start[comm]);
}

// 追踪退出时要执行 action 
END 
{
    printf("**** end trace cpps.hello function time cost   ****\n");
    clear(@start);
}

现在 bpftrace 程序已经写好了,执行一下它就能把我们意图传递给 kernel ,追踪现在是如此简单。

1. 执行 bpftrace 追踪程序

代码语言:javascript
复制
bpftrace trace-hello-func-time-cost.bt

2. 执行我们刚才的 C++ 程序

代码语言:javascript
复制
/data/repos/cpp-20/build/cpps

3. 追踪程序会打印 hello 函数的执行耗时

代码语言:javascript
复制
Attaching 4 probes...
**** start trace cpps.hello function time cost ****
enter function hello .
exit  function hello .
time-cost 30979 (ns) 

enter function hello .
exit  function hello .
time-cost 5490 (ns) 

enter function hello .
exit  function hello .
time-cost 4880 (ns) 

^C**** end trace cpps.hello function time cost   ****

最后

1.私信 "cpp-20" 我看到之后会给你发上面示例的源码。

2都到这里了,是时候图穷匕见了!我这人比较 real 就直说了,我想涨粉帮忙点下关注!我的技术文章质量还可以,关注应该不亏。

“在看” + “分享” + “点赞” + “收藏” 也是我继续写下去的动力;再次感谢!!!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-02-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初代庄主 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档