Linux內(nèi)核ftrace原理

gcc的-pg選項(xiàng)

ftrace 支持動態(tài)trace态兴,即可以跟蹤內(nèi)核和模塊中任意的全局函數(shù)狠持。它利用了gcc的-pg編譯選項(xiàng),在每個函數(shù)的開始增加一個stub瞻润,這樣在需要的時候可以控制函數(shù)跳轉(zhuǎn)到指定的代碼中去執(zhí)行喘垂。用過gprof工具應(yīng)該對gcc的-pg選項(xiàng)不陌生了献汗。

  • 當(dāng)CONFIG_FUNCTION_TRACER打開時,編譯時會增加-pg編譯選項(xiàng)王污,gcc會在每個函數(shù)的入口處增加對mcount的調(diào)用罢吃。
  • gcc 4.6新增加了-pg -mfentry支持,這樣可以在函數(shù)的最開始插入一條調(diào)用fentry的指令昭齐。
[root@localhost kernel-4.4.27]# echo 'void foo(){}' | gcc -x c -S -o - - -pg -mfentry

foo:
.LFB0:
    .cfi_startproc
    call    __fentry__
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

通過nm可以看到多了一個未定義的符號fentry

                 U __fentry__
0000000000000000 T foo

對于動態(tài)ftrace尿招,有一個很重要的工作就是記錄這些被-pg影響的函數(shù),最終可以通過讀debugfs的文件/sys/kernel/debug/tracing/available_filter_functions來查看哪些函數(shù)是支持trace的阱驾。

編譯內(nèi)核

內(nèi)核在編譯代碼時就谜,先指定-pg -fentry選項(xiàng)編譯生成.o文件,然后通過scripts/recordmcount.pl腳本來處理.o文件

以一個簡單的foo.c文件舉例

static void foo() {}
static void foo2() {}
static void foo3() {}

經(jīng)過scripts/recordmcount.pl處理之后里覆,.o文件中新增了一個__mcount_loc段丧荐,在最終鏈接時被重定向,里面記錄了所有插入了mcount或者fentry的函數(shù)地址喧枷。

[root@localhost kernel-4.4.27]# objdump -s foo.o

Contents of section __mcount_loc:
 0000 00000000 00000000 00000000 00000000  ................
 0010 00000000 00000000                    ........    
[root@localhost kernel-4.4.27]# objdump -r foo.o

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE 
0000000000000001 R_X86_64_PC32     __fentry__-0x0000000000000004
000000000000000c R_X86_64_PC32     __fentry__-0x0000000000000004
0000000000000017 R_X86_64_PC32     __fentry__-0x0000000000000004


RELOCATION RECORDS FOR [__mcount_loc]:
OFFSET           TYPE              VALUE 
0000000000000000 R_X86_64_64       foo
0000000000000008 R_X86_64_64       foo+0x000000000000000b
0000000000000010 R_X86_64_64       foo+0x0000000000000016

最終內(nèi)核的鏈接腳本include/asm-generic/vmlinux.lds.h將__mcount_loc段的內(nèi)容放在.init.data段中虹统,并且通過__start_mcount_loc和__stop_mcount_loc兩個全局符號來訪問。

#define MCOUNT_REC()    . = ALIGN(8);                           \
                        VMLINUX_SYMBOL(__start_mcount_loc) = .; \
                        *(__mcount_loc)                         \
                        VMLINUX_SYMBOL(__stop_mcount_loc) = .;
[root@localhost kernel-4.4.27] objdump -t vmlinux -j .init.data | egrep "__start_mcount_loc|__stop_mcount_loc"
ffffffff817109e0 g       .init.data 0000000000000000 __stop_mcount_loc
ffffffff816fb0c0 g       .init.data 0000000000000000 __start_mcount_loc

ftrace初始化

gcc的-pg -mfentry選項(xiàng)在每個函數(shù)開始處增加了一條callq指令隧甚,它和對應(yīng)的retq據(jù)統(tǒng)計(jì)會帶來13%的性能開銷车荔,因此在內(nèi)核的初始化階段將這些callq指令全部修改為5 Byte的NOP指令: 66 66 66 66 90H,同時將這些指令的地址記錄下來戚扳。

  • scripts/recordmcount.pl過濾了kernel/trace/ftrace.o忧便,沒有為其增加__mcount_loc段,所以ftrace代碼不會修改其自身的代碼帽借。
  • ftrace_init在start_kernel中調(diào)用珠增,早于kernel_init,此時不會有其它Core正在執(zhí)行代碼砍艾,因此也不用擔(dān)心修改指令導(dǎo)致其它Core出現(xiàn)crash(系統(tǒng)運(yùn)行時修改指令就要麻煩很多:被修改的指令正在其它Core上執(zhí)行蒂教,5個字節(jié)的指令有可能跨兩個cache line)。
  • 由于ftrace_init執(zhí)行時間較早辐董,所以.initcall中的初始化函數(shù)都是可以被trace的(在cmdline中增加"ftrace_filter="參數(shù)來指定要trace的函數(shù))悴品。
