性能測試中,內存是一個不可或缺的方面。好比說在跑 Monkey 的過程當中,如何準確持續的獲取到內存數據就顯得尤其重要。shell
今天分享一個腳本,能夠在給定時間內持續監控內存,最後輸出成一份 CSV 文件,經過 Excel 的插入圖表功能能夠造成一副內存走勢圖。數組
腳本中最關鍵的兩個步驟以下,其他看代碼吧(註釋很詳細):bash
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"日誌