用户静态定义探测点USDT

usdt(User-Statically-Defined-Tracepoint)是一种向应用插入跟踪点的技术方案,其特点是跟踪点的插入是静态的,通常需要修改应用的源码并再次编译。该技术方案源于DTrace,不过usdt应用跟踪的功能在Systemtap和bcc等内核跟踪调试工具中已有良好的支持。Systemtap开源工具提供了sys/sdt.h头文件,该头文件没有相应用C代码实现,仅仅提供了多个STAP_PROBExxxDTRACE_PROBExxx宏定义。这些宏定义通过GCC编译器的内联汇编和段操作在编译成生的ELF文件中加入一些用于跟踪的信息(主要加入一个nop机器指令和.note.stapsdt段信息)。当应用未被跟踪调试时,这些额外的信息不会影响应用的正常功能,也不会造成性能的消耗;当应用被跟踪调试时,Systemtapbcc工具会通过Linux内核修改应用的代码段,将nop命令替换为陷入Linux内核或调试代码的指令;具体的实现请参考Systemtap的相关文档。

本文记录用笔者编写的简单usdt调试应用及在64位arm64/Linux设备上的调试过程。

跟踪应用层的内存申请与释放

在编写代码前,笔者需要在x86_64/Linux主机上两次编译Systemtap:分别为主机和嵌入式ARM设备编译;该操作这里不再详述。之后,笔者编写了以下代码mymalloc.c,使用sys/sdt.h中提供的宏,对应用的内存分配函数封装的同时,加入跟踪点:

#include <sys/sdt.h>extern void * __libc_malloc(size_t);
extern void __libc_free(void * ptr);
extern void * __libc_calloc(size_t nmemb, size_t size);
extern void * __libc_realloc(void * ptr, size_t size);void * my_malloc(size_t size)
{void * ret;ret = __libc_malloc(size);DTRACE_PROBE2(mymalloc, my_malloc, size, ret);return ret;
}void my_free(void * ptr)
{DTRACE_PROBE1(mymalloc, my_free, ptr);__libc_free(ptr);
}void * my_calloc(size_t nmemb, size_t size)
{void * ret;ret = __libc_calloc(nmemb, size);DTRACE_PROBE3(mymalloc, my_calloc, nmemb, size, ret);return ret;
}void * my_realloc(void * ptr, size_t size)
{void * ret;ret = __libc_realloc(ptr, size);DTRACE_PROBE3(mymalloc, my_realloc, ptr, size, ret);return ret;
}

使用了以上内存分配函数的封装,由C/C++编写的应用的绝大多数内存分配操作都可以被跟踪到,只需在编译选项中加入-Dmalloc=my_malloc -Dcalloc=my_calloc -Drealloc=my_realloc -Dfree=my_free宏定义参数并重新编译即可(注意,这些编译参数可能有副作用)。其中,DTRACE_PROBEx宏会在mymalloc.o目标文件中引入nop指令和.note.stapsdt段信息,同时也支持一些调试参数的传递;这些信息是生成Systemtap内核调试模块和调试时需要的:

