記一次Python指令碼實現記憶體洩漏測試的方法及解決過程,經驗分享篇
我下面這篇文章提供了一種輕巧的記憶體洩漏測試方法及其python實現,該方法在Lenovo Bamboo系統的驗收測試活動中得到過諸多檢驗,是一種易用有效的記憶體洩漏測試方法。
一、記憶體洩漏測試原理
1、記憶體洩漏的危害。
記憶體洩漏的危害不必多說,會導致系統的可用記憶體越來越少,影響系統長時間執行的穩定性。
2、常用的記憶體洩漏測試方法
一般而言,可概括為兩種思路:
1)記憶體分配、釋放工具檢查
如valgrind等記憶體測試工具。
2)Linux系統效能監測
如zabbix等linux效能監測工具,以及團隊自研的檢測linux效能的工具。
在方法一中,工具的原理一般是透過檢查當程式動態分配記憶體後,是否有釋放來判斷有沒有發生記憶體洩漏。其優點很明顯,發現洩漏時能很方便定位到程式碼的具體哪個地方在洩漏記憶體。但缺點也很明顯:一是工具容易誤判,二是工具要干涉程式編譯過程,使得工具使用起來很麻煩。這兩個缺點對於黑盒測試人員來說尤其容易感受到。
其實,還有第三個缺點。作為測試人員,真正關心的難道是程式結束時記憶體有沒有free嗎?不然。絕大多數的程式原本就該持續執行永不結束(至少我們希望是這樣),測試人員真正關心的是程式所使用的記憶體是基本穩定的,還是持續增長的。如果是持續增長的,就有影響系統長時間執行穩定性的風險。
綜上, Bamboo OS測試人員根據方法二總結了記憶體洩漏測試方法,並利用python實現了工具指令碼。
3、linux記憶體管理機制
Linux的記憶體管理機制,網上的資料很多,就不多談了。本文只對一些基本的概念囉嗦幾句。
虛擬記憶體:linux上每個程式都有一個虛擬的地址空間,這個虛擬的地址空間大小在ps命令裡體現為VSZ,在top命令裡體現為VIRT。這空間可以很大,單個程式的VSZ超出整個系統的記憶體,也是很常見的。
常駐記憶體:只有實際要發生訪問的記憶體,才會被對映到RAM上,在ps命令裡體現為RSS,在top命令裡體現為RES
如下圖所示,vpp的VSZ為99G(你看,任性。),RSS為787M。
(注:PS命令中記憶體基本單位是KB,B代表bit)
程式使用的記憶體,還有一種分法:私有記憶體和共享記憶體。
顧名思義,私有記憶體即是被該程式獨享的記憶體,共享記憶體是多個程式共享的記憶體,一般地,當多個程式依賴相同的連結庫時,連結庫也會被對映到每一個程式的地址空間裡。所以,即使RSS也未真正的反映程式到底佔用了系統多少記憶體資源。
綜上,測試linux系統上程式記憶體洩漏的一個基本思路為:
定期透過ps、cat proc/$pid/status、pmap –d $pid等命令,觀測程式的rss和私有記憶體的變化。如果兩者都在持續上漲,那麼該程式有很大嫌疑存在記憶體洩漏。如果只有其中之一在漲,需進一步使用定位方法(或者請求開發協助),最好能弄明白具體原因。
監測linux記憶體效能的命令有很多,除上文提到過的命令外,使用者態程式還有smaps,vmstat以及其他命令監測,核心一般是檢視slabinfo。本文不介紹這些命令的詳細用法。
二、Bamboo 系統記憶體洩漏檢測指令碼的實現
1、設計測試方法
視對系統的瞭解程度,可以選擇性地
1)透過審閱設計文件或歷史缺陷、與開發溝通交流,找出專案動態分配記憶體的地方;
2)設計測試步驟,測試步驟應當讓最終的狀態與初始狀態時一個狀態。比如,建立router ospf例項,進行例項的初始化和資源分配、協議互動後,又刪去了此例項,系統回到初始狀態;
3)反覆執行測試步驟並透過linux的命令觀察記憶體變化。如果rss和私有記憶體持續上漲,則有嫌疑出現了記憶體洩漏;
4)定位(或協調開發來定位)問題,可透過pmap命令的詳細資訊對比、gdb除錯、valgrind或其他工具來定位。
2、實現記憶體拷機測試指令碼
記憶體拷機指令碼工具包含兩部分,memMonitor和mytest。
memMonitor是工作框架,依賴Bamboo系統的自動化測試平臺,該平臺能提供bamboos_ssh功能讓指令碼可以建立一個Bamboo系統例項並在該系統執行命令。筆者提供的版本是透過命令列來獲取系統記憶體資訊的,各功能函式透過分析字串來獲取資料,讀者可以自行實現。記憶體監測結果的呈現可炫酷可簡易,讀者可自行實現。
memMonitor 的傳參mytest是一個函式,這個函式里是測試人員設計的測試步驟(比如上文說過的建立和銷燬ospf例項),對於工具的使用人員來說,他不需要關心memMonitor內部實現機制,只要設計他個人的mytest就可以了。
memMonitor指令碼的基本架構如下。
import bamboos_ssh
import re
import time
'''
本用例透過linux提供的ps和pmap命令監控Bamboo系統裡指定程式的記憶體變化。
'''
##--------------全域性引數設定--設定檢測範圍-------------------------------##
processList = ['./omu','/bin/ffe/vpp/vpp','l3stackMain','srvc','nse','ospfd','nettool_server',
'infoc','ffe_mgmt','usermgr','nat','dhcpd','ntpd','bgpd','sysrepod']
##++++++++++++++++++++++++++++++++++++++++++++++++++++##
def memMonitor(targetSystemIP,psName,repeatRounds,mytest):
rssList = []
privateList = []
##-----------------------------初始化工作------------------------------------- -##
dut1_ssh = bamboos_ssh.bamboos_ssh(targetSystemIP)
print('\n初始時完整獲取ps資訊,找到目標程式的程式號,獲取該程式的pmap資訊')
psAllInfo = dut1_ssh.exe_cmd( ['ps -aux'])
psID = getPsID(psName,psAllInfo)
psAllRssStart = getPsAllRss(processList,psAllInfo)
dut1_ssh.exe_cmd( ['pmap -d %s'%psID])
dut1_ssh.exe_cmd(['exit'])
##-----------------------------拷機過程-----------------------------------------##
##主迴圈多次執行測試員的拷機指令碼,每次執行後獲取rss記憶體和private記憶體資訊
for i in range(0,repeatRounds+1):
try:
if 0 == i:
print('\n獲取初始記憶體資訊')
else:
print('\n第%d輪測試' % i)
mytest(dut1_ssh)
print('\n第%d次獲取記憶體資訊' % i)
rssMem = getRssMem(dut1_ssh,psID)
rssList.append(rssMem)
privateMem = getPrivateMem(dut1_ssh,psID)
privateList.append(int(privateMem))
dut1_ssh.exe_cmd(['exit'])
except BaseException as e:
print(e);print('第%d輪測試時異常終止了'%i);resultPrint(psName, rssList, privateList);break
##最後一輪測試時再次獲取ps資訊,以及目標程式的pmap資訊
print('\n結束時完整獲取ps資訊,以及目標程式的pmap資訊')
psAllInfo = dut1_ssh.exe_cmd(['ps -aux'])
psAllRssEnd = getPsAllRss(processList,psAllInfo)
dut1_ssh.exe_cmd(['pmap -d %s' % psID])
dut1_ssh.exe_cmd(['exit'])
##-------------------顯示最終的監測結果------------------------------------------##
print('=========================拷機測試的結果=====================')
resultPrint(psName, rssList, privateList)
compareAllPsRss(processList, psAllRssStart, psAllRssEnd)
##-------------------清理測試環境,結束測試---------------------------------------##
dut1_ssh.close()
##++++++++++++++++++++++++++++++++++++++++++++##
def getPsID(psName,psInfo):
#根據PSInfo檢索出psID
# return psID
def getPsAllRss(psNameList,psAllInfo):
#透過psALLInfo檢索出所有ps的RSS並返回
# return psRssList
def getRssMem(dut1_ssh,psID):
#根據 'cat /proc/%s/status | grep VmRSS' 命令找出每一個ps的rss記憶體值
# return rssMem
def getPrivateMem(dut1_ssh,psID):
#根據 'pmap -d %s | grep mapped' 命令找出每一個ps的私有記憶體值
# return privateMem
def resultPrint(psName,rssList,privateList):
#根據需要輸出記憶體監測結果,可以是炫酷的圖形化輸出,也可以是簡易輸出,例如:
print('\n程式%s的實體記憶體佔用趨勢為:'%psName)
print(rssList)
print('\n程式%s的私有記憶體使用趨勢為:'%psName)
print(privateList)
def compareAllPsRss(processList,psAllRssStart,psAllRssEnd):
#對所有程式的一個輸出,可以是圖形化的,也可以是簡易的
3、使用記憶體拷機測試指令碼
使用例項:
1)測試同學懷疑ntp特性在配置本地時鐘存在記憶體洩漏,所以設定主要監測的程式是ntpd
2)在mytest函式里定義了將反覆執行的命令。
def mytest(dut1_ssh):
for i in range(0,10):
dut1_ssh.config(['ntp-service refclock-master 9'])
time.sleep(2)
dut1_ssh.config(['no ntp-service refclock-master'])
3)主程式將反覆執行mytest,並週期性的檢視ntpd程式的rss記憶體和私有記憶體資訊
4)主程式在初始時和結束時獲取了一次ntpd程式的完整pmap資訊,方便在懷疑ntpd存在記憶體洩漏時進行進一步的定位分析。
執行結果:
主要的結果如下圖所示:(筆者使用的版本當前未將資料圖形化)
可以看到,ntpd的rss記憶體增長顯著,私有記憶體也呈一直上漲趨勢。所以,ntpd有很大嫌疑存在記憶體洩漏。
因為想進一步分析記憶體的變化,所以對比了程式執行前後ntpd的pmap資訊。可以看到在結束時,pmap資訊裡多了更多的大小為4K的分頁。測試人員將這一資訊提供給開發同學,開發同學據此很快找到了記憶體洩漏的原因在於NTP記錄日誌後沒有釋放分頁。
加我VX:ww-51testing 回覆關鍵詞“測試”領取限量軟體測試學習資料哦~~
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31407649/viewspace-2654179/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 記一次透過Memory Analyzer分析記憶體洩漏的解決過程記憶體
- 記一次使用windbg排查記憶體洩漏的過程記憶體
- 記一則伺服器記憶體洩漏解決過程伺服器記憶體
- Handler記憶體洩漏分析及解決記憶體
- 記一次 Java 應用記憶體洩漏的定位過程Java記憶體
- Handler記憶體洩漏原因及解決方案記憶體
- 一次排查Java專案記憶體洩漏的過程Java記憶體
- Java記憶體洩漏解決之道Java記憶體
- 記一次"記憶體洩露"排查過程記憶體洩露
- VueJS SSR 後端繪製記憶體洩漏的相關解決經驗VueJS後端記憶體
- Android技術分享| Android 中部分記憶體洩漏示例及解決方案Android記憶體
- Android 輕鬆解決記憶體洩漏Android記憶體
- 記憶體洩漏治理實戰:TDengine 研發團隊使用 Windbg 的經驗分享記憶體
- 解決記憶體洩漏(1)-ApacheKylin InternalThreadLocalMap洩漏問題分析記憶體Apachethread
- iOS檢測記憶體洩漏iOS記憶體
- 記錄一次記憶體洩漏排查過程記憶體
- Spring Boot引起的“堆外記憶體洩漏”排查及經驗總結Spring Boot記憶體
- 一次 Java 記憶體洩漏的排查Java記憶體
- 記一次堆外記憶體洩漏分析記憶體
- 萬惡的前端記憶體洩漏及萬善的解決方案前端記憶體
- 記一次堆外記憶體洩漏排查過程記憶體
- Handler的使用、記憶體洩漏和解決記憶體
- 記憶體洩漏的原因記憶體
- 程式碼安全測試第十一期:記憶體洩漏漏洞記憶體
- 記一次使用 laravel-s 記憶體洩漏Laravel記憶體
- Java應用程式中的記憶體洩漏及記憶體管理Java記憶體
- jvm 記憶體洩漏JVM記憶體
- Android 記憶體洩漏Android記憶體
- Java記憶體洩漏Java記憶體
- js記憶體洩漏JS記憶體
- Android記憶體洩漏Android記憶體
- 分析記憶體洩漏和goroutine洩漏記憶體Go
- 記一次記憶體溢位問題的排查、分析過程及解決思路記憶體溢位
- Salesforce使用Spring Data Redis記憶體洩漏的經驗教訓SalesforceSpringRedis記憶體
- 一次Kafka記憶體洩露排查經過Kafka記憶體洩露
- Android 記憶體洩漏檢測工具 LeakCanary(Kotlin版)的實現原理Android記憶體Kotlin
- 深入瞭解 JavaScript 記憶體洩漏JavaScript記憶體
- 記一次 Ruby 記憶體洩漏的排查和修復記憶體