性能测试中,内存是一个不可或缺的方面。比如说在跑 Monkey 的过程中,如何准确持续的获取到内存数据就显得尤为重要。

今天分享一个脚本,可以在给定时间内持续监控内存,最后输出成一份 CSV 文件,通过 Excel 的插入图表功能可以形成一副内存走势图。

脚本中最关键的两个步骤如下,其余看代码吧(注释很详细):

  1. 通过 adb 命令获取内存文件
  2. 通过 Python 脚本解析内存文件,取出其中的 "TOTAL" 值

run.sh

  1. #!/usr/bin/env bash
  2. # Description: 获取内存TOTAL值
  3. # How to use: sh +x run.sh <package_name> <time>
  4. # 新建输出文件夹
  5. function init_data() {
  6. if [[ ! -d ${OUTPUT} ]];then
  7. mkdir -p ${OUTPUT}
  8. fi
  9. touch ${MEMINFO_FILE}
  10. }
  11. # 将日期追加入MEMINFO_FILE
  12. # 将内存信息追加入MEMINFO_FILE
  13. function dump_memory_info() {
  14. echo "TIME FLAG:" `date "+%Y-%m-%d %H:%M:%S"` >> ${1}
  15. adb shell dumpsys meminfo ${2} >> ${1}
  16. }
  17. # 每隔一分钟拉取一次内存信息
  18. function start_monitor() {
  19. for((i=1;i<=${1};i++));
  20. do
  21. dump_memory_info ${2} ${3}
  22. sleep 60
  23. done
  24. }
  25. # 处理"TOTAL:"格式的内存文件
  26. # 调用report脚本,传入参数MEMINFO_FILE
  27. # 将logs/csv/t_u.csv文件拷贝并重命名为MEMINFO_CSV_FILE
  28. # 删除"logs"文件夹,减少硬盘空间占用
  29. function report_with_colon() {
  30. sh +x report.sh ${1}
  31. cp -p logs/csv/t_u.csv ${2}
  32. rm -r logs
  33. }
  34. # 处理"TOTAL"格式的内存文件
  35. function report_without_colon() {
  36. sh +x report_no_colon.sh ${1}
  37. cp -p logs/csv/t_u.csv ${2}
  38. rm -r logs
  39. }
  40. # 调用report脚本,输出csv文件
  41. function report_memory_info() {
  42. TOTAL_TIME=$(cat ${1} | grep "TOTAL:" -c)
  43. if [[ ${TOTAL_TIME} != 0 ]]; then
  44. report_with_colon ${1} ${2}
  45. else
  46. report_without_colon ${1} ${2}
  47. fi
  48. }
  49. # 运行脚本时传入的第一个参数:包名
  50. PACKAGE_NAME=$1
  51. # 第二个参数:运行时间(分钟)
  52. TIME=$2
  53. # 绝对路径
  54. WORKSPACE=`pwd`
  55. # 输出文件夹
  56. OUTPUT=${WORKSPACE}/output_memory
  57. # 内存文件
  58. MEMINFO_FILE=${OUTPUT}/meminfo.txt
  59. MEMINFO_CSV_FILE=${OUTPUT}/meminfo.csv
  60. # 删除"output_memory",避免数据混淆
  61. if [[ -d "output_memory" ]]; then
  62. rm -r output_memory
  63. fi
  64. # 开始调用方法
  65. init_data
  66. start_monitor ${TIME} ${MEMINFO_FILE} ${PACKAGE_NAME}
  67. report_memory_info ${MEMINFO_FILE} ${MEMINFO_CSV_FILE}

