扒开源安卓性能测试工具moblieperf源码——开发属于你自己的性能稳定性测试工具

这篇具有很好参考价值的文章主要介绍了扒开源安卓性能测试工具moblieperf源码——开发属于你自己的性能稳定性测试工具。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

moblieperf下载和使用

moblieperf由阿里巴巴开源的Android性能测试工具

下载:官方源码地址mobileperf github

使用:

  1. 使用pycharm打开下载的项目
  2. 使用只需要修改配置文件config.conf即可
  3. 运行采集:a.mac、linux 在mobileperf工具根目录下执行sh run.sh ; b.windows 双击run.bat

配置图:(简单使用只需要修改包名和设备序列号即可)

扒开源安卓性能测试工具moblieperf源码——开发属于你自己的性能稳定性测试工具

 源码阅读

原来我们主要阅读我们想实现功能是如何实现的(cpu,内存)

我们先从启动类StartUp中run方法看起:

for i in range(0,5):
    if self.device.adb.is_connected(self.serialnum):
        is_device_connect = True
        break
    else:
        logger.error("device not found:"+self.serialnum)
        time.sleep(2)

 self.serialnum就是配置表中的设备序列号,这一段是用过ADB类中的静态方法is_connected实现的,我们来看一下他们是怎么检查配置中的手机是否链接

@staticmethod
def is_connected(device_id):
    '''
                检查设备是否连接上
    '''
    if device_id in ADB.list_device():
        return True
    else:
        return False
    @staticmethod
    def list_device():
        '''获取设备列表

        :return: 返回设备列表
        :rtype: list
        '''
        proc = subprocess.Popen("adb devices", stdout=subprocess.PIPE, shell=True)
        result = proc.stdout.read()
        if not isinstance(result, str):
            result = result.decode('utf-8')
        result = result.replace('\r', '').splitlines()
        logger.debug("adb devices:")
        logger.debug(result)
        device_list = []
        for device in result[1:]:
            if len(device) <= 1 or not '\t' in device: continue
            if device.split('\t')[1] == 'device':
                # 只获取连接正常的
                device_list.append(device.split('\t')[0])
        return device_list

通过这一段代码我们可以发现,mobileperf是基于adb命令去读取相应的信息,而他的使用是通过subprocess类中的Popen去操作adb命令,通过adb devices命令判断是否已经链接成功

注:subprocess是python标准库兼容性较好,且是非阻塞性执行,在输入输出的拓展性、错误处理都比较健全完善,后面会在提到这个方法使用。

  # 对是否安装被测app的检查 只在最开始检查一次
        if not self.device.adb.is_app_installed(self.packages[0]):
            logger.error("test app not installed:" + self.packages[0])
            return
        try:
            #初始化数据处理的类,将没有消息队列传递过去,以便获取数据,并处理
            # datahandle = DataWorker(self.get_queue_dic())
            # 将queue传进去,与datahandle那个线程交互
            self.add_monitor(CpuMonitor(self.serialnum, self.packages, self.frequency, self.timeout))
            self.add_monitor(MemMonitor(self.serialnum, self.packages, self.frequency, self.timeout))

这里我们节选了一部分代码,当我们知道他基本实现逻辑其实我们大致也可以猜到is_app_installed判断app是否安装他是如何实现的!应该也是通过adb命令去寻找已安装的包是否存在

    def is_app_installed(self, package):
        '''
        判断app是否安装
        '''
        if package in self.list_installed_app():
            return True
        else:
            return False
    def list_installed_app(self):
        '''
                        获取已安装app列表
        :return: 返回app列表
        :rtype: list
        '''
        result = self.run_shell_cmd('pm list packages')
        result = result.replace('\r', '').splitlines()
        logger.debug(result)
        installed_app_list = []
        for app in result:
            if not 'package' in app: continue
            if app.split(':')[0] == 'package':
                # 只获取连接正常的
                installed_app_list.append(app.split(':')[1])
        logger.debug(installed_app_list)
        return installed_app_list

正如我们所料,他是用过命令adb shell pm list packages 返回的接口进行切割拿到包名,去判断需要测试包名是否在其中

