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

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

Issue 1332603002: [Android] Move perf utilities into devil. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: redirections for v8 Created 5 years, 3 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
OLDNEW
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2015 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 # pylint: disable=unused-wildcard-import
6 import threading 6 # pylint: disable=wildcard-import
7 7
8 8 from devil.android.perf.surface_stats_collector import *
9 # Log marker containing SurfaceTexture timestamps.
10 _SURFACE_TEXTURE_TIMESTAMPS_MESSAGE = 'SurfaceTexture update timestamps'
11 _SURFACE_TEXTURE_TIMESTAMP_RE = r'\d+'
12
13
14 class SurfaceStatsCollector(object):
15 """Collects surface stats for a SurfaceView from the output of SurfaceFlinger.
16
17 Args:
18 device: A DeviceUtils instance.
19 """
20
21 def __init__(self, device):
22 self._device = device
23 self._collector_thread = None
24 self._surface_before = None
25 self._get_data_event = None
26 self._data_queue = None
27 self._stop_event = None
28 self._warn_about_empty_data = True
29
30 def DisableWarningAboutEmptyData(self):
31 self._warn_about_empty_data = False
32
33 def Start(self):
34 assert not self._collector_thread
35
36 if self._ClearSurfaceFlingerLatencyData():
37 self._get_data_event = threading.Event()
38 self._stop_event = threading.Event()
39 self._data_queue = Queue.Queue()
40 self._collector_thread = threading.Thread(target=self._CollectorThread)
41 self._collector_thread.start()
42 else:
43 raise Exception('SurfaceFlinger not supported on this device.')
44
45 def Stop(self):
46 assert self._collector_thread
47 (refresh_period, timestamps) = self._GetDataFromThread()
48 if self._collector_thread:
49 self._stop_event.set()
50 self._collector_thread.join()
51 self._collector_thread = None
52 return (refresh_period, timestamps)
53
54 def _CollectorThread(self):
55 last_timestamp = 0
56 timestamps = []
57 retries = 0
58
59 while not self._stop_event.is_set():
60 self._get_data_event.wait(1)
61 try:
62 refresh_period, new_timestamps = self._GetSurfaceFlingerFrameData()
63 if refresh_period is None or timestamps is None:
64 retries += 1
65 if retries < 3:
66 continue
67 if last_timestamp:
68 # Some data has already been collected, but either the app
69 # was closed or there's no new data. Signal the main thread and
70 # wait.
71 self._data_queue.put((None, None))
72 self._stop_event.wait()
73 break
74 raise Exception('Unable to get surface flinger latency data')
75
76 timestamps += [timestamp for timestamp in new_timestamps
77 if timestamp > last_timestamp]
78 if len(timestamps):
79 last_timestamp = timestamps[-1]
80
81 if self._get_data_event.is_set():
82 self._get_data_event.clear()
83 self._data_queue.put((refresh_period, timestamps))
84 timestamps = []
85 except Exception as e:
86 # On any error, before aborting, put the exception into _data_queue to
87 # prevent the main thread from waiting at _data_queue.get() infinitely.
88 self._data_queue.put(e)
89 raise
90
91 def _GetDataFromThread(self):
92 self._get_data_event.set()
93 ret = self._data_queue.get()
94 if isinstance(ret, Exception):
95 raise ret
96 return ret
97
98 def _ClearSurfaceFlingerLatencyData(self):
99 """Clears the SurfaceFlinger latency data.
100
101 Returns:
102 True if SurfaceFlinger latency is supported by the device, otherwise
103 False.
104 """
105 # The command returns nothing if it is supported, otherwise returns many
106 # lines of result just like 'dumpsys SurfaceFlinger'.
107 results = self._device.RunShellCommand(
108 'dumpsys SurfaceFlinger --latency-clear SurfaceView')
109 return not len(results)
110
111 def GetSurfaceFlingerPid(self):
112 results = self._device.RunShellCommand('ps | grep surfaceflinger')
113 if not results:
114 raise Exception('Unable to get surface flinger process id')
115 pid = results[0].split()[1]
116 return pid
117
118 def _GetSurfaceFlingerFrameData(self):
119 """Returns collected SurfaceFlinger frame timing data.
120
121 Returns:
122 A tuple containing:
123 - The display's nominal refresh period in milliseconds.
124 - A list of timestamps signifying frame presentation times in
125 milliseconds.
126 The return value may be (None, None) if there was no data collected (for
127 example, if the app was closed before the collector thread has finished).
128 """
129 # adb shell dumpsys SurfaceFlinger --latency <window name>
130 # prints some information about the last 128 frames displayed in
131 # that window.
132 # The data returned looks like this:
133 # 16954612
134 # 7657467895508 7657482691352 7657493499756
135 # 7657484466553 7657499645964 7657511077881
136 # 7657500793457 7657516600576 7657527404785
137 # (...)
138 #
139 # The first line is the refresh period (here 16.95 ms), it is followed
140 # by 128 lines w/ 3 timestamps in nanosecond each:
141 # A) when the app started to draw
142 # B) the vsync immediately preceding SF submitting the frame to the h/w
143 # C) timestamp immediately after SF submitted that frame to the h/w
144 #
145 # The difference between the 1st and 3rd timestamp is the frame-latency.
146 # An interesting data is when the frame latency crosses a refresh period
147 # boundary, this can be calculated this way:
148 #
149 # ceil((C - A) / refresh-period)
150 #
151 # (each time the number above changes, we have a "jank").
152 # If this happens a lot during an animation, the animation appears
153 # janky, even if it runs at 60 fps in average.
154 #
155 # We use the special "SurfaceView" window name because the statistics for
156 # the activity's main window are not updated when the main web content is
157 # composited into a SurfaceView.
158 results = self._device.RunShellCommand(
159 'dumpsys SurfaceFlinger --latency SurfaceView')
160 if not len(results):
161 return (None, None)
162
163 timestamps = []
164 nanoseconds_per_millisecond = 1e6
165 refresh_period = long(results[0]) / nanoseconds_per_millisecond
166
167 # If a fence associated with a frame is still pending when we query the
168 # latency data, SurfaceFlinger gives the frame a timestamp of INT64_MAX.
169 # Since we only care about completed frames, we will ignore any timestamps
170 # with this value.
171 pending_fence_timestamp = (1 << 63) - 1
172
173 for line in results[1:]:
174 fields = line.split()
175 if len(fields) != 3:
176 continue
177 timestamp = long(fields[1])
178 if timestamp == pending_fence_timestamp:
179 continue
180 timestamp /= nanoseconds_per_millisecond
181 timestamps.append(timestamp)
182
183 return (refresh_period, timestamps)
OLDNEW
« no previous file with comments | « build/android/pylib/perf/perf_control_unittest.py ('k') | build/android/pylib/perf/thermal_throttle.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698