report.sh

  1. #!/usr/bin/env bash
  2. # Description: 提取meminfo.txt中的TOTAl值并输出到CSV文件(适用于'TOTAL:'格式的内存文件)
  3. # 根据dumpsys meminfo后的文件中不同的标签, 设定文件名
  4. # 因为标签诸如'.ttf mmap'等, 中间有空格, 不适合直接做文件名
  5. getMemFileName()
  6. {
  7. local tag=$1
  8. case ${tag} in
  9. "Native")
  10. fileName="native_meminfo.txt"
  11. ;;
  12. "Dalvik")
  13. fileName="dalvik_meminfo.txt"
  14. ;;
  15. "Cursor")
  16. fileName="cursor_meminfo.txt"
  17. ;;
  18. "Other dev")
  19. fileName="otherdev_meminfo.txt"
  20. ;;
  21. "Ashmem")
  22. fileName="ashmem_meminfo.txt"
  23. ;;
  24. ".so mmap")
  25. fileName="so_meminfo.txt"
  26. ;;
  27. ".jar mmap")
  28. fileName="jar_meminfo.txt"
  29. ;;
  30. ".apk mmap")
  31. fileName="apk_meminfo.txt"
  32. ;;
  33. ".ttf mmap")
  34. fileName="ttf_meminfo.txt"
  35. ;;
  36. ".dex mmap")
  37. fileName="dex_meminfo.txt"
  38. ;;
  39. "Other mmap")
  40. fileName="other_meminfo.txt"
  41. ;;
  42. "Unknown")
  43. fileName="unknown_meminfo.txt"
  44. ;;
  45. "TOTAL:")
  46. fileName="total_meminfo.txt"
  47. ;;
  48. *)
  49. ;;
  50. esac
  51. echo ${fileName}
  52. }
  53. # 解析MonkeyTest完成后的meminfo.txt
  54. # 按列读取, 1, 2, 3, 4, 5列分别对应:Pss, SharedDirty, PrivateDirty, HeapSize, HeapFree
  55. splitMeminfo()
  56. {
  57. local fileName=$1
  58. # 删除VALUE字符串中以分隔符“.”匹配的右边字符,保留左边字符。${VALUE%.*}
  59. local folderName=${fileName%.*}
  60. mkdir logs/${folderName}
  61. awk '{print $1}' logs/${fileName} > logs/${folderName}/Pss
  62. awk '{print $2}' logs/${fileName} > logs/${folderName}/SharedDirty
  63. awk '{print $3}' logs/${fileName} > logs/${folderName}/PrivateDirty
  64. awk '{print $4}' logs/${fileName} > logs/${folderName}/HeapSize
  65. awk '{print $5}' logs/${fileName} > logs/${folderName}/HeapFree
  66. }
  67. # 将MonkeyTest完成后的meminfo.txt中的tag去掉
  68. # 如: PSS 234 222 333 555 0 -> 234 222 333 555 0
  69. # 原因:统一成5列数据, 方便'splitMeminfo'按列读取数据
  70. removeTag()
  71. {
  72. local fileName=$1
  73. local tag=$2
  74. case ${tag} in
  75. "Native")
  76. # 删除第一列,然后输出到logs/native.txt
  77. awk '{$1="";print}' ${fileName} > logs/native.txt
  78. splitMeminfo native.txt
  79. ;;
  80. "Dalvik")
  81. awk '{$1="";print}' ${fileName} > logs/dalvik.txt
  82. splitMeminfo dalvik.txt
  83. ;;
  84. "Cursor")
  85. awk '{$1="";print}' ${fileName} > logs/cursor.txt
  86. splitMeminfo cursor.txt
  87. ;;
  88. "Other dev")
  89. awk '{$1=""; $2="";print}' ${fileName} > logs/otherdev.txt
  90. splitMeminfo otherdev.txt
  91. ;;
  92. "Ashmem")
  93. awk '{$1="";print}' ${fileName} > logs/ashmem.txt
  94. splitMeminfo ashmem.txt
  95. ;;
  96. ".so mmap")
  97. awk '{$1=""; $2="";print}' ${fileName} > logs/sommap.txt
  98. splitMeminfo sommap.txt
  99. ;;
  100. ".jar mmap")
  101. awk '{$1=""; $2="";print}' ${fileName} > logs/jarmmap.txt
  102. splitMeminfo jarmmap.txt
  103. ;;
  104. ".apk mmap")
  105. awk '{$1=""; $2="";print}' ${fileName} > logs/apkmmap.txt
  106. splitMeminfo apkmmap.txt
  107. ;;
  108. ".ttf mmap")
  109. awk '{$1=""; $2="";print}' ${fileName} > logs/ttfmmap.txt
  110. splitMeminfo ttfmmap.txt
  111. ;;
  112. ".dex mmap")
  113. awk '{$1="";$2="";print}' ${fileName} > logs/dexmmap.txt
  114. splitMeminfo dexmmap.txt
  115. ;;
  116. "Other mmap")
  117. awk '{$1="";$2="";print}' ${fileName} > logs/othermmap.txt
  118. splitMeminfo othermmap.txt
  119. ;;
  120. "Unknown")
  121. awk '{$1="";print}' ${fileName} > logs/unknown.txt
  122. splitMeminfo unknown.txt
  123. ;;
  124. "TOTAL:")
  125. awk '{$1="";print}' ${fileName} > logs/total.txt
  126. splitMeminfo total.txt
  127. ;;
  128. *)
  129. ;;
  130. esac
  131. }
  132. # 生成.csv文件, 方便网页中用js读取, 并传值給HighCharts
  133. # 将splitMeminfo中生成的多个文件, 列转行
  134. # 格式:Pss, 234,333,444,556,444......
  135. getCSVFile()
  136. {
  137. mkdir logs/csv
  138. local meminfo_Files=("Pss" "SharedDirty" "PrivateDirty" "HeapSize" "HeapFree")
  139. # 数组长度
  140. local count=${#meminfo_Files[@]}
  141. for((i=0;i<$count;i++))
  142. do
  143. local item=${meminfo_Files[$i]}
  144. echo "Categories" >> logs/csv/${item}.csv
  145. for data in `find ./ -name "${item}"`
  146. do
  147. # 删除VALUE字符串中以分隔符“.”匹配的右边字符,保留左边字符。${VALUE%.*}
  148. seriesName=${data%/*}
  149. # 删除VALUE字符串中以分隔符“.”匹配的左边字符,保留右边字符。${VALUE##*.}
  150. seriesName=${seriesName##*/}
  151. csvline=${seriesName}
  152. for line in `cat ${data}`
  153. do
  154. csvline=${csvline},${line}
  155. done
  156. echo ${csvline} >> logs/csv/${item}.csv
  157. sed -i '' "s/,//g" logs/csv/${item}.csv
  158. done
  159. done
  160. }
  161. # 第一列的所有参数
  162. MEMINFO_ARGS=("Native" "Dalvik" "Cursor" "Other dev" "Ashmem" ".so mmap" ".jar mmap" ".apk mmap" ".ttf mmap" ".dex mmap" "Other mmap" "Unknown" "TOTAL:")
  163. # 从run.sh传入的参数
  164. MEMINFO_File=${1}
  165. # MEMINFO_ARGS的长度(length)
  166. count=${#MEMINFO_ARGS[@]}
  167. # 创建logs/, 用以存放日志
  168. mkdir logs
  169. # 解析日志
  170. for((i=0;i<$count;i++));
  171. do
  172. # 调用getMemFileName方法,传入参数MEMINFO_ARGS,返回文件名
  173. fileName=`getMemFileName "${MEMINFO_ARGS[$i]}"`
  174. # 输出包含${MEMINFO_ARGS[$i]}的行
  175. awk /"${MEMINFO_ARGS[$i]}"/'{print}' ${MEMINFO_File} > logs/${fileName}
  176. removeTag logs/${fileName} "${MEMINFO_ARGS[$i]}"
  177. done
  178. # 将分析过的日志转换成csv文件
  179. getCSVFile
  180. # 将时间取出来放到logs/time文件中
  181. grep 'TIME FLAG:' ${MEMINFO_File} > logs/logtime
  182. cat logs/logtime | while read line
  183. do
  184. echo ${line#*:} >> logs/time
  185. done
  186. # 处理完所有行,输出行数
  187. line_count=`awk 'END{print NR}' logs/total/Pss`
  188. # 提取时间和TOTAL值,输出到t_u.csv文件
  189. echo "Time,TOTAL" > logs/csv/t_u.csv
  190. for ((j=1;j<=${line_count};j++));
  191. do
  192. total_mem=`tail -n ${j} logs/total/Pss | head -n 1`
  193. time_mem=`tail -n ${j} logs/time | head -n 1`
  194. echo "${time_mem},${total_mem}" >> logs/csv/t_u_bk.csv
  195. done
  196. line_count=`awk 'END{print NR}' logs/csv/t_u_bk.csv`
  197. for ((k=1;k<=${line_count};k++));
  198. do
  199. total_line=`tail -n ${k} logs/csv/t_u_bk.csv | head -n 1`
  200. echo "$total_line" >> logs/csv/t_u.csv
  201. done

注意:

部分手机获取到的内存文件会同时包含 "TOTAL" 和 "TOTAL:" 字段,通过替换 report.sh 脚本中的 "TOTAL" 进行区分即可


欢迎关注微信公众号"测试开发Stack"

Shell脚本 | 性能测试之内存的更多相关文章

  1. Shell脚本 | 性能测试之启动流量

    安卓应用的流量统计有多种方式,点击「阅读原文」可以看到一篇别人写的文章,关于安卓流量数据的获取,写的挺全的,列举了几种不同方式的优劣.(见文末参考链接) 今天我要分享的是通过脚本一键获取应用的启动流量 ...

  2. Shell脚本 | 性能测试之启动时间

    安卓应用的性能测试,通常包括六个指标:启动时间.内存.CPU.耗电量.流量.流畅度. 除了耗电量,其他五个指标的数据在我们团队中已经可以通过运行脚本的方式获取到. 今天给大家分享下启动时间的脚本吧- ...

  3. Shell脚本 | 性能测试之CPU占有率

    Android 是一个基于 Linux 内核的移动操作系统,Linux 的 CPU 占有率的计算方式也可以应用到 Android App 上. 今天分享的这个脚本的功能,是在多核情况下计算进程的 CP ...

  4. 在CentOS6.9上Shell脚本定时释放内存cache

    一.写Shell脚本 mkdir -p /var/script/ vim /var/script/freemem.sh 写入以下Shell脚本: #!/bin/bash # 当前已使用的内存大小 us ...

  5. linux实现shell脚本监控磁盘内存达到阈值时清理catalina.out日志

    想在服务器上写一个shell脚本,在磁盘使用率达到80%时,自动清理掉一些没有用的日志文件,根据这个想法,在生产环境上写了一个以下脚本,按照该流程,可实现在linux环境做一个定时任务来执行shell ...

  6. Android 性能测试之内存 --- 追加腾讯性能案例,安卓抓取性能扫盲帖

    内存测试: 思路 目前做的是酒店APP,另下载安装几个个第三方酒店的APP以方便对比(相当于可以做竞品测试) 数据的获取来源是ADB底层命令,而且最好是不需要root权限,因为很多手机root很麻烦或 ...

  7. shell脚本监控cpu/内存使用率 转

    该脚本检测cpu和内存的使用情况,只需要调整memorySetting.cpuSetting.userEmail要发邮件报警的email地址即可 如果没有配置发邮件参数的哥们,已配置了的,直接飞到代码 ...

  8. 【shell脚本】检查内存使用情况===chenkMen.sh

    检查内存使用情况,当内存可使用等于100时,释放缓存 [root@localhost thy]# cat checkMem.sh #!/bin/bash #防止内存溢出问题 used=`free -m ...

  9. shell脚本编写监控内存并发送邮件

    1.准备发送邮件的工具: #!/usr/bin/python# -*- coding: UTF-8 -*-import sysimport smtplibimport email.mime.multi ...

随机推荐

  1. 机器学习(二)--------单变量线性回归(Linear Regression with One Variable)

    面积与房价 训练集 (Training Set) Size       Price 2104       460 852         178 ...... m代表训练集中实例的数量x代表输入变量 ...

  2. jenkins shell部署

    jenkins执行shell脚本 jenkins执行shell 上一篇说的是jenkins+svn+maven把war包自动部署到Tomcat,这篇是从SVN上拉取代码maven生成jar文件,并且拷 ...

  3. 特殊篮子问题——C语言暴力破解

    You are given N baskets of gold coins. The baskets are numbered from 1 to N. In all except one of th ...

  4. 探索未知种族之osg类生物---呼吸分解之更新循环二

    _scene->updateSceneGraph(*_updateVisitor); 我们用了前面4节才刚刚算是完成对DatabasePager::DatabaseThread::run()函数 ...

  5. java特殊字符分隔符

    点,string.split("[.]") . 竖线, string.split("\\|"). 星号, string.split("\\*" ...

  6. [Algorithm]Algorithm章1 排序算法

    1.冒泡排序-相邻交换 (1)算法描述 冒泡排序是一种简单的排序算法.它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来.走访数列的工作是重复地进行直到没有再需要交换,也 ...

  7. boost asio 学习(一)io_service的基础

    原文  http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting- started-with-boostasio/ 编译环境 b ...

  8. java多线程系列12 ConcurrentHashMap CopyOnWriteArrayList 简介

    我们知道 ,hashmap 和 arraylist 是线程不安全的 在多线程环境下有数据安全问题, 当然 我们可以通过Collections的一些方法把他们变成线程安全的, Collections.s ...

  9. 【APP测试(Android)】--客户端数据库

  10. RQNOJ 3 Jam的计数法

    一道模拟题,用的vector比用链表要方便很多,毕竟不需要自己写,因为是递增的,所以每一次你都要去检查最后一位加1之后有没有越界,如果没越界你就可以把他当前的字符删掉替换成他下一位的字符就可以了,如果 ...