这篇文章介绍SystemTap在用户层的实现原理。对文档进行了部分摘抄翻译,原文易懂,建议阅读原文:How SystemTap Userspace Probes Work。另外介绍另一篇文档:动态追踪技术漫谈

在给Bitcoin增加SystemTap时,对它的实现感到很疑惑,还有这会增加多少开销。 实际上,我想知道的是:

  • 使用SystemTap的可执行程序会增加什么指令?
  • 使用stap跟踪进程时实际上会发生什么?
  • 不跟踪程序时开销有多少?
  • 在跟踪程序时开销有多少?

SystemTap的wiki上有一些解释 userspace probe implementation, 但是不够详细。

增加了SystemTap探点的代码

这个例子中会看一下Bitcoin的函数IsInitialBlockDownload()。源码修改后是这样的:

bool IsInitialBlockDownload()
{// Once this function has returned false, it must remain false.static std::atomic<bool> latchToFalse{false};// Optimization: pre-test latch before taking the lock.if (latchToFalse.load(std::memory_order_relaxed))return false;LOCK(cs_main);if (latchToFalse.load(std::memory_order_relaxed))return false;if (fImporting || fReindex)return true;if (chainActive.Tip() == nullptr)return true;if (chainActive.Tip()->nChainWork < nMinimumChainWork)return true;if (chainActive.Tip()->GetBlockTime() < (GetTime() - nMaxTipAge))return true;LogPrintf("Leaving InitialBlockDownload (latching to false)\n");latchToFalse.store(true, std::memory_order_relaxed);if (PROBE_FINISH_IBD_ENABLED()) { // 关注这两句PROBE_FINISH_IBD();}return false;
}

跟探测相关的是PROBE_FINISH_IBD_ENABLED()PROBE_FINISH_IBD()。这些宏定义是作为构建程序的一部分由SystemTap自动生成的。

生成的x86汇编

下面是GDB disas反汇编IsInitialBlockDownload()生成的代码,附带源码:

