adb命令获取app端性能参数(Android)

  • 在线正则表达式调试:https://regex101.com/

  • 这里自定义包名和activity,在下方使用:

    • packagename:com.chuanblog91.ui
    • activity:com.chuanblog91.ui/mainActivity
  • 启动app
    1
    2
    3
    def LaunchApp(self):
    cmd = 'adb shell am start -W -n com.chuanblog91.ui'
    content = os.popen(cmd)
  • 停止app进程
    1
    2
    3
    def StopApp(self):
    cmd = 'adb shell am force-stop com.chuanblog91.ui'
    os.popen(cmd)
  • 获取机器识别码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def GetDevices(self):
    cmd = 'adb devices'
    content = os.popen(cmd)
    devicesinfo = content.read()
    devicesregx = r"(\S{1,30})\sdevice"
    devicesmatches = re.finditer(devicesregx, devicesinfo)
    for devicesmatchNum, devicesmatch in enumerate(devicesmatches):
    devicesmatchNum = devicesmatchNum + 1
    self.devicesname = devicesmatch.group(len(devicesmatch.groups()))
    return self.devicesname
  • 获取app的pid
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def CheckAppPid(self):
    cmd = 'adb -s {} shell ps com.chuanblog91.ui'.format(self.devicesname)
    content = os.popen(cmd)
    pidinfo = content.read()
    pidregex = r"\S\d_\S\d{2}\s{4}(\d{1,6})"
    pidmatches = re.finditer(pidregex, pidinfo)
    for pidmatchNum, pidmatch in enumerate(pidmatches):
    pidmatchNum = pidmatchNum + 1
    self.pidnum = pidmatch.group(len(pidmatch.groups()))
    return self.pidnum
  • 获取app的uid

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def CheckAppUid(self):
    cmd = "adb -s {0} shell cat /proc/{1}/status".format(self.devicesname, self.pidnum)
    content = os.popen(cmd)
    uidinfo = content.read()
    uidregex = r"Uid:\s(\d{1,6})"
    uidmatches = re.finditer(uidregex, uidinfo)
    for uidmatchNum, uidmatch in enumerate(uidmatches):
    uidmatchNum = uidmatchNum + 1
    self.uidnum = uidmatch.group(len(uidmatch.groups()))
    return self.uidnum
  • 获取app所占用的内存量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    def CheckAppMeminfo(self):
    """
    PSS – Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
    USS – Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)
    如果没有root权限的Android手机可能获取不到uss;
    """
    cmd = 'adb shell "procrank |grep {}"'.format(self.pidnum)
    content = os.popen(cmd)
    meminfo = content.read()
    # 获取pss内存值
    pssregex = r"(\d{1,9})\S\s{1,3}\S{1,9}\s{2}com.chuanblog91.ui"
    pssmatches = re.finditer(pssregex, meminfo)
    for pssmatchNum, pssmatch in enumerate(pssmatches):
    pssmatchNum = pssmatchNum + 1
    self.pssnum = pssmatch.group(len(pssmatch.groups()))
    self.logDict({"PssMem":self.pssnum})
    # 获取uss内存值
    ussregex = r"(\d{1,9})\S\s{2}com.chuanblog91.ui"
    ussmatches = re.finditer(ussregex, meminfo)
    for ussmatchNum, ussmatch in enumerate(ussmatches):
    ussmatchNum = ussmatchNum + 1
    self.ussnum = ussmatch.group(len(ussmatch.groups()))
    # 获得机器最大内存设置数
    cmd = 'adb -s {} shell cat /system/build.prop'.format(self.devicesname)
    content = os.popen(cmd)
    maxmeminfo = content.read()
    maxmemregex = r"dalvik\.vm\.heapsize=(?P<maxmem>\d+\S)"
    # get maxmam value
    maxmemmatches = re.finditer(maxmemregex, maxmeminfo)
    for maxmemmatchNum, maxmemmatch in enumerate(maxmemmatches):
    maxmemmatchNum = maxmemmatchNum + 1
    maxmemstr = maxmemmatch.group(len(maxmemmatch.groups()))
    maxmem = re.sub("\D", "", maxmemstr)
    return maxmem, self.pssnum, self.ussnum
  • 获取app占用的cpu量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    def CheckAppCpuinfo(self):
    cmd = 'adb -s {} shell dumpsys cpuinfo com.chuanblog91.ui'.format(self.devicesname)
    content = os.popen(cmd)
    cpuinfo = content.read()
    # 获得总cpu占用率
    totalcpuregex = r"(\d{1,2}|100|\d{1,2}\.\d)%\sTOTAL"
    totalcpumatches = re.finditer(totalcpuregex, cpuinfo)
    for totalcpumatchNum, totalcpumatch in enumerate(totalcpumatches):
    totalcpumatchNum = totalcpumatchNum + 1
    totalcpu = totalcpumatch.group(len(totalcpumatch.groups()))
    # 获得用户操作cpu占用率
    usercpuregex = r"TOTAL:\s(\d{1,2}|100|\d{1,2}\.\d)%"
    usercpumatches = re.finditer(usercpuregex, cpuinfo)
    for usercpumatchNum, usercpumatch in enumerate(usercpumatches):
    usercpumatchNum = usercpumatchNum + 1
    usercpu = usercpumatch.group(len(usercpumatch.groups()))
    # 获得系统内核cpu占用率
    kernelcpuregex = r"TOTAL:\s(\d{1,2}|100|\d{1,2}\.\d)%\s\S{4}\s\S\s(\d{1,2}|100|\d{1,2}\.\d)%"
    kernelcpumatches = re.finditer(kernelcpuregex, cpuinfo)
    for kernelcpumatchNum, kernelcpumatch in enumerate(kernelcpumatches):
    kernelcpumatchNum = kernelcpumatchNum + 1
    kernelcpu = kernelcpumatch.group(len(kernelcpumatch.groups()))
    return totalcpu, usercpu, kernelcpu
  • 获取上下行流量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def CheckNetFlow(self):
    # 获取上下行流量,需要获取两次取差值
    cmd = "adb -s adb -s {0} shell \"cat /proc/net/xt_qtaguid/stats|grep {1} |awk '{{rx_bytes+=$6}}END{{print rx_bytes}}'\"".format(self.devicesname, self.uidnum)
    content = os.popen(cmd)
    rxnetinfo = content.read().strip()
    cmd = "adb -s adb -s {0} shell \"cat /proc/net/xt_qtaguid/stats|grep {1} |awk '{{tx_bytes+=$8}}END{{print tx_bytes}}'\"".format(self.devicesname, self.uidnum)
    content = os.popen(cmd)
    txnetinfo = content.read().strip()
    self.logDict({"NetInfo":{"RxNetInfo":rxnetinfo,"TxNetInfo":txnetinfo}})
    return rxnetinfo, txnetinfo
  • app流畅度(gfxinfo)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    def GfxInfo(self):
    # 流畅度
    '''
    打开手机gfx监控: 设置-开发者选项-GPU呈现模式分析-在adb shell dumpsys gfxinfo中
    当渲染时间大于16.67,按照垂直同步机制,该帧就已经渲染超时
    那么,如果它正好是16.67的整数倍,比如66.68,则它花费了4个垂直同步脉冲,减去本身需要一个,则超时3个
    如果它不是16.67的整数倍,比如67,那么它花费的垂直同步脉冲应向上取整,即5个,减去本身需要一个,即超时4个,可直接算向下取整
    最后的计算方法思路:
    执行一次命令,总共收集到了m帧(理想情况下m=128),但是这m帧里面有些帧渲染超过了16.67毫秒,算一次jank,一旦jank,
    需要用掉额外的垂直同步脉冲。其他的就算没有超过16.67,也按一个脉冲时间来算(理想情况下,一个脉冲就可以渲染完一帧)
    所以FPS的算法可以变为:
    m / (m + 额外的垂直同步脉冲) * 60
    '''
    cmd = "adb -s 127.0.0.1:21503 shell dumpsys \"gfxinfo com.chuanblog91.ui|awk '/Execute/,/hierarchy/{if(i>1)print x;x=$0;i++}'\""
    content = os.popen(cmd)
    def str2float(strf):
    # 保留原本位数的小数点
    def char2num(s):
    return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
    def char2int(x, y):
    return 10 * x + y
    tstr = strf.split('.')
    hightre = reduce(char2int, map(char2num, tstr[0]))
    if len(tstr)>1:
    lowre = reduce(char2int, map(char2num, tstr[1]))*(0.1**len(tstr[1]))
    else:
    lowre = 0
    return hightre + lowre
    gfList = []
    gfxinfo = content.readlines()
    jank_count = 0
    vsync_overtime = 0
    for gf in gfxinfo:
    if gf.strip() != '':
    gf = gf.strip().split('\t')
    render_time = str2float(gf[0]) + str2float(gf[1]) + str2float(gf[2])
    gfList.append(render_time)
    if render_time > 16.67:
    jank_count += 1
    if render_time % 16.67 == 0 :
    # 计算额外垂直脉冲次数
    vsync_overtime += int(render_time / 16.67) - 1
    else:
    vsync_overtime += int(render_time / 16.67)
    # print jank_count
    if len(gfList) != 0:
    gfx_count = len(gfList)
    else:
    gfx_count = 1
    fps = int(gfx_count * 60 / (gfx_count + vsync_overtime))
    self.logDict({"GfxInfo":{"gfx_count":str(gfx_count),"jank_count":str(jank_count),"fps":str(fps)}})
    return (gfx_count, jank_count, fps)