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

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

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

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

run.sh

#!/usr/bin/env bash
# Description: 获取内存TOTAL值
# How to use: sh +x run.sh <package_name> <time>
# 新建输出文件夹
function init_data() {
if [[ ! -d ${OUTPUT} ]];then
mkdir -p ${OUTPUT}
fi
touch ${MEMINFO_FILE}
}
# 将日期追加入MEMINFO_FILE
# 将内存信息追加入MEMINFO_FILE
function dump_memory_info() {
echo "TIME FLAG:" `date "+%Y-%m-%d %H:%M:%S"` >> ${1}
adb shell dumpsys meminfo ${2} >> ${1}
}
# 每隔一分钟拉取一次内存信息
function start_monitor() {
for((i=1;i<=${1};i++));
do
dump_memory_info ${2} ${3}
sleep 60
done
}
# 处理"TOTAL:"格式的内存文件
# 调用report脚本,传入参数MEMINFO_FILE
# 将logs/csv/t_u.csv文件拷贝并重命名为MEMINFO_CSV_FILE
# 删除"logs"文件夹,减少硬盘空间占用
function report_with_colon() {
sh +x report.sh ${1}
cp -p logs/csv/t_u.csv ${2}
rm -r logs
}
# 处理"TOTAL"格式的内存文件
function report_without_colon() {
sh +x report_no_colon.sh ${1}
cp -p logs/csv/t_u.csv ${2}
rm -r logs
}
# 调用report脚本,输出csv文件
function report_memory_info() {
TOTAL_TIME=$(cat ${1} | grep "TOTAL:" -c)
if [[ ${TOTAL_TIME} != 0 ]]; then
report_with_colon ${1} ${2}
else
report_without_colon ${1} ${2}
fi
}
# 运行脚本时传入的第一个参数:包名
PACKAGE_NAME=$1
# 第二个参数:运行时间(分钟)
TIME=$2
# 绝对路径
WORKSPACE=`pwd`
# 输出文件夹
OUTPUT=${WORKSPACE}/output_memory
# 内存文件
MEMINFO_FILE=${OUTPUT}/meminfo.txt
MEMINFO_CSV_FILE=${OUTPUT}/meminfo.csv
# 删除"output_memory",避免数据混淆
if [[ -d "output_memory" ]]; then
rm -r output_memory
fi
# 开始调用方法
init_data
start_monitor ${TIME} ${MEMINFO_FILE} ${PACKAGE_NAME}
report_memory_info ${MEMINFO_FILE} ${MEMINFO_CSV_FILE}

report.sh

