歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux綜合 >> Linux內核

Linux內核的ftrace調試接口

為了抓住一個自定義的內核函數是如何被執行的,需要一定的調試手段,其實就需要一種跟蹤手段就可以了,理論上不太復雜,可是Linux內核的調試接口太多了,始終找不到一個方便的,直到遇到了ftrace,它簡單的使用文件系統作為接口,不需要安裝任何用戶態程序,和雜亂的發行版毫無關系,這正合我意,相比SystemTap等復雜的前置設置等調試手段,簡直棒極了。因為我很討厭為了做一件理論上很簡單的事而去花去大量的時間去做前置工作。

使用文件系統作為接口的優勢自然不必多說,它可以將任意復雜的操作映射到既有的簡單的讀,寫,控制,打開,關閉等簡單操作上,ftrace的另一個妙點在於其動態二進制修正技術。其實kprobe也是使用了二進制修正技術,然而它做的很硬,而ftrace則使用了GCC內置的mcount機制,通過重載mcount函數來完成對任意函數調用的統計。

mcount機制是GCC的一個特性,在任何函數調用時,會紀錄關於該函數的一些信息。比如以下的程序:
mcount.c:
#include <stdio.h>
void  mcount()
{        
    printf("@@@@\n");
}

gcc -c mcount.c
main.c:
#include <stdlib.h>
#include <stdio.h>
extern void mcount(void);
void b(int i)
{
        printf("b:%d\n", i);
}
int a(int i)
{
        b(i);
        return 3;
}
int main()
{
        int i = 3;
        int k = a(i);
        return k;
}

gcc -c main.c -pg
gcc mcount.o main.o -o test
執行test,則會發現每個函數調用都會打印出@@@,這說明我們重載mcount成功了。如果能將mcount做成一個只執行ret的stub函數,或者連call mcount一起都執行nop的stub,那麼相當於沒有這個mcount函數,如果某個時間用戶啟用了ftrace,則將上述stub替換為真正的trace函數,那不就可以動態開啟/關閉trace功能了麼?Linux kernel正是這麼做的。要想這麼做,stub函數要做的足夠靈活,以上面的mcount.c/main.c為例,一個比較靈活但不絕對靈活的設計框架如下:
char code[] = {0xc3, 0x90, 0x90...} //0xc3為直接ret

void mcount()
{
    int (*pf)(void);
    pf = &code[0];
    pf();
}

如果用戶開啟了trace,則將code進行替換,替換成call real_func的操作碼,而real_func不止一個固定的函數,而是可以register的,那麼我們就可以根據自己的愛好來任意替換trace函數進而實現任意的trace風格了。Linux內核的做法比我這個要靈活的多,通過回調func的機制,它甚至可以畫出一副函數調用圖,十分強大。順便說一句,trace回調函數的實現利用了內核編譯時產生的內核函數位置表,它的條目就是函數名和位置這一對映射,trace回調函數會根據當前的地址查到函數名。

我上述的框架只是一個框架,如果你真的去編譯運行了,會發現出現了惡心的segment fault,這是因為如今大多數的內核都實施了data section不可執行,text section不可寫的保護功能,如果你硬要那麼做,會出現通用保護異常,因此還要做大量的鏈接腳本的工作。這個費力的事就不說了,說多了都是淚!

ftrace的核心在於利用了mcount機制以及文件系統機制,它的使用非常簡單,只需要掛載debugfs,你就可以任意調試了:
mount -t debugfs debugfs /debug

然後進入/debug/tracing目錄,檢查available_tracers,看看你當前的kernel支持的trace功能都有哪些,如果有function,說明你的內核支持函數跟蹤功能,ftrace支持過濾功能,比如按照內核函數過濾,按照進程過濾。下面是對於一個長ping的trace結果片斷:
 0)               |  sys_socketcall() {
 0)               |    copy_from_user() {
 0)               |      _copy_from_user() {
 0)   0.137 us    |        _cond_resched();
 0)   0.457 us    |      }
 0)   0.806 us    |    }
 0)   0.130 us    |    audit_socketcall();
 0)               |    sys_recvmsg() {
 0)               |      sockfd_lookup_light() {
 0)   0.228 us    |        fget_light();
 0)   0.558 us    |      }
 0)               |      __sys_recvmsg() {
 0)               |        _copy_from_user() {
 0)   0.130 us    |          _cond_resched();
 0)   0.405 us    |        }
 0)               |        verify_iovec() {
 0)               |          _copy_from_user() {
 0)   0.129 us    |            _cond_resched();
 0)   0.429 us    |          }
 0)   0.736 us    |        }
 0)               |        sock_recvmsg() {
 0)               |          security_socket_recvmsg() {
 0)               |            apparmor_socket_recvmsg() {
 0)   0.179 us    |              aa_revalidate_sk();
 0)   0.482 us    |            }
 0)   0.945 us    |          }
 0)   0.210 us    |          sock_update_classid();
 0)               |          inet_recvmsg() {
不光可以繪制出函數調用圖,其計時統計信息對於性能分析也是很有參考意義的。

ftrace很強大,不需要你對系統做任何額外的配置,不需要安裝額外的軟件,直接使用文件系統接口即可。我十分喜歡這個機制,因為我討厭需要額外配置的機制,那樣促使很多人走偏了路,他們在炫耀敲命令的技巧的同時,給了別人很大的壓力,其實行內人士都知道,他們的大部分命令都是為了搭建這個trace環境,而不是解決真正的問題,因此這種命令也包括apt-get,你不懂,你就會覺得他很猛!
Copyright © Linux教程網 All Rights Reserved