shell 脚本通过 dumpsys SurfaceFlinger --latency 数据计算 FPS 和评价流畅度。

news2024/12/29 7:22:48

目录

前言:

开篇前述:

一、设计初衷

二、设定预期倒推查找解决方案

设计实现部分

一、确定数据来源原因(dumpsys SurfaceFlinger --latency)

二、根据需求确定计算规则

三、代码实现

四、监控数据可视化交互结果设计


前言:

在 Android App 的持续集成测试中,流畅度是一个非常重要的指标,它表示应用程序的运行流畅程度。流畅度的评价通常可以通过监控应用程序的 CPU、内存、GPU 等资源的使用情况来进行评价。 

开篇前述:

由于采购机械臂测试性能用例和屏幕实际帧率变化,最终工具方案实现后搁置,拿出来” 晒晒 “。

一、设计初衷

1、面临用户和公司内领导试用中反馈的卡顿问题,思考如何能有效量化评估?
2、如何在尝试复现卡顿的过程中持续监控 FPS 和丢帧情况?

二、设定预期倒推查找解决方案

1、无 root 权限限制,可直接采集数据计算 FPS、丢帧率、最大单帧间隔。
2、控制脚本影响,不要使监控脚本成为 “负担”。
3、数据获取灵活,即可控制台实时输出数据,也可以后台长时间监控。
4、设计评价得分标准,可按:百分比 * 用例单项评分量化每条用例,从而计算总分使用。

设计实现部分

参考了网上现有的 FPS 计算方式原理,绕来绕去也没有满足自己的预期需求,索性自己从数据源出发自己设计脚本计算逻辑处理。参考如下​
用第三方库 (surface_stats_collector.py) 获取 AndroidFPS 过程分析 (用第三方库 (surface_stats_collector.py) 获取 AndroidFPS 过程分析 · TesterHome)
如何准确评测 Android 应用的流畅度? (http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=75&extra=page%3D1)

一、确定数据来源原因(dumpsys SurfaceFlinger --latency)

1、可以清零重新记录,避免如何分清哪些数据是上次的。(dumpsys SurfaceFlinger --latency-clear)
2、按 window 获取数据,可以配合手工操作逐一获取每个 case 的流畅度。
3、历史记录 127 行数据,按 60 帧算可记录 2.12S 数据,从而不用频繁获取。(最终考虑设定 1.6S 间隔刷新数据。)

二、根据需求确定计算规则

1、有刷新则计算帧率,无刷新则不输出数据。
原因:
(1)要做成监控指定窗口流畅度的功能,所以要控制无意义数据。
(2)配合手工操作,静置状态不输出,操作停止后直接刷新数据,从而使数据和操作对应。
2、间隔 500ms 以上则判定为操作延迟,每到间隔 500ms 情况发生重新计算帧率。
原因:
(1)一般做 monkey 压力测试设置的是 500ms 间隔
(2)一般用户操作频率间隔是大于 500ms 情况
3、每次采样数据大于等于 1 帧则计算 FPS,丢帧率,最大帧间隔。
原因:帧数/总耗时=帧率,所以无论有多少帧都可以直接计算
4、设定流畅度评价规则:
(1)满足 KPI 帧率则达成一半需求,占比 50%
(2)小于 KPI 单帧耗时比例评价画面变化是否稳定,占比 40%
(3)单帧渲染峰值代表瞬时卡顿最大影响,占比 10%
5、代码实现过程中遇到一坑:SurfaceFlinger 中同一帧存在间隔复用情况,即相同一行数据间隔几帧出现两次。(通过监控微信红包点击后的弹出框的帧率发现的。)
补充规则:发生两帧同步时间做差小于第一行帧刷新周期,则总时间 + 帧刷新周期,上一帧数据=前一帧同步时间 + 帧刷新周期,总帧数 +1

三、代码实现

#!/system/bin/sh

show_help() {
echo "
Usage: sh fps.sh [ -t target_FPS ] [ -w monitor_window ] [ -k KPI ] [ -f csv_path ] [ -h ]

Show: FU(s) LU(s) Date FPS Frames jank MFS(ms) OKT SS(%)

    FU(s): Uptime of the first frame.
    LU(s): Uptime of the last frame.
    Date: The date and time of LU.
    FPS: Frames Per Second.
    Frames: All frames of a loop.
    jank: When the frame latency crosses a refresh period, jank is added one.
    MFS(ms): Max Frame Spacing.
    OKT: Over KPI Times. The KPI is the used time of one frame.
    SS(%): Smoothness Score. SS=(FPS/target FPS)*60+(KPI/MFS)*20+(1-OKPIT/Frames)*20
           IF FPS > target FPS: FPS/The target FPS=1
           IF KPI > MFS: KPI/MFS=1
    WN: the window number of same name's window. Eg. SurfaceView

POSIX options | GNU long options

    -t   | --target         The target FPS of the choosed window. Default: 60
    -w   | --window         The choosed window. Default: no window.
    -k   | --KPI            The used time of a frame. Default: KPI=1000/The target FPS.
    -f   | --file           The path of the csv file. Default: output result to console.
    -h   | --help           Display this help and exit
"
}

file=""
window=""
target=60
KPI=16
while :
do
    case $1 in
        -h | --help)
            show_help
            exit 0
            ;;
        -t | --target)
            shift
            target=$1
            KPI=$((1000/$1))
            shift
            ;;
        -w | --window)
            shift
            window="$1"
            shift
            ;;
        -k | --KPI)
            shift
            KPI=$1
            shift
            ;;
        -f | --file)
            shift
            file="$1"
            shift
            ;;
        --) # End of all options
            shift
            break
            ;;
        *)  # no more options. Stop while loop
            break
            ;;  
    esac
done

if [ -f /data/local/tmp/busybox ];then
    export bb="/data/local/tmp/busybox"
else
    echo "No /data/local/tmp/busybox"
    exit
fi
if [ -f /data/local/tmp/stop ];then
    $bb rm /data/local/tmp/stop
fi

if [ -f /data/local/tmp/FPS.pid ];then
    pid=`cat /data/local/tmp/FPS.pid`
    if [ -f /proc/$pid/cmdline ];then
        if [ `$bb awk 'NR==1{print $1}' /proc/$pid/cmdline`"a" == "sha" ];then
            echo "The $pid is sh command."
            exit
        fi
    fi
fi
echo $$ >/data/local/tmp/FPS.pid

if [ $target -le 60 -a $target -gt 0 ];then
    sleep_t=1600000
else
    echo "$target is out of (0-60]"
    exit
fi
mac=`cat /sys/class/net/*/address|$bb sed -n '1p'|$bb tr -d ':'`
model=`getprop ro.product.model|$bb sed 's/ /_/g'`
build=`getprop ro.build.fingerprint`
if [ -z $build ];then
    build=`getprop ro.build.description`
fi

uptime=`$bb awk -v T="$EPOCHREALTIME" 'NR==3{printf("%.6f",T-$3/1000000000+8*3600)}' /proc/timer_list`
if [ -z "$file" ];then
    echo ""
    echo `date +%Y/%m/%d" "%H:%M:%S`": $window"
    if [ `$bb awk -F. '{print $1}' /proc/uptime` -lt 1000 ];then
        echo -e "FU(s) \tLU(s) \tDate \t\t\tFPS:$target\tFrames\tjank\tjank2\tMFS(ms)\tOKT:$KPI\tSS(%)\tWN"
    else
        echo -e "FU(s) \t\tLU(s) \t\tDate \t\t\tFPS:$target\tFrames\tjank\tjank2\tMFS(ms)\tOKT:$KPI\tSS(%)\tWN"
    fi
    while true;do
        dumpsys SurfaceFlinger --latency-clear
        $bb usleep $sleep_t
        dumpsys SurfaceFlinger --latency "$window"|$bb awk -v time=$uptime -v target=$target -v kpi=$KPI '{if(NR==1){r=$1/1000000;if(r<0)r=$1/1000;b=0;n=0;w=1}else{if(n>0&&$0=="")O=1;if(NF==3&&$2!=0&&$2!=9223372036854775807){x=($3-$1)/1000000/r;if(b==0){b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)*r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}else{c=($2-b)/1000000;if(c>500){O=1}else{n+=1;if(c>=r){C+=c;if(c>kpi)o+=1;if(c>=m)m=c;if(x>1)d+=1;if(x>2)D+=1;b=$2}else{C+=r;b=sprintf("%.0f",b+r*1000000)}}};if(n==1)s=sprintf("%.3f",$2/1000000000)};if(n>0&&O==1){O=0;if(n==1)t=sprintf("%.3f",s+C/1000);else t=sprintf("%.3f",b/1000000000);T=strftime("%F %T",time+t)"."sprintf("%.0f",(time+t)%1*1000);f=sprintf("%.2f",n*1000/C);m=sprintf("%.0f",m);g=f/target;if(g>1)g=1;h=kpi/m;if(h>1)h=1;e=sprintf("%.2f",g*60+h*20+(1-o/n)*20);print s"\t"t"\t"T"\t"f+0"\t"n"\t"d"\t"D"\t"m"\t"o"\t"e"\t"w;n=0;if($0==""){b=0;w+=1}else{b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)*r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}}}}'
        if [ -f /data/local/tmp/stop ];then
            break
        fi
    done
else
    start_time="`date +%Y/%m/%d" "%H:%M:%S`"
    echo "PID:$$\nWindow:$window\nT-FPS:$target\nKPI:$KPI\nStart time:$start_time\nmodel:$model\nmac:$mac\nbuild:$build"
    echo "FU(s),LU(s),Date:$window,FPS:$target,Frames,jank,jank2,MFS(ms),OKT:$KPI,SS(%),WN" >$file
    while true;do
        dumpsys SurfaceFlinger --latency-clear
        if [ -f /data/local/tmp/stop ];then
            echo "Stop Time:`date +%Y/%m/%d" "%H:%M:%S`"
            break
        fi
        $bb usleep $sleep_t
        dumpsys SurfaceFlinger --latency "$window"|$bb awk -v time=$uptime -v target=$target -v kpi=$KPI '{if(NR==1){r=$1/1000000;if(r<0)r=$1/1000;b=0;n=0;w=1}else{if(n>0&&$0=="")O=1;if(NF==3&&$2!=0&&$2!=9223372036854775807){x=($3-$1)/1000000/r;if(b==0){b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)*r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}else{c=($2-b)/1000000;if(c>500){O=1}else{n+=1;if(c>=r){C+=c;if(c>kpi)o+=1;if(c>=m)m=c;if(x>1)d+=1;if(x>2)D+=1;b=$2}else{C+=r;b=sprintf("%.0f",b+r*1000000)}}};if(n==1)s=sprintf("%.3f",$2/1000000000)};if(n>0&&O==1){O=0;if(n==1)t=sprintf("%.3f",s+C/1000);else t=sprintf("%.3f",b/1000000000);T=strftime("%F %T",time+t)"."sprintf("%.0f",(time+t)%1*1000);f=sprintf("%.2f",n*1000/C);m=sprintf("%.0f",m);g=f/target;if(g>1)g=1;h=kpi/m;if(h>1)h=1;e=sprintf("%.2f",g*60+h*20+(1-o/n)*20);print s","t","T","f+0","n","d","D","m","o","e","w;n=0;if($0==""){b=0;w+=1}else{b=$2;n=1;d=0;D=0;if(x<=1)C=r;if(x>1){d+=1;C=int(x)*r;if(x%1>0)C+=r};if(x>2)D+=1;m=r;o=0}}}}' >>$file
    done
fi

四、监控数据可视化交互结果设计

既然设计了数据监控的形式,自然要设计配套的数据可视化呈现方式
1、呈现数据
(1)x 轴为同步时间点,每次采样数据为起始时间点到采样结束时间的一条横线。
(2)y 轴数据为平均 FPS、超 KPI 帧数比例、和流畅度得分。左右双 y 轴设计,左侧为帧率,右侧为百分比。
(3)两次数据起始时间间隔超 500ms 则断开
(4)每点交互数据显示此次原始数据记录
(5)按每次间隔时间超过 500ms 为准计算每次操作对应的响应时长,作图呈现。持续监控情况则每超 10 秒计算一次。
2、一次监视频控播放窗口结果图实例:

下方总趋势图是可选的,鼠标按住左键拖动选取查看范围。

  作为一位过来人也是希望大家少走一些弯路

在这里我给大家分享一些自动化测试前进之路的必须品,希望能对你带来帮助。

(软件测试相关资料,自动化测试相关资料,技术问题答疑等等)

相信能使你更好的进步!

点击下方小卡片

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/789240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uni-app个人中心

一. 介绍uni-app&#xff1a; uni-app 是基于Vue.js框架开发的一个跨平台移动应用开发框架&#xff0c;可以同时支持多个平台&#xff08;如iOS、Android、Web等&#xff09;的应用开发。采用了统一的语法和组件规范&#xff0c;可以大大简化跨平台开发的工作&#xff0c;提高…

Redis持久化 :rdb与aof的持久化操作

redis持久化&#xff1a;分别启用rdb和aof&#xff0c;并查看是否有对应文件生成 rdb&#xff1a; #save 秒钟 写操作次数 如果在设置时间内写入数据达到规定的次数&#xff0c;则产生一次快照 [rootlocalhost redis-stable]# vim /etc/redis.conf :/save #查找有save关键字的…

Oracle 多条记录根据某个字段获取相邻两条数据间的间隔天数,小于31天的记录都筛选出来

需求描述&#xff1a;在Oracle中 住院记录记录表为v_hospitalRecords&#xff0c;表中FIHDATE入院时间&#xff0c;FBIHID是住院号&#xff0c; 我想查询出每个患者在他们的所有住院记录中是否在一个月内再次入院(相邻的两条记录进行比较)&#xff0c;并且住院记录大于一的患者…

window10脚本转服务教程

先说下脚本/我们启动的一些三方服务转window本机服务目前我了解到的好处 一键设置开机自启、随用随启、延时自启解决一些服务类应用启动后会阻塞当前dos窗口导致桌面一直要开着的问题脚本化服务注册&#xff0c;方便管理&#xff0c;统一运维… 1. 实践涉及内容介绍 编写好的…

力扣刷题SQL-619. 只出现一次的最大数字

MyNumbers 表&#xff1a; ------------------- | Column Name | Type | ------------------- | num | int | ------------------- 这张表没有主键。可能包含重复数字。这张表的每一行都含有一个整数。 单一数字 是在 MyNumbers 表中只出现一次的数字。 请你编写一…

解决uview1.x使用i18n,props在切换语言的时候未及时修改视图的问题

操作流程&#xff0c;用u-modal举例 未修改的u-modal.vue props取消文案 props:{// 取消文案cancelText: {type: String,default: 取消}, } 在这里插入代码片需要修改成适配i18n的 u-modal.vue //跟着官方的this.$t(lang.intro)写法&#xff0c;不知道是我没引好还是怎么的&a…

TCP 协议【传输层协议】

文章目录 1. 简介1.1 TCP 协议是什么1.2 TCP 协议的作用1.3 什么是“面向连接” 2. 简述 TCP2.1 封装和解包2.2 TCP 报文格式2.3 什么是“面向字节流”2.4 通过 ACK 机制实现一定可靠性 3. 详述 TCP3.1 基本认识TCP 报头格式16 位源/目标端口号32 位序列号*32 位确认应答号4 位…

机器学习:GPT3

GPT3 模型过于巨大 GPT3是T5参数量的10倍&#xff01; 训练GPT3的代价是$12百万美元 Zero-shot Ability GPT3的思想是不是能拿掉Fine-tune 只需要给定few-shot或者zero-shot就能干相应的任务了。 few-shot learning&#xff08;no gradient descent&#xff09;&#…

25.3 matlab里面的10中优化方法介绍——Nelder-Mead法(matlab程序)

1.简述 fminsearch函数用来求解多维无约束的线性优化问题 用derivative-free的方法找到多变量无约束函数的最小值 语法 x fminsearch(fun,x0) x fminsearch(fun,x0,options) [x,fval] fminsearch(...) [x,fval,exitflag] fminsearch(...) [x,fval,exitflag,output] fmins…

基于MSP432P401R爬坡小车【2020年电赛C题】

文章目录 一、任务清单1. 硬件部分2. 软件部分 二、OpenMV巡线三、舵机转向四、停止线识别五、技术交流 一、任务清单 1. 硬件部分 主控板&#xff1a; MSP432P401R数据显示&#xff1a; OLED电机&#xff1a; 霍尔编码器电机电池&#xff1a; 7.3V航模电池巡线&#xff1a; …

网络知识点之-路由

路由&#xff08;routing&#xff09;是指分组从源到目的地时&#xff0c;决定端到端路径的网络范围的进程。路由工作在OSI参考模型第三层——网络层的数据包转发设备。路由器通过转发数据包来实现网络互连。虽然路由器可以支持多种协议&#xff08;如TCP/IP、IPX/SPX、AppleTa…

iptables与防火墙

目录 防火墙 安全技术 划分方式 iptables 构成 四表 优先级 五链 iptables的规则 匹配顺序 iptables的命令格式 管理选项 匹配条件 控制类型 隐藏扩展模块 注意事项 防火墙 隔离功能&#xff0c;一般部署在网络边缘或者主机边缘&#xff0c;在工作中防火墙的…

【【51单片机LCD1602模块介绍】】

LCD1602的介绍 显示容量16x2 每个字符是5x7的点阵 VDD 是电源正极 4.5-5.5v VO 是对比度调节电压 RS 数据/指令 选择 1为数据0为指令 RW 读写选择1是读 0为写 E 使能 1为数据有效 下降沿执行命令 D0-D7 数据输入输出 A 背光电源正极 K 背光电源负极 LCD1602的操作流程 1.初始…

艺人商务代言:避雷策略与成功合作之道

避免在艺人商务代言中遇到风险&#xff0c;是每个企业和艺人都应该高度重视的问题。代言活动是一种有效的市场营销手段&#xff0c;可以为企业带来广泛的曝光和销售增长&#xff0c;同时也能让艺人获得额外的收入和更高的知名度。然而&#xff0c;不慎选择错误的代言合作可能带…

PostgreSQL实战-数据库迁移部署

PostgreSQL实战-数据库迁移部署 介绍 根据项目需求&#xff0c;我们需要将现有的PostgreSQL数据库重新部署到新的服务器上。由于项目本身就是基于PostgreSQL数据库构建的&#xff0c;因此数据库迁移将变得十分便捷。接下来&#xff0c;我将简要介绍我们的迁移步骤。 迁移步骤…

IDEA安装热部署插件JRebel详解

JRebel 简介 JRebel是一套JavaEE开发工具。JRebel允许开发团队在有限的时间内完成更多的任务修正更多的问题&#xff0c;发布更高质量的软件产品。 JRebel是收费软件&#xff0c;用户可以在JRebel官方站点下载30天的评估版本。 Jrebel 可快速实现热部署&#xff0c;节省了大量重…

微服务的各种边界在架构演进中的作用

演进式架构 在微服务设计和实施的过程中&#xff0c;很多人认为&#xff1a;“将单体拆分成多少个微服务&#xff0c;是微服务的设计重点。”可事实真的是这样吗&#xff1f;其实并非如此&#xff01; Martin Fowler 在提出微服务时&#xff0c;他提到了微服务的一个重要特征—…

【Linux】Ubuntu基本使用与配置, 以及常见问题汇总(一)

前言 大学期间&#xff0c;感觉很多时候学习课外知识都是被推着往前走&#xff0c;很多内容并没有深入去学习&#xff0c;知识的记录受限于所学比较片面&#xff0c;如今渐渐意识到似乎并没有建立起相关知识的体系架构&#xff0c;缺乏一个系统学习并整理的过程。本文将以Ubunt…

跨境独立站如何应对恶意网络爬虫?

目录 跨境出海独立站纷纷成立 爬虫威胁跨境电商生存 如何有效识别爬虫&#xff1f; 技术反爬方案 防爬虫才能保发展 中国出海跨境电商业务&#xff0c;主要选择大平台开设店铺&#xff0c;例如&#xff0c;亚马逊、eBay、Walmart、AliExpress、Zalando等。随着业务的扩大&…

Error “slow_conv2d_cpu“ not implemented for ‘Half‘

Error “slow_conv2d_cpu” not implemented for ‘Half’ 报错原因&#xff1a; 将输入数据的类型设置为half(半精度浮点数&#xff0c;能加快计算速度)&#xff0c;但是half只有GPU支持&#xff0c;pytorch cpu不支持半(half)精度训练 解决方式&#xff1a; 既然不支持&am…