一个Android内存泄漏相关的自动测试脚本

在安卓开发中,我们经常需要对app的性能进行优化,其中就包括解决内存泄漏问题。

在app不大的情况下,我们借助Android Studio的Android Monitor,简单操作app,观察内存情况,就可以找出内存泄漏点。

或者引入开源项目LeakCanary,也可以很快发现内存泄漏点。

当然也可以借助monkey测试,省去了我们操作app的步骤,还可以进入到一些非常规操作,然而monkey测试一般效率较低,耗时较长,这个时候我们就不能傻看着内存曲线了,那剩下的工作就是定时记录内存信息及定时导出hprof内存堆栈文件了。

基于此目的,编写了一个简单脚本,用于自动跑monkey测试及记录内存信息

脚本的逻辑很简单: 插入monkey命令->循环记录内存信息->导出堆栈文件->kill monkey进程

以下是代码(附Gayhub地址):

#! /usr/bin/env python3# -*- coding: utf-8 -*-import os,sys,time,logging# 下列时间单位均为秒# 执行时间exec_time = 15 * 60 * 60   # 10 hours, 可改成60s供测试该脚本# 记录内存间隔时间,exec_time/exec_interval + 1 即为记录内存次数exec_interval = 10         # 10 s# 导出hprof文件间隔dump_interval =  60 * 60   # 1 hour, 可改成30s供测试该脚本time_passed = 0# 打印提示间隔次数,以查看当前进度print_interval = 1packageName = "com.gionee.filemanager"bulid_type = ""# 所有产生文件的输出目录,必须指定且存在OUTPUT_DIR = os.path.join(os.path.expanduser('~'), "test")  # 目录"~/test"logger = logging.getLogger('memoryleak')FILE_LOG = TrueLOG_LEVEL = logging.DEBUGdef init_logger():    logger.setLevel(LOG_LEVEL)    # create formatter    # [%(filename)s:%(lineno)d] 代码位置,暂不配    log_format = logging.Formatter("%(asctime)s %(name)s %(levelname)-8s: %(message)s")    def add_ch():        # create console handler and set level to debug        ch = logging.StreamHandler()        ch.setLevel(LOG_LEVEL)        ch.setFormatter(log_format)        # add handler to logger        logger.addHandler(ch)    def add_fh():        logfile = os.path.join(OUTPUT_DIR, "memoryleak_" + time.strftime('%Y%m%d%H%M%S') + ".log")        fh = logging.FileHandler(logfile)        fh.setLevel(LOG_LEVEL)        fh.setFormatter(log_format)        logger.warning('log will be outputed to console and file:[%s]' % logfile)        logger.addHandler(fh)    add_ch()    if not (OUTPUT_DIR and os.path.isdir(OUTPUT_DIR) and os.access(OUTPUT_DIR, os.W_OK)):        logger.error('OUTPUT_DIR: "%s" not exist or not writable, please check it up, exiting...')        sys.exit(-1)    if FILE_LOG:        add_fh()def start_monkey():    # adb shell monkey -p com.gionee.filemanager --throttle 800 -v -v 300    command = "adb shell monkey -p " + packageName    command += " --ignore-crashes"    command += " --ignore-timeouts"    command += " --ignore-security-exceptions"    command += " --ignore-native-crashes"    command += " --monitor-native-crashes"    command += " --throttle 800"    command += " -v -v 1000000"    command += " > " + os.path.join(OUTPUT_DIR, "monkeytest.log")    logger.info("插入monkey命令:" + command)    os.popen(command)def record_memory():    global time_passed    if "eng" in bulid_type:        memfile = os.path.join(OUTPUT_DIR, 'procrank.txt')        # 第一次执行命令        command = command1 = 'adb shell procrank | grep "' + packageName + '\|cmdline" > ' + memfile        # 后续执行命令        commandOther = 'adb shell procrank | grep ' + packageName + ' >> ' + memfile    else:        memfile = os.path.join(OUTPUT_DIR, 'meminfo.txt')        command = command1 = 'adb shell dumpsys meminfo ' + packageName + \            ' | grep "Dalvik Heap" -A 14 -B 4 | grep -i "Private\|Total\|--" > ' + memfile        commandOther = 'adb shell dumpsys meminfo ' + packageName + ' | grep TOTAL -m 1 >> ' + memfile    exec_count = exec_time // exec_interval + 1    logger.info("开始记录内存信息,待记录次数:" + str(exec_count))    for i in range(exec_count):        os.popen(command)   #运行命令        # 执行初始命令后切换为后续命令        if i == 0:            command = commandOther        if i % print_interval == 0:            logger.info("当前记录内存次数: " + str(i))        if (time_passed) % dump_interval == 0:            logger.info("当前dump hprof次数: " + str(time_passed // dump_interval))            dumpheap(str(time_passed // dump_interval))        time_passed += exec_interval        time.sleep(exec_interval)   #休息n秒,再进入下一个循环,也就是每隔n秒打印一次procrank的信息    logger.info("记录内存信息结束")    #运行完毕的标志def dumpheap(name):    command = "adb shell am dumpheap " + packageName + " /data/local/tmp/hprofs/"    command += "count"+ name + ".hprof"    os.popen(command)def stop_monkey():    # adb shell kill -9 `adb shell ps | grep com.android.commands.monkey | awk '{print $2}'`    pid = os.popen("adb shell ps | grep monkey | awk '{print $2}'").read()    pid = pid.replace("\n","")    logger.info("monkey pid is: " + pid + ", kill it")    os.system("adb shell kill " + pid)def copyheap():    logger.info("开始导出hprof文件...")    os.system("adb pull /data/local/tmp/hprofs/ " + OUTPUT_DIR)    os.system("adb shell rm -r /data/local/tmp/hprofs")    logger.info("导出hprof文件结束")# Ensure in eng release or seleted app has flag android:debuggable="true"def check_env():    global bulid_type    bulid_type_prop = os.popen("adb shell getprop | grep ro.build.type").read()    if "eng" in bulid_type_prop:        bulid_type = "eng"        logger.info("当前rom版本: eng")    else:        bulid_type = "user"        logger.info("当前rom版本: user")        package_flags = os.popen("adb shell dumpsys package " + packageName +" | grep pkgFlags=").read()        if "DEBUGGABLE" not in package_flags:            logger.info("当前为user版本且应用没有设置android:debuggable=\"true\", 无法导出内存信息, 请确认环境。")            sys.exit(-1)    # 清空及建立hprof文件存放目录    if 'hprofs' in os.popen('adb shell ls /data/local/tmp').read():        logger.info('在设备中清除上次运行产生的临时目录"/data/local/tmp/hprofs"...')        os.system("adb shell rm -r /data/local/tmp/hprofs")    logger.info('在设备中新建临时目录"/data/local/tmp/hprofs"...')    os.system("adb shell mkdir -p /data/local/tmp/hprofs")def main():    init_logger()    check_env()    start_monkey()    # 循环进行,程序主体    record_memory()    stop_monkey()    copyheap()if __name__ == '__main__':    main()

更多相关文章

  1. Android(安卓)Studio & ADT 快捷键配置文件所在目录,自定义后可导
  2. 【android测试】值得学习的android测试知识连接
  3. Android(安卓)获取内存信息
  4. 怎么去掉联系人、通话记录、拨号列表界面中的电话号码中间的空格
  5. 理解Android中垃圾回收日志信息
  6. android 设置bitmap 设置图片的大小
  7. Android:计算剩余内存
  8. 如何避免android上的进程的内存限制
  9. android 内存缓冲机制:MemoryCache

随机推荐

  1. Android(安卓)游戏与应用开发最佳学习路
  2. android 使用gdb调试的方式
  3. Android(安卓)软键盘问题总结
  4. Android(安卓)获取网络时间
  5. [置顶] Android4.2.2自增物理按键(framewo
  6. 关于cocos2dx的eclipse的"serializing cd
  7. PullToRefresh的简单使用
  8. android使用全局变量的两种方法
  9. android之实现底部TabHost
  10. Android文件浏览器的开发