溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Python腳本實(shí)現(xiàn)內(nèi)存泄漏測試的方法及解決過程

發(fā)布時間:2021-11-03 16:21:03 來源:億速云 閱讀:166 作者:柒染 欄目:建站服務(wù)器

Python腳本實(shí)現(xiàn)內(nèi)存泄漏測試的方法及解決過程,針對這個問題,這篇文章詳細(xì)介紹了相對應(yīng)的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

我下面這篇文章提供了一種輕巧的內(nèi)存泄漏測試方法及其python實(shí)現(xiàn),該方法在Lenovo Bamboo系統(tǒng)的驗(yàn)收測試活動中得到過諸多檢驗(yàn),是一種易用有效的內(nèi)存泄漏測試方法。

一、內(nèi)存泄漏測試原理

1、內(nèi)存泄漏的危害。

內(nèi)存泄漏的危害不必多說,會導(dǎo)致系統(tǒng)的可用內(nèi)存越來越少,影響系統(tǒng)長時間運(yùn)行的穩(wěn)定性。

2、常用的內(nèi)存泄漏測試方法

一般而言,可概括為兩種思路:

1)內(nèi)存分配、釋放工具檢查

如valgrind等內(nèi)存測試工具。

2)Linux系統(tǒng)性能監(jiān)測

如zabbix等linux性能監(jiān)測工具,以及團(tuán)隊自研的檢測linux性能的工具。

在方法一中,工具的原理一般是通過檢查當(dāng)程序動態(tài)分配內(nèi)存后,是否有釋放來判斷有沒有發(fā)生內(nèi)存泄漏。其優(yōu)點(diǎn)很明顯,發(fā)現(xiàn)泄漏時能很方便定位到代碼的具體哪個地方在泄漏內(nèi)存。但缺點(diǎn)也很明顯:一是工具容易誤判,二是工具要干涉程序編譯過程,使得工具使用起來很麻煩。這兩個缺點(diǎn)對于黑盒測試人員來說尤其容易感受到。

其實(shí),還有第三個缺點(diǎn)。作為測試人員,真正關(guān)心的難道是程序結(jié)束時內(nèi)存有沒有free嗎?不然。絕大多數(shù)的進(jìn)程原本就該持續(xù)運(yùn)行永不結(jié)束(至少我們希望是這樣),測試人員真正關(guān)心的是進(jìn)程所使用的內(nèi)存是基本穩(wěn)定的,還是持續(xù)增長的。如果是持續(xù)增長的,就有影響系統(tǒng)長時間運(yùn)行穩(wěn)定性的風(fēng)險。

綜上, Bamboo OS測試人員根據(jù)方法二總結(jié)了內(nèi)存泄漏測試方法,并利用python實(shí)現(xiàn)了工具腳本。

3、linux內(nèi)存管理機(jī)制

Linux的內(nèi)存管理機(jī)制,網(wǎng)上的資料很多,就不多談了。本文只對一些基本的概念啰嗦幾句。

虛擬內(nèi)存:linux上每個進(jìn)程都有一個虛擬的地址空間,這個虛擬的地址空間大小在ps命令里體現(xiàn)為VSZ,在top命令里體現(xiàn)為VIRT。這空間可以很大,單個進(jìn)程的VSZ超出整個系統(tǒng)的內(nèi)存,也是很常見的。

常駐內(nèi)存:只有實(shí)際要發(fā)生訪問的內(nèi)存,才會被映射到RAM上,在ps命令里體現(xiàn)為RSS,在top命令里體現(xiàn)為RES

如下圖所示,vpp的VSZ為99G(你看,任性。),RSS為787M。

(注:PS命令中內(nèi)存基本單位是KB,B代表bit)

Python腳本實(shí)現(xiàn)內(nèi)存泄漏測試的方法及解決過程

進(jìn)程使用的內(nèi)存,還有一種分法:私有內(nèi)存和共享內(nèi)存。

