1 图 1 Linux profiling 手段一览

  • 软件埋点:

    • 手动埋点:主动调用 trace 函数来实现埋点。

      Android systrace 即是这样一个例子,如图 2 和 图 3 所示
    • 自动埋点:借助工具链,自动埋点,对函数的 entry 和 return 进行 hook。

      Linux ftrace 即是这样一个例子,图 4 简示了其实现原理
    • 动态埋点:运行时刻,在指定位置上加断点,断点触发时执行相应 handler。

      Handler 为注入内核的 eBPF 字节码

      Linux kprobe / uprobe 就是这样的例子,图 5 和 图 6 简示了 uprobe 以及 uretprobe 的实现原理
  • 硬件统计

    • 计数累加:统计一段时间内,某个性能监控单元(PMU)的计数。

      例如:perf stat -e cache-misses -p PID,参见 brendangregg.com/perf.html ,Counting Events 一节

      函数接口:参见 libperf 的封装,fd = perf_event_open(...); read(fd, …)
    • 采样:计数达标,产生中断,伴随 Backtrace 对应到代码行。

      例如:perf record -F 99 -p PID sleep 10,以及对应图形化展示 FlameGraph

      函数接口:参见 perf_event_open,fd = perf_event_open(…); void *addr = mmap(…, fd, …);

      图 7 简示了其实现原理

2 图 2 Android systrace 工作原理

  • User space,分为两部分

    • 埋点处:通过嵌入代码中的 Trace API 调用,向 Linux kernel 的 tracing buffer 写日志。

      上图中,裸示了写 tracing buffer 的过程
    • atrace:读取 tracing buffer,存于磁盘文件,以免 tracing buffer 溢出丢失信息。
  • Kernel space,通过 scheduler 嵌入的 tracepoint,将调度事件,写入 tracing buffer。

tracing buffer 犹如一段 in-memory 的日志流,对齐了写入的各个标记和事件。

3 图 3 Android atrace 输出文件及图形化展示

atrace 转储的 tracing buffer 内容,以及载入到 Chrome 浏览器,进行图形化分析。

Discuss:想象一个进程同时播放两段视频,视频解码库是多线程的,线程来自全局的 thread pool。通过 systrace,能区分这两个视频播放任务的 CPU 时间片吗?

4 图 4 Linux ftrace 实现原理回顾

  • 通过 gcc -pg 选项,编译时,函数开头自动插入 _mcount 调用。
  • _mcount 处:除了 hook entry ,还通过修改返回地址,来 hook return。

Linux kernel 热补丁方案,”kernel livepatch“,便借用了 ftrace 的原理:替换有漏洞的函数实现,从而实现热补丁。

更多关于 ftrace 使用,参考「Advanced Features of Ftrace」

5 图 5 uprobe 的实现原理

注:上图修改自 dev.framing.life/tracing/kernel-and-user-probes-magic

指定位置上的指令,头部修改为软件中断指令(同时原指令存档他处):

  1. 当执行到该位置时,触发软件中断,陷入内核
  2. 在内核,执行以 eBPF 字节码形式注入的 Handler
  3. 单步执行原指令
  4. 修正寄存器和栈,回到原有指令流

Discuss:这与 gdb 中设断点有什么区别?

断点的 Handler 运行于 Kernel space,无需多次的 User space Kernel space 通信

Discuss:用户空间注入的 Handler 在 Kernel space 执行,安全性如何保证?

听说过 eBPF 吗?

简单介绍下 extended Berkeley Packet Filter(eBPF)

  • 一种功能有限、沙箱化的字节码。
  • 由 User space 注入到 Kernel space 执行。
  • 基于 BPF 扩展。

原始的 BPF 用于网路包过滤,下面是一个 BPF 裸用的例子:

