如何診斷SLUB問題【轉】

轉自:http://linuxperf.com/?p=184

前文中,我們介紹了在RHEL6及較早的kernel上診斷slab泄漏問題的兩種方法,可以說相當麻煩了,這是因爲以前的slab沒有提供原生的故障診斷機制。Linux kernel自2.6.23之後採用的Slub自帶了故障診斷機制,就方便很多,然而習慣上仍然把slub稱作slab。如何判斷你的系統kernel是否在用slub呢?僅從kernel版本號是看不準的,比如RHEL6的kernel版本2.6.x仍然在使用slab,從RHEL7纔開始採用Slub。有一個簡單的判斷方法,就是看是否存在/sys/kernel/slab目錄,有就是slub,沒有就是slab。

Slub的debug機制

瞭解slub的debug機制之前,先要弄清楚可能會出現哪些問題,無論是slab還是slub,問題無非是以下幾類:

  • 內存泄露(leak),alloc之後忘了free,導致內存佔用不斷增長;
  • 越界(overrun),訪問了alloc分配的區域之外的內存,覆蓋了不屬於自己的數據;
  • 使用已經釋放的內存(use after free),正常情況下,已經被free釋放的內存是不應該再被讀寫的,否則就意味着程序有bug;
  • 使用未經初始化的數據(use uninitialised bytes),缺省模式下alloc分配的內存是不被初始化的,內存值是隨機的,直接使用的話後果可能是災難性的。

Slub提供了red zone和poisoning等debug機制來檢測以上問題。

  • Red zone來自於橄欖球術語,是指球場底線附近的區域,slub通過在每一個對象後面額外添加一塊red zone區域來幫助檢測越界(overrun)問題,在red zone裏填充了特徵字符,如果代碼訪問到了red zone就意味着越界了。
  • Poisoning是通過往slub對象中填充特徵字符的方式來檢測use-after-free、use-uninitialised等問題,比如在分配slub對象時填充0x5a,在釋放時填充0x6b,然後debug代碼檢查時如果看到本該是0x6b的位置變成了別的內容,就可能是發生了use-after-free,而本該是0x5a的位置如果變成了其它內容就意味着可能是use-uninitialised問題。更多的poison字節定義參見以下文件:
    /lib/modules/$(uname -r)/build/include/linux/poison.h

(圖)SLUB對象的格式
* 綠色的 Payload表示分配出去的 slub object;
* slub debug機制需要佔用額外的內存,比如 Red zone,還有,爲了追溯 slub object的分配和釋放過程,需要額外的空間來存放 stack trace,即圖中的 Tracking/Debugging;
* 圖中的 FP是 Free Pointer的縮寫,處於 free狀態的 object是以鏈表的形式串在一起的,FP就是鏈表指針。

怎樣開啓slub debug

Slub本身包含了完整的debug功能,缺省是關閉的,需要的時候打開就行了。

開啓slub debug有兩種方式:

  1. 【啓動時開啓】
    在kernel command line中加入以下參數:
    slub_debug=<Debug-Options>,<slab name>
    它會在重啓時生效。
    注:
    slub_debug後面不跟任何參數表示打開所有的debug功能;
    slub_debug=<Debug-Options> 對所有的slab打開指定的debug options;
    slub_debug=<Debug-Options>,<slab name> 對指定的slab打開指定的debug options;
    slub_debug=,<slab name> 對指定的slab打開所有的debug options。
  2. 【運行中開啓】
    在運行系統上可以通過以下文件對指定的slab打開指定的debug option:
    /sys/kernel/slab/<slab name>/<debug_file>

Debug options如下:

debug option sysfs debug file 功能
F sanity_checks 激活完整性檢查功能,在特定的環節比如free的時候增加各種條件判斷,驗證數據是否完好。
Z red_zone 用於檢測overrun。通過在slub object後面插入一塊額外的紅色區域(橄欖球術語),一旦進入就表示有錯。
P poison 用於檢測use-after-free和use-uninitialised。給slub對象填充特徵字符,比如在分配時填充0x5a,在釋放時填充0x6b,根據特徵字符是否被覆蓋來檢測是否出錯。更多的poison字節定義參見: /lib/modules/$(uname -r)/build/include/linux/poison.h
U store_user 在slub object後面添加一塊額外的空間,記錄調用alloc/free的stack trace
T trace 在slub object alloc/free時,向系統日誌中輸出相關信息,包括stack trace

開啓debug option會降低系統性能,所以儘量只開啓必要的選項。

  • 如果slab出現data corruption問題,可以考慮read_zone,poison,store_user,sanity_checks;
  • 如果某個slab的大小持續瘋漲,則可能是leak(內存泄露),可以開啓trace,觀察統計slab的alloc/free情況,尋找線索。
    對於內核內存泄漏問題,還有另一個工具可供選用,詳見:用KMEMLEAK檢測內核內存泄漏

在kernel commandline中開啓slub debug與在運行系統上開啓是有區別的。有些debug option不能在運行系統上開啓,比如red_zone、store_user需要額外的存儲空間來保存debug信息,如果不是從一開始就開啓,那麼以前分配出去的slub對象就沒有debug數據區,會導致對齊問題,而且debug代碼也很難分辨新老slub對象;再比如poison,如果不是從一開始就開啓,那麼以前分配出去的slub對象就沒有填充特徵字符,debug代碼也辨別不了哪些slub對象是有填充的、哪些是沒有填充的。所以,在運行系統上開啓slub debug,如果指定的slab裏面已經有了object,那麼只能動態開啓sanity_checks和trace,惟有當指定的slab還是空的,其它的debug option纔可以動態開啓。我們實際使用的時候,儘管去試好了,如果某個debug option不允許動態開啓,命令就不會成功,比如:

 

要進行slub debug,有一個重要特性不可不知,那就是slab merging。

Slab merging

很多slab的大小和參數是相似的,slub會把這些不同的slab合併到一起,好處是可以減少內存碎片,提高內存使用效率,這個稱爲slab merging(合併)。

怎麼知道一個slab有沒有發生合併呢?通過查看以下文件:
/sys/kernel/slab/<slab name>/aliases
aliases表示參與合併的slab的數量(自己除外),如果大於0就意味着發生了合併。

slabinfo工具(見下一節的介紹)可以具體列出哪些slab合併到了一起:

Slab merging會干擾debug,因爲不同的slab合併到了一起,出了問題以後很難分辨是哪一個slab導致的。關閉slab merging的方法有兩個:

  • 在kernel command line中加入“slub_nomerge”;
  • 開啓slub debug之後,slab merging就會自動關閉。如果slub_debug指定了某個slab,那麼只有指定的slab會關閉merging。
    注:最好是在啓動時開啓slub_debug,如果是在運行系統上通過sysfs開啓slub debug,那麼之前已經合併的那些slab仍然會保持合併狀態。
slabinfo工具

隨內核源程序提供了一個slabinfo工具,但是需要自己手工編譯。源程序的位置是在源代碼樹下的 tools/vm/slabinfo.c,編譯方法是:
$ gcc -o slabinfo tools/vm/slabinfo.c
或者進入 tools/vm 目錄下直接執行make:
$ make slabinfo

slabinfo工具能做的事情見它的幫助信息:

 

參考資料:

https://www.kernel.org/doc/Documentation/vm/slub.txt
https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-slab
https://lwn.net/Articles/340267/
https://gitlab.eurecom.fr/oai/odroid-linux-3.10.y-rt/commit/4c13dd3b48fcb6fbe44f241eb11a057ecd1cba75
一個實例分析

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