kmemleak的使用---内存泄露检测工具【转】
轉自:http://blog.csdn.net/lishenglong666/article/details/8287783
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
目錄(?)[-]
內核泄露檢測(kmemleak)
介紹:
Kmemleak?提供了一種可選的內核泄漏檢測,其方法類似于跟蹤內存收集器。(http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Tracing_garbage_collectors)當獨立的對象沒有被釋放時,其報告記錄在?/sys/kernel/debug/kmemleak中。
用法:
CONFIG_DEBUG_KMEMLEAK?在Kernel?hacking中被使能,一個內核線程每10分鐘(默認值)掃描內存,并打印發現新的未引用的對象的數量。
查看內核打印信息詳細過程如下:
1、掛載debugfs文件系統
???mount?-t?debugfs?nodev?/sys/kernel/debug/
2、開啟內核自動檢測線程
???echo?scan?>?/sys/kernel/debug/kmemleak
3、查看打印信息
???cat?/sys/kernel/debug/kmemleak
4、清除內核檢測報告,新的內存泄露報告將重新寫入/sys/kernel/debug/kmemleak
???echo?clear?>?/sys/kernel/debug/kmemleak
?
內存掃描參數可以進行修改通過向/sys/kernel/debug/kmemleak?文件寫入。?參數使用如下:
??off 禁用kmemleak(不可逆)
??stack=on 啟用任務堆棧掃描(default)
??stack=off 禁用任務堆棧掃描
??scan=on 啟動自動記憶掃描線程(default)
??scan=off 停止自動記憶掃描線程
??scan=<secs> 設置n秒內自動記憶掃描,默認600s
??scan 開啟內核掃描
??clear 清除內存泄露報告
??dump=<addr> 轉存信息對象在<addr>
?
通過“kmemleak?=?OFF”,也可以在啟動時禁用Kmemleak在內核命令行。在初始化kmemleak之前,內存的分配或釋放這些動作被存儲在一個前期日志緩沖區。這個緩沖區的大小通過配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE設置。
?
功能實現的基本方法原理
通過的kmalloc、vmalloc、kmem_cache_alloc等內存分配會跟蹤其指針,連同其他
的分配大小和堆棧跟蹤信息,存儲在PRIO搜索樹。
相應的釋放函數調用跟蹤和指針就會從kmemleak數據結構中移除。
?
分配的內存塊,被認為是獨立的,如果沒有指針指向它起始地址或塊的內部的任何位置,可以發現掃描內存(包括已保存的寄存器)。這意味著,有可能沒有辦法為內核通過所分配的地址傳遞塊到一個釋放函數,因此,該塊被認為是一個內存泄漏。
?
掃描算法步驟:
?
??1。標記的所有分配對象為白色(稍后將剩余的白色物體
?????考慮獨立的)
??2。掃描存儲器與所述數據片段和棧開始,檢查對地址的值存儲在PRIO搜索樹。如果
?????一個白色的對象的指針被發現,該對象將被添加到黑名單
??3。掃描的灰色對象匹配的地址(一些白色物體可以變成黑色,并添加結束時的黑名單),直到黑色集結束
??4。剩下的白色物體被認為是獨立兒,并報告寫入/sys/kernel/debug/kmemleak。
?
一些分配的內存塊的指針在內核的內部數據結構和它們不能被檢測為孤兒。對
避免這種情況,kmemleak也可以存儲的數量的值,指向一個
內的塊的地址范圍內的地址,需要找到使
塊不被認為是泄漏。其中一個例子是使用vmalloc()函數。
Kmemleak?API
------------
見include?/?linux?/?kmemleak.h中的函數原型的頭。
kmemleak_init??-?初始化kmemleak
kmemleak_alloc??-?一個內存塊分配的通知
kmemleak_alloc_percpu??-?通知的一個percpu的內存塊分配
kmemleak_free??-?通知的內存塊釋放
kmemleak_free_part??-?通知釋放部分內存塊
kmemleak_free_percpu??-?一個percpu內存塊釋放的通知
kmemleak_not_leak??-?當不是泄露時,標記對象
kmemleak_ignore??-?當泄漏時不掃描或報告對象
kmemleak_scan_area??-?添加掃描區域內的內存塊
kmemleak_no_scan??-?不掃描的內存塊
kmemleak_erase??-?刪除一個指針變量的舊值
kmemleak_alloc_recursive??-?為kmemleak_alloc,只檢查遞歸
kmemleak_free_recursive??-?為kmemleak_free,只檢查遞歸
?
處理假陽性/陰性
--------------------------------------
?
?
對于假性的內存泄漏,但不需要報告的,由于值的內存掃描過程中發現kmemleak是指向這樣的對象。為了減少假性報告的數目,kmemleak提供kmemleak_
ignore,kmemleak_scan_area,kmemleak_no_scan,kmemleak_erase的功能,可以指定指針掃描方式,他們的掃描默認情況下不啟用。
對于不能確定是否是內存泄露的,kmemleak提供kmemleak_not_leak。kmemleak_ignore的功能可以指定固定類型的數據是否需要掃描或打印,以上具體函數分析詳見3.3詳細處理處理過程及功能函數分析。
有的泄露只是瞬間的,尤其是在SMP系統,因為指針暫時存儲在CPU的寄存器或棧。當內存泄漏時Kmemleak定義MSECS_MIN_AGE(默認為1000)一個對象的最低時間。
?
限制和缺點
-------------------------
?
主要缺點是減少了內存分配和性能釋放。為了避免其他開銷,只進行內存掃描,當在/?sys?/kernel/debug/?kmemleak文件被讀取。不管怎樣,這個工具是用于調試目的,其表現的性能不是重要的。為了保持算法簡單,kmemleak的值指向任何掃描一個塊的地址范圍內的地址。這可能會導致增加假陰性的報告。然而,它包括真正的內存泄漏,最終內存泄露將變得可見。
假陰性的另一個來源是數據存儲在非指針值。
在未來的版本中,kmemleak只能掃描指針成員中分配的結構。此功能解決了許多上述假陰性的情況下。
該工具可能存在誤報。這些個案的分配塊可能不需要被釋放(如一些在init_call功能的情況下),這樣的指針通過其他方法計算,與通常的container_of宏或指針被存儲在一個位置相比不會被kmemleak掃描。頁分配和ioremap不被跟蹤
測試的特定部分kmemleak
---------------------------------------
在初始啟動時,/sys/kernel/debug/kmemleak輸出頁面比較多。這樣的情況下,當檢測指定已經開發的代碼錯誤時,可以通過清除/sys/kerner/debug/kmemleak的輸出。通過啟動kmemleak的掃描后,你可以找到新的未引用的對象,這應該與測試特定的代碼段。
詳細步驟如下:
要測試的關鍵部分之前需要清除kmemleak報告:
echo?clear?>?/sys/kernel/debug/kmemleak
測試你的內核或模塊...
echo?scan?=5>?/sys/kernel/debug/kmemleak
然后像往常一樣查看報告:
cat?/sys/kernel/debug/kmemleak
已經測試的實例詳見內核文檔kmenleak_test.txt文檔
?
1:檢測內核內存泄漏的功能
2:Documentation/kmemleak.txt 3:內核demo:mm/kmemleak-test.c 對于kmemleak,需要理解下面三點就可以了 1:我們需要知道它能檢測哪幾種內存泄漏(即用什么方法分配的內存可以檢測) 2:內核存在特殊情況,即分配內存但沒有引用。使用什么方法可以防止kmemleak report 3:檢測的機理是什么,如何知道分配的內存被引用,或者沒有引用。- 關注點1
- 關注點2
* kmemleak_not_leak - mark an allocated object as false positive
* @ptr:??????? pointer to beginning of the object
*
* Calling this function on an object will cause the memory block to no longer
* be reported as leak and always be scanned.
*/ 不打印;但是要掃描這個指針所分配的內存的內容。分配數據結構那么該結構本身不打印,但是會掃描結構內部的成員變量,是否引用其他指針。 這個函數往往用在:分配內存的內存永遠不會被釋放(與內核是一體,vmlinux或者不可移除的模塊一類)。 kmemleak_ignore /**
* kmemleak_ignore - ignore an allocated object
* @ptr:??????? pointer to beginning of the object
*
* Calling this function on an object will cause the memory block to be
* ignored (not scanned and not reported as a leak). This is usually done when
* it is known that the corresponding block is not a leak and does not contain
* any references to other allocated memory blocks.
*/ 既不打印,也不掃描指針所指的數據結構的成員變量。如果知道分配的數據結構內部不包含其他引用(不含指針)。 kmemleak_no_scan /**
* kmemleak_no_scan - do not scan an allocated object
* @ptr:??????? pointer to beginning of the object
*
* This function notifies kmemleak not to scan the given memory block. Useful
* in situations where it is known that the given object does not contain any
* references to other objects. Kmemleak will not scan such objects reducing
* the number of false negatives.
*/ 該指針本身被掃描,但是內容不會掃描。
- 關注點3
| ? | ? ? ?that's where your strings go, usually the things you forgot when linking and that cause your kernel not to work. objdump -s -j .rodata .process.o will hexdump it. Note that depending on the compiler, you may have more sections like this. |
- kmemleak_scan()
???? scan_block(__bss_start, __bss_stop, NULL, 1); data..percpu #ifdef CONFIG_SMP
???? /* per-cpu sections scanning */
???? for_each_possible_cpu(i)
????????? scan_block(__per_cpu_start + per_cpu_offset(i),
?????????????? ?? __per_cpu_end + per_cpu_offset(i), NULL, 1);
#endif -->>>>以上都是全局指針變量、per_cpu變量 struct pagep[]數組 /*??
???????? * Struct page scanning for each node.
???????? */
??????? lock_memory_hotplug();
??????? for_each_online_node(i) {
??????????????? pg_data_t *pgdat = NODE_DATA(i);
??????????????? unsigned long start_pfn = pgdat->node_start_pfn;
??????????????? unsigned long end_pfn = start_pfn + pgdat->node_spanned_pages;
??????????????? unsigned long pfn;
??????????????? for (pfn = start_pfn; pfn < end_pfn; pfn++) {
??????????????????????? struct page *page;
??????????????????????? if (!pfn_valid(pfn))
??????????????????????????????? continue;
??????????????????????? page = pfn_to_page(pfn);
??????????????????????? /* only scan if page is in use */
??????????????????????? if (page_count(page) == 0)
??????????????????????????????? continue;
??????????????????????? scan_block(page, page + 1, NULL, 1);
??????????????? }???
??????? }???
??????? unlock_memory_hotplug(); 內核struct page數組是動態分配的,所以也要單獨的進行檢測。 內核進程棧 if (kmemleak_stack_scan) {
????????? struct task_struct *p, *g;
????????? read_lock(&tasklist_lock);
????????? do_each_thread(g, p) {
?????????????? scan_block(task_stack_page(p), task_stack_page(p) +
??????????????????? ?? THREAD_SIZE, NULL, 0);
????????? } while_each_thread(g, p);
????????? read_unlock(&tasklist_lock);
? ? ? 一般遍歷內核所有的進程用的是:for_each_process(); 但是這里卻使用:do_each_thread(){};while_each_thread() >>>for_each_process:只打印進程;而不打印進程內的線程 >>>do_each_thread(){};while_each_thread():打印進程以及進程內的線程信息。這是因為線程有自己單獨的內核棧信息。 分配的內存塊的內部 分配一塊內存(一般是分配數據結構),內部的成員變量是指針,所以這部分也需要檢測。 >>>???? scan_gray_list();---->scan_object(): 掃描分配內存的全部內容或者部分內容,是否引用其他指針。 pointer+size
- 問題
總結
以上是生活随笔為你收集整理的kmemleak的使用---内存泄露检测工具【转】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ELK Stack 日志分析 Elast
- 下一篇: 读写应用程序数据-SQLite3