內核中的插樁調試

插樁法是內核常用的一種調試手段,利用代碼中插樁,執行到此時執行對應的鉤子函數(hook)來達到調試的目的,從實現來說,它不可避免的會帶來一些性能上的開銷,不過隨着實現的不斷優化,這部分的開銷已經越來越小了。比如使能了 dynamic ftrace 後的內核,在關閉 ftrace 開關的情況下,實際上只是多了個幾個 nop 指令,並不會帶來很大的性能開銷。下面就來看下內核中都有哪幾種常用的插樁調試機制。

ftrace 我在其他博客中有介紹過,它的原理是使用 gcc 編譯器的 -pg 選項,達到在內核代碼中插樁的目的。該選項會自動在函數的入口處加上對 mcount 的調用指令。這樣每次進入一個函數時都會調用對應的鉤子函數來打印函數信息。

tracepoint 的原理和 ftrace 很類似,只不過它並不是利用 gcc 的特性來實現的,而是在內核代碼中提前做了插樁,代碼運行到跟蹤點(樁)上註冊了鉤子函數(hook),那麼就會執行鉤子函數達到調試的目的。和 ftrace 相比,它的劣勢是實現更加複雜,需要程序員按需要在特定位置加入跟蹤點代碼,ftrace 可以實現所有函數的跟蹤,但是 tracepoint 就只會跟蹤添加了代碼的跟蹤點。除了劣勢,自然 tracepoint 也有它的優點,它的靈活性更加好,我們可以自己定製hook函數,添加想要獲取的信息,而 ftrace 默認只會打印函數名。

eventtrace 這種機制實際上是相當於 tracepoint + ftrace 的組合,它的前端使用了 tracepoint 來實現跟蹤點,後端使用 ftrace 的架構,把調試信息輸出到 ftrace ring buffer 中,這樣在內核中可以通過 cat /sys/kernel/debug/tracing/trace 來獲取 eventtrace 中的調試信息。

kprobe 是另外一種基於打樁機制實現的調試手法,在註冊一個kprobe時會先拷貝出內核函數指令,然後在該函數前插入一條指令,以i386/x86_64爲例,它是一個 int3 指令,相當於是插入樁位,在執行到此時會觸發內核進入 int3 中斷,然後跳轉執行註冊的hook和已經拷貝的代碼段,最後再返回到函數後面的地址處繼續運行,它支持在監測的函數前後分別插入hook函數。在使用時可以實現一個kernel module的方式註冊 kprobe ,並且可以對所有的內核函數進行動態插樁,只要我們能夠找到對應的地址。

jprobekretprobe 這兩種是和 kprobe 類似的實現機制,也是通過注入中斷指令執行hook來添加調試信息的,和kprobe的區別是,jprobe只在函數入口處被調用,而kretprobe只在函數return處被調用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章