void __init ftrace_init(void)
{
    extern unsigned long __start_mcount_loc[];
    extern unsigned long __stop_mcount_loc[];
    unsigned long count;

    count = __stop_mcount_loc - __start_mcount_loc;

    ret = ftrace_process_locs(NULL,
                  __start_mcount_loc,
                  __stop_mcount_loc);
}

在ftrace_process_locs函數(shù)中禀综,內(nèi)核為__start_mcount_loc和__stop_mcount_loc之間的每個地址都創(chuàng)建一個struct dyn_ftrace結(jié)構(gòu)简烘,其中ip記錄著函數(shù)開始的stub地址,ftrace_code_disable函數(shù)會將這個地址的內(nèi)容替換為nop指令定枷,這樣在沒有trace時孤澎,系統(tǒng)的性能幾乎沒有影響。

struct dyn_ftrace {
    unsigned long       ip; /* address of mcount call-site */
    unsigned long       flags;
    struct dyn_arch_ftrace  arch;
};

當(dāng)開始trace時欠窒,內(nèi)核根據(jù)函數(shù)名找到ip覆旭,將該地址處的nop指令修改為call指令退子,以控制其跳轉(zhuǎn)到指定的位置。

模塊

編譯模塊時會用到內(nèi)核源碼樹中的Makefile和.config文件(實(shí)際上是根據(jù).config生成的include/config/auto.conf文件)型将,如果內(nèi)核源碼樹中的配置打開了CONFIG_FUNCTION_TRACER寂祥,那么在編譯模塊時也會增加-pg -mfentry,并將影響了的函數(shù)地址保存在__mcount_loc段中七兜。

在加載.ko時首先根據(jù)模塊放置的實(shí)際地址為__mcount_loc段重定向丸凭,并記錄在mod->ftrace_callsites中,最后同樣會調(diào)用ftrace_process_locs函數(shù)來處理腕铸。

如果當(dāng)前運(yùn)行的內(nèi)核打開了CONFIG_FUNCTION_TRACER惜犀,但編譯module時未打開,實(shí)際上編出來的.ko也能加載狠裹,只是其中的函數(shù)都不支持trace虽界。

附:scripts/recordmcount.pl實(shí)現(xiàn)

首先是逐行處理objdump -hdr foo.o, 將插入了mcount或者fentry的函數(shù)地址記錄到一個臨時的.s文件中涛菠,并將臨時.s文件編譯成.o文件并和原來的.o文件鏈接到一起

[root@localhost kernel-4.4.27]# cat .tmp_mc_foo.s 
    .section __mcount_loc,"a",@progbits
    .align 8
    .quad foo + 0
    .quad foo + 11

需要注意的是如果.o文件中的第一個函數(shù)是static或者weak莉御,需要先通過objcopy --globalize-symbol將其轉(zhuǎn)換為全局符號,然后再和上面的.tmp_mc_foo.o一起鏈接

$cc -o $mcount_o -c $mcount_s

$objcopy $globallist $inputfile $globalobj

$ld -r $globalobj $mcount_o -o $globalmix

$objcopy $locallist $globalmix $inputfile
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俗冻,一起剝皮案震驚了整個濱河市颈将,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌言疗,老刑警劉巖晴圾,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異噪奄,居然都是意外死亡死姚,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門勤篮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來都毒,“玉大人,你說我怎么就攤上這事碰缔≌司ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵金抡,是天一觀的道長瀑焦。 經(jīng)常有香客問我,道長梗肝,這世上最難降的妖魔是什么榛瓮? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮巫击,結(jié)果婚禮上禀晓,老公的妹妹穿的比我還像新娘精续。我一直安慰自己,他們只是感情好粹懒,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布重付。 她就那樣靜靜地躺著,像睡著了一般凫乖。 火紅的嫁衣襯著肌膚如雪堪夭。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天拣凹,我揣著相機(jī)與錄音森爽,去河邊找鬼。 笑死嚣镜,一個胖子當(dāng)著我的面吹牛爬迟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播菊匿,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼付呕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了跌捆?” 一聲冷哼從身側(cè)響起徽职,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎佩厚,沒想到半個月后姆钉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抄瓦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年潮瓶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钙姊。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡毯辅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出煞额,到底是詐尸還是另有隱情思恐,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布膊毁,位于F島的核電站胀莹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏媚媒。R本人自食惡果不足惜嗜逻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望缭召。 院中可真熱鬧栈顷,春花似錦、人聲如沸嵌巷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽搪哪。三九已至靡努,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晓折,已是汗流浹背惑朦。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漓概,地道東北人漾月。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像胃珍,于是被迫代替她去往敵國和親梁肿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容