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

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

Issue 13046007: android: Add interactive surface statistics viewer (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Allow more than one bad timestamp. 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 | build/android/surface_stats.py » ('j') | 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 19 matching lines...) Expand all
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
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 }
OLDNEW
« no previous file with comments | « no previous file | build/android/surface_stats.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698