核心探針 (Kprobes)¶
- 作者:
Jim Keniston <jkenisto@us.ibm.com>
- 作者:
Prasanna S Panchamukhi <prasanna.panchamukhi@gmail.com>
- 作者:
Masami Hiramatsu <mhiramat@kernel.org>
概念:Kprobes 和返回探針¶
Kprobes 允許您動態地中斷任何核心例程,並以非侵入的方式收集除錯和效能資訊。您幾乎可以在任何核心程式碼地址設定陷阱 [1],並指定一個在斷點被觸發時呼叫的處理程式例程。
目前有兩種型別的探針:kprobes 和 kretprobes(也稱為返回探針)。kprobe 可以插入到核心中幾乎任何指令上。當指定的函式返回時,會觸發返回探針。
在典型情況下,基於 Kprobes 的檢測被打包為核心模組。模組的 init 函式安裝(“註冊”)一個或多個探針,exit 函式登出它們。像 register_kprobe() 這樣的註冊函式指定探針要插入的位置以及在探針被觸發時要呼叫的處理程式。
還有 register_/unregister_*probes() 函式用於批次註冊/登出一組 *probes。當您需要一次登出大量探針時,這些函式可以加快登出過程。
接下來的四個小節解釋了不同型別的探針如何工作以及跳轉最佳化如何工作。它們解釋了一些您需要了解的內容,以便最好地利用 Kprobes -- 例如,pre_handler 和 post_handler 之間的區別,以及如何使用 kretprobe 的 maxactive 和 nmissed 欄位。但是,如果您急於開始使用 Kprobes,您可以跳到 支援的架構。
Kprobe 如何工作?¶
當註冊 kprobe 時,Kprobes 會複製被探測的指令,並將被探測指令的第一個位元組(或多個位元組)替換為斷點指令(例如,i386 和 x86_64 上的 int3)。
當 CPU 命中該斷點指令時,會發生陷阱,CPU 的暫存器被儲存,並透過 notifier_call_chain 機制將控制權傳遞給 Kprobes。Kprobes 執行與 kprobe 關聯的“pre_handler”,並將 kprobe 結構和儲存的暫存器的地址傳遞給處理程式。
接下來,Kprobes 單步執行其被探測指令的副本。(單步執行實際指令會更簡單,但這樣 Kprobes 就必須暫時刪除斷點指令。這會開啟一個很小的時間視窗,在此期間,另一個 CPU 可能會直接跳過該探針點。)
在指令被單步執行後,Kprobes 執行與 kprobe 關聯的“post_handler”(如果有)。然後,執行繼續從探針點之後的指令開始。
更改執行路徑¶
由於 kprobes 可以探測正在執行的核心程式碼,因此它可以更改暫存器集,包括指令指標。此操作需要非常小心,例如保持堆疊幀,恢復執行路徑等。因為它在執行的核心上執行,並且需要深入瞭解計算機體系結構和併發計算,所以您很容易自食其果。
如果在 pre_handler 中更改指令指標(並設定其他相關暫存器),則必須返回 !0,以便 kprobes 停止單步執行並返回到給定的地址。這也意味著不應再呼叫 post_handler。
請注意,在某些使用 TOC(目錄表)進行函式呼叫的體系結構上,此操作可能會更困難,因為您必須在模組中為您的函式設定一個新的 TOC,並在從該函式返回後恢復舊的 TOC。
返回探針¶
返回探針如何工作?¶
當您呼叫 register_kretprobe() 時,Kprobes 在函式的入口處建立一個 kprobe。當被探測的函式被呼叫並且此探針被觸發時,Kprobes 會儲存返回地址的副本,並將返回地址替換為“trampoline”的地址。trampoline 是一段任意的程式碼 -- 通常只是一條 nop 指令。在啟動時,Kprobes 在 trampoline 處註冊一個 kprobe。
當被探測的函式執行其返回指令時,控制權傳遞給 trampoline 並且該探針被觸發。Kprobes 的 trampoline 處理程式呼叫與 kretprobe 關聯的使用者指定的返回處理程式,然後將儲存的指令指標設定為儲存的返回地址,並且執行將在從陷阱返回時恢復到該位置。
在被探測的函式執行時,其返回地址儲存在 kretprobe_instance 型別的物件中。在呼叫 register_kretprobe() 之前,使用者設定 kretprobe 結構的 maxactive 欄位,以指定可以同時探測指定函式的多少個例項。register_kretprobe() 預先分配指示數量的 kretprobe_instance 物件。
例如,如果該函式是非遞迴的並且在持有自旋鎖的情況下被呼叫,則 maxactive = 1 應該足夠了。如果該函式是非遞迴的並且永遠不會放棄 CPU(例如,透過訊號量或搶佔),則 NR_CPUS 應該足夠了。如果 maxactive <= 0,則將其設定為預設值:max(10, 2*NR_CPUS)。
如果您將 maxactive 設定得太低,那不會是災難性的;您只會錯過一些探針。在 kretprobe 結構中,nmissed 欄位在註冊返回探針時設定為零,並且每次進入被探測的函式但沒有可用於建立返回探針的 kretprobe_instance 物件時,該欄位都會遞增。
Kretprobe 進入處理程式¶
Kretprobes 還提供了一個可選的使用者指定處理程式,該處理程式在函式進入時執行。此處理程式透過設定 kretprobe 結構的 entry_handler 欄位來指定。每當 kretprobe 放置在函式入口處的 kprobe 被觸發時,如果存在使用者定義的 entry_handler,則會呼叫它。如果 entry_handler 返回 0(成功),則保證在函式返回時呼叫相應的返回處理程式。如果 entry_handler 返回非零錯誤,則 Kprobes 將返回地址保持原樣,並且 kretprobe 對於該特定函式例項不再有效。
多個進入和返回處理程式的呼叫使用與其關聯的唯一 kretprobe_instance 物件進行匹配。此外,使用者還可以指定每個返回例項的私有資料,作為每個 kretprobe_instance 物件的一部分。當在相應的使用者進入和返回處理程式之間共享私有資料時,這尤其有用。每個私有資料物件的大小可以在 kretprobe 註冊時透過設定 kretprobe 結構的 data_size 欄位來指定。可以透過每個 kretprobe_instance 物件的 data 欄位訪問此資料。
如果進入被探測的函式但沒有可用的 kretprobe_instance 物件,則除了遞增 nmissed 計數之外,還會跳過使用者 entry_handler 的呼叫。
跳轉最佳化如何工作?¶
如果您的核心是使用 CONFIG_OPTPROBES=y 構建的(目前,此標誌在 x86/x86-64 上自動設定為“y”,非搶佔核心),並且“debug.kprobes_optimization”核心引數設定為 1(請參閱 sysctl(8)),則 Kprobes 嘗試透過在每個探針點使用跳轉指令而不是斷點指令來減少探針命中開銷。
初始化 Kprobe¶
當註冊一個探針時,在嘗試此最佳化之前,Kprobes 會在指定的地址插入一個普通的、基於斷點的 kprobe。因此,即使無法最佳化此特定探針點,也會有一個探針存在。
安全檢查¶
在最佳化探針之前,Kprobes 執行以下安全檢查
Kprobes 驗證將被跳轉指令替換的區域(“最佳化區域”)完全位於一個函式內。(跳轉指令是多個位元組,因此可能會覆蓋多個指令。)
Kprobes 分析整個函式並驗證沒有跳轉到最佳化區域。具體來說
該函式不包含任何間接跳轉;
該函式不包含任何導致異常的指令(因為由異常觸發的修復程式碼可能會跳回最佳化區域 -- Kprobes 檢查異常表以驗證這一點);
沒有到最佳化區域的近跳轉(除了第一個位元組之外)。
對於最佳化區域中的每個指令,Kprobes 驗證該指令是否可以在行外執行。
準備繞行緩衝區¶
接下來,Kprobes 準備一個“繞行”緩衝區,其中包含以下指令序列
用於推送 CPU 暫存器的程式碼(模擬斷點陷阱)
呼叫 trampoline 程式碼的程式碼,該程式碼呼叫使用者的探針處理程式。
用於恢復暫存器的程式碼
來自最佳化區域的指令
跳回到原始執行路徑的跳轉。
預最佳化¶
在準備好繞行緩衝區後,Kprobes 驗證以下情況都不存在
該探針具有 post_handler。
最佳化區域中的其他指令被探測。
該探針被停用。
在上述任何情況下,Kprobes 都不會開始最佳化該探針。由於這些是臨時情況,如果情況發生變化,Kprobes 會嘗試再次開始最佳化它。
如果可以最佳化 kprobe,Kprobes 會將 kprobe 排隊到最佳化列表中,並啟動 kprobe-optimizer 工作佇列來最佳化它。如果在最佳化之前命中了要最佳化的探針點,Kprobes 會透過將 CPU 的指令指標設定為繞行緩衝區中的複製程式碼來將控制權返回到原始指令路徑 -- 從而至少避免了單步執行。
最佳化¶
Kprobe 最佳化器不會立即插入跳轉指令;相反,它首先呼叫 synchronize_rcu() 以確保安全,因為 CPU 可能在執行最佳化區域的中間被中斷 [3]。如您所知,synchronize_rcu() 可以確保在呼叫 synchronize_rcu() 時處於活動狀態的所有中斷都已完成,但前提是 CONFIG_PREEMPT=n。因此,此版本的 kprobe 最佳化僅支援 CONFIG_PREEMPT=n 的核心 [4]。
之後,Kprobe 最佳化器呼叫 stop_machine() 以使用 text_poke_smp() 將最佳化區域替換為到繞行緩衝區的跳轉指令。
取消最佳化¶
當登出、停用或被另一個 kprobe 阻止已最佳化的 kprobe 時,它將被取消最佳化。如果這種情況發生在最佳化完成之前,kprobe 只會被從最佳化列表中取消排隊。如果最佳化已經完成,則透過使用 text_poke_smp() 將跳轉替換為原始程式碼(除了第一個位元組中的 int3 斷點之外)。
請想象一下,第二條指令被中斷,然後最佳化器在中斷處理程式執行時將第二條指令替換為跳轉地址。當中斷返回到原始地址時,沒有有效的指令,這會導致意外的結果。
這種最佳化安全檢查可能會被 ksplice 用於支援 CONFIG_PREEMPT=y 核心的 stop-machine 方法所取代。
極客注意事項:跳轉最佳化會改變 kprobe 的 pre_handler 行為。如果沒有最佳化,pre_handler 可以透過更改 regs->ip 並返回 1 來更改核心的執行路徑。但是,當探針被最佳化時,該修改將被忽略。因此,如果您想調整核心的執行路徑,則需要使用以下技術之一來抑制最佳化
為 kprobe 的 post_handler 指定一個空函式。
或
執行 'sysctl -w debug.kprobes_optimization=n'
黑名單¶
Kprobes 可以探測除自身之外的大部分核心。這意味著有些函式 kprobes 無法探測。探測(捕獲)此類函式可能會導致遞迴陷阱(例如雙重錯誤),或者巢狀的探針處理程式可能永遠不會被呼叫。Kprobes 將此類函式管理為黑名單。如果您想將一個函式新增到黑名單中,您只需要 (1) 包含 linux/kprobes.h 和 (2) 使用 NOKPROBE_SYMBOL() 宏來指定一個列入黑名單的函式。Kprobes 根據黑名單檢查給定的探針地址,如果給定的地址在黑名單中,則拒絕註冊它。
支援的架構¶
Kprobes 和返回探針在以下架構上實現
i386 (支援跳轉最佳化)
x86_64 (AMD-64, EM64T) (支援跳轉最佳化)
ppc64
sparc64 (尚未實現返回探針。)
arm
ppc
mips
s390
parisc
loongarch
riscv
配置 Kprobes¶
當使用 make menuconfig/xconfig/oldconfig 配置核心時,請確保 CONFIG_KPROBES 設定為“y”,在“General architecture-dependent options”下查詢“Kprobes”。
為了能夠載入和解除安裝基於 Kprobes 的檢測模組,請確保“Loadable module support”(CONFIG_MODULES)和“Module unloading”(CONFIG_MODULE_UNLOAD)設定為“y”。
還要確保 CONFIG_KALLSYMS 甚至 CONFIG_KALLSYMS_ALL 設定為“y”,因為 kallsyms_lookup_name() 被核心中的 kprobe 地址解析程式碼使用。
如果您需要在函式的中間插入一個探針,您可能會發現“Compile the kernel with debug info”(CONFIG_DEBUG_INFO)很有用,這樣您就可以使用“objdump -d -l vmlinux”來檢視原始碼到目的碼的對映。
API 參考¶
Kprobes API 包括每種型別的探針的“註冊”函式和“登出”函式。API 還包括用於(取消)註冊探針陣列的“register_*probes”和“unregister_*probes”函式。以下是這些函式和您將編寫的關聯探針處理程式的簡潔的、迷你手冊頁規範。有關示例,請參閱 samples/kprobes/ 子目錄中的檔案。
register_kprobe¶
#include <linux/kprobes.h>
int register_kprobe(struct kprobe *kp);
在地址 kp->addr 處設定斷點。當命中該斷點時,Kprobes 呼叫 kp->pre_handler。在單步執行被探測的指令後,Kprobe 呼叫 kp->post_handler。任何或所有處理程式都可以為 NULL。如果 kp->flags 設定為 KPROBE_FLAG_DISABLED,則該 kp 將被註冊但被停用,因此,在呼叫 enable_kprobe(kp) 之前,不會命中其處理程式。
注意
隨著“symbol_name”欄位引入到 struct kprobe,探測點地址解析現在將由核心處理。以下現在可以工作
kp.symbol_name = "symbol_name";
(64 位 powerpc 的複雜性(例如函式描述符)會被透明地處理)
如果已知符號的偏移量以安裝探測點,請使用 struct kprobe 的“offset”欄位。此欄位用於計算探測點。
指定 kprobe“symbol_name”或“addr”。如果兩者都指定,kprobe 註冊將失敗,並返回 -EINVAL。
對於 CISC 架構(例如 i386 和 x86_64),kprobes 程式碼不驗證 kprobe.addr 是否位於指令邊界處。謹慎使用“offset”。
register_kprobe() 在成功時返回 0,否則返回一個負的 errno。
使用者的 pre-handler (kp->pre_handler)
#include <linux/kprobes.h>
#include <linux/ptrace.h>
int pre_handler(struct kprobe *p, struct pt_regs *regs);
呼叫時,p 指向與斷點關聯的 kprobe,regs 指向包含命中該斷點時儲存的暫存器的結構體。除非您是 Kprobes 極客,否則此處返回 0。
使用者的 post-handler (kp->post_handler)
#include <linux/kprobes.h>
#include <linux/ptrace.h>
void post_handler(struct kprobe *p, struct pt_regs *regs,
unsigned long flags);
p 和 regs 的描述與 pre_handler 相同。flags 似乎總是為零。
register_kretprobe¶
#include <linux/kprobes.h>
int register_kretprobe(struct kretprobe *rp);
為地址為 rp->kp.addr 的函式建立返回探針。當該函式返回時,Kprobes 呼叫 rp->handler。在呼叫 register_kretprobe() 之前,您必須適當地設定 rp->maxactive;有關詳細資訊,請參閱“返回探針如何工作?”。
register_kretprobe() 在成功時返回 0,否則返回一個負的 errno。
使用者的返回探針處理程式 (rp->handler)
#include <linux/kprobes.h>
#include <linux/ptrace.h>
int kretprobe_handler(struct kretprobe_instance *ri,
struct pt_regs *regs);
regs 的描述與 kprobe.pre_handler 相同。ri 指向 kretprobe_instance 物件,您可能對以下欄位感興趣
ret_addr:返回地址
rp:指向相應的 kretprobe 物件
task:指向相應的 task 結構體
- data:指向每個返回例項的私有資料;請參閱“Kretprobe
進入處理程式”以獲取詳細資訊。
regs_return_value(regs) 宏提供了一個簡單的抽象,用於從架構的 ABI 定義的適當暫存器中提取返回值。
目前忽略處理程式的返回值。
unregister_*probe¶
#include <linux/kprobes.h>
void unregister_kprobe(struct kprobe *kp);
void unregister_kretprobe(struct kretprobe *rp);
刪除指定的探針。可以在註冊探針後的任何時間呼叫登出函式。
注意
如果函式找到不正確的探針(例如,未註冊的探針),它們會清除探針的 addr 欄位。
register_*probes¶
#include <linux/kprobes.h>
int register_kprobes(struct kprobe **kps, int num);
int register_kretprobes(struct kretprobe **rps, int num);
註冊指定陣列中的每個 num 探針。如果在註冊期間發生任何錯誤,則在 register_*probes 函式返回之前,將安全地登出陣列中直到錯誤探針的所有探針。
kps/rps:指向
*probe資料結構的指標陣列num:陣列條目的數量。
注意
在使用這些函式之前,您必須分配(或定義)一個指標陣列並設定所有陣列條目。
unregister_*probes¶
#include <linux/kprobes.h>
void unregister_kprobes(struct kprobe **kps, int num);
void unregister_kretprobes(struct kretprobe **rps, int num);
一次刪除指定陣列中的每個 num 探針。
注意
如果函式在指定陣列中找到一些不正確的探針(例如,未註冊的探針),它們會清除這些不正確探針的 addr 欄位。但是,陣列中的其他探針會被正確地登出。
disable_*probe¶
#include <linux/kprobes.h>
int disable_kprobe(struct kprobe *kp);
int disable_kretprobe(struct kretprobe *rp);
暫時停用指定的 *probe。您可以使用 enable_*probe() 再次啟用它。您必須指定已註冊的探針。
enable_*probe¶
#include <linux/kprobes.h>
int enable_kprobe(struct kprobe *kp);
int enable_kretprobe(struct kretprobe *rp);
啟用已被 disable_*probe() 停用的 *probe。您必須指定已註冊的探針。
Kprobes 的特性和限制¶
Kprobes 允許在同一地址設定多個探針。此外,無法最佳化具有 post_handler 的探針點。因此,如果您在最佳化的探針點安裝具有 post_handler 的 kprobe,則該探針點將自動取消最佳化。
通常,您可以在核心中的任何位置安裝探針。特別是,您可以探測中斷處理程式。本節討論已知的例外情況。
如果您嘗試在實現 Kprobes 的程式碼中安裝探針,register_*probe 函式將返回 -EINVAL(主要是 kernel/kprobes.c 和 arch/*/kernel/kprobes.c,但也包括 do_page_fault 和 notifier_call_chain 等函式)。
如果您在可內聯的函式中安裝一個探針,Kprobes 不會嘗試追查該函式的所有內聯例項並在那裡安裝探針。gcc 可能會在沒有被要求的情況下內聯一個函式,因此如果您沒有看到您期望的探針命中,請記住這一點。
探針處理程式可以修改被探測函式的環境 -- 例如,透過修改核心資料結構,或者透過修改 pt_regs 結構的內容(這些內容在從斷點返回時會恢復到暫存器)。因此,Kprobes 可以用於例如安裝錯誤修復或注入故障以進行測試。當然,Kprobes 無法區分故意注入的故障和意外的故障。不要酒後探測。
Kprobes 不會嘗試阻止探針處理程式相互踩踏 -- 例如,探測 printk(),然後在探針處理程式中呼叫 printk()。如果一個探針處理程式命中了一個探針,則不會在該例項中執行第二個探針的處理程式,並且第二個探針的 kprobe.nmissed 成員將會遞增。
從 Linux v2.6.15-rc1 開始,多個處理程式(或同一處理程式的多個例項)可能會在不同的 CPU 上同時執行。
Kprobes 除了在註冊和登出期間外,不使用互斥鎖或分配記憶體。
探針處理程式在停用搶佔或停用中斷的情況下執行,這取決於架構和最佳化狀態。(例如,kretprobe 處理程式和最佳化的 kprobe 處理程式在 x86/x86-64 上執行時沒有停用中斷)。在任何情況下,您的處理程式都不應放棄 CPU(例如,透過嘗試獲取訊號量或等待 I/O)。
由於返回探針是透過將返回地址替換為 trampoline 的地址來實現的,因此堆疊回溯和對 __builtin_return_address() 的呼叫通常會生成 trampoline 的地址,而不是 kretprobed 函式的實際返回地址。(據我們所知,__builtin_return_address() 僅用於檢測和錯誤報告。)
如果一個函式被呼叫的次數與它返回的次數不匹配,則在該函式上註冊返回探針可能會產生不良結果。在這種情況下,會列印一行:kretprobe BUG!: Processing kretprobe d000000000041aa8 @ c00000000004f48c。透過此資訊,您將能夠關聯導致問題的 kretprobe 的確切例項。我們已經涵蓋了 do_exit() 的情況。do_execve() 和 do_fork() 不是問題。我們不知道其他可能出現問題的特定情況。
如果在一個函式進入或退出時,CPU 在當前任務的堆疊之外的堆疊上執行,則在該函式上註冊返回探針可能會產生不良結果。因此,Kprobes 不支援在 x86_64 版本的 __switch_to() 上使用返回探針(或 kprobes);註冊函式返回 -EINVAL。
在 x86/x86-64 上,由於 Kprobes 的跳轉最佳化廣泛地修改了指令,因此對最佳化有一些限制。為了解釋這一點,我們引入一些術語。想象一個由兩個 2 位元組指令和一個 3 位元組指令組成的 3 指令序列。
IA
|
[-2][-1][0][1][2][3][4][5][6][7]
[ins1][ins2][ ins3 ]
[<- DCR ->]
[<- JTPR ->]
ins1: 1st Instruction
ins2: 2nd Instruction
ins3: 3rd Instruction
IA: Insertion Address
JTPR: Jump Target Prohibition Region
DCR: Detoured Code Region
DCR 中的指令會被複制到 kprobe 的離線緩衝區中,因為 DCR 中的位元組會被替換為 5 位元組的跳轉指令。因此,存在一些限制。
DCR 中的指令必須是可重定位的。
DCR 中的指令不能包含 call 指令。
JTPR 不能成為任何跳轉或 call 指令的目標。
DCR 不能跨越函式邊界。
無論如何,這些限制都由核心中的指令解碼器檢查,所以您無需擔心這些問題。
探針開銷¶
在 2005 年使用的典型 CPU 上,處理一個 kprobe 命中需要 0.5 到 1.0 微秒。具體來說,一個重複命中相同探針點的基準測試,每次觸發一個簡單的處理程式,報告每秒 100-200 萬次命中,這取決於架構。return-probe 命中通常比 kprobe 命中花費的時間長 50-75%。當您在一個函式上設定了 return probe 時,在該函式的入口處新增一個 kprobe 基本上不會增加任何開銷。
以下是不同架構的示例開銷資料(單位:微秒)
k = kprobe; r = return probe; kr = kprobe + return probe
on same function
i386: Intel Pentium M, 1495 MHz, 2957.31 bogomips
k = 0.57 usec; r = 0.92; kr = 0.99
x86_64: AMD Opteron 246, 1994 MHz, 3971.48 bogomips
k = 0.49 usec; r = 0.80; kr = 0.82
ppc64: POWER5 (gr), 1656 MHz (SMT disabled, 1 virtual CPU per physical CPU)
k = 0.77 usec; r = 1.26; kr = 1.45
最佳化探針開銷¶
通常,處理一個最佳化的 kprobe 命中需要 0.07 到 0.1 微秒。以下是 x86 架構的示例開銷資料(單位:微秒)
k = unoptimized kprobe, b = boosted (single-step skipped), o = optimized kprobe,
r = unoptimized kretprobe, rb = boosted kretprobe, ro = optimized kretprobe.
i386: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
k = 0.80 usec; b = 0.33; o = 0.05; r = 1.10; rb = 0.61; ro = 0.33
x86-64: Intel(R) Xeon(R) E5410, 2.33GHz, 4656.90 bogomips
k = 0.99 usec; b = 0.43; o = 0.06; r = 1.24; rb = 0.68; ro = 0.30
待辦事項¶
SystemTap (http://sourceware.org/systemtap):為基於探針的檢測提供簡化的程式設計介面。試用一下。
用於 sparc64 的核心返回探針。
支援其他架構。
使用者空間探針。
觀察點探針(在資料引用時觸發)。
Kprobes 示例¶
請參見 samples/kprobes/kprobe_example.c
Kretprobes 示例¶
請參見 samples/kprobes/kretprobe_example.c
已棄用的功能¶
Jprobes 現在是一個已棄用的功能。依賴它的人應該遷移到其他跟蹤功能或使用舊核心。請考慮將您的工具遷移到以下選項之一
使用 trace-event 跟蹤帶有引數的目標函式。
trace-event 是一種低開銷(如果關閉則幾乎沒有可見開銷)的靜態定義的事件介面。您可以定義新事件並透過 ftrace 或任何其他跟蹤工具對其進行跟蹤。
請參見以下網址
將 ftrace 動態事件(kprobe 事件)與 perf-probe 結合使用。
如果您使用除錯資訊構建核心(CONFIG_DEBUG_INFO=y),您可以使用 perf-probe 找到哪個暫存器/堆疊分配給哪個區域性變數或引數,並設定新事件來跟蹤它。
請參見以下文件
tools/perf/Documentation/perf-probe.txt
kprobes debugfs 介面¶
對於最新的核心(> 2.6.20),註冊的 kprobes 列表在 /sys/kernel/debug/kprobes/ 目錄下可見(假設 debugfs 掛載在 //sys/kernel/debug)。
/sys/kernel/debug/kprobes/list:列出系統上所有註冊的探針
c015d71a k vfs_read+0x0
c03dedc5 r tcp_v4_rcv+0x0
第一列提供插入探針的核心地址。第二列標識探針的型別(k - kprobe,r - kretprobe),而第三列指定探針的 symbol+offset。如果探測的函式屬於模組,則還會指定模組名稱。後面的列顯示探針狀態。如果探針位於不再有效的虛擬地址上(模組初始化段,與已解除安裝的模組相對應的模組虛擬地址),則此類探針標記為 [GONE]。如果探針暫時停用,則此類探針標記為 [DISABLED]。如果探針已最佳化,則標記為 [OPTIMIZED]。如果探針基於 ftrace,則標記為 [FTRACE]。
/sys/kernel/debug/kprobes/enabled:強制開啟/關閉 kprobes。
提供一個旋鈕,以全域性且強制的方式開啟或關閉註冊的 kprobes。預設情況下,所有 kprobes 都已啟用。透過向此檔案回顯“0”,所有註冊的探針將被解除武裝,直到向此檔案回顯“1”。請注意,此旋鈕僅解除武裝和武裝所有 kprobes,而不會更改每個探針的停用狀態。這意味著,如果您透過此旋鈕開啟所有 kprobes,則停用的 kprobes(標記為 [DISABLED])將不會被啟用。
kprobes sysctl 介面¶
/proc/sys/debug/kprobes-optimization:開啟/關閉 kprobes 最佳化。
當 CONFIG_OPTPROBES=y 時,會出現此 sysctl 介面,它提供一個旋鈕,以全域性且強制的方式開啟或關閉跳轉最佳化(請參見跳轉最佳化如何工作?)。預設情況下,允許跳轉最佳化(ON)。如果您向此檔案回顯“0”或透過 sysctl 將“debug.kprobes_optimization”設定為 0,則所有最佳化的探針將被取消最佳化,並且之後註冊的任何新探針將不會被最佳化。
請注意,此旋鈕更改最佳化狀態。這意味著最佳化的探針(標記為 [OPTIMIZED])將被取消最佳化([OPTIMIZED] 標籤將被刪除)。如果開啟該旋鈕,它們將再次被最佳化。
參考資料¶
有關 Kprobes 的更多資訊,請參閱以下 URL