/**
* 通过 netlink socket,获得关心进程的消亡信息
*/
int sock_fd = socket (PF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, NETLINK_CONNECTOR);
union {
struct sockaddr sa;
struct sockaddr_nl nl;
} addr = { .nl.nl_family = AF_NETLINK, .nl.nl_pid = getpid(), .nl.nl_groups = CN_IDX_PROC };
enum proc_cn_mcast_op op = PROC_CN_MCAST_LISTEN;
struct cn_msg cn_msg = { .id.idx = CN_IDX_PROC, .id.val = CN_VAL_PROC, .len = sizeof(op) };
struct iovec iov[3] = {
[0] = { .iov_base = nlmsghdrbuf, .iov_len = NLMSG_LENGTH(0) },
[1] = { .iov_base = &cn_msg, .iov_len = sizeof(cn_msg) },
[2] = { .iov_base = &op, .iov_len = sizeof(op) }
};
bind(sock_fd, &addr.sa, sizeof(addr.nl));
/* start proc connector */
writev (sock_fd, iov, 3);
/* 借助 BPF,从 Process Events 中,滤出「进程消亡事件」 */
struct sock_filter filter[] = {
...
/* 6-7: filter out proc connector message other than 'PROC_EVENT_EXIT' */
BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
NLMSG_LENGTH(0) + offsetof(struct cn_msg, data)
+ offsetof(struct proc_event, what)),
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K,
htonl(PROC_EVENT_EXIT), 0 /* true offset */, 1 /* false offset */),
/* 8: the @ret_cmd_idx */
BPF_STMT (BPF_RET|BPF_K, 0xffffffff),
/* 9: the @drop_cmd_idx */
BPF_STMT (BPF_RET|BPF_K, 0)
};
struct sock_fprog fprog = { .filter = filter, .len = 10 };
setsockopt (sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof (fprog)) < 0)

eBPF 形式上类似,裸用相当不方便,好在有编译器 bcc,以及高级语言 bpftrace。

6 图 6 uretprobe 工作原理

图 5 展示的是 User level 埋点,故而叫做 uprobe。Kernel level 对应款叫做 kprobe。

uprobe 和 kprobe 的通常用法中,以函数入口地址,进行埋点。而对于函数返回,其位置可能有多处:

int foo(..) {
size_t n_written = 0;
if (cond1) return -EINVAL;
if (cond2) goto fail;
n_written = do_io(...);
return n_written;
fail:
free(...); return -EIO;
}

于是就有了 kretprobe 以及 uretprobe。下图展示了 uretprobe 工作原理,同样修改自dev.framing.life/tracing/kernel-and-user-probes-magic

uprobe 的工作流中,需要指定位置方能埋点。除了上述提及的函数 entry & return,在函数中间某处埋点,意味着要反汇编,找到源代码行对应汇编地址,有些反人类。

于是,可以预先在代码中埋“标记”,再通过 uprobe 找到“标记”,进行埋点 —— USDT(User Statically-Defined Tracing) 就是这样一个技术,其实现简介参见这个链接。

节末,再提一个 uprobe 应用 “malloc() Flame Graph” ,通过埋点 malloc() / free() 来剖析目标进程的内存使用情况,分析是否存在泄漏。

7 图 7 性能计数采样工作原理

云上: 服务端程序需要扛住超高的并发请求;手机上:需要有效降低延时;计算密集型的神经网络中:GPU 需要极致优化的 kernel。

借助诸如硬件性能监控单元(PMU),可以统计高速硬件上的各种 “塞车” 事件,指出程序为何跑的慢。进一步对其采样,还可将 “塞车事件”,对应到造成 “塞车” 的热点代码行上。

“Cache Line 伪共享发现与优化“一文,介绍了如何发现名为 “Cache Line 伪共享” 的 “塞车事件”,并对应到代码行上。

下图简示了采样的工作原理:当 “塞车” 计数达到采样频率时,产生一次中断,转储现场,从而回溯到代码行和相关上下文。

