blob: 53dd238beb7e7b9a3aec06f568fd7e58a0cad51b [file] [log] [blame]
Sergei Trofimov195085e2017-06-06 14:41:54 +01001import csv
2import logging
3import os
4import re
5import shutil
6import sys
7import tempfile
8import threading
9import time
10from collections import namedtuple, OrderedDict
11from distutils.version import LooseVersion
12
13from devlib.exception import WorkerThreadError, TargetNotRespondingError, TimeoutError
14
15
16logger = logging.getLogger('rendering')
17
18
19class FrameCollector(threading.Thread):
20
21 def __init__(self, target, period):
22 super(FrameCollector, self).__init__()
23 self.target = target
24 self.period = period
25 self.stop_signal = threading.Event()
26 self.frames = []
27
28 self.temp_file = None
29 self.refresh_period = None
30 self.drop_threshold = None
31 self.unresponsive_count = 0
32 self.last_ready_time = None
33 self.exc = None
34 self.header = None
35
36 def run(self):
37 logger.debug('Surface flinger frame data collection started.')
38 try:
39 self.stop_signal.clear()
40 fd, self.temp_file = tempfile.mkstemp()
41 logger.debug('temp file: {}'.format(self.temp_file))
42 wfh = os.fdopen(fd, 'wb')
43 try:
44 while not self.stop_signal.is_set():
45 self.collect_frames(wfh)
46 time.sleep(self.period)
47 finally:
48 wfh.close()
49 except (TargetNotRespondingError, TimeoutError): # pylint: disable=W0703
50 raise
51 except Exception, e: # pylint: disable=W0703
52 logger.warning('Exception on collector thread: {}({})'.format(e.__class__.__name__, e))
53 self.exc = WorkerThreadError(self.name, sys.exc_info())
54 logger.debug('Surface flinger frame data collection stopped.')
55
56 def stop(self):
57 self.stop_signal.set()
58 self.join()
59 if self.unresponsive_count:
60 message = 'FrameCollector was unrepsonsive {} times.'.format(self.unresponsive_count)
61 if self.unresponsive_count > 10:
62 logger.warning(message)
63 else:
64 logger.debug(message)
65 if self.exc:
66 raise self.exc # pylint: disable=E0702
67
68 def process_frames(self, outfile=None):
69 if not self.temp_file:
70 raise RuntimeError('Attempting to process frames before running the collector')
71 with open(self.temp_file) as fh:
72 self._process_raw_file(fh)
73 if outfile:
74 shutil.copy(self.temp_file, outfile)
75 os.unlink(self.temp_file)
76 self.temp_file = None
77
78 def write_frames(self, outfile, columns=None):
79 if columns is None:
80 header = self.header
81 frames = self.frames
82 else:
83 header = [c for c in self.header if c in columns]
84 indexes = [self.header.index(c) for c in header]
85 frames = [[f[i] for i in indexes] for f in self.frames]
86 with open(outfile, 'w') as wfh:
87 writer = csv.writer(wfh)
88 if header:
89 writer.writerow(header)
90 writer.writerows(frames)
91
92 def collect_frames(self, wfh):
93 raise NotImplementedError()
94
95 def clear(self):
96 raise NotImplementedError()
97
98 def _process_raw_file(self, fh):
99 raise NotImplementedError()
100
101
102def read_gfxinfo_columns(target):
103 output = target.execute('dumpsys gfxinfo --list framestats')
104 lines = iter(output.split('\n'))
105 for line in lines:
106 if line.startswith('---PROFILEDATA---'):
107 break
108 columns_line = lines.next()
109 return columns_line.split(',')[:-1] # has a trailing ','
110
111
112class GfxinfoFrameCollector(FrameCollector):
113
114 def __init__(self, target, period, package, header=None):
115 super(GfxinfoFrameCollector, self).__init__(target, period)
116 self.package = package
117 self.header = None
118 self._init_header(header)
119
120 def collect_frames(self, wfh):
121 cmd = 'dumpsys gfxinfo {} framestats'
122 wfh.write(self.target.execute(cmd.format(self.package)))
123
124 def clear(self):
125 pass
126
127 def _init_header(self, header):
128 if header is not None:
129 self.header = header
130 else:
131 self.header = read_gfxinfo_columns(self.target)
132
133 def _process_raw_file(self, fh):
134 found = False
135 try:
136 while True:
137 for line in fh:
138 if line.startswith('---PROFILEDATA---'):
139 found = True
140 break
141
142 fh.next() # headers
143 for line in fh:
144 if line.startswith('---PROFILEDATA---'):
145 break
146 self.frames.append(map(int, line.strip().split(',')[:-1])) # has a trailing ','
147 except StopIteration:
148 pass
149 if not found:
150 logger.warning('Could not find frames data in gfxinfo output')
151 return