那么他是采集和收集内存/cpu等信息是不是也是这样实现的呢,答案是肯定的

        end_time = time.time() + self._timeout
        cpu_title = ["datetime", "device_cpu_rate%", "user%", "system%","idle%"]
        cpu_file = os.path.join(RuntimeData.package_save_path, 'cpuinfo.csv')
        for i in range(0, len(self.packages)):
            cpu_title.extend(["package", "pid", "pid_cpu%"])
        if len(self.packages) > 1:
            cpu_title.append("total_pid_cpu%")
        try:
            with open(cpu_file, 'a+') as df:
                csv.writer(df, lineterminator='\n').writerow(cpu_title)
        except RuntimeError as e:
            logger.error(e)
        while not self._stop_event.is_set() and time.time() < end_time:
            try:
                logger.debug("---------------cpuinfos, into _collect_package_cpu_thread loop thread is : " + str(threading.current_thread().name))
                before = time.time()
                #为了cpu值的准确性,将采集的时间间隔放在top命令中了
                cpu_info = self._top_cpuinfo()
                after = time.time()
                time_consume = after - before
                logger.debug("  ============== time consume for cpu info : "+str(time_consume))

这里我们截取了一段收集cpu的代码,可以发现他是通过一个while循环去调用_top_cpuinfo方法

    def _top_cpuinfo(self):
        self._top_pipe = self.device.adb.run_shell_cmd(self.top_cmd, sync=False)
        out = self._top_pipe.stdout.read()
        error = self._top_pipe.stderr.read()

我们接着往下看

    def run_shell_cmd(self, cmd, **kwds):
        '''执行 adb shell 命令
        '''
        # 如果失去连接后,adb又正常连接了
        if not self.before_connect and self.after_connect:
            cpu_uptime_file = os.path.join(RuntimeData.package_save_path, "uptime.txt")
            with open(cpu_uptime_file, "a+",encoding = "utf-8") as writer:
                writer.write(TimeUtils.getCurrentTimeUnderline() + " /proc/uptime:" + self.run_adb_cmd("shell cat /proc/uptime") + "\n")
            self.before_connect = True
        ret = self.run_adb_cmd('shell', '%s' % cmd, **kwds)
        # 当 adb 命令传入 sync=False时,ret是Poen对象
        if ret == None:
            logger.error(u'adb cmd failed:%s ' % cmd)
        return ret

不难发现,最终依旧是调用的run_adb_cmd方法去执行的adb命令,最后把收集到数据写入对应文件中,看到这里我们大概可以自己实现一下了

开发自己的安卓性能工具

注:这里工具页面开发我们使用gui开发工具pyside6

前面我们提到了标准库subprocess这里介绍一下我们会用到的两个方法communicate从子进程的stdoutstderr读取数据。这个方法会阻塞主程序,直到子进程完成,poll检查子进程是否结束,如果子进程正常退出,输出应该为 0;否则为非0值

页面简单拖拽

扒开源安卓性能测试工具moblieperf源码——开发属于你自己的性能稳定性测试工具

 ui文件转换py文件(performanceTools.py

pyside6-uic <ui文件名>.ui -o <py文件名>.py
# -*- coding: utf-8 -*-

################################################################################
## Form generated from reading UI file 'performanceTools.ui'
##
## Created by: Qt User Interface Compiler version 6.6.1
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################

from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
    QMetaObject, QObject, QPoint, QRect,
    QSize, QTime, QUrl, Qt)
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
    QFont, QFontDatabase, QGradient, QIcon,
    QImage, QKeySequence, QLinearGradient, QPainter,
    QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QHBoxLayout, QLabel, QLineEdit,
    QPushButton, QSizePolicy, QVBoxLayout, QWidget)

class Ui_Form(object):
    def setupUi(self, Form):
        if not Form.objectName():
            Form.setObjectName(u"Form")
        Form.resize(400, 300)
        self.horizontalLayoutWidget = QWidget(Form)
        self.horizontalLayoutWidget.setObjectName(u"horizontalLayoutWidget")
        self.horizontalLayoutWidget.setGeometry(QRect(20, 90, 201, 80))
        self.horizontalLayout = QHBoxLayout(self.horizontalLayoutWidget)
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
        self.label = QLabel(self.horizontalLayoutWidget)
        self.label.setObjectName(u"label")

        self.horizontalLayout.addWidget(self.label)

        self.packagename = QLineEdit(self.horizontalLayoutWidget)
        self.packagename.setObjectName(u"packagename")

        self.horizontalLayout.addWidget(self.packagename)

        self.verticalLayoutWidget = QWidget(Form)
        self.verticalLayoutWidget.setObjectName(u"verticalLayoutWidget")
        self.verticalLayoutWidget.setGeometry(QRect(230, 30, 160, 231))
        self.verticalLayout = QVBoxLayout(self.verticalLayoutWidget)
        self.verticalLayout.setObjectName(u"verticalLayout")
        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
        self.cold_time_cal_button = QPushButton(self.verticalLayoutWidget)
        self.cold_time_cal_button.setObjectName(u"cold_time_cal_button")

        self.verticalLayout.addWidget(self.cold_time_cal_button)

        self.cold_time_cal_result_button = QPushButton(self.verticalLayoutWidget)
        self.cold_time_cal_result_button.setObjectName(u"cold_time_cal_result_button")

        self.verticalLayout.addWidget(self.cold_time_cal_result_button)

        self.cpuinfo_button = QPushButton(self.verticalLayoutWidget)
        self.cpuinfo_button.setObjectName(u"cpuinfo_button")

        self.verticalLayout.addWidget(self.cpuinfo_button)

        self.cpuinfo_result_button = QPushButton(self.verticalLayoutWidget)
        self.cpuinfo_result_button.setObjectName(u"cpuinfo_result_button")

        self.verticalLayout.addWidget(self.cpuinfo_result_button)


        self.retranslateUi(Form)

        QMetaObject.connectSlotsByName(Form)
    # setupUi

    def retranslateUi(self, Form):
        Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
        self.label.setText(QCoreApplication.translate("Form", u"\u8bf7\u8f93\u5165\u5305\u540d", None))
        self.cold_time_cal_button.setText(QCoreApplication.translate("Form", u"\u51b7\u542f\u52a8\u5e73\u5747\u65f6\u95f4\u7edf\u8ba1", None))
        self.cold_time_cal_result_button.setText(QCoreApplication.translate("Form", u"\u51b7\u542f\u52a8\u65f6\u95f4\u7ed3\u679c\u67e5\u8be2", None))
        self.cpuinfo_button.setText(QCoreApplication.translate("Form", u"CPU\u4fe1\u606f\u6570\u636e\u7edf\u8ba1", None))
        self.cpuinfo_result_button.setText(QCoreApplication.translate("Form", u"CPU\u4fe1\u606f\u6570\u636e\u7ed3\u679c\u67e5\u8be2", None))
    # retranslateUi

adb命令封装(util.py)

import subprocess
class ADB:
    def run_cmd_once(self,cmdStr):
        process = subprocess.Popen(cmdStr, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                                   shell=True)
        # 读取子进程的输出
        stdout, stderr = process.communicate()
        # 如果子进程正常退出,输出应该为 0;否则为非0值
        if process.poll():
            print(f"执行命令:{cmdStr} 失败, {stderr.decode()}")
            return None
        else:
            print(f"执行命令:{cmdStr} 成功, {stdout.decode()}")
            return stdout.decode()

    #查看当前活动页面
    def get_current_activity(self):
        activity_line = ''
        activity_focus = self.run_cmd_once('adb shell dumpsys window | findstr mCurrentFocus')
        #成功运行
        if activity_focus:
            for l in activity_focus.split("\n"):
                #如果存在关键词mCurrentFocus=Window
                if l.find("mCurrentFocus=Window") != -1:
                    activity_line = l.strip().split(" ")
        # mCurrentFocus=Window{ea9aaee u0 com.mumu.store/com.mumu.store.MainActivity}
        if activity_line:
            return activity_line[2].rstrip("}")
    #获取当前活动包名
    def get_current_packagename(self):
        current_activity = self.get_current_activity()
        if current_activity:
            return current_activity.split("/")[0]
    #启动
    def start_activity(self,activity_name):
        result = self.run_cmd_once(f'adb shell am start -S -W {activity_name}')
        return result
    #关闭app
    def stop_app(self, packagename):
        result = self.run_cmd_once(f'adb shell am force-stop {packagename}')
        return result
    #冷启动并获取冷启动的TotalTime
    def get_launchState_cold_totalTime(self,activity_name):
        result = self.start_activity(activity_name)
        for l in result.split("\n"):
            #TotalTime: int
            if l.find("TotalTime") != -1:
                return int(l.split(":")[1])
    #采集cpu信息
    def get_cpuinfo(self, packagename):
        cpuinfo = self.run_cmd_once('adb shell dumpsys cpuinfo')
        for l in cpuinfo.split("\n"):
            if l.find(packagename) != -1:
                total_cpu, user, kernel = [s for s in l.strip(" ").split(" ") if "%" in s]
                return total_cpu, user, kernel

功能实现逻辑代码(appperf.py)

import time

import pyecharts.charts
from pyecharts import options as opts
from util import ADB
import subprocess
import os
import sys
from PySide6.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QMessageBox
from PySide6.QtCore import Slot, Signal
import performanceTools
import threading

class QmyWeidge(QWidget):
    #建立信号
    packagenameSignal = Signal(str)
    def __init__(self):
        super().__init__()
        self.ui = performanceTools.Ui_Form()
        self.ui.setupUi(self)
        self.cold_time_cal_htmlname = ""
        self.cpuinfo_htmlname = ""
        self.packagenameSignal.connect(self.isPackagenameExciting)#判断包名是否存在信号槽链接
    def open_html_by_firefox(self, local_html_name):
        # 获取当前工作目录
        current_dir = os.getcwd()
        # 指定本地HTML文件的路径
        local_file = os.path.join(current_dir, local_html_name)
        # 指定火狐浏览器的可执行文件路径
        firefox_path = "C:/Program Files/Mozilla Firefox/firefox.exe"  # 替换为你的火狐浏览器可执行文件的路径
        # 构建火狐浏览器的命令行参数
        url = 'file://' + local_file + '.html'
        cmd = [firefox_path, url]
        # 使用subprocess启动火狐浏览器并打开本地HTML文件
        subprocess.Popen(cmd)

    def result_to_html(self, xaxis, yaxis, y_name, title):
        pyecharts.charts.Line().add_xaxis(xaxis).add_yaxis(y_name, yaxis).set_global_opts(
            title_opts=opts.TitleOpts(title=title)).render(path=f'./{title}.html')

    @Slot()
    def on_cold_time_cal_button_clicked(self):
        """
        运行10次取平均值
        :param current_activity: 当前运行的app页面,或者是待测的页面
        :return: 范围运行第x次的数据y
        """
        #按钮置灰
        self.ui.cold_time_cal_button.setEnabled(False)
        self.ui.cold_time_cal_button.repaint()
        self.ui.cold_time_cal_result_button.setEnabled(False)
        self.ui.cold_time_cal_result_button.repaint()

        current_activity = ADB().get_current_activity()
        x = []
        y = []
        for i in range(1, 11):
            x.append(f"第{i}次")
            cold_start_time = ADB().get_launchState_cold_totalTime(current_activity)
            y.append(cold_start_time)
            ADB().stop_app(current_activity)
        #输出结果
        self.cold_time_cal_htmlname = f"冷启动时间趋势图_平均耗时{sum(y) / 10}"
        self.result_to_html(x, y ,"启动时间", self.cold_time_cal_htmlname)
        #恢复按钮
        self.ui.cold_time_cal_button.setEnabled(True)
        self.ui.cold_time_cal_result_button.setEnabled(True)

    @Slot()
    def on_cold_time_cal_result_button_clicked(self):
        self.open_html_by_firefox(self.cold_time_cal_htmlname)

    @Slot()
    def on_cpuinfo_button_clicked(self):
        """
        运行10次取平均值
        :return: 范围运行第x次的数据y
        """
        #按钮置灰
        self.ui.cpuinfo_button.setEnabled(False)
        self.ui.cpuinfo_button.repaint()
        self.ui.cpuinfo_result_button.setEnabled(False)
        self.ui.cpuinfo_result_button.repaint()

        x = []
        y = []
        if self.ui.packagename.text():
            for i in range(1,11):
                x.append(f"第{i}次")
                try:
                    cpu, _, _ = ADB().get_cpuinfo(self.ui.packagename.text())
                    y.append(round(float(cpu.replace('%', '')), 4))
                    time.sleep(2)
                    # 输出结果
                    self.cpuinfo_htmlname = f"cpu占比趋势图_平均占比{sum(y) / 10} %"
                    self.result_to_html(x, y, "cpu占比(%)", self.cpuinfo_htmlname)
                except:
                    msgBox = QMessageBox()
                    msgBox.setWindowTitle("提醒")
                    msgBox.setText(f"{self.ui.packagename.text()}确认是否运行!未获取到对应cpu信息!")
                    msgBox.exec()
                    break
        else:
            self.packagenameSignal.emit(self.ui.packagename.text())

        #恢复按钮
        self.ui.cpuinfo_result_button.setEnabled(True)
        self.ui.cpuinfo_button.setEnabled(True)

    @Slot()
    def on_cpuinfo_result_button_clicked(self):
        self.open_html_by_firefox(self.cpuinfo_htmlname)

    def isPackagenameExciting(self, value):
        if not value :
            msgBox  = QMessageBox()
            msgBox.setWindowTitle("提醒")
            msgBox.setText("请输入包名!")
            msgBox.exec()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    myWindow = QmyWeidge()
    myWindow.show()
    sys.exit(app.exec())

 文章来源地址https://www.toymoban.com/news/detail-825374.html

到了这里,关于扒开源安卓性能测试工具moblieperf源码——开发属于你自己的性能稳定性测试工具的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • jmeter 性能测试工具的使用(Web性能测试)

    1、下载 该软件不用安装,直接解压打开即可使用。 2、使用 这里就在win下进行,图形界面较为方便 在目录apache-jmeter-2.13bin 下可以见到一个jmeter.bat文件,双击此文件,即看到JMeter控制面板。主界面如下: 3、创建线程组 测试计划--右键——》添加——》hreads——》线程组 设

    2024年02月10日
    浏览(71)
  • 性能测试怎么做?测试工具怎么选择?

    在当前软件测试行业,熟练掌握性能测试已经是测试工程师们面试的敲门砖了,当然还有很多测试朋友们每天的工作更多的是点点点,性能方面可能也只是做过简单的并发测试,对于编写脚本,搭建环境方面也比较陌生。今天这篇文章就给大家梳理一下如何去做性能测试,和

    2024年02月13日
    浏览(55)
  • iOS性能指标和性能测试工具

    作为一名软件测试工程师,在测试 iOS 应用的性能时,需要关注以下几个方面: 1. 响应时间:应用的启动时间、页面加载速度、接口响应时间等。 2. CPU 使用率:应用在各种操作下的 CPU 占用情况。 3. 内存使用:应用在各种操作下的内存占用情况。 4. 网络性能:应用在各种

    2024年02月13日
    浏览(54)
  • Web 性能测试工具

    作为网站应用的开发者或维护者,我们需要时常关注网站当前的健康状况,譬如在主流程运行正常的情况下,各方面性能体验是否满足期望,是否存在改进与提升的空间,如何进行快速且准确的问题定位等,为了满足这些诉求,我们需要进行全面且客观的性能检测。 性能检测

    2024年02月02日
    浏览(62)
  • Linux性能测试工具

    在Linux Benchmark Suite Homepage网站上列举了诸多Linux性能测试工具,包括CPU/RAM/ROM/Cache/net等性能测试。 测试工具 这个工具集中包含以下几种测试工具,我们主要使用到bw_mem工具进行Memory read和write测试。 Bandwidth benchmarks(带宽测试) Cached file read Memory copy (bcopy) Memory read Memory write Pi

    2023年04月08日
    浏览(54)
  • 网络性能测试工具

    网络的基本目的是有效地共享资源。利用可靠的网络测试工具建立定期的网络测试对于:了解网络的状态、确保配置更改按预期工作、检测瘫痪网络攻击、提供一流的终端用户体验。那么网络性能也是非常关键的。只要是需要快速而且大量的网络数据传输的应用都可以作为网

    2023年04月15日
    浏览(52)
  • 自动化测试工具之 Jmeter性能测试

    1.性能测试概念: 通过自动化测试工具模拟多种正常,峰值以及异常负载条件来对系统的各项性能指标进行测试 2.性能测试条件 系统基础功能测试验证完成 系统趋于稳定 3.性能测试基本流程 需求分析 (1)系统信息调研 (2)业务信息调研 (3)性能需求评估   判断是否进行

    2023年04月09日
    浏览(70)
  • iometer - 性能压力测试工具

            《存储工具系列文章》主要介绍存储相关的测试和调试工具,包括不限于dd、fio、vdbench、iozone、iometer、cosbench等性能负载工具,及strace等调试工具。         Iometer 是一个免费的开源的测试磁盘性能的工具。和其他磁盘工具相比,可以测试 I/O 的传输率和平均的

    2024年02月02日
    浏览(39)
  • FIO磁盘性能测试工具

    简介 一般我们测试硬盘或者存储的性能的时候,会用Linux系统自带的dd命令,因为是自带命令,简单易使用,因此一些客户喜欢使用dd命令来测试磁盘的读写性能。 但是用dd命令来测试性能,有如下问题: 1. dd命令的IO模型单一,只能测试顺序IO,不能测试随机IO。 2. dd命令可

    2024年02月03日
    浏览(45)
  • 初识性能测试工具Jmeter

    (1)Jmeter下载地址:https://jmeter.apache.org/download_jmeter.cgi; (2)Java运行环境下载地址:https://www.oracle.com/java/technologies/downloads/; (3)JAVA尽量配置到环境变量中去; (4)Jmeter解压出来; (1)Windows的点击jmeter/bin/下的jmter.bat; (2)mac和linux点击jmeter.sh; (3)点击Options—

    2023年04月08日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包