Skip to content

Commit 6c5ed81

Browse files
committed
add fps.py
1 parent f793136 commit 6c5ed81

File tree

5 files changed

+217
-0
lines changed

5 files changed

+217
-0
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ Android测试中常用到的脚本
77

88
批量安装应用(支持以中文命名的 apk)、批量卸载、截屏、录制视频、获取当前应用的 apk 文件、包名、Activity 名等。<br>
99

10+
###2016.04.22
11+
增加 `fps.py`,获取测试界面的 `fps``jankniess`
12+
使用方法:安装提示输入参数,测试过程中操作界面,最终数据结果存放于 `fps_data` 目录下,csv 格式。
13+
如有错误,请指出!
14+
demo:
15+
测试界面
16+
![tuiku](image/tuiku.png)
17+
18+
cmd 界面:
19+
![fps_cmd](image/fps_cmd.png)
20+
21+
最终结果:
22+
![fps_chart](image/fps_chart.png)
23+
24+
1025
###2016.01.21
1126
增加 `logcat.py`,windows 中在 cmd 里面运行 logcat 命令,会给输出的日志内容根据优先级添加颜色。使用前提是已配置 adb 及 python 的环境变量,在 cmd 里面可以直接运行 adb 命令和python 脚本。
1227
用法:

image/fps_chart.png

50.8 KB
Loading

image/fps_cmd.png

19.2 KB
Loading

image/tuiku.png

471 KB
Loading

python/fps.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*
3+
4+
__author__ = 'xuxu'
5+
6+
import time
7+
import os
8+
import sys
9+
10+
from scriptUtils import utils
11+
12+
PATH = lambda p: os.path.abspath(p)
13+
14+
class SurfaceFlinger(object):
15+
16+
CLEAR_BUFFER_CMD = "dumpsys SurfaceFlinger --latency-clear"
17+
FRAME_LATENCY_CMD = "dumpsys SurfaceFlinger --latency"
18+
PAUSE_LATENCY = 20 #两个frame之间的latency大于20,则不是一个jankniess
19+
PENDING_FENCE_TIME = (1 << 63) - 1 #Symbol of unfinished frame time
20+
21+
refresh_period = -1 #刷新周期
22+
frame_buffer_data = []
23+
frame_latency_data_size = 0
24+
max_Vsync = 0
25+
26+
def __init__(self, activity_name, dump_time):
27+
"""
28+
:param activity_name: 当前界面的"package/activity"
29+
:param dump_time: 每次获取SurfaceFlinger数据的时间间隔
30+
"""
31+
self.activity_name = activity_name
32+
self.dump_time = dump_time
33+
34+
#清除SurfaceFlinger缓存数据
35+
def __clear_buffer(self):
36+
results = utils.shell("{0} {1}".format(self.CLEAR_BUFFER_CMD, self.activity_name))\
37+
.stdout.readlines()
38+
return not len(results)
39+
40+
#开始获取SurfaceFlinger数据
41+
def start_dump_latency_data(self, ignore_pending_fence_time = False):
42+
results = []
43+
if self.__clear_buffer():
44+
time.sleep(self.dump_time)
45+
results = utils.shell("{0} {1}".format(self.FRAME_LATENCY_CMD, self.activity_name))\
46+
.stdout.readlines()
47+
self.refresh_period = int(results[0].strip())
48+
49+
if self.refresh_period < 0:
50+
return False
51+
52+
data_invalid_flag = False
53+
for line in results:
54+
if not line.strip():
55+
break
56+
57+
if len(line.split()) == 1 or line.split()[0] == "0":
58+
continue
59+
elif line.split()[1] == str(self.PENDING_FENCE_TIME):
60+
if ignore_pending_fence_time:
61+
data_invalid_flag = True
62+
else:
63+
return False
64+
65+
self.frame_buffer_data.append(line.split())
66+
if not data_invalid_flag:
67+
self.frame_latency_data_size += 1
68+
69+
return True
70+
71+
def get_frame_latency_data_size(self):
72+
return self.frame_latency_data_size
73+
74+
def get_refresh_period(self):
75+
return self.refresh_period
76+
77+
#获取Vsync增量数据
78+
def __get_delta_Vsync_data(self):
79+
delta_Vsync_data = []
80+
if self.frame_buffer_data:
81+
first_Vsync_time = long(self.frame_buffer_data[0][1])
82+
for i in xrange(0, self.frame_latency_data_size-1):
83+
cur_Vsync_time = long(self.frame_buffer_data[i+1][1])
84+
delta_Vsync_data.append(cur_Vsync_time - first_Vsync_time)
85+
first_Vsync_time = cur_Vsync_time
86+
if self.max_Vsync < delta_Vsync_data[i]:
87+
self.max_Vsync = delta_Vsync_data[i]
88+
return delta_Vsync_data
89+
90+
#在delta_Vsync_data基础上再获取增量数据
91+
def __get_delta2_Vsync_data(self):
92+
delta_Vsync_data = self.__get_delta_Vsync_data()
93+
delta2_Vsync_data = []
94+
num_delta_Vsync = self.frame_latency_data_size - 1
95+
96+
for i in xrange(0, num_delta_Vsync-1):
97+
delta2_Vsync_data.append(delta_Vsync_data[i+1] - delta_Vsync_data[i])
98+
return delta2_Vsync_data
99+
100+
def __get_normalized_delta2_Vsync(self):
101+
delta2_Vsync_data = self.__get_delta2_Vsync_data()
102+
normalized_delta2_Vsync = []
103+
for i in xrange(0, self.frame_latency_data_size-2):
104+
normalized_delta2_Vsync.append(delta2_Vsync_data[i]/self.refresh_period)
105+
return normalized_delta2_Vsync
106+
107+
def __get_round_normalized_delta2_Vsync(self):
108+
normalized_delta2_Vsync = self.__get_normalized_delta2_Vsync()
109+
round_normalized_delta2_Vsync = []
110+
for i in xrange(0, self.frame_latency_data_size-2):
111+
value = round(max(normalized_delta2_Vsync[i], 0.0))
112+
round_normalized_delta2_Vsync.append(value)
113+
114+
return round_normalized_delta2_Vsync
115+
116+
def get_Vsync_jankiness(self):
117+
if (self.refresh_period< 0):
118+
return -1
119+
round_normalized_delta2_Vsync = self.__get_round_normalized_delta2_Vsync()
120+
121+
num_jankiness = 0
122+
for i in xrange(0, self.frame_latency_data_size-2):
123+
value = round_normalized_delta2_Vsync[i]
124+
if value > 0 and value < self.PAUSE_LATENCY:
125+
num_jankiness += 1
126+
127+
return num_jankiness
128+
129+
def get_max_delta_Vsync(self):
130+
return round(self.max_Vsync/self.refresh_period)
131+
132+
def get_frame_rate(self):
133+
if self.refresh_period < 0:
134+
return -1
135+
if not self.frame_buffer_data:
136+
return -1
137+
start_time = long(self.frame_buffer_data[0][1])
138+
end_time = long(self.frame_buffer_data[-1][1])
139+
total_time = end_time - start_time
140+
return (self.frame_latency_data_size - 1) * 1e9 / total_time
141+
142+
#停止数据采集
143+
def stop_dump_latency_data(self):
144+
self.refresh_period = -1
145+
self.frame_buffer_data = []
146+
self.frame_latency_data_size = 0
147+
self.max_Vsync = 0
148+
149+
def write_csv(*list):
150+
path = PATH("{}/fps_data".format(os.getcwd()))
151+
if not os.path.isdir(path):
152+
os.makedirs(path)
153+
f = open(PATH("%s/fps-%s.csv" %(path, utils.timestamp())), "w")
154+
times = list[0]
155+
print times
156+
fps = list[1]
157+
jankniess = list[2]
158+
159+
for i in xrange(0, len(fps) - 1):
160+
f.write("{0},{1},{2},\n".format(str(times[i]), str(fps[i]), str(jankniess[i])))
161+
162+
f.close()
163+
164+
if __name__ == "__main__":
165+
if not raw_input("Make sure the test Activity, in this process, you should keep in this Activity!\n"
166+
"Please press Enter continue..."):
167+
sleep_time = -1
168+
dump_time = -1
169+
while not 0 < sleep_time <= 10:
170+
try:
171+
sleep_time = float(raw_input("Please input sleep time(0-10s) :"))
172+
except:
173+
continue
174+
while dump_time < 0:
175+
try:
176+
dump_time = int(raw_input("Please input dump times: "))
177+
except:
178+
continue
179+
180+
activity_name = utils.get_focused_package_and_activity()
181+
print "Current Activity: "
182+
print activity_name
183+
sf = SurfaceFlinger(activity_name, sleep_time)
184+
times = ["time"] + [i for i in xrange(1, dump_time+1)]
185+
jankniess = ["Jankniess"]
186+
fps = ["fps"]
187+
for i in xrange(0, dump_time):
188+
sf.start_dump_latency_data()
189+
frame= sf.get_frame_rate();
190+
if frame != -1:
191+
jankniess.append(sf.get_Vsync_jankiness())
192+
fps.append(frame)
193+
sf.stop_dump_latency_data()
194+
print "jankniess:"
195+
print jankniess
196+
print "fps:"
197+
print fps
198+
199+
write_csv(times, fps, jankniess)
200+
201+
raw_input("Please press Enter quit...")
202+
sys.exit(0)

0 commit comments

Comments
 (0)