可观测性 USDT
介绍
USDT全称是Userland Statically Defined Tracing用户态静态定义追踪。
USDT类似内核态的tracepoint,是静态打桩埋点方式。
USDT在很多开源软件,如MySQL、PostgreSQL、Ruby、Python 和Node.js等都有广泛的应用。
工作原理
首先开发人员将通过DTRACE_PROBE
相关宏在程序开发过程中植入跟踪点,也就是所谓的埋点。
其次在编译程序的时候,编译器会在目标二进制文件的ELF的.note.stapstd段部分中压制USDT追踪点。
编译器和追踪工具之间规定,USDT的元数据所在的.note.stapstd段必须存在。
这里的note section, 是ELF文件的一个段,以某种方式标记文件的信息,这个在后续的linux内核专题系列
中会讲解。对内核感兴趣的朋友,记得关注,不要错过。
最后USDT跟踪工具会对ELF部分进行自检,并在跟踪点的位置替换为int 3中断。当执行到跟踪点的标记处的指令时,会触发中断处理程序,并在内核中调用与uprobe关联的程序来处理事件并将它们发送到用户空间处理。
详细处理可参考上篇的uprobe文章。
以上便是使用USDT的工作原理和使用流程。
实际中我们也许不是程序的开发和维护者,需要跟踪调试的时候,也许该程序已经包含了USDT探测点,或者没有,此时我们需要做个检查。
通过上面工作原理描述,我们知道加了USDT程序埋点会出现在ELF文件的.note.stapstd段,那么我们便可以借助readelf工具来检查是否支持USDT。
实验
安装 systemtap-sdt-dev
# apt-get -y install systemtap-sdt-dev
编译实例程序
#include <sys/sdt.h>
#include <sys/time.h>
#include <unistd.h>
int main(int argc, char ** argv)
{
struct timeval tv;
for(int x; x < 1024; x++) {
gettimeofday(&tv, NULL);
DTRACE_PROBE2(test_app, test_probe, tv.tv_sec, x);
sleep(1);
}
return 0;
}
查看.note.stapsdt段信息,可以看到有埋点的详细信息Displaying notes found in: .note.stapsdt
。
# readelf -n a.out
Displaying notes found in: .note.gnu.property
Owner Data size Description
GNU 0x00000020 NT_GNU_PROPERTY_TYPE_0
Properties: x86 feature: IBT, SHSTK
x86 ISA needed: x86-64-baseline
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: 50d9916129f0b28c92defa0c5a1368ae731e42a0
Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0
Displaying notes found in: .note.stapsdt
Owner Data size Description
stapsdt 0x00000041 NT_STAPSDT (SystemTap probe descriptors)
Provider: test_app
Name: test_probe
Location: 0x00000000000011c2, Base: 0x0000000000002004, Semaphore: 0x0000000000000000
Arguments: -8@%rax -4@-36(%rbp)
g#
查看汇编代码,可以发现DTRACE_PROBE2
被替换为了NOP指令。
# objdump -d a.out
0000000000001189 <main>:
1189: f3 0f 1e fa endbr64
118d: 55 push %rbp
118e: 48 89 e5 mov %rsp,%rbp
1191: 48 83 ec 40 sub $0x40,%rsp
1195: 89 7d cc mov %edi,-0x34(%rbp)
1198: 48 89 75 c0 mov %rsi,-0x40(%rbp)
119c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
11a3: 00 00
11a5: 48 89 45 f8 mov %rax,-0x8(%rbp)
11a9: 31 c0 xor %eax,%eax
11ab: eb 24 jmp 11d1 <main+0x48>
11ad: 48 8d 45 e0 lea -0x20(%rbp),%rax
11b1: be 00 00 00 00 mov $0x0,%esi
11b6: 48 89 c7 mov %rax,%rdi
11b9: e8 c2 fe ff ff call 1080 <gettimeofday@plt>
11be: 48 8b 45 e0 mov -0x20(%rbp),%rax
11c2: 90 nop
11c3: bf 01 00 00 00 mov $0x1,%edi
11c8: e8 c3 fe ff ff call 1090 <sleep@plt>
11cd: 83 45 dc 01 addl $0x1,-0x24(%rbp)
11d1: 81 7d dc ff 03 00 00 cmpl $0x3ff,-0x24(%rbp)
11d8: 7e d3 jle 11ad <main+0x24>
11da: b8 00 00 00 00 mov $0x0,%eax
11df: 48 8b 55 f8 mov -0x8(%rbp),%rdx
11e3: 64 48 2b 14 25 28 00 sub %fs:0x28,%rdx
11ea: 00 00
11ec: 74 05 je 11f3 <main+0x6a>
11ee: e8 7d fe ff ff call 1070 <__stack_chk_fail@plt>
11f3: c9 leave
11f4: c3 ret
增加探测点,我们这次使用tracing的一个前端工具perf跟ftrace做个对比。
使用perf工具
# perf buildid-cache --add ./a.out # perf list |grep sdt sdt_test_app:test_probe [SDT event] # perf probe --add=sdt_test_app:test_probe Added new event: sdt_test_app:test_probe (on %test_probe in /code/tracing/a.out) You can now use it in all perf tools, such as: perf record -e sdt_test_app:test_probe -aR sleep 1
通过ftrace的debugfs接口查看,已经有uprobe event了,也就是perf工具增加的探测点事件。
cat /sys/kernel/debug/tracing/uprobe_events p:sdt_test_app/test_probe /code/tracing/a.out:0x00000000000011c2 arg1=%ax:s64 arg2=-36(%bp):s32
统计探测的事件记录,之前工作中都是源码编译的perf,这里偷懒从官方源下载了perf。perf record时遇到了一个问题,属于官方源的一个坑。在后续的可观测性工具篇幅中详细讲解。
# perf record -e sdt_test_app:test_probe -p $(pidof ./a.out) sleep 10 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.013 MB perf.data (10 samples) ]
查看record报告
# perf script a.out 13209 [000] 135040.744146: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891733 arg2=845 a.out 13209 [000] 135041.744866: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891734 arg2=846 a.out 13209 [000] 135042.746374: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891735 arg2=847 a.out 13209 [000] 135043.746716: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891736 arg2=848 a.out 13209 [000] 135044.749240: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891737 arg2=849 a.out 13209 [000] 135045.750033: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891738 arg2=850 a.out 13209 [000] 135046.750779: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891739 arg2=851 a.out 13209 [000] 135047.751260: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891740 arg2=852 a.out 13209 [000] 135048.751595: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891741 arg2=853 a.out 13209 [000] 135049.753703: sdt_test_app:test_probe: (55ca15c371c2) arg1=1699891742 arg2=854
操作ftrace的debugfs接口
增加探测点
# echo 'p:test_usdt ./a.out:0x11c2' > /sys/kernel/debug/tracing/uprobe_events 0x11c2 即实例程序反汇编看到的nop指令地址,也就是增加的埋点处地址。
激活探测点
# echo 1 > /sys/kernel/debug/tracing/events/uprobes/test_usdt/enable
查看trace
# cat /sys/kernel/debug/tracing/trace # tracer: nop # # entries-in-buffer/entries-written: 4/4 #P:2 # # _-----=> irqs-off/BH-disabled # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / _-=> migrate-disable # |||| / delay # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | a.out-19260 [000] DNZff 136227.665453: test_usdt: (0x560ec73191c2) a.out-19260 [000] DNZff 136228.666249: test_usdt: (0x560ec73191c2) a.out-19260 [000] DNZff 136229.666713: test_usdt: (0x560ec73191c2) a.out-19260 [000] DNZff 136230.669335: test_usdt: (0x560ec73191c2)
可以看到使用前端工具与操作ftrace的debugfs是一样的效果,这些前端工具方便做跟踪操作,提高效率。
前端工具有很多,后面会调几个介绍如何使用。关注公众号,不要错过。
参考
https://www.brendangregg.com/blog/2015-06-28/linux-ftrace-uprobe.html https://installati.one/install-systemtap-sdt-dev-ubuntu-22-04/ https://www.openvswitch.org/support/ovscon2021/slides/debugging_using_tracepoints.pdf https://medium.com/@yunwei356/ebpf-tutorial-by-example-15-capturing-user-space-java-gc-event-duration-using-usdt-46436f772ce8 https://docs.openvswitch.org/en/latest/topics/usdt-probes/ https://www.openvswitch.org/support/ovscon2021/slides/debugging_using_tracepoints.pdf https://bugs.archlinux.org/task/77826 https://docs.frrouting.org/projects/dev-guide/en/latest/tracing.html https://opeonikute.dev/posts/observing-node-with-ebpf https://lists.iovisor.org/g/iovisor-dev/topic/how_to_use_usdt_probes/21386285 https://docs.rs/usdt/latest/usdt/ https://www.brendangregg.com/blog/2015-07-03/hacking-linux-usdt-ftrace.html
欢迎大家转发分享。未经授权,严禁任何复制、转载、摘编或以其它方式进行使用,转载须注明来自eBPFLAB并附上本文链接。如果有侵犯到您权益的地方,请及时联系我删除。