可观测性 USDT


介绍

USDT全称是Userland Statically Defined Tracing用户态静态定义追踪。

USDT类似内核态的tracepoint,是静态打桩埋点方式。

USDT在很多开源软件,如MySQL、PostgreSQL、Ruby、Python 和Node.js等都有广泛的应用。

工作原理

首先开发人员将通过DTRACE_PROBE相关宏在程序开发过程中植入跟踪点,也就是所谓的埋点。

其次在编译程序的时候,编译器会在目标二进制文件的ELF的.note.stapstd段部分中压制USDT追踪点。

编译器和追踪工具之间规定,USDT的元数据所在的.note.stapstd段必须存在。

Alt text 这里的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做个对比。

  1. 使用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
    
  2. 操作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并附上本文链接。如果有侵犯到您权益的地方,请及时联系我删除。