Bug描述:壓力測試一個小工程時發現內存逐漸減少,10個小時后出現OOM
Bug定位過程:
- 對整個工程模塊進行分解,逐步縮小范圍,由于整個工程包括幾個相對獨立的小模塊,而整個工程采用單進程多線程的模型,導致進行分解時,要特別注意相互之間的耦合,只能逐步分離各個模塊,運行測試(這里如果采用多進程模型,定位會更快一些,一個完整的功能,放在一個進程和多進程中,多進程天然的將功能細化了,定位問題,范圍更小)
- 在經過一段折磨人的拆分過程后,最后把問題定位到整個工程中一個小模塊功能內。在對該模塊進行了反復的代碼review后,沒有發現什么異常,甚至沒有內存申請的操作。
- 代碼層面沒有找到突破的情況下,重新通過各種命令查看了內存狀態,由于在此之前一直通過free命令查看內存,發現長時間后free命令輸出的可用內存在逐漸減少,但忽略了一點:通過top命令單獨查看模塊進程占用的內存時,該進程的rss段一直保持穩定,沒有大幅度增長。
- 基于前一步的發現,懷疑是kernel的內存有泄漏,查看/proc/meminfo發現一個疑點:slab內存占用很高,且SUnreclaim的slab一直在增加,此時基本確定kernel內存泄漏。
- 通過kmemleak對內核內存進行了分析,定位在到一個函數接口中:
char *wr_pr_debug_begin(u8 const *data, u32 len, char *string)
{int ii;string = kmalloc(len * 2 + 1, GFP_KERNEL);for (ii = 0; ii < len; ii++)sprintf(&string[ii * 2], "%02X", data[ii]);string[len * 2] = 0;return string;
}
char *wr_pr_debug_end(char *string)
{kfree(string);return "";
}
void test()
{char *read = 0;pr_debug("%s RD%02X%02X%02X -> %s%s\n", st->hw->name,i2c_addr, reg, length,wr_pr_debug_begin(data, length, read),wr_pr_debug_end(read));
}
一眼可能不容易看出上面的有什么問題,有kmalloc,有kfree啊,好像成對出現的。
考驗基本功的時候到了,熟悉函數調用傳參的人應該會知道編譯器一般對參數的處理采用堆棧的方式,是一個先進后出的過程,這樣參數的執行一般是逆序的(由于編譯器實現的不同,這個過程不是確定的),這樣kfree會在kmalloc之前運行,導致每次運行都會泄漏一點內存。上面是一個debug輸出,暫時注釋掉后壓測,問題解決,內存保持穩定。
總結:整個定位過程其實比較簡單,如果第一步看下/proc/meminfo可能會更快的定位問題(由于這個kernel driver是“大廠”提供,以為不會出問題,一直從上層的角度去找問題,所以沒有太關注kernel相關內存的使用),導致內存泄漏的原因也很簡單,出現這種問題的原因,首先編寫者的基本功一般,更主要的原因是編寫者出于“炫技”的方式去寫了這段代碼,如果老老實實封裝一個debug函數,按照正常順序調用也就沒有問題了,而且這種每次打印進行kmalloc的方式,對性能也是有些影響的。總之基本功還是很重要,而且不要駕馭自己駕馭不了的編碼方式。