| OLD | NEW |
| 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 19 matching lines...) Expand all Loading... |
| 30 | 30 |
| 31 def __init__(self, adb): | 31 def __init__(self, adb): |
| 32 self._adb = adb | 32 self._adb = adb |
| 33 self._collector_thread = None | 33 self._collector_thread = None |
| 34 self._use_legacy_method = False | 34 self._use_legacy_method = False |
| 35 self._surface_before = None | 35 self._surface_before = None |
| 36 self._get_data_event = None | 36 self._get_data_event = None |
| 37 self._data_queue = None | 37 self._data_queue = None |
| 38 self._stop_event = None | 38 self._stop_event = None |
| 39 self._results = [] | 39 self._results = [] |
| 40 self._warn_about_empty_data = True |
| 41 |
| 42 def DisableWarningAboutEmptyData(self): |
| 43 self._warn_about_empty_data = False |
| 40 | 44 |
| 41 def Start(self): | 45 def Start(self): |
| 42 assert not self._collector_thread | 46 assert not self._collector_thread |
| 43 | 47 |
| 44 if self._ClearSurfaceFlingerLatencyData(): | 48 if self._ClearSurfaceFlingerLatencyData(): |
| 45 self._get_data_event = threading.Event() | 49 self._get_data_event = threading.Event() |
| 46 self._stop_event = threading.Event() | 50 self._stop_event = threading.Event() |
| 47 self._data_queue = Queue.Queue() | 51 self._data_queue = Queue.Queue() |
| 48 self._collector_thread = threading.Thread(target=self._CollectorThread) | 52 self._collector_thread = threading.Thread(target=self._CollectorThread) |
| 49 self._collector_thread.start() | 53 self._collector_thread.start() |
| 50 else: | 54 else: |
| 51 self._use_legacy_method = True | 55 self._use_legacy_method = True |
| 52 self._surface_before = self._GetSurfaceStatsLegacy() | 56 self._surface_before = self._GetSurfaceStatsLegacy() |
| 53 | 57 |
| 54 def Stop(self): | 58 def Stop(self): |
| 55 self._StorePerfResults() | 59 self._StorePerfResults() |
| 56 if self._collector_thread: | 60 if self._collector_thread: |
| 57 self._stop_event.set() | 61 self._stop_event.set() |
| 58 self._collector_thread.join() | 62 self._collector_thread.join() |
| 59 self._collector_thread = None | 63 self._collector_thread = None |
| 60 | 64 |
| 65 def SampleResults(self): |
| 66 self._StorePerfResults() |
| 67 results = self._results |
| 68 self._results = [] |
| 69 return results |
| 70 |
| 61 def GetResults(self): | 71 def GetResults(self): |
| 62 return self._results | 72 return self._results |
| 63 | 73 |
| 64 @staticmethod | 74 @staticmethod |
| 65 def _GetNormalizedDeltas(data, refresh_period): | 75 def _GetNormalizedDeltas(data, refresh_period): |
| 66 deltas = [t2 - t1 for t1, t2 in zip(data, data[1:])] | 76 deltas = [t2 - t1 for t1, t2 in zip(data, data[1:])] |
| 67 return (deltas, [delta / refresh_period for delta in deltas]) | 77 return (deltas, [delta / refresh_period for delta in deltas]) |
| 68 | 78 |
| 69 def _StorePerfResults(self): | 79 def _StorePerfResults(self): |
| 70 if self._use_legacy_method: | 80 if self._use_legacy_method: |
| 71 surface_after = self._GetSurfaceStatsLegacy() | 81 surface_after = self._GetSurfaceStatsLegacy() |
| 72 td = surface_after['timestamp'] - self._surface_before['timestamp'] | 82 td = surface_after['timestamp'] - self._surface_before['timestamp'] |
| 73 seconds = td.seconds + td.microseconds / 1e6 | 83 seconds = td.seconds + td.microseconds / 1e6 |
| 74 frame_count = (surface_after['page_flip_count'] - | 84 frame_count = (surface_after['page_flip_count'] - |
| 75 self._surface_before['page_flip_count']) | 85 self._surface_before['page_flip_count']) |
| 76 else: | 86 else: |
| 77 assert self._collector_thread | 87 assert self._collector_thread |
| 78 (refresh_period, timestamps) = self._GetDataFromThread() | 88 (refresh_period, timestamps) = self._GetDataFromThread() |
| 79 if not refresh_period or not len(timestamps) >= 3: | 89 if not refresh_period or not len(timestamps) >= 3: |
| 80 logging.warning('Surface stat data is empty') | 90 if self._warn_about_empty_data: |
| 91 logging.warning('Surface stat data is empty') |
| 81 return | 92 return |
| 82 frame_count = len(timestamps) | 93 frame_count = len(timestamps) |
| 83 seconds = timestamps[-1] - timestamps[0] | 94 seconds = timestamps[-1] - timestamps[0] |
| 84 | 95 |
| 85 frame_lengths, normalized_frame_lengths = \ | 96 frame_lengths, normalized_frame_lengths = \ |
| 86 self._GetNormalizedDeltas(timestamps, refresh_period) | 97 self._GetNormalizedDeltas(timestamps, refresh_period) |
| 87 length_changes, normalized_changes = \ | 98 length_changes, normalized_changes = \ |
| 88 self._GetNormalizedDeltas(frame_lengths, refresh_period) | 99 self._GetNormalizedDeltas(frame_lengths, refresh_period) |
| 89 jankiness = [max(0, round(change)) for change in normalized_changes] | 100 jankiness = [max(0, round(change)) for change in normalized_changes] |
| 90 pause_threshold = 20 | 101 pause_threshold = 20 |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 201 # composited into a SurfaceView. | 212 # composited into a SurfaceView. |
| 202 results = self._adb.RunShellCommand( | 213 results = self._adb.RunShellCommand( |
| 203 'dumpsys SurfaceFlinger --latency SurfaceView', log_result=True) | 214 'dumpsys SurfaceFlinger --latency SurfaceView', log_result=True) |
| 204 if not len(results): | 215 if not len(results): |
| 205 return (None, None) | 216 return (None, None) |
| 206 | 217 |
| 207 timestamps = [] | 218 timestamps = [] |
| 208 nanoseconds_per_second = 1e9 | 219 nanoseconds_per_second = 1e9 |
| 209 refresh_period = long(results[0]) / nanoseconds_per_second | 220 refresh_period = long(results[0]) / nanoseconds_per_second |
| 210 | 221 |
| 222 # SurfaceFlinger sometimes gives an invalid timestamp for the very latest |
| 223 # frame if it is queried while the frame is still being presented. We ignore |
| 224 # these timestamps. |
| 225 bad_timestamp = (1 << 63) - 1 |
| 226 |
| 211 for line in results[1:]: | 227 for line in results[1:]: |
| 212 fields = line.split() | 228 fields = line.split() |
| 213 if len(fields) != 3: | 229 if len(fields) != 3: |
| 214 continue | 230 continue |
| 215 timestamp = long(fields[1]) / nanoseconds_per_second | 231 timestamp = long(fields[1]) |
| 232 if timestamp == bad_timestamp: |
| 233 continue |
| 234 timestamp /= nanoseconds_per_second |
| 216 timestamps.append(timestamp) | 235 timestamps.append(timestamp) |
| 217 | 236 |
| 218 return (refresh_period, timestamps) | 237 return (refresh_period, timestamps) |
| 219 | 238 |
| 220 def _GetSurfaceStatsLegacy(self): | 239 def _GetSurfaceStatsLegacy(self): |
| 221 """Legacy method (before JellyBean), returns the current Surface index | 240 """Legacy method (before JellyBean), returns the current Surface index |
| 222 and timestamp. | 241 and timestamp. |
| 223 | 242 |
| 224 Calculate FPS by measuring the difference of Surface index returned by | 243 Calculate FPS by measuring the difference of Surface index returned by |
| 225 SurfaceFlinger in a period of time. | 244 SurfaceFlinger in a period of time. |
| 226 | 245 |
| 227 Returns: | 246 Returns: |
| 228 Dict of {page_flip_count (or 0 if there was an error), timestamp}. | 247 Dict of {page_flip_count (or 0 if there was an error), timestamp}. |
| 229 """ | 248 """ |
| 230 results = self._adb.RunShellCommand('service call SurfaceFlinger 1013') | 249 results = self._adb.RunShellCommand('service call SurfaceFlinger 1013') |
| 231 assert len(results) == 1 | 250 assert len(results) == 1 |
| 232 match = re.search('^Result: Parcel\((\w+)', results[0]) | 251 match = re.search('^Result: Parcel\((\w+)', results[0]) |
| 233 cur_surface = 0 | 252 cur_surface = 0 |
| 234 if match: | 253 if match: |
| 235 try: | 254 try: |
| 236 cur_surface = int(match.group(1), 16) | 255 cur_surface = int(match.group(1), 16) |
| 237 except Exception: | 256 except Exception: |
| 238 logging.error('Failed to parse current surface from ' + match.group(1)) | 257 logging.error('Failed to parse current surface from ' + match.group(1)) |
| 239 else: | 258 else: |
| 240 logging.warning('Failed to call SurfaceFlinger surface ' + results[0]) | 259 logging.warning('Failed to call SurfaceFlinger surface ' + results[0]) |
| 241 return { | 260 return { |
| 242 'page_flip_count': cur_surface, | 261 'page_flip_count': cur_surface, |
| 243 'timestamp': datetime.datetime.now(), | 262 'timestamp': datetime.datetime.now(), |
| 244 } | 263 } |
| OLD | NEW |