gcc4.6 添加了一個編譯選項 -mfentry, 當(dāng)程序編譯之后,程序中的所有函數(shù),除了notrace屬性
#define notrace __attribute__((no_instrument_function))
的函數(shù)頭上都會添加上call __fentry__,占用5個字節(jié),__fentry__函數(shù)在程序中可以自定義, 比如在Linux kernel中被定義為 retq直接返回。
SYM_FUNC_START(__fentry__)
retq
SYM_FUNC_END(__fentry__)
定義成retq的意思是我不想直接使用__fentry__, 其實現(xiàn)也是在內(nèi)核啟動的時候把__fentry__換成了nopl, 然后在需要trace內(nèi)核函數(shù)時,再替換成對應(yīng)的trampoline(中文: 蹦床)。
本篇講解ftrace(function trace)在用戶空間的應(yīng)用。
以下代碼來自此git工程:
https://github.com/x-lugoo/ftracer.git
ftracer.c中對__fentry__函數(shù)進行了自定義:
ftracer.c
asm(
“ .globl __fentry__
”
“__fentry__:
”
/* save arguments */
“ push %rax
”
“ push %rdi
”
“ push %rsi
”
“ push %rdx
”
“ push %rcx
”
“ push %r8
”
“ push %r9
”
“ movq %rsp,%rdi
”
“ call ftracer
”
“ pop %r9
”
“ pop %r8
”
“ pop %rcx
”
“ pop %rdx
”
“ pop %rsi
”
“ pop %rdi
”
“ pop %rax
”
“ ret
”);
上面__fentry__函數(shù)的實現(xiàn)把所有傳參寄存器(x86_64架構(gòu))全部壓棧,然后把sp指針傳給ftracer()的第一個參數(shù)。
__attribute__((used)) void ftracer(struct frame *fr)
{
if (!tenabled)
return;
struct trace *t = &tbuf[tcur++];
if (tcur 》= TSIZE)
tcur = 0;
t-》tstamp = __builtin_ia32_rdtsc();
t-》src = fr-》caller;
t-》dst = fr-》callee;
t-》arg1 = fr-》rdi;
t-》arg2 = fr-》rsi;
t-》arg3 = fr-》rdx;
}
struct frame {
uint64_t r9;
uint64_t r8;
uint64_t rcx;
uint64_t rdx;
uint64_t rsi;
uint64_t rdi;
uint64_t rax;
uint64_t callee;
uint64_t caller;
};
其中callee是被調(diào)用函數(shù)地址,caller是調(diào)用函數(shù)地址 ,比如f1()調(diào)用f2(), f2函數(shù)頭上調(diào)用了__fentry__, 那么__fentry__ 就可以從frame結(jié)構(gòu)中的rax變量地址之后找到callee和caller
f1() {
call f2
f2() {
call __fentry__
ftracer()的實現(xiàn)把函數(shù)調(diào)用參數(shù),被調(diào)用函數(shù),調(diào)用函數(shù),函數(shù)執(zhí)行時間戳都存在tbuf中
使用一個測試程序驗證ftrace功能:
test.c
#include “ftracer.h”
#define mb() asm volatile (“” ::: “memory”)
void f3(int a, int b, int c)
{
mb();
}
void f2(int a, int b, int c)
{
f3(4, 5, 6);
}
void f1(int a, int b, int c)
{
f2(7, 8, 9);
}
main()
{
ftrace_dump_at_exit(0);
ftrace_enable();
f1(1, 2, 3);
}
函數(shù)調(diào)用關(guān)系:main-》f1-》f2-》f3
編譯:
gcc -c ftracer.cgcc -pg -mfentry ftracer.o test.c -o test
執(zhí)行。/test的時候調(diào)用ftrace_dump(), 打印出tbuf中的數(shù)據(jù),
void ftrace_dump(unsigned max)
t = &tbuf[i];
printf(“%llx %llx-》%llx %llx %llx %llx
”,
t-》tstamp,
t-》src, t-》dst,
t-》arg1, t-》arg2, t-》arg3);
tbuf中包含函數(shù)調(diào)用關(guān)系和函數(shù)執(zhí)行時時間戳:
。/test
2b4fcfe84137ab 4008d1-》400893 4 5 6 (f2-》f3)
2b4fcfe8413763 4008fe-》4008ac 7 8 9 (f1-》f2)
2b4fcfe84136ee 40092d-》4008d9 1 2 3 (main-》f1)
以上函數(shù)調(diào)用關(guān)系對應(yīng)各個函數(shù)代碼段:
function f2:
0x00000000004008a7 《+0》: callq 0x400657 《__fentry__》
0x00000000004008ac 《+5》: push %rbp
0x00000000004008ad 《+6》: mov %rsp,%rbp
0x00000000004008cc 《+37》: callq 0x40088e 《f3》
0x00000000004008d1 《+42》: nop
0x00000000004008d2 《+43》: leaveq
0x00000000004008d3 《+44》: retq
function f3:
0x000000000040088e 《+0》: callq 0x400657 《__fentry__》
0x0000000000400893 《+5》: push %rbp
0x00000000004008a6 《+24》: retq
function f1
0x00000000004008d4 《+0》: callq 0x400657 《__fentry__》
0x00000000004008d9 《+5》: push %rbp
0x00000000004008f4 《+32》: mov $0x7,%edi
0x00000000004008f9 《+37》: callq 0x4008a7 《f2》
0x00000000004008fe 《+42》: nop
0x00000000004008ff 《+43》: leaveq
0x0000000000400900 《+44》: retq
function main
0x0000000000400901 《+0》: callq 0x400657 《__fentry__》
0x0000000000400928 《+39》: callq 0x4008d4 《f1》
0x000000000040092d 《+44》: mov $0x0,%eax
0x0000000000400932 《+49》: pop %rbp
0x0000000000400933 《+50》: retq
總結(jié):以上分析了ftracer用于用戶空間,可以跟蹤函數(shù)調(diào)用參數(shù)和函數(shù)執(zhí)行時間戳。
小編最新一直被催更微信公眾號文章,我最近一直在設(shè)計優(yōu)化tracer視頻課程,內(nèi)容已經(jīng)迭代了四五次了,希望到時候能通俗易懂、圖文并茂地講解Linux內(nèi)核中function tracer /function graph/ kprobe/kretprobe/trace event 的最底層原理和應(yīng)用,預(yù)期三月下旬發(fā)布。
掌握之后將對Linux kernel的研究學(xué)習(xí)方式和debug方式帶來很大的幫助,big picture 如下圖所示:
原文標(biāo)題:當(dāng)ftrace用于用戶空間
文章出處:【微信公眾號:Linuxer】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
責(zé)任編輯:haq
-
Linux
+關(guān)注
關(guān)注
87文章
11509瀏覽量
213744 -
程序
+關(guān)注
關(guān)注
117文章
3826瀏覽量
82965
原文標(biāo)題:當(dāng)ftrace用于用戶空間
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
如何在Linux內(nèi)核5.18版本之后和64位架構(gòu)中從內(nèi)核空間調(diào)用ioctl?
請問Linux用戶空間可以調(diào)用SCFW API嗎?
嵌入式學(xué)習(xí)-飛凌嵌入式ElfBoard ELF 1板卡-內(nèi)核空間與用戶空間的數(shù)據(jù)拷貝之獲取用戶空間數(shù)據(jù)
嵌入式學(xué)習(xí)-飛凌嵌入式ElfBoard ELF 1板卡-內(nèi)核空間與用戶空間的數(shù)據(jù)拷貝之獲取內(nèi)核空間數(shù)據(jù)
飛凌嵌入式ElfBoard ELF 1板卡-內(nèi)核空間與用戶空間的數(shù)據(jù)拷貝之獲取用戶空間數(shù)據(jù)
嵌入式學(xué)習(xí)-飛凌嵌入式ElfBoard ELF 1板卡-內(nèi)核空間與用戶空間的數(shù)據(jù)拷貝之?dāng)?shù)據(jù)拷貝介紹
飛凌嵌入式ElfBoard ELF 1板卡-內(nèi)核空間與用戶空間的數(shù)據(jù)拷貝之獲取內(nèi)核空間數(shù)據(jù)
飛凌嵌入式ElfBoard ELF 1板卡-內(nèi)核空間與用戶空間的數(shù)據(jù)拷貝之?dāng)?shù)據(jù)拷貝介紹
飛凌嵌入式ElfBoard ELF 1板卡-Linux設(shè)備驅(qū)動的分類
Linux用戶管理終極指南:從基礎(chǔ)到進階

評論