七张图看懂 Linux profiling 机制的更多相关文章

  1. 一张图看懂开源许可协议,开源许可证GPL、BSD、MIT、Mozilla、Apache和LGPL的区别

    一张图看懂开源许可协议,开源许可证GPL.BSD.MIT.Mozilla.Apache和LGPL的区别 首先借用有心人士的一张相当直观清晰的图来划分各种协议:开源许可证GPL.BSD.MIT.Mozi ...

  2. [转帖]两张图看懂GDT、GDTR、LDT、LDTR的关系

    两张图看懂GDT.GDTR.LDT.LDTR的关系 2018-06-09 18:13:53 Six_666A 阅读数 2044更多 分类专栏: 深入理解linux内核   转自:http://ju.o ...

  3. 一张图看懂ANSYS17.0 流体 新功能与改进

    一张图看懂ANSYS17.0 流体 新功能与改进   提交 我的留言 加载中 已留言   一张图看懂ANSYS17.0 流体 新功能与改进 原创2016-02-03ANSYS模拟在线模拟在线 模拟在线 ...

  4. FUNMVP:几张图看懂区块链技术到底是什么?(转载)

    几张图看懂区块链技术到底是什么? 本文转载自:http://www.cnblogs.com/behindman/p/8873191.html “区块链”的概念可以说是异常火爆,好像互联网金融峰会上没人 ...

  5. 4张图看懂delphi 10生成ipa和在iPhone虚拟器上调试(教程)

    4张图看懂delphi 10生成ipa和在iPhone虚拟器上调试(教程) (2016-02-01 03:21:06) 转载▼ 标签: delphi ios delphi10 教程 编程 分类: 编程 ...

  6. 一张图看懂css的position里的relative和absolute的区别

    position有以下属性:static.inherit.fixed.absolute.relative前三个好理解好区分:static:是默认状态,没有定位,元素出现在正常的流中(忽略 top, b ...

  7. 一张图看懂Function和Object的关系及简述instanceof运算符

    我在写一篇图解prototype和__proto__的区别时,搜资料搜到了一个有意思的现象,下面这两个运算返回的结果是一样的: Function instanceof Object;//true Ob ...

  8. Nodejs学习笔记(三)——一张图看懂Nodejs建站

    前言:一条线,竖着放,如果做不到精进至深,那就旋转90°,至少也图个幅度宽广. 通俗解释上面的胡言乱语:还没学会爬,就学起走了?! 继上篇<Nodejs学习笔记(二)——Eclipse中运行调试 ...

  9. 一张图看懂Rxjava的原理

    前言 Rxjava是NetFlix出品的Java框架, 官方描述为 a library for composing asynchronous and event-based programs usin ...

  10. 12 张图看懂 CPU 缓存一致性与 MESI 协议,真的一致吗?

    本文已收录到  GitHub · AndroidFamily,有 Android 进阶知识体系,欢迎 Star.技术和职场问题,请关注公众号 [彭旭锐] 进 Android 面试交流群. 前言 大家好 ...

随机推荐

  1. 2023/4/16 SCRUM个人博客

    1.我昨天的任务 大体学习并了解初始化pyqt5的一些可视化问题 2.遇到了什么困难 对于py的字典使用 3.我今天的任务 学习了easydict库的基本操作

  2. 使用update-alternatives管理GCC版本

    目录 简介 操作过程 简介 当操作系统中存在多个版本的GCC时,可以使用使用update-alternatives管理默认使用的编译器版本. 本文使用gcc-9和gcc-11做演示,操作系统为ubun ...

  3. Fidder响应数据SyntaxView乱码的处理方法

    当Fidder查看响应数据"SyntaxView"出现乱码时,可以点击上方菜单栏的"Decode"按钮,等"Decode"出现蓝色边框后再重 ...

  4. (持续更新)国内计算机领域相关的SCI和EI期刊,以及好中的SCI和EI期刊(不限国内外)

    好中的: SCI刊: EI刊: EI会议: 参考: CCF发布计算领域高质量科技期刊分级目录   国内的: SCI-Expanded   1. <Journal of Computer Scie ...

  5. 强化学习的REIINFORCE算法和交叉熵RL算法

    注意: 本文并不讲REINFORCE算法,而是讲强化学习的交叉熵算法,关于REINFORCE算法可以参看: https://www.cnblogs.com/devilmaycry812839668/p ...

  6. maven项目打包时排除依赖包

    1.背景 为了快速上传jar包到服务器,很多时候我们需要把依赖包单独独立出来,避免每次修改都传依赖包 2.实现方式 maven的pom文件,没有独立依赖包时配置如下: <build> &l ...

  7. C语言基础- Hello World

    第一个C语言程序 Hello World # include <stdio.h> //#关键标识符,表示用用头文件:include:引入都文件关键字 // stdio.h:系统标准输入.输 ...

  8. 处理一直显示npm WARN using –force Recommended protections disabled.的问题

    使用 npm config set force false 可以消除.

  9. MJUCTF—WP

    1.猫娘 点开发现有两个文件, 一个加密压缩包, 一个word文档 点开word发现是兽音加密, 点开在线网站进行解密 # 得到一段文本, 先进行分割 小小年内则伏勤, 阵阵寒风刺骨寒. 是处寂寞无人 ...

  10. 部署在阿里云上的项目收到了阿里云发送的shiro漏洞

    编辑 ​ 还记得在十月份凯哥发布过一篇修改若依系统编辑器的文章,然后为了方便大家浏览,凯哥就部署在服务器上了,结果,没想到最近收到了阿里云漏洞扫描通知: 编辑 ​ 如果不修改的话:对于长期存在安全隐患 ...