• 1
  • 2
分享
  • 内存泄漏测试方法及其python实现
  • 恬恬圈 2019-08-27 10:05:47 字数 5507 阅读 2080 收藏 2

本文提供了一种轻巧的内存泄漏测试方法及其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)

1.png

进程使用的内存,还有一种分法:私有内存和共享内存。

顾名思义,私有内存即是被该进程独享的内存,共享内存是多个进程共享的内存,一般地,当多个进程依赖相同的链接库时,链接库也会被映射到每一个进程的地址空间里。所以,即使RSS也未真正的反映进程到底占用了系统多少内存资源。

定期通过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有很大嫌疑存在内存泄漏。

2.png

因为想进一步分析内存的变化,所以对比了程序执行前后ntpd的pmap信息。可以看到在结束时,pmap信息里多了更多的大小为4K的分页。测试人员将这一信息提供给开发同学,开发同学据此很快找到了内存泄漏的原因在于NTP记录日志后没有释放分页。

3.png


版权声明:本文出自51Testing会员投稿,51Testing软件测试网及相关内容提供者拥有内容的全部版权,未经明确的书面许可,任何人或单位不得对本网站内容复制、转载或进行镜像,否则将追究法律责任。

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 一、为什么抓包1、从功能测试角度通过抓包查看隐藏字段Web 表单中会有很多隐藏的字段,这些隐藏字段一般都有一些特殊的用途,比如收集用户的数据,预防 CRSF 攻击,防网络爬虫,以及一些其他用途。这些隐藏字段在界面上都看不到,如果想检测这些字段,就必须要使用抓包工具。2、通过抓包工具了解协议内容方便开展接口和性能测试性能测试方面,性能测试其实就是大量模拟用户的请求,所以我们必须要知道请求中的协议内容和特点,才能更好的模拟用户请求,分析协议就需要用到抓包工具;接口测试方面,在接口测试时,虽然我们尽量要求有完善的接口文档。但很多时候接口文档不可能覆盖所有的情况,或者因为文档滞后,在接口测试过程中,还...
            1 1 2719
            分享
          •   不知道大家有没有这样的习惯,每天在下班之后,坐在电脑面前,小憩一会儿,回想下今天的目标,是否还有遗漏,没去完成的,统一进行mark一下,看看企业微信是否还有未回复的短消息。  今天呢主要还是想给大家想分享一下软件测试人员密切接触的一个关键词 ”BUG“;主题是:测试人员如何正确的提交BUG。  分享前给大家分享一个工作中小案例,该场景应该部分测试人员在实际工作中有碰到过。  某天,某办公楼,在项目版本迫切上线的紧张周期下,石某某按照预期测试进度疯狂测试输出成果的一天,发现系统的各类潜在BUG,终于熬到下班时刻,将测试进度按照预期mark一下,同时将缺陷面板BUG清单链接周知在项目群,周知开...
            0 0 1153
            分享
          •        “羊毛党”蕴含着强大势能,危可断独角兽的角,用可创建增长神话。如同“道德经”——道可道,非常道。羊有毛,非常毛。羊毛群体除了巨大的破坏力外,还具有强大的传播势能。了解利弊加以利用,从此以后再也不愁裂变活动没有大量免费种子用户传播啦~       但如果没有系统性了解羊毛党的破坏力,轻易尝试引火烧身,重着被按在地上强撸灰飞烟灭。       羊毛党的攻防本质是成本的较量!       运营有风控的组合拳,羊毛有机刷的工具刀,...
            0 0 1213
            分享
          •   前言  一般在面试的时候,经常会被问到关于什么是cookies、session、token,大多数面试官可以通过这几个概念基本上了解到你对接口请求方面是否了解或者工作中掌握的熟练程度。  看似很小的问题,但是背后却藏着大大的阴谋,今天跟着小编一起来了解下到底什么是cookies、session、token。  http无状态  我们都知道目前的http的协议是超文本传输协议,是属于服务器传世超文本到本地浏览器的传送协议,但是很多都不清楚http的无状态是什么意思。  其实无状态协议就是类似客户端发送一次http请求完成后,客户端再次发送一次http请求后,http协议不清楚第一次发送的和第...
            0 0 1088
            分享
          •   测试课程免费送,点击下方链接填写测试行业调查问卷,提交后即刻获得!链接:http://vote.51testing.com/  软件测试行业供需现状  随着敏捷、DevOps等开发模式的引入以及大数据治理与应用、人工智能机器学习与深度学习的应用的发展、软件交付周期逐渐缩短、技术复杂度不断提升对测试人员质量保障与效率提升等方面提出了越来越高的要求。因此,对人员的要求也是在不断提高的,一方面响应基础功能需求的手工测试人员基本饱和,另一方面懂测试的测试开发面试达标者比例过低。  软件测试行业的发展现状  通过之前对近几年《软件测试行业现状报告》的解读,以及结合对当下软件测试左移与右移思考,总结了...
            1 1 1863
            分享
      • 51testing软件测试圈微信