#!/usr/bin/env bash
# Description: 提取meminfo.txt中的TOTAl值并输出到CSV文件(适用于'TOTAL:'格式的内存文件)
# 根据dumpsys meminfo后的文件中不同的标签, 设定文件名
# 因为标签诸如'.ttf mmap'等, 中间有空格, 不适合直接做文件名
getMemFileName()
{
local tag=$1
case ${tag} in
"Native")
fileName="native_meminfo.txt"
;;
"Dalvik")
fileName="dalvik_meminfo.txt"
;;
"Cursor")
fileName="cursor_meminfo.txt"
;;
"Other dev")
fileName="otherdev_meminfo.txt"
;;
"Ashmem")
fileName="ashmem_meminfo.txt"
;;
".so mmap")
fileName="so_meminfo.txt"
;;
".jar mmap")
fileName="jar_meminfo.txt"
;;
".apk mmap")
fileName="apk_meminfo.txt"
;;
".ttf mmap")
fileName="ttf_meminfo.txt"
;;
".dex mmap")
fileName="dex_meminfo.txt"
;;
"Other mmap")
fileName="other_meminfo.txt"
;;
"Unknown")
fileName="unknown_meminfo.txt"
;;
"TOTAL:")
fileName="total_meminfo.txt"
;;
*)
;;
esac
echo ${fileName}
}
# 解析MonkeyTest完成后的meminfo.txt
# 按列读取, 1, 2, 3, 4, 5列分别对应:Pss, SharedDirty, PrivateDirty, HeapSize, HeapFree
splitMeminfo()
{
local fileName=$1
# 删除VALUE字符串中以分隔符“.”匹配的右边字符,保留左边字符。${VALUE%.*}
local folderName=${fileName%.*}
mkdir logs/${folderName}
awk '{print $1}' logs/${fileName} > logs/${folderName}/Pss
awk '{print $2}' logs/${fileName} > logs/${folderName}/SharedDirty
awk '{print $3}' logs/${fileName} > logs/${folderName}/PrivateDirty
awk '{print $4}' logs/${fileName} > logs/${folderName}/HeapSize
awk '{print $5}' logs/${fileName} > logs/${folderName}/HeapFree
}
# 将MonkeyTest完成后的meminfo.txt中的tag去掉
# 如: PSS 234 222 333 555 0 -> 234 222 333 555 0
# 原因:统一成5列数据, 方便'splitMeminfo'按列读取数据
removeTag()
{
local fileName=$1
local tag=$2 case ${tag} in
"Native")
# 删除第一列,然后输出到logs/native.txt
awk '{$1="";print}' ${fileName} > logs/native.txt
splitMeminfo native.txt
;;
"Dalvik")
awk '{$1="";print}' ${fileName} > logs/dalvik.txt
splitMeminfo dalvik.txt
;;
"Cursor")
awk '{$1="";print}' ${fileName} > logs/cursor.txt
splitMeminfo cursor.txt
;;
"Other dev")
awk '{$1=""; $2="";print}' ${fileName} > logs/otherdev.txt
splitMeminfo otherdev.txt
;;
"Ashmem")
awk '{$1="";print}' ${fileName} > logs/ashmem.txt
splitMeminfo ashmem.txt
;;
".so mmap")
awk '{$1=""; $2="";print}' ${fileName} > logs/sommap.txt
splitMeminfo sommap.txt
;;
".jar mmap")
awk '{$1=""; $2="";print}' ${fileName} > logs/jarmmap.txt
splitMeminfo jarmmap.txt
;;
".apk mmap")
awk '{$1=""; $2="";print}' ${fileName} > logs/apkmmap.txt
splitMeminfo apkmmap.txt
;;
".ttf mmap")
awk '{$1=""; $2="";print}' ${fileName} > logs/ttfmmap.txt
splitMeminfo ttfmmap.txt
;;
".dex mmap")
awk '{$1="";$2="";print}' ${fileName} > logs/dexmmap.txt
splitMeminfo dexmmap.txt
;;
"Other mmap")
awk '{$1="";$2="";print}' ${fileName} > logs/othermmap.txt
splitMeminfo othermmap.txt
;;
"Unknown")
awk '{$1="";print}' ${fileName} > logs/unknown.txt
splitMeminfo unknown.txt
;;
"TOTAL:")
awk '{$1="";print}' ${fileName} > logs/total.txt
splitMeminfo total.txt
;;
*)
;;
esac
}
# 生成.csv文件, 方便网页中用js读取, 并传值給HighCharts
# 将splitMeminfo中生成的多个文件, 列转行
# 格式:Pss, 234,333,444,556,444......
getCSVFile()
{
mkdir logs/csv
local meminfo_Files=("Pss" "SharedDirty" "PrivateDirty" "HeapSize" "HeapFree")
# 数组长度
local count=${#meminfo_Files[@]}
for((i=0;i<$count;i++))
do
local item=${meminfo_Files[$i]}
echo "Categories" >> logs/csv/${item}.csv
for data in `find ./ -name "${item}"`
do
# 删除VALUE字符串中以分隔符“.”匹配的右边字符,保留左边字符。${VALUE%.*}
seriesName=${data%/*}
# 删除VALUE字符串中以分隔符“.”匹配的左边字符,保留右边字符。${VALUE##*.}
seriesName=${seriesName##*/}
csvline=${seriesName}
for line in `cat ${data}`
do
csvline=${csvline},${line}
done
echo ${csvline} >> logs/csv/${item}.csv
sed -i '' "s/,//g" logs/csv/${item}.csv
done
done
}
# 第一列的所有参数
MEMINFO_ARGS=("Native" "Dalvik" "Cursor" "Other dev" "Ashmem" ".so mmap" ".jar mmap" ".apk mmap" ".ttf mmap" ".dex mmap" "Other mmap" "Unknown" "TOTAL:")
# 从run.sh传入的参数
MEMINFO_File=${1}
# MEMINFO_ARGS的长度(length)
count=${#MEMINFO_ARGS[@]}
# 创建logs/, 用以存放日志
mkdir logs
# 解析日志
for((i=0;i<$count;i++));
do
# 调用getMemFileName方法,传入参数MEMINFO_ARGS,返回文件名
fileName=`getMemFileName "${MEMINFO_ARGS[$i]}"`
# 输出包含${MEMINFO_ARGS[$i]}的行
awk /"${MEMINFO_ARGS[$i]}"/'{print}' ${MEMINFO_File} > logs/${fileName}
removeTag logs/${fileName} "${MEMINFO_ARGS[$i]}"
done
# 将分析过的日志转换成csv文件
getCSVFile
# 将时间取出来放到logs/time文件中
grep 'TIME FLAG:' ${MEMINFO_File} > logs/logtime
cat logs/logtime | while read line
do
echo ${line#*:} >> logs/time
done
# 处理完所有行,输出行数
line_count=`awk 'END{print NR}' logs/total/Pss`
# 提取时间和TOTAL值,输出到t_u.csv文件
echo "Time,TOTAL" > logs/csv/t_u.csv
for ((j=1;j<=${line_count};j++));
do
total_mem=`tail -n ${j} logs/total/Pss | head -n 1`
time_mem=`tail -n ${j} logs/time | head -n 1`
echo "${time_mem},${total_mem}" >> logs/csv/t_u_bk.csv
done
line_count=`awk 'END{print NR}' logs/csv/t_u_bk.csv`
for ((k=1;k<=${line_count};k++));
do
total_line=`tail -n ${k} logs/csv/t_u_bk.csv | head -n 1`
echo "$total_line" >> logs/csv/t_u.csv
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. SQL Server 2008 R2 根据WSDL访问WebService

    参考网站:WebService学习整理(一)——客户端三种调用方式整理 自我概括: WebService 通过HTTP通讯,数据以XML格式传输使两个系统进行数据交互 SOAP 是访问协议(注明访问W ...

  2. 如何通过RNA-Seq了解转录本的结构

    [转载]如何通过RNA-Seq了解转录本的结构 已有 1942 次阅读 2014-12-26 15:22 |个人分类:转录组测序|系统分类:科研笔记|关键词:RNA-Seq,转录组测序,转录本结构|  ...

  3. linux就该这么学,第六天了

    今天学了第六天了,主要讲计划任务了,,at,命令,单次有效,一次性的,crontd服务(周期性)计划任务,crontab -e创建,编辑计划任务.crontab -l查看计划任务,crontaab - ...

  4. Linux 只列出目录的方法

    1. ls -d 2. find -type d -maxdepth 1 3. ls -F | grep "/$" 4. ls -l | grep "^d"

  5. windows内核对象管理学习笔记

    目前正在阅读毛老师的<windows内核情景分析>一书对象管理章节,作此笔记. Win内核中是使用对象概念来描述管理内核中使用到的数据结构.此对象(Object)均是由对象头(Object ...

  6. 主键生成策略sequence

    http://blog.csdn.net/shanhuhau/article/details/24978253 表示:如果不写序列名,会走默认的序列 若写,则seq_表名_属性名

  7. The current state of generics in Delphi( 转载)

    The current state of generics in Delphi   To avoid duplication of generated code, the compiler build ...

  8. SQL STUFF函数 拼接字符串 多列 合并成一列 转

    关于和并列的 要这种效果. create table tb(idint, value varchar(10)) insert into tbvalues(1,'aa') insert into tbv ...

  9. java_io

    JAVA IO流(一)参考文章:http://ifeve.com/java-io-network/,并发编程网原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: Java ...

  10. 设置UniDbGrid的整行显示颜色,如果某字段值是我们的控制字段

    设置UniDbGrid的整行显示颜色,如果某字段值是我们的控制字段,使用下列判断设置更快捷一点: procedure TUniForm.UniDBGridDrawColumnCell(Sender: ...