$ gdb ./src/bitcoind(gdb) disas /s IsInitialBlockDownload1170        if (PROBE_FINISH_IBD_ENABLED()) {0x00000000001a2954 <+420>:   cmpw   $0x0,0x4f6724(%rip)        # 0x699080 <bitcoin_finish_ibd_semaphore>0x00000000001a295c <+428>:   je     0x1a2831 <IsInitialBlockDownload()+129>1171            PROBE_FINISH_IBD();0x00000000001a2962 <+434>:   nop0x00000000001a2963 <+435>:   jmpq   0x1a2831 <IsInitialBlockDownload()+129>

GDB disas默认生成的是AT&T语法。x86指令可以有变长操作数长度,AT&T语法在指令中用长度后缀注释。在这个例子中,cmpw中的w后缀表示”word”(16位)操作数。jmpq表示跳转指令有一个”quad”(64位)操作数。”if”语句编译成cmp指令,检查内存地址0x699080上的数值是否等于0。这里的注释说实际上在ELF文件中0x699080是bitcoin_finish_ibd_semaphore。如果这个内存地址的值是0,je指令直接跳出”if”语句。否则指令会走到nop指令,nop是一个字节的指令,什么都不做。在nop指令之后是一个无条件跳转的jmp。注意两个跳转(je和jmpq)都跳转到了相同的地址0x1a2831。这个地址是return false语句。GCC通常会生成与源码顺序不一致的x86代码,这是为了优化,所以不能简单的自动向下流转到return语句。

这个代码看起来有点小怪,因为这个控制流程是这样的:

  • 检查这个值:bitcoin_finish_ibd_semaphore

    • 如果不是0,执行一个no-op指令,然后跳转到return false语句。
    • 如果是0,立即跳转到return false语句。
      为什么要特定条件下执行no-op指令,并且实际上什么都不会做?如果对类似于GDB的调试器工作原理比较熟悉,就可能已经猜到了。

观察SystemTap跟踪时的内存

首先看一下运行时的程序disas的输出。我启动了一个bitcoind进程,进程号是4077。当执行disas时,看起来有点不一样:

$ gdb -p 4077(gdb) disas /s IsInitialBlockDownload1170        if (PROBE_FINISH_IBD_ENABLED()) {0x000056352db8d954 <+420>:   cmpw   $0x0,0x4f6724(%rip)        # 0x56352e084080 <bitcoin_finish_ibd_semaphore>0x000056352db8d95c <+428>:   je     0x56352db8d831 <IsInitialBlockDownload()+129>1171            PROBE_FINISH_IBD();0x000056352db8d962 <+434>:   nop0x000056352db8d963 <+435>:   jmpq   0x56352db8d831 <IsInitialBlockDownload()+129>

这些指令看起来跟之前一样,除了内存地址。内存地址因为ASLR的原因改变了,但是整个流程还是不变。还是看bitcoin_finish_ibd_semaphore:

(gdb) x/hx &bitcoin_finish_ibd_semaphore
0x56352e084080 <bitcoin_finish_ibd_semaphore>:  0x0000

这里显示是0x0000,这样我们就知道cmpw语句总会跳过nop。

关联SystemTap脚本

我现在准备关联一个SystemTap脚本,探测前面的内容。这是脚本内容:

# This probe is run when stap initially attaches.
probe begin {println("attached to bitcoind process...")
}# This probe is run when our IBD probe is triggered.
probe process("./src/bitcoind").mark("finish_ibd") {println("IBD finished")
}

begin可以告诉我们SystemTap什么时候初始化结束,这个时候就真正的attach了。用这个脚本执行:

# Run our stap script and attach to process 4077.
$ stap -x 4077 demo.stp
attached to bitcoind process...

再次在同一个地址执行disas:

(gdb) disas /s IsInitialBlockDownload1170        if (PROBE_FINISH_IBD_ENABLED()) {0x000056352db8d954 <+420>:   cmpw   $0x0,0x4f6724(%rip)        # 0x56352e084080 <bitcoin_finish_ibd_semaphore>0x000056352db8d95c <+428>:   je     0x56352db8d831 <IsInitialBlockDownload()+129>1171            PROBE_FINISH_IBD();0x000056352db8d962 <+434>:   int3   0x000056352db8d963 <+435>:   jmpq   0x56352db8d831 <IsInitialBlockDownload()+129>

事情变得不一样了。nop指令变成了int3。这个就是”trap”指令。工作原理是:当进程执行这个指令时,会在内核产生一个中断。内核可以使用任何手段处理它。正常情况下会产生一个SIGTRAP信号给进程。GDB这样的调试器就使用这样的机制实现断点。SystemTap有些不一样,稍后解释。bitcoin_finish_ibd_semaphore发生了什么?

(gdb) x/hx &bitcoin_finish_ibd_semaphore
0x56352e084080 <bitcoin_finish_ibd_semaphore>:  0x0001

变成了0x0001,之前是0x0000。这就意味着当程序跑到cmpw指令时,就会执行”if”语句,然后在内核中产生中断。

SystemTap中断怎么工作的

执行一个SystemTap脚本时,会发生这些事情:

  • stap命令将.stp代码转换成Linux内核模块C代码。
  • C代码编译成一个本地内核模块(.ko文件)。
  • 内核模块注册感兴趣的trap事件,探测要求的探测点,在内核上下文中作为本地x86代码执行。

这种实现让SystemTap非常快。但是因为脚本运行在内核态,所以可能会导致内核的不稳定,因此需要执行stap的用户具有权限。

其它的探测点会发生什么

我执行了一些其它的测试,发现了更多的工作原理。当SystemTap运行一个脚本时,它会观察你想要探测的那个点,只有你想要监控的探测点会改变。比如,我在另外一个函数CCoinsViewCache::FetchCoin中增加了一个探测点。即使IsInitialBlockDownload()已经被SystemTap修改,这个函数还是没有变化:

(gdb) disas /s CCoinsViewCache::FetchCoin49      if (PROBE_CACHE_MISS_ENABLED() && m_enable_probing) {0x000056352dc2bea8 <+328>:   cmpb   $0x0,0x80(%rbx)0x000056352dc2beaf <+335>:   je     0x56352dc2bdd6 <CCoinsViewCache::FetchCoin(COutPoint const&) const+118>49      if (PROBE_CACHE_MISS_ENABLED() && m_enable_probing) {0x000056352dc2bdc8 <+104>:   cmpw   $0x0,0x4582ae(%rip)        # 0x56352e08407e <bitcoin_cache_miss_semaphore>0x000056352dc2bdd0 <+112>:   jne    0x56352dc2bea8 <CCoinsViewCache::FetchCoin(COutPoint const&) const+328>50          PROBE_CACHE_MISS();0x000056352dc2beb5 <+341>:   nop0x000056352dc2beb6 <+342>:   jmpq   0x56352dc2bdd6 <CCoinsViewCache::FetchCoin(COutPoint const&) const+118>

这个代码看起来有点不一样,因为还要检查一个成员变量,不过基本流程还是一样的,而且”if”语句都是一样的。正如你所看到的,这里还是有一个nop指令,并没有变化。跟预期相符,bitcoin_cache_miss_semaphore的值是0:

(gdb) x/hx &bitcoin_cache_miss_semaphore
0x56352e08407e <bitcoin_cache_miss_semaphore>:  0x0000

SystemTap detach的时候发生什么?

如果用原先的stap从进程上detach下来,IsInitialBlockDownload 函数就会恢复到原来的样子,bitcoin_finish_ibd_semaphore这个值又变成了0.当semaphore值变成0时,对应的int3指令也会变回nop。

这个检查会增加多少开销?

当考虑到SystemTap探测的开销时,需要考虑两种场景:没有被跟踪的进程和已经跟踪的进程。
当进程没有被跟踪时,每个探测增加的开销就是一个cmp指令和一个jmp指令。因为这两个指令总是同时出现,现代处理器把它们融合在了一起。在一个现代处理器上(比如Intel Skylake),这个组合会消耗0.5到2指令周期。总结:没有跟踪的进程开销几乎是0
当进程被跟踪时,每个探测点增加的开销是一个cmp指令和上下文切换。具体的上下文开销取决于内核版本和CPU。根据我发现的最好的资源(从2010年开始)显示,开销在2到50微秒之间,取决于CPU类型和其它的一些细节。探测点的代码可能会很复杂,SystemTap语言允许执行循环,调用其它函数,hash map等等),所以这个开销可能会很大。SystemTap考虑到了这点,会监测探测会执行多少时间。如果执行太长时间,stap脚本会自动终止。这个可以通过stap命令行选项调整,比如有一个复杂的探测逻辑并且不在乎消耗。在任何事件中,我认为这个开销对跟踪的事件来说都不是那么重要,因为只会在调试一个程序的时候才会跟踪。不会占据主要时间。

用户空间的SystemTap探测是怎么工作的相关推荐

  1. 嵌入式之linux用户空间与内核空间,进程上下文与中断上下文

    文章目录 前言 用户空间与内核空间 内核态与用户态 进程上下文和中断上下文 上下文 原子 进程上下文 中断上下文 进程上下文VS中断上下文 原子上下文 前言 之前在学习嵌入式linux系统的时候,一直 ...

  2. I/O流(包括操作系统与内核,用户空间),I/O工作原理,Java I/O流的设计及Java IO系统

    文章目录 一.操作系统与内核 1.1操作系统 1.2内核 1.3 关系图 二.内核空间和用户空间 2.1:目的: 2.2.内核空间(Kernel-space): 2.3.用户空间(User-space ...

  3. linux平台驱动运行空间,UIO(linux Userspace I/O子系统)用户空间设备驱动I/O技术介绍...

    UIO(linux Userspace I/O子系统)用户空间设备驱动I/O技术介绍(由搜集整理) UIO(Userspace I/O)是运行在用户空间的I/O技术.Linux系统中一般的驱动设备都是 ...

  4. 一种内核到用户空间的高效数据传输技术

    级别: 中级 桂 剑 (guijian@cn.ibm.com), IBM 中国开发中心,Linux Performance 项目软件工程师 2006 年 12 月 28 日 Relay 是一种从 Li ...

  5. udev——设备文件管理的用户空间实现

    <udev--设备文件管理的用户空间实现> 来自:刘建文 | 学术半·IT歌·文 作者:刘建文 关键字:Linux 驱动程序 永久链接地址:http://arttech.us/y-2011 ...

  6. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

  7. FUSE——用户空间文件系统

    用户空间文件系统(Filesystem in Userspace,简称FUSE)是操作系统中的概念,指完全在用户态实现的文件系统. 目前Linux通过内核模块对此进行支持.一些文件系统如ZFS,glu ...

  8. Linux预备知识(二):进程空间地划分-用户空间/内核空间

    查看机器上栈大小命令 ulimit -a 或者 ulimit -s 大小不固定,可以用 ulimit -s 进行调整,默认一般为 8M ** 栈区(stack sagment)**:由操作系统自动分配 ...

  9. 用户空间增加、缩减内存

    1.brk系统调用服务例程 malloc()是一个API,这个函数在库中封装了系统调用brk.因此如果调用malloc,那么首先会引发brk系统调用执行的过程.brk()在内核中对应的系统调用服务例程 ...

最新文章

  1. STM32-USART发送程序
  2. 类中成员函数声明后面的const的含义
  3. java 调试 gdb_android gdb 调试实例演示(有源代码篇)
  4. Windows mosek
  5. 如何在不到1ms的延迟内完成100K TPS
  6. java基础方法笔记
  7. InDesign 软件教程,如何新建文档?
  8. Python技术公众号100天了
  9. Atitit 软件设计中的各种图纸 uml 之道 1. 常见设计成果与图纸 2 1.1. 总图 2 1.2. ui原型图与html 2 1.3. 业务逻辑 伪代码 各种uml图 2 1.4. 总体
  10. Thrift 个人实战--Thrift RPC服务框架日志的优化
  11. harmonyOS应用开发环境搭建
  12. 【数字图像处理5.3】SLIC算法 超像素分割(无监督聚类方式)python
  13. 小程序 · 引入企业微信中的「在小程序中加入群聊」插件
  14. 学堂在线笔记——前端与后台的故事——SQL语言及其编程
  15. 小黄鸡 php,Simsimi (小黄鸡) API接口(PHP)公布,小黄鸡API接口非官方PHP版本来啦...
  16. 矩和质心之积分的应用
  17. excel 日期选择器_Excel日期选择器工具
  18. 圣商,牢记使命成就当代圣商
  19. 知帆科技创始人叶茂: 链上数据分析将大有作为
  20. Python发送网易邮件554错误解决

热门文章

  1. 苹果xsmax是什么接口_媲美苹果官方同款液态硅胶手机壳,拿手里太舒服了~
  2. 接亲现场,新郎被要求写代码
  3. GEE:指数计算(NDVI、NBR、EVI、NDMI、NDSI、TC、NDFI、EBBI、VCI、BSI、NDBI)
  4. 2022真无线蓝牙耳机推荐:618性价比超高的蓝牙耳机品牌
  5. 无法打开“XXX.app”,因为Apple无法检查其是否包含恶意软件的解决办法
  6. 记一次现网sentinel 造成的端口差异问题
  7. 父子mounted生命周期顺序导致的问题解决
  8. 【已解决】Pr导出渲染报错
  9. C++ Builder调用辰昶仪器ChNetDriver库
  10. 关于爱情的几个寓言故事