$ aarch64-linux-gnu-objdump -d mymalloc.o
mymalloc.o:     file format elf64-littleaarch64
Disassembly of section .text:
0000000000000000 <my_malloc>:0:   a9be7bfd    stp x29, x30, [sp, #-32]!4: 910003fd    mov x29, sp8:   f9000bf3    str x19, [sp, #16]c:    aa0003f3    mov x19, x010:  94000000    bl  0 <__libc_malloc>14:  d503201f    nop18:  f9400bf3    ldr x19, [sp, #16]1c:   a8c27bfd    ldp x29, x30, [sp], #3220:  d65f03c0    ret24:  d503201f    nop
0000000000000028 <my_free>:28:    d503201f    nop2c:  14000000    b   0 <__libc_free>
0000000000000030 <my_calloc>:30:  a9be7bfd    stp x29, x30, [sp, #-32]!34:    910003fd    mov x29, sp38:  a90153f3    stp x19, x20, [sp, #16]3c:  aa0003f3    mov x19, x040:  aa0103f4    mov x20, x144:  94000000    bl  0 <__libc_calloc>48:  d503201f    nop4c:  a94153f3    ldp x19, x20, [sp, #16]50:  a8c27bfd    ldp x29, x30, [sp], #3254:  d65f03c0    ret
0000000000000058 <my_realloc>:58: a9be7bfd    stp x29, x30, [sp, #-32]!5c:    910003fd    mov x29, sp60:  a90153f3    stp x19, x20, [sp, #16]64:  aa0003f3    mov x19, x068:  aa0103f4    mov x20, x16c:  94000000    bl  0 <__libc_realloc>70: d503201f    nop74:  a94153f3    ldp x19, x20, [sp, #16]78:  a8c27bfd    ldp x29, x30, [sp], #327c:  d65f03c0    ret
$ aarch64-linux-gnu-readelf -S mymalloc.o
There are 25 section headers, starting at offset 0x20e8:
Section Headers:[Nr] Name              Type             Address           OffsetSize              EntSize          Flags  Link  Info  Align[ 6] .note.stapsdt     NOTE             0000000000000000  000000c80000000000000134  0000000000000000           0     0     4[ 7] .rela.note.stapsd RELA             0000000000000000  000014f800000000000000c0  0000000000000018   I      22     6     8[ 8] .stapsdt.base     PROGBITS         0000000000000000  000001fc0000000000000001  0000000000000000  AG       0     0     1

Systemtap应用调试脚本

笔者将mymalloc.o链接到简单的调试应用,test-malloc;并把test-malloc分别复制到主机和嵌入式设备的/tmp目录下(仅仅是为了保证应用在主机和嵌入式设备上的路程径一致)。编写Systemtap脚本,仅仅是将跟踪到的应用内存分配操作输出到调试终端:

$ cat mymalloc.stp
probe process("/tmp/test-malloc").provider("mymalloc").mark("my_malloc")
{printf("malloc(%lx) has returned: %lx\n", $arg1, $arg2);
}
probe process("/tmp/test-malloc").provider("mymalloc").mark("my_calloc")
{printf("calloc(%lx, %lx) has returned: %lx\n", $arg1, $arg2, $arg3);
}
probe process("/tmp/test-malloc").provider("mymalloc").mark("my_realloc")
{printf("realloc(%lx, %lx) has returned: %lx\n", $arg1, $arg2, $arg3);
}
probe process("/tmp/test-malloc").provider("mymalloc").mark("my_free")
{printf("free(%lx) has been invoked\n", $arg1);
}

之后,在x86_64/Linux主机上使用stap命令为嵌入式arm64/Linux设备生成调试的内核模块(这是一个交叉编译的过程,在上一篇文章中笔者提到过):

# stap -v -a arm64 -B CROSS_COMPILE=aarch64-linux-gnu- \-r /home/yejq/program/linux-5.10 -m mymalloc mymalloc.stp

如果以上操作成功执行,就在会操作目录下生名称为mymalloc.ko的内核模块,需要将该模块复制到嵌入式设备上,任意目录可以选择——接下来就是调试过程了。

在嵌入式设备上跟踪应用的内存操作

首先,需要把Linux内核的符号文件Module.symvers和配置文件.config复制到嵌入式设备的固定目录下,因为staprun相关的命令在执行时可能需要读取这些文件:

# mkdir -p /lib/modules/$(uname -r)/build
# cp .config Modules.symvers /lib/modules/$(uname -r)/build/

之后就可以使用staprun命令加载Systemtap内核模块:

# staprun -v -v mymalloc.ko
staprun:main:493 modpath="./mymalloc.ko", modname="mymalloc"
staprun:init_staprun:399 init_staprun
staprun:insert_module:71 inserting module ./mymalloc.ko
staprun:insert_module:97 module options: _stp_bufsize=0
staprun:insert_module:105 module path canonicalized to '/tmp/mymalloc.ko'
staprun:insert_module:191 Module mymalloc inserted from file /tmp/mymalloc.ko
staprun:close_ctl_channel:88 Closed ctl fd 4
execing: /opt/addon/libexec/systemtap/stapio -v -v ./mymalloc.ko -F3
stapio:main:50 modpath="./mymalloc.ko", modname="mymalloc"
stapio:init_stapio:266 init_stapio
stapio:stp_main_loop:470 in main loop
stapio:stp_main_loop:490 select_supported: 1
stapio:init_relayfs:313 initializing relayfs
stapio:init_relayfs:342 attempting to openat trace0
stapio:init_relayfs:342 attempting to openat trace1
stapio:init_relayfs:342 attempting to openat trace2
stapio:init_relayfs:342 attempting to openat trace3
stapio:init_relayfs:366 ncpus=4, nprocs=4, bulkmode=0
stapio:init_relayfs:368 cpui=0, relayfd=0
stapio:init_relayfs:368 cpui=1, relayfd=1
stapio:init_relayfs:368 cpui=2, relayfd=2
stapio:init_relayfs:368 cpui=3, relayfd=3
stapio:init_relayfs:442 starting threads
stapio:stp_main_loop:690 systemtap_module_init() returned 0

接下来打开设备的另一个终端,手动执行/tmp/test-malloc调试文件,可以看到staprun -v -v ./mymalloc.ko所在的终端会输出test-malloc被调试应用的内存分配信息:

malloc(b) has returned: 3119a260
calloc(c, 8) has returned: 3119a280
realloc(0, d) has returned: 3119a2f0
realloc(3119a2f0, e) has returned: 3119a2f0
free(3119a260) has been invoked
free(3119a280) has been invoked
free(0) has been invoked
free(3119a2f0) has been invoked

可见,usdt跟踪在应用的调试中是很有效的,而且几乎不影响应用的执行效率;这种跟踪调试方案很适合用于大型的嵌入式应用(如音视频服务)的开发过程。在Systemtap调试脚本mymalloc.stp的基础上,可以加入一些Hash表,记录每一个应用分配的内存是否会释放,从一定程度上定位到内存是否存在泄露;或者增加一些放问应用的调用栈的操作,可以得到应用中具体分配内存的某个模块,从而深入地观深应用的行为。

SystemTap应用跟踪探测的使用相关推荐

  1. 用户空间的SystemTap探测是怎么工作的

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

  2. Linux中使用Systemtap调试SLUB

    <Linux指令:SystemTap内核跟踪和探测工具> 之前的文章曾利用systemtap的"watchpoint"功能来监测变量数值的变化,但systemtap在内 ...

  3. 【技术干货】听阿里云CDN安防技术专家金九讲SystemTap使用技巧

    1.简介 SystemTap是一个Linux非常有用的调试(跟踪/探测)工具,常用于Linux内核或者应用程序的信息采集,比如:获取一个函数里面运行时的变量.调用堆栈,甚至可以直接修改变量的值,对诊断 ...

  4. BPF学习笔记(五)--Systemtap BPF/BCC bpftrace 实践对比

    文章目录 1. 使用BPF/BCC 1.1在centos8操作系统上安装对应的软件二进制包 1.2 源码包安装 1.3 程序示例 2. Systemtap 2.1安装 systemtap 2.2 St ...

  5. SystemTap使用技巧【四】

    1.查看内核文件中函数的执行流程 前段时间研究了一下Linux内核信号处理流程,记录一下用到的技巧吧. 其实如果不用工具,硬是看代码去分析这个信号处理流程的话,还真的可能搞不定,因为不知道看到的代码是 ...

  6. SystemTap使用指南

    1.简介 SystemTap是一个Linux非常有用的调试(跟踪/探测)工具,常用于Linux内核或者应用程序的信息采集,比如:获取一个函数里面运行时的变量.调用堆栈,甚至可以直接修改变量的值,对诊断 ...

  7. DSTREAM-PT仿真器——ARM公司推出的最高性能的调试跟踪解决方案

    2019年4月,ARM公司推出最高性能的调试跟踪解决方案--DSTREAM-PT. DSTREAM-PT具有高性能调试和跟踪功能,快速下载特点,是自适应JTAG时钟速率和并行流跟踪的理想选择. DST ...

  8. systemtap调试linux内核源码,内核调试工具SystemTap:适合懒人的printk替代品

    SystemTap是一个Linux调试和性能分析工具,可用于应用层和内核层的分析,但主要侧重内核层.SystemTab可以在不修改内核代码.不重复编译内核.不重启机器的情况下,收集运行内核的信息并使信 ...

  9. 跟踪百度服务器的路由信息,路由跟踪

    本词条缺少概述图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧! Tracert(跟踪路由)是路由跟踪实用程序,用于确定 IP 数据报访问目标所采取的路径.Tracert 命令用 IP 生存时 ...

最新文章

  1. 最新的10个优质Python开源项目
  2. 高性能JavaScript笔记三(编程实践)
  3. VM虚拟机桥接模式无法联网解决办法
  4. Python爬虫学到什么程度就可以去找工作了?
  5. 如何使用async和await这对组合设计统一的取Access Token的函数
  6. C#中搜索关键词高亮显示
  7. 栈-线性表(代码、分析、汇编)
  8. Origin Pro2022教育版官方申请、安装及汉化、续期
  9. 禁止恶意域名访问服务器方法
  10. 常用计算机检索算符,计算机信息检索过程中常用的检索表达式
  11. 情商和逆商比智商更重要
  12. coffeescript html5,CoffeeScript函数
  13. Linux使用Maven部署SpiderFlow爬虫平台
  14. Spring Boot Actuator 端点启用和暴露
  15. wordpress创建_您可以使用WordPress创建的19种网站类型
  16. 编译问题追踪 :高通驱动移植 <utils/Log.h>问题
  17. RJ45隔离变压器作用
  18. 亚运赛场阿联酋公主亲自上阵 爱骑超千万
  19. windows10防火墙设置
  20. 微信小程序使用GoEasy实现websocket实时通讯

热门文章

  1. 学习笔记-第十二章 恶意代码分析实战
  2. 将android studio产生的.gradle .android .androidStudio缓存从默认C盘移动到D盘
  3. acer 驱动程序 linux,Ubuntu扫描仪驱动(Acer 3300U)
  4. IT-银行运维-广告方案概述
  5. U3D 自动更新/打Bundle/打包
  6. ligerui tree mysql_jQuery LigerUI ligerGrid 在开发中的应用记录
  7. 伤感mp3歌曲打包下载,云盘伤感歌曲打包下载
  8. 西南大学统考英语计算机有答案吗,2017年西南大学英语网络统考大学英语B复习题...
  9. 网络小黑揭秘系列之黑产江湖黑吃黑—中国菜刀的隐形把手
  10. ICV光子盒:2023全球量子通信与安全产业发展展望