性能测试中,内存是一个不可或缺的方面。比如说在跑 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. spring多线程

    Spring4.x高级话题(二):多线程 一. 点睛 Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程.使用ThreadPoolTaskExecutor可实现一个基于线程池 ...

  2. Ubuntu部署可视化爬虫Portia2.0环境以及入门

    http://www.cnblogs.com/kfpa/p/Portia.html http://brucedone.com/archives/986

  3. 深入理解 requestAnimationFrame

    在Web应用中,实现动画效果的方法比较多,Javascript 中可以通过定时器 setTimeout 来实现,css3 可以使用 transition 和 animation 来实现,html5 中 ...

  4. RNA测序的质量控制

    RNA测序的质量控制 发表评论 3,112 A+ 所属分类:Transcriptomics   收  藏 ENCODE项目向我们揭示,人类基因组中超过70%能得到转录,只不过不会发生在同一个细胞里.为 ...

  5. Python列表详解

    #列表:增,删,改,查.names=['N0','N1','N2',['EX1','EX2'],'N3'] '''#------------------------------------------ ...

  6. jquery 表单校验

    <link type="text/css" href="<%=basepath%>css/form/validate.css" rel=&qu ...

  7. 【Node.js】安装及使用

    Node.js是在Chrome的V8 JavaScript引擎上构建的JavaScript运行时.Node.js使用事件驱动的非阻塞I / O模型,使其轻量且高效.Node.js的软件包生态系统npm ...

  8. Scrum冲刺阶段2

    成员今日完成的任务 人员 任务 何承华 后端设计 陈宇 后端设计 丁培辉 后端设计 温志铭 主页面的设计 杨宇潇 主页面的设计 张主强 服务器构建 成员遇到的问题 人员 问题 何承华 暂无 陈宇 暂无 ...

  9. python发送邮箱

    一.Python发送邮件 import smtplib from email.mime.text import MIMEText def send(email_to,title,content): m ...

  10. More x64 assembler fun-facts–new assembler directives(转载)

    原文地址 The Windows x64 ABI (Application Binary Interface) presents some new challenges for assembly pr ...