顧名思義,私有內(nèi)存即是被該進(jìn)程獨(dú)享的內(nèi)存,共享內(nèi)存是多個進(jìn)程共享的內(nèi)存,一般地,當(dāng)多個進(jìn)程依賴相同的鏈接庫時,鏈接庫也會被映射到每一個進(jìn)程的地址空間里。所以,即使RSS也未真正的反映進(jìn)程到底占用了系統(tǒng)多少內(nèi)存資源。

綜上,測試linux系統(tǒng)上進(jìn)程內(nèi)存泄漏的一個基本思路為:

定期通過ps、cat proc/$pid/status、pmap –d $pid等命令,觀測進(jìn)程的rss和私有內(nèi)存的變化。如果兩者都在持續(xù)上漲,那么該進(jìn)程有很大嫌疑存在內(nèi)存泄漏。如果只有其中之一在漲,需進(jìn)一步使用定位方法(或者請求開發(fā)協(xié)助),最好能弄明白具體原因。

監(jiān)測linux內(nèi)存性能的命令有很多,除上文提到過的命令外,用戶態(tài)進(jìn)程還有smaps,vmstat以及其他命令監(jiān)測,內(nèi)核一般是查看slabinfo。本文不介紹這些命令的詳細(xì)用法。

二、Bamboo 系統(tǒng)內(nèi)存泄漏檢測腳本的實(shí)現(xiàn)

1、設(shè)計測試方法

視對系統(tǒng)的了解程度,可以選擇性地

1)通過審閱設(shè)計文檔或歷史缺陷、與開發(fā)溝通交流,找出項(xiàng)目動態(tài)分配內(nèi)存的地方;

2)設(shè)計測試步驟,測試步驟應(yīng)當(dāng)讓最終的狀態(tài)與初始狀態(tài)時一個狀態(tài)。比如,創(chuàng)建router ospf實(shí)例,進(jìn)行實(shí)例的初始化和資源分配、協(xié)議交互后,又刪去了此實(shí)例,系統(tǒng)回到初始狀態(tài);

3)反復(fù)執(zhí)行測試步驟并通過linux的命令觀察內(nèi)存變化。如果rss和私有內(nèi)存持續(xù)上漲,則有嫌疑出現(xiàn)了內(nèi)存泄漏;

4)定位(或協(xié)調(diào)開發(fā)來定位)問題,可通過pmap命令的詳細(xì)信息對比、gdb調(diào)試、valgrind或其他工具來定位。

2、實(shí)現(xiàn)內(nèi)存拷機(jī)測試腳本

內(nèi)存拷機(jī)腳本工具包含兩部分,memMonitor和mytest。

memMonitor是工作框架,依賴Bamboo系統(tǒng)的自動化測試平臺,該平臺能提供bamboos_ssh功能讓腳本可以創(chuàng)建一個Bamboo系統(tǒng)實(shí)例并在該系統(tǒng)執(zhí)行命令。筆者提供的版本是通過命令行來獲取系統(tǒng)內(nèi)存信息的,各功能函數(shù)通過分析字符串來獲取數(shù)據(jù),讀者可以自行實(shí)現(xiàn)。內(nèi)存監(jiān)測結(jié)果的呈現(xiàn)可炫酷可簡易,讀者可自行實(shí)現(xiàn)。

memMonitor 的傳參mytest是一個函數(shù),這個函數(shù)里是測試人員設(shè)計的測試步驟(比如上文說過的創(chuàng)建和銷毀ospf實(shí)例),對于工具的使用人員來說,他不需要關(guān)心memMonitor內(nèi)部實(shí)現(xiàn)機(jī)制,只要設(shè)計他個人的mytest就可以了。

memMonitor腳本的基本架構(gòu)如下。

import bamboos_ssh

  import re

  import time

  '''

  本用例通過linux提供的ps和pmap命令監(jiān)控Bamboo系統(tǒng)里指定進(jìn)程的內(nèi)存變化。

  '''

  ##--------------全局參數(shù)設(shè)置--設(shè)置檢測范圍-------------------------------##

  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信息,找到目標(biāo)進(jìn)程的進(jìn)程號,獲取該進(jìn)程的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'])

      ##-----------------------------拷機(jī)過程-----------------------------------------##

      ##主循環(huán)多次執(zhí)行測試員的拷機(jī)腳本,每次執(zhí)行后獲取rss內(nèi)存和private內(nèi)存信息

      for i in range(0,repeatRounds+1):

          try:

              if 0 == i:

                  print('\n獲取初始內(nèi)存信息')

              else:

                  print('\n第%d輪測試' % i)

                  mytest(dut1_ssh)

                  print('\n第%d次獲取內(nèi)存信息' % 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信息,以及目標(biāo)進(jìn)程的pmap信息

      print('\n結(jié)束時完整獲取ps信息,以及目標(biāo)進(jìn)程的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'])

      ##-------------------顯示最終的監(jiān)測結(jié)果------------------------------------------##

      print('=========================拷機(jī)測試的結(jié)果=====================')

      resultPrint(psName, rssList, privateList)

      compareAllPsRss(processList, psAllRssStart, psAllRssEnd)

      ##-------------------清理測試環(huán)境,結(jié)束測試---------------------------------------##

      dut1_ssh.close()

      ##++++++++++++++++++++++++++++++++++++++++++++##

  def getPsID(psName,psInfo):

  #根據(jù)PSInfo檢索出psID

  #    return psID

  def getPsAllRss(psNameList,psAllInfo):

  #通過psALLInfo檢索出所有ps的RSS并返回

  #    return psRssList

  def getRssMem(dut1_ssh,psID):

  #根據(jù) 'cat /proc/%s/status | grep VmRSS' 命令找出每一個ps的rss內(nèi)存值

  #    return rssMem

  def getPrivateMem(dut1_ssh,psID):

  #根據(jù) 'pmap -d %s | grep mapped' 命令找出每一個ps的私有內(nèi)存值

  #    return privateMem

  def resultPrint(psName,rssList,privateList):

  #根據(jù)需要輸出內(nèi)存監(jiān)測結(jié)果,可以是炫酷的圖形化輸出,也可以是簡易輸出,例如:

      print('\n進(jìn)程%s的物理內(nèi)存占用趨勢為:'%psName)

      print(rssList)

      print('\n進(jìn)程%s的私有內(nèi)存使用趨勢為:'%psName)

      print(privateList)

  def compareAllPsRss(processList,psAllRssStart,psAllRssEnd):

  #對所有進(jìn)程的一個輸出,可以是圖形化的,也可以是簡易的

3、使用內(nèi)存拷機(jī)測試腳本

使用實(shí)例:

1)測試同學(xué)懷疑ntp特性在配置本地時鐘存在內(nèi)存泄漏,所以設(shè)置主要監(jiān)測的進(jìn)程是ntpd

2)在mytest函數(shù)里定義了將反復(fù)執(zhí)行的命令。

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)主程序?qū)⒎磸?fù)執(zhí)行mytest,并周期性的查看ntpd進(jìn)程的rss內(nèi)存和私有內(nèi)存信息

4)主程序在初始時和結(jié)束時獲取了一次ntpd進(jìn)程的完整pmap信息,方便在懷疑ntpd存在內(nèi)存泄漏時進(jìn)行進(jìn)一步的定位分析。

運(yùn)行結(jié)果:

主要的結(jié)果如下圖所示:(筆者使用的版本當(dāng)前未將數(shù)據(jù)圖形化)

可以看到,ntpd的rss內(nèi)存增長顯著,私有內(nèi)存也呈一直上漲趨勢。所以,ntpd有很大嫌疑存在內(nèi)存泄漏。

Python腳本實(shí)現(xiàn)內(nèi)存泄漏測試的方法及解決過程

因?yàn)橄脒M(jìn)一步分析內(nèi)存的變化,所以對比了程序執(zhí)行前后ntpd的pmap信息??梢钥吹皆诮Y(jié)束時,pmap信息里多了更多的大小為4K的分頁。測試人員將這一信息提供給開發(fā)同學(xué),開發(fā)同學(xué)據(jù)此很快找到了內(nèi)存泄漏的原因在于NTP記錄日志后沒有釋放分頁。

Python腳本實(shí)現(xiàn)內(nèi)存泄漏測試的方法及解決過程

關(guān)于Python腳本實(shí)現(xiàn)內(nèi)存泄漏測試的方法及解決過程問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI