Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(403)

Side by Side Diff: build/android/pylib/surface_stats_collector.py

Issue 12438022: telemetry: Update Android frame latency metrics (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import Queue 5 import Queue
6 import datetime 6 import datetime
7 import logging 7 import logging
8 import re 8 import re
9 import threading 9 import threading
10 10
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 63
64 def _StorePerfResults(self): 64 def _StorePerfResults(self):
65 if self._use_legacy_method: 65 if self._use_legacy_method:
66 surface_after = self._GetSurfaceStatsLegacy() 66 surface_after = self._GetSurfaceStatsLegacy()
67 td = surface_after['timestamp'] - self._surface_before['timestamp'] 67 td = surface_after['timestamp'] - self._surface_before['timestamp']
68 seconds = td.seconds + td.microseconds / 1e6 68 seconds = td.seconds + td.microseconds / 1e6
69 frame_count = (surface_after['page_flip_count'] - 69 frame_count = (surface_after['page_flip_count'] -
70 self._surface_before['page_flip_count']) 70 self._surface_before['page_flip_count'])
71 else: 71 else:
72 assert self._collector_thread 72 assert self._collector_thread
73 (seconds, latencies) = self._GetDataFromThread() 73 (refresh_period, timestamps) = self._GetDataFromThread()
74 if not seconds or not len(latencies): 74 if not refresh_period or not len(timestamps) >= 3:
75 logging.warning('Surface stat data is empty') 75 logging.warning('Surface stat data is empty')
76 return 76 return
77 frame_count = len(timestamps)
78 seconds = timestamps[-1] - timestamps[0]
77 79
78 frame_count = len(latencies) 80 frame_lengths = [t2 - t1 for t1, t2 in zip(timestamps, timestamps[1:])]
79 jitter_count = 0 81 normalized_frame_lengths = [length / refresh_period for length in
80 last_latency = latencies[0] 82 frame_lengths]
81 for latency in latencies[1:]: 83
82 if latency > last_latency: 84 length_changes = [d2 - d1 for d1, d2 in zip(frame_lengths,
83 jitter_count = jitter_count + 1 85 frame_lengths[1:])]
84 last_latency = latency 86 normalized_changes = [change / refresh_period
87 for change in length_changes]
bulach 2013/03/18 18:45:03 not sure if it'd be any more readable, but it seem
Sami 2013/03/18 19:03:15 That's much more readable, thanks!
88 jankiness = [max(0, round(change)) for change in normalized_changes]
89 pause_threshold = 20
90 jank_count = sum(1 for change in jankiness
91 if change > 0 and change < pause_threshold)
85 92
86 self._results.append(SurfaceStatsCollector.Result( 93 self._results.append(SurfaceStatsCollector.Result(
87 'surface_latencies', latencies, '')) 94 'refresh_period', [refresh_period], 'seconds'))
88 self._results.append(SurfaceStatsCollector.Result( 95 self._results.append(SurfaceStatsCollector.Result(
89 'peak_jitter', [max(latencies)], '')) 96 'jank_count', [jank_count], 'janks'))
90 self._results.append(SurfaceStatsCollector.Result( 97 self._results.append(SurfaceStatsCollector.Result(
91 'jitter_percent', [jitter_count * 100.0 / frame_count], 'percent')) 98 'max_frame_delay', [round(max(normalized_frame_lengths))],
99 'vsyncs'))
100 self._results.append(SurfaceStatsCollector.Result(
101 'frame_lengths', normalized_frame_lengths, 'vsyncs'))
92 self._results.append(SurfaceStatsCollector.Result( 102 self._results.append(SurfaceStatsCollector.Result(
93 'avg_surface_fps', [int(round(frame_count / seconds))], 'fps')) 103 'avg_surface_fps', [int(round(frame_count / seconds))], 'fps'))
94 104
95 def _CollectorThread(self): 105 def _CollectorThread(self):
96 last_timestamp = 0 106 last_timestamp = 0
97 first_timestamp = 0 107 timestamps = []
98 latencies = []
99 retries = 0 108 retries = 0
100 has_collected_data = False
101 109
102 while not self._stop_event.is_set(): 110 while not self._stop_event.is_set():
103 self._get_data_event.wait(1) 111 self._get_data_event.wait(1)
104 try: 112 try:
105 (t, last_timestamp) = self._GetSurfaceFlingerLatencyData(last_timestamp, 113 refresh_period, new_timestamps = self._GetSurfaceFlingerFrameData()
106 latencies) 114 if refresh_period is None or timestamps is None:
107 if (t, last_timestamp) == (None, None):
108 retries += 1 115 retries += 1
109 if retries < 3: 116 if retries < 3:
110 continue 117 continue
111 if has_collected_data: 118 if last_timestamp:
112 # Some data has already been collected, but either the app 119 # Some data has already been collected, but either the app
113 # was closed or there's no new data. Signal the main thread and 120 # was closed or there's no new data. Signal the main thread and
114 # wait. 121 # wait.
115 self._data_queue.put((None, None)) 122 self._data_queue.put((None, None))
116 self._stop_event.wait() 123 self._stop_event.wait()
117 break 124 break
118 raise Exception('Unable to get surface flinger latency data') 125 raise Exception('Unable to get surface flinger latency data')
119 126
120 has_collected_data = True 127 timestamps += [timestamp for timestamp in new_timestamps
121 128 if timestamp > last_timestamp]
122 if not first_timestamp: 129 if len(timestamps):
123 first_timestamp = t 130 last_timestamp = timestamps[-1]
124 131
125 if self._get_data_event.is_set(): 132 if self._get_data_event.is_set():
126 self._get_data_event.clear() 133 self._get_data_event.clear()
127 self._data_queue.put(((last_timestamp - first_timestamp) / 1e9, 134 self._data_queue.put((refresh_period, timestamps))
128 latencies)) 135 timestamps = []
129 latencies = []
130 first_timestamp = 0
131 except Exception as e: 136 except Exception as e:
132 # On any error, before aborting, put the exception into _data_queue to 137 # On any error, before aborting, put the exception into _data_queue to
133 # prevent the main thread from waiting at _data_queue.get() infinitely. 138 # prevent the main thread from waiting at _data_queue.get() infinitely.
134 self._data_queue.put(e) 139 self._data_queue.put(e)
135 raise 140 raise
136 141
137 def _GetDataFromThread(self): 142 def _GetDataFromThread(self):
138 self._get_data_event.set() 143 self._get_data_event.set()
139 ret = self._data_queue.get() 144 ret = self._data_queue.get()
140 if isinstance(ret, Exception): 145 if isinstance(ret, Exception):
141 raise ret 146 raise ret
142 return ret 147 return ret
143 148
144 def _ClearSurfaceFlingerLatencyData(self): 149 def _ClearSurfaceFlingerLatencyData(self):
145 """Clears the SurfaceFlinger latency data. 150 """Clears the SurfaceFlinger latency data.
146 151
147 Returns: 152 Returns:
148 True if SurfaceFlinger latency is supported by the device, otherwise 153 True if SurfaceFlinger latency is supported by the device, otherwise
149 False. 154 False.
150 """ 155 """
151 # The command returns nothing if it is supported, otherwise returns many 156 # The command returns nothing if it is supported, otherwise returns many
152 # lines of result just like 'dumpsys SurfaceFlinger'. 157 # lines of result just like 'dumpsys SurfaceFlinger'.
153 results = self._adb.RunShellCommand( 158 results = self._adb.RunShellCommand(
154 'dumpsys SurfaceFlinger --latency-clear SurfaceView') 159 'dumpsys SurfaceFlinger --latency-clear SurfaceView')
155 return not len(results) 160 return not len(results)
156 161
157 def _GetSurfaceFlingerLatencyData(self, previous_timestamp, latencies): 162 def _GetSurfaceFlingerFrameData(self):
158 """Returns collected SurfaceFlinger latency data. 163 """Returns collected SurfaceFlinger frame timing data.
159
160 Args:
161 previous_timestamp: The timestamp returned from the previous call or 0.
162 Only data after this timestamp will be returned.
163 latencies: A list to receive latency data. The latencies are integers
164 each of which is the number of refresh periods of each frame.
165 164
166 Returns: 165 Returns:
167 A tuple containing: 166 A tuple containing:
168 - The timestamp of the beginning of the first frame (ns), 167 - The display's nominal refresh period in seconds.
169 - The timestamp of the end of the last frame (ns). 168 - A list of timestamps signifying frame presentation times in seconds.
170 The tuple may be (None, None) if there was no data collected (for example, 169 The return value may be (None, None) if there was no data collected (for
171 if the app was closed before the collector thread has finished). 170 example, if the app was closed before the collector thread has finished).
172 """ 171 """
173 # adb shell dumpsys SurfaceFlinger --latency <window name> 172 # adb shell dumpsys SurfaceFlinger --latency <window name>
174 # prints some information about the last 128 frames displayed in 173 # prints some information about the last 128 frames displayed in
175 # that window. 174 # that window.
176 # The data returned looks like this: 175 # The data returned looks like this:
177 # 16954612 176 # 16954612
178 # 7657467895508 7657482691352 7657493499756 177 # 7657467895508 7657482691352 7657493499756
179 # 7657484466553 7657499645964 7657511077881 178 # 7657484466553 7657499645964 7657511077881
180 # 7657500793457 7657516600576 7657527404785 179 # 7657500793457 7657516600576 7657527404785
181 # (...) 180 # (...)
(...skipping 15 matching lines...) Expand all
197 # janky, even if it runs at 60 fps in average. 196 # janky, even if it runs at 60 fps in average.
198 # 197 #
199 # We use the special "SurfaceView" window name because the statistics for 198 # We use the special "SurfaceView" window name because the statistics for
200 # the activity's main window are not updated when the main web content is 199 # the activity's main window are not updated when the main web content is
201 # composited into a SurfaceView. 200 # composited into a SurfaceView.
202 results = self._adb.RunShellCommand( 201 results = self._adb.RunShellCommand(
203 'dumpsys SurfaceFlinger --latency SurfaceView', log_result=True) 202 'dumpsys SurfaceFlinger --latency SurfaceView', log_result=True)
204 if not len(results): 203 if not len(results):
205 return (None, None) 204 return (None, None)
206 205
207 refresh_period = int(results[0]) 206 timestamps = []
208 last_timestamp = previous_timestamp 207 nanoseconds_per_second = 1e9
209 first_timestamp = 0 208 refresh_period = long(results[0]) / nanoseconds_per_second
209
210 for line in results[1:]: 210 for line in results[1:]:
211 fields = line.split() 211 fields = line.split()
212 if len(fields) == 3: 212 if len(fields) != 3:
213 timestamp = long(fields[0]) 213 continue
214 last_timestamp = long(fields[2]) 214 timestamp = long(fields[1]) / nanoseconds_per_second
215 if (timestamp > previous_timestamp): 215 timestamps.append(timestamp)
216 if not first_timestamp: 216
217 first_timestamp = timestamp 217 return (refresh_period, timestamps)
218 # This is integral equivalent of ceil((C-A) / refresh-period)
219 latency_ns = int(last_timestamp - timestamp)
220 latencies.append((latency_ns + refresh_period - 1) / refresh_period)
221 return (first_timestamp, last_timestamp)
222 218
223 def _GetSurfaceStatsLegacy(self): 219 def _GetSurfaceStatsLegacy(self):
224 """Legacy method (before JellyBean), returns the current Surface index 220 """Legacy method (before JellyBean), returns the current Surface index
225 and timestamp. 221 and timestamp.
226 222
227 Calculate FPS by measuring the difference of Surface index returned by 223 Calculate FPS by measuring the difference of Surface index returned by
228 SurfaceFlinger in a period of time. 224 SurfaceFlinger in a period of time.
229 225
230 Returns: 226 Returns:
231 Dict of {page_flip_count (or 0 if there was an error), timestamp}. 227 Dict of {page_flip_count (or 0 if there was an error), timestamp}.
232 """ 228 """
233 results = self._adb.RunShellCommand('service call SurfaceFlinger 1013') 229 results = self._adb.RunShellCommand('service call SurfaceFlinger 1013')
234 assert len(results) == 1 230 assert len(results) == 1
235 match = re.search('^Result: Parcel\((\w+)', results[0]) 231 match = re.search('^Result: Parcel\((\w+)', results[0])
236 cur_surface = 0 232 cur_surface = 0
237 if match: 233 if match:
238 try: 234 try:
239 cur_surface = int(match.group(1), 16) 235 cur_surface = int(match.group(1), 16)
240 except Exception: 236 except Exception:
241 logging.error('Failed to parse current surface from ' + match.group(1)) 237 logging.error('Failed to parse current surface from ' + match.group(1))
242 else: 238 else:
243 logging.warning('Failed to call SurfaceFlinger surface ' + results[0]) 239 logging.warning('Failed to call SurfaceFlinger surface ' + results[0])
244 return { 240 return {
245 'page_flip_count': cur_surface, 241 'page_flip_count': cur_surface,
246 'timestamp': datetime.datetime.now(), 242 'timestamp': datetime.datetime.now(),
247 } 243 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698