天天看點

perf usdt

  目前perf 支援添加動态探測核心;通過 perf ,來自定義動态事件(perf probe),隻關注真正感興趣的事件。如下使用。

perf usdt

   筆者有時需要使用perf 調試使用者态,so記錄之

靜态探針

是指事先在代碼中定義好,并編譯到應用程式或者核心中的探針。這些探針隻有在開啟探測功能時,才會被執行到;未開啟時并不會執行。常見的靜态探針包括核心中的跟蹤點(tracepoints)和 USDT(Userland Statically Defined Tracing)探針。

  • USDT 探針,全稱是使用者級靜态定義跟蹤,需要在源碼中插入 DTRACE_PROBE() 代碼,并編譯到應用程式中。不過,也有很多應用程式内置了 USDT 探針,比如 MySQL、PostgreSQL 等。

動态探針

則是指沒有事先在代碼中定義,但卻可以在運作時動态添加的探針,比如函數的調用和傳回等。動态探針支援按需在核心或者應用程式中添加探測點,具有更高的靈活性。常見的動态探針有兩種,即用于核心态的 kprobes 和用于使用者态的 uprobes。

  • kprobes 用來跟蹤核心态的函數,包括用于函數調用的 kprobe 和用于函數傳回的 kretprobe。
  • uprobes 用來跟蹤使用者态的函數,包括用于函數調用的 uprobe 和用于函數傳回的 uretprobe。

  注意,kprobes 需要核心編譯時開啟 CONFIG_KPROBE_EVENTS;而 uprobes 則需要核心編譯時開啟 CONFIG_UPROBE_EVENTS。

strace 基于系統調用 ptrace 實作

  • 由于 ptrace 是系統調用,就需要在核心态和使用者态切換。當事件數量比較多時,繁忙的切換必然會影響原有服務的性能;
  • ptrace 需要借助 SIGSTOP 信号挂起目标程序。這種信号控制和程序挂起,會影響目标程序的行為。

是以,在性能敏感的應用(比如資料庫)中,我并不推薦你用 strace (或者其他基于 ptrace 的性能工具)去排查和調試。

在 strace 的啟發下,結合核心中的 utrace 機制, perf 也提供了一個 trace 子指令,是取代 strace 的首選工具。相對于 ptrace 機制來說,perf trace 基于核心事件,自然要比程序跟蹤的性能好很多。perf trace 的使用方法如下所示,跟 strace 其實很像:

perf usdt

  ftrace 和 perf 的功能已經比較豐富了,不過,它們有一個共同的缺陷,那就是不夠靈活,沒法像 DTrace 那樣通過腳本自由擴充。

   BCC 把 eBPF 中的各種事件源(比如 kprobe、uprobe、tracepoint 等)和資料操作(稱為 Maps),也都轉換成了 Python 接口(也支援 lua)。這樣,使用 BCC 進行動态追蹤時,編寫簡單的腳本就可以了。

不過要注意,因為需要跟核心中的資料結構互動,真正核心的事件處理邏輯,還是需要我們用 C 語言來編寫。

  SystemTap 也是一種可以通過腳本進行自由擴充的動态追蹤技術。在 eBPF 出現之前,SystemTap 是 Linux 系統中,功能最接近 DTrace 的動态追蹤機制。不過要注意,SystemTap 在很長時間以來都遊離于核心之外(而 eBPF 自誕生以來,一直根植在核心中)。

是以,從穩定性上來說,SystemTap 隻在 RHEL 系統中好用,在其他系統中則容易出現各種異常問題。當然,反過來說,支援 3.x 等舊版本的核心,也是 SystemTap 相對于 eBPF 的一個巨大優勢。

perf usdt

目前性能優化大師已經在linux4.x核心上給出例程!!​​6.5. Static User Tracing​​

Support was added in later 4.x series kernels. The following demonstrates Linux 4.10 (with an additional patchset), and tracing the Node.js USDT probes:

# perf buildid-cache --add `which node`
# perf list | grep sdt_node
  sdt_node:gc__done                                  [SDT event]
  sdt_node:gc__start                                 [SDT event]
  sdt_node:http__client__request                     [SDT event]
  sdt_node:http__client__response                    [SDT event]
  sdt_node:http__server__request                     [SDT event]
  sdt_node:http__server__response                    [SDT event]
  sdt_node:net__server__connection                   [SDT event]
  sdt_node:net__stream__end                          [SDT event]
# perf record -e sdt_node:http__server__request -a
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.446 MB perf.data (3 samples) ]
# perf script
            node  7646 [002]   361.012364: sdt_node:http__server__request: (dc2e69)
            node  7646 [002]   361.204718: sdt_node:http__server__request: (dc2e69)
            node  7646 [002]   361.363043: sdt_node:http__server__request: (dc2e69)      

XXX fill me in, including how to use arguments.

  If you are on an older kernel, say, Linux 4.4-4.9, you can probably get these to work with adjustments (I've even hacked them up with ​​ftrace​​ for older kernels), but since they have been in development, I haven't seen documentation outside of lkml, so you'll need to figure it out. (On this kernel range, you might find more documentation for tracing these with ​​bcc/eBPF​​, including using the trace.py tool.)

參考此篇文章的udst 使用例程:usdt-on-linux

apt-get install systemtap-sdt-dev      
#include <sys/sdt.h>

int main() {
  DTRACE_PROBE(hello_usdt, enter);
  int reval = 0;
  return reval;
}      
gcc ./hello-usdt.c -o ./hello-usdt      
perf usdt
perf buildid-cache --add 'hello-usdt'      
perf probe sdt_hello_usdt:enter      
perf usdt
perf record -e sdt_hello_usdt:enter -ag      

繼續閱讀