一个获取 Android 某个 package 的 CPU 占用率,内存占用率,系统 IOPS .. 程序

前言

本文是作者结合自己的开发经验,参考 Linux 官方文档,和其他的一些 Android 文档编辑而成,但因开发此 脚本的时候并没有记录参考资料,如果在此文没有指出出去的地方,在此深表歉意。

我认为,代码是最能说明功用的手段,除非代码写得太复杂,或者太乱。因此我不会再额外说明脚本中处理流程。 但此脚本中几个注意的点,我会事先说明。

注意

首先该脚本依赖 adb, 因为作者本意是通过此脚本去获取 Android APK 的各项参数。 而 Android 系统一般会自带 adb, 而且更具系统的不同您可能要处理权限方面的问题。 但是该脚本可以轻松的换成 ssh 去采集也是可以的。而为什么采用 Shell 而不是用 Python, Node.js, Go 编写采集程序,主要是因为这样开发效率更高,但是在某些 Windows 机器上 shell 的执行效率比较底下(比如说跑在 4 代或者 5 代 i3 win10),此时最好换成 ssh 和更高效的语言去实现。,但基本采集数据的思想都是相同的。

因为效率原因后面将该脚本换成了从系统内部执行,adb 部分删除了。

脚本采集的参数有 CPU 占用率,内存消耗,整体 IO 的消耗,线程数。 下面我会具体的说明各个参数是如何采集的。

另外一般 Linux 里面表示 CPU 占用率表示的是单颗 CPU 的占用率,如果硬件有超过一颗 CPU (虚拟的也算)那么总的 CPU 占用率会超过 100%, 比如说 4 核的 CPU 占用率是 400%。 而此脚本中计算的 CPU 为每秒占总 CPU 的占比,因此恒定为 100%。但如果想要换成通用的计算标准,在最后不去平均核数即可。

为了美化打印,这里采用了 3/4 bit 的颜色模式,但是可能在某些终端不支持,从而会打印出一些多余的乱码, 此时直接在前面的函数中去掉颜色控制方面的代码即可(换成其他的前导码或者 256 色模式也可以试一试)。

脚本最后会在目标路径生成一个 csv 文件。该脚本还有一个配套的数据统计脚本,脚本时间轴还有些问题先不上传了(后续更新)。

如果想了解更多关于 Proc 相关的信息,请在 Linux 系统下执行 man 5 proc

如果想了解 /proc/diskstate 相关的信息,请访问 Linux 内核工程中 Documentation/iostats.txt 文档。

主菜
    #!/system/bin/sh

    # set -e
    # ------------------------------------------------------------------------------
    # Author: mojies
    # 使用说明:
    #   1. 先将该脚本上传到测试设备
    #   2. 给改脚本家可执行权限 chmod +x ./SamplingSysData-xxxxxxxx.sh
    #   3. 该脚本采集五个数据
    #       1. date 采集时的系统时间
    #       2. flash /data/data/${package} 消耗 Flash 大小
    #       3. mem 应用启动之后消耗的系统内存
    #       4. cpu 应用启动之后在监测点监测到的数学平均 CPU 占用率,单位 1/1000
    #       5. iow 系统造成的 iops
    #   4. 使用时请运行以下命令采集指定包的运行时数据
    #       ./SamplingSysData-xxxxxxxx.sh ${package}
    #       如:
    #       ./SamplingSysData-xxxxxxxx.sh com.xxx.xxx
    #   5. 生成的数据在 /data/SamplingSysDaya/${package} 目录下用如下命令将采集数据
    #      打印到终端
    #       ls /data/SamplingSysDaya/${package} | while read line; do echo "$line"; cat /data/SamplingSysDaya/${package}/${line}; echo ""; done
    #
    #
    # ------------------------------------------------------------------------------
    # -----------------------------------------------------------------------------
    echo_red(){
        echo -e "\e[1;31m$1\e[0m"
    }
    echo_yellow(){
        echo -e "\e[1;33m$1\e[0m"
    }
    echo_green(){
        echo -e "\e[1;32m$1\e[0m"
    }
    echo_blue(){
        echo -e "\e[1;34m$1\e[0m"
    }
    echo_purple(){
        echo -e "\e[1;35m$1\e[0m"
    }
    echo_cyan(){
        echo -e "\e[1;36m$1\e[0m"
    }
    
    
    function show_help_info(){
        echo_green "------------------------------------------------------------"
        echo_green "Option:"
        echo_green "    --pkg | -p                  包名"
        echo_green "    --duration | -d             采样频率"
        echo_green "    --verbose                   是否打印详细信息( 不需要指定参数 )"
        echo_green "    --result_dir                指定保存采样数据目录"
        echo_green "ex:"
        echo_green "    ./deploy.sh -p com.xxx.xxxx -d 1"
        echo_green "------------------------------------------------------------"
        exit -1
    }
    # -----------------------------------------------------------------------------
    export PARAM_PACKAGE_NAME=""
    export PARAM_DURATION=""
    export PARAM_VERBOSE=false
    export PARAM_RESULT_DIR=false
    
    if [ $# -eq 0 ]; then
        show_help_info
        exit
    else
        PARAM=("$@")
        I=0
        while true; do
            case ${PARAM[$I]} in
            --pkg|-p)
                I=`expr $I + 1`
                PARAM_PACKAGE_NAME=${PARAM[$I]}
                ;;
            --duration|-d)
                I=`expr $I + 1`
                PARAM_DURATION=${PARAM[$I]}
                ;;
            --verbose)
                PARAM_VERBOSE=true
                ;;
            --result_dir)
                I=`expr $I + 1`
                PARAM_RESULT_DIR=${PARAM[$I]}
                ;;
            *)
                show_help_info
                exit 1
                ;;
            esac
            I=`expr $I + 1`
            if [ $I -eq $# ]; then
                break;
            fi
        done
    fi
    
    #设置包名
    if [ "$PARAM_PACKAGE_NAME" = "" ]; then
        echo_red "Please specify the package name"
        echo_red "ex: ./SamplingSysData-* com.xxx.xxx"
        exit 1
    fi
    if [ "$PARAM_DURATION" = "" ]; then
        PARAM_DURATION=5
        echo_red "The default duration: ${PARAM_DURATION}"
    fi
    
    echo_v(){
        if [ ${PARAM_VERBOSE} = true ]; then
            echo_blue "$@"
        fi
    }
    
    #设置U盘路径
    export RESULT_PATH=""
    if [ "${PARAM_RESULT_DIR}" = "" ]; then
        RESULT_PATH="/data/SamplingSysDaya/$PARAM_PACKAGE_NAME";
    else
        RESULT_PATH="${PARAM_RESULT_DIR}/$PARAM_PACKAGE_NAME"
    fi
    
    res="result.$(date +%Y%m%d-%H%M%S).csv"
    
    #设置 CPU 采样周期(s)
    sampling_CPU_interval=5
    
    a=0
    #设置总次数
    total=100000
    
    if [ ! -d "$RESULT_PATH" ]; 
    then
        mkdir -p $RESULT_PATH
        if [ $? -ne 0 ]; then
            echo_red "You specifyc error path or you didn't have permission"
            exit 1
        fi
    fi ;    
    # -----------------------------------------------------------------------------
    #获取CPU个数
    processor=$(cat /proc/cpuinfo| grep "processor"|wc -l)
    
    # -----------------------------------------------------------------------------
    # 获取 PID
    export PROGRESS_PID=""
    
    get_package_pid(){
        PID=$(ps | grep "${1}" | sed 's/ [ ]*/ /g' - | cut -d' ' -f2 | xargs echo)
        if [ "${PID}" = "" ]; then
            PID=$(ps -A | grep "${1}" | sed 's/ [ ]*/ /g' - | cut -d' ' -f2 | xargs echo)
            if [ "${PID}" = "" ]; then
                exit 1
            else
                echo ${PID}
            fi
        else
            echo ${PID}
        fi
    }
    
    is_process_exist(){
        safty_param="$@ end"
        I=0
        while true;
        do
            let "I += 1"
            PID=$(echo ${safty_param}|cut -d' ' -f${I})
            if [ ${PID} == end ]; then break; fi
    
            if [ -f /proc/${PID}/stat ]; then
                continue;
            else
                return 1
            fi
        done
        return 0
    }
    
    get_pids_threadNumbers(){
        safty_param="$@ end"
        sum=0;
        I=0
        while true;
        do
            let "I += 1"
            PID=$(echo ${safty_param}|cut -d' ' -f${I})
            if [ ${PID} == end ]; then break; fi
    
            nums=$(ls /proc/${PID}/task | wc -l)
            let "sum+=${nums}"
        done
        echo ${sum}
    }
    
    PROGRESS_PID=$(get_package_pid ${PARAM_PACKAGE_NAME})
    
    # -----------------------------------------------------------------------------
    function get_disk_iow(){
        export DISK_NAME;
        if [ "$1" = "" ];then
            DISK_NAME=mmcblk0
        else
            DISK_NAME=${1}
        fi
    
        export diskstate=$(cat /proc/diskstats | grep " ${DISK_NAME} " | sed 's/[ ][ ]*/ /g' -)
        rt=$(echo ${diskstate}|cut -d' ' -f8)
        wt=$(echo ${diskstate}|cut -d' ' -f12)
        iot=$(echo ${diskstate}|cut -d' ' -f14)
        let "sum=${rt}+${wt}+${iot}"
        echo ${sum}
    }
    DISK_SUM_COST_TIME=$(get_disk_iow)
    
    # -----------------------------------------------------------------------------
    function get_pid_ticket(){
        safty_param="$@ end"
        sum=0;
        I=0
        while true;
        do
            let "I += 1"
            PID=$(echo ${safty_param}|cut -d' ' -f${I})
            if [ "$PID" == "end" ]; then
                echo ${sum}
                return
            fi
            stats=$(cat /proc/${PID}/stat)
            utime=$(echo ${stats}|cut -d' ' -f14)
            stime=$(echo ${stats}|cut -d' ' -f15)
            let "sum+=${stime}+${utime}"
        done
    }
    
    CPU_SUM_TIKI=$(get_pid_ticket ${PROGRESS_PID})
    echo $CPU_SUM_TIKI
    
    # -----------------------------------------------------------------------------
    export t1=0
    export t2=0
    export t3=0
    export t4=0
    export t5=0
    
    export DATE=0
    export FLASH_USAGE=0
    export MEM_USAGE=0
    export CPU_USAGE=0
    export IO_USAGE=0
    
    # -----------------------------------------------------------------------------
    rm -rf $RESULT_PATH/$res
    echo "date,flash(KB),mem(KB),cpu(‰),iorw(ms),thread" >> $RESULT_PATH/$res
    echo "date,flash(KB),mem(KB),cpu(‰),iorw(ms),thread"
    
    #生成应用占用flash数据
    while (($a < $total));do 
    
        if [ "${PROGRESS_PID}" = "" ]; then
    
            is_process_exist ${PROGRESS_PID}
    
            if [ $? -eq 1 ]; then
                sleep 1
    
                FLASH_USAGE=0
                MEM_USAGE=0
                CPU_USAGE=0
                IO_USAGE=0
                PROGRESS_PID=$(get_package_pid ${PARAM_PACKAGE_NAME})
                continue;
    
            fi
        fi
        # ------------------------------------------------------------
        DATE=$(date "+%H:%M:%S")
    
        # ------------------------------------------------------------
        let "t4 += 1";
        if [ $t4 -ge ${PARAM_DURATION} ]; then
            t4=0;
    
            # ------------------------------------------------------------
            # Flash Usage
            # du -sh /data/data/$PARAM_PACKAGE_NAME | sed 's/ [ ]*/ /g' - | cut -d" " -f1 >> $RESULT_PATH/flash.csv;
            FLASH_USAGE=$(du -sh /data/data/${PARAM_PACKAGE_NAME} | sed 's/[ \t][ \t]*/ /g' - | cut -d" " -f1)
            echo_v "FLASH_USAGE: ${FLASH_USAGE}"
    
            # ------------------------------------------------------------
            # mem usage
            # 输出Meminfo大小 单位:M
            # echo `dumpsys meminfo ${PARAM_PACKAGE_NAME} | grep "TOTAL SWAP" | sed 's/ [ ]*/ /g' - | cut -d' ' -f3` >>$RESULT_PATH/ddr.csv;
            MEM_USAGE=$(dumpsys meminfo ${PARAM_PACKAGE_NAME} | grep "TOTAL SWAP" | sed 's/ [ ]*/ /g' - | cut -d' ' -f3)
            echo_v "MEM_USAGE: ${MEM_USAGE}"
    
            # ------------------------------------------------------------
            # io usage
            NOW_DISK_SUM=$(get_disk_iow)
            let "diff=${NOW_DISK_SUM}-${DISK_SUM_COST_TIME}"
            let "diff=${diff}/${PARAM_DURATION}"
            IO_USAGE=${diff}
            DISK_SUM_COST_TIME=${NOW_DISK_SUM}
            echo_v "IO_USAGE: ${IO_USAGE}"
    
            # ------------------------------------------------------------
            # Thread numbs
            # THREAD_NUM=`ls /proc/${PROGRESS_PID}/task | wc -l`
            THREAD_NUM=$(get_pids_threadNumbers ${PROGRESS_PID})
            echo_v "THREAD_NUM: ${THREAD_NUM}"
            # echo "Thread Num: ${THREAD_NUM}"
        fi
    
        # ------------------------------------------------------------
        let "t3 += 1";
        if [ $t3 -ge ${sampling_CPU_interval} ]; then
            t3=0;
    
            tmpsum=$(get_pid_ticket ${PROGRESS_PID})
            if [ $tmpsum -eq 0 ]; then
                PROGRESS_PID=$(ps | grep "${PARAM_PACKAGE_NAME}" | sed 's/ [ ]*/ /g' - | cut -d' ' -f2 | xargs echo)
                CPU_SUM_TIKI=$(get_pid_ticket ${PROGRESS_PID})
            else
                let "diff=${tmpsum}-${CPU_SUM_TIKI}"
                let "diff=${diff}"
                let "diff=${diff}/${processor}"
                CPU_SUM_TIKI=${tmpsum}
    
                CPU_USAGE=${diff}
                echo_v "CPU_USAGE: ${CPU_USAGE}"
            fi
        fi
    
        # ------------------------------------------------------------
        let "t5 += 1";
        if [ $t5 -ge ${PARAM_DURATION} ]; then
            t5=0
            echo "${DATE},${FLASH_USAGE},${MEM_USAGE},${CPU_USAGE},${IO_USAGE},${THREAD_NUM}"
            # echo "DATE: ${DATE}"
            # echo "FLASH_USAGE: ${FLASH_USAGE}"
            # echo "MEM_USAGE: ${MEM_USAGE}"
            # echo "CPU_USAGE: ${CPU_USAGE}"
            # echo "IO_USAGE: ${IO_USAGE}"
            # echo "THREAD_NUM: ${THREAD_NUM}"
    
            echo "${DATE},${FLASH_USAGE},${MEM_USAGE},${CPU_USAGE},${IO_USAGE},${THREAD_NUM}" >> ${RESULT_PATH}/${res}
        fi
    
        sleep 1
    done
原创文章,版权所有,转载请获得作者本人允许并注明出处
我是留白;我是留白;我是留白;(重要的事情说三遍)
posted @ 2020-09-01 15:30  Mojies  阅读(363)  评论(0编辑  收藏  举报