1import threading
2
3from autotest_lib.client.common_lib.cros import system_metrics_collector
4from autotest_lib.client.common_lib.cros.cfm.metrics import (
5        media_metrics_collector)
6
7
8class PerfMetricsCollector(object):
9    """
10    Metrics collector that runs in seprate thread than the caller.
11    """
12    def __init__(self, system_facade, cfm_facade, writer_function,
13                 additional_system_metrics=[]):
14        """
15        Constructor.
16
17        @param system_facade facade object to access system utils.
18        @param cfm_facade facade object to access cfm utils.
19        @param writer_function function called to collected metrics.
20        @param additional_system_metrics Additional metrics to collect.
21        """
22        metric_set = system_metrics_collector.create_default_metric_set(
23            system_facade)
24        for metric in additional_system_metrics:
25            metric_set.append(metric)
26        self._system_metrics_collector = (
27            system_metrics_collector.SystemMetricsCollector(system_facade,
28                                                            metric_set))
29        # Media metrics
30        data_point_collector = media_metrics_collector.DataPointCollector(
31            cfm_facade)
32        self._media_metrics_collector = (media_metrics_collector
33                                        .MetricsCollector(data_point_collector))
34
35        self._writer_function = writer_function
36        # Collector thread.
37        self._collector_thread = threading.Thread(
38            target=self._collect_snapshots_until_stopped)
39        self._stop = threading.Event()
40
41    def start(self):
42        """
43        Starts metrics collection.
44        """
45        self._system_metrics_collector.pre_collect()
46        self._collector_thread.start()
47
48    def stop(self):
49        """
50        Stops metrics collection.
51        """
52        self._stop.set()
53
54    def upload_metrics(self):
55        """
56        Uploads collected metrics.
57        """
58        self._upload_system_metrics()
59        self._upload_media_metrics()
60
61    def _collect_snapshots_until_stopped(self):
62        while not self._stop.wait(10):
63            self._system_metrics_collector.collect_snapshot()
64            self._media_metrics_collector.collect_snapshot()
65
66    def _upload_system_metrics(self):
67        self._system_metrics_collector.write_metrics(self._writer_function)
68
69    def _get_jmi_data(self, data_type):
70        """
71        Gets jmi data for the given data type.
72
73        @param data_type: Type of data to be retrieved from jmi data logs.
74        @return Data for given data type from jmidata log.
75        """
76        timestamped_values = self._media_metrics_collector.get_metric(data_type)
77        # Strip timestamps.
78        values = [x[1] for x in timestamped_values]
79        # Each entry in values is a list, extract the raw values:
80        res = []
81        for value_list in values:
82            res.extend(value_list)
83        # Ensure we always return at least one element, or perf uploads will
84        # be sad.
85        return res or [0]
86
87    def _get_last_value(self, data_type):
88        """
89        Gets last value of a list of numbers.
90
91        @param data_type: Type of data to be retrieved from jmi data log.
92        @return The last value in the jmidata for the specified data_type. 0 if
93                there are no values in the jmidata for this data_type.
94        """
95        data = self._get_jmi_data(data_type)
96        if not data:
97            return 0
98        return data[-1]
99
100    def _upload_media_metrics(self):
101        """
102        Write jmidata results to results-chart.json file for Perf Dashboard.
103        """
104        # Video/Sender metrics
105        self._writer_function(
106            description='avg_encode_ms',
107            value=self._get_jmi_data(media_metrics_collector.AVG_ENCODE_MS),
108            units='ms',
109            higher_is_better=False)
110
111        self._writer_function(
112            description='vid_out_frame_height', # video_out_res
113            value=self._get_jmi_data(media_metrics_collector.
114                VIDEO_SENT_FRAME_HEIGHT),
115            units='px',
116            higher_is_better=True)
117
118        self._writer_function(
119            description='vid_out_frame_width',
120            value=self._get_jmi_data(media_metrics_collector.
121                VIDEO_SENT_FRAME_WIDTH),
122            units='px',
123            higher_is_better=True)
124
125        self._writer_function(
126            description='vid_out_framerate_captured',
127            value=self._get_jmi_data(media_metrics_collector.
128                FRAMERATE_CAPTURED),
129            units='fps',
130            higher_is_better=True)
131
132        self._writer_function(
133            description='vid_out_framerate_encoded',
134            value=self._get_jmi_data(media_metrics_collector.
135                FRAMERATE_ENCODED),
136            units='fps',
137            higher_is_better=True)
138
139        self._writer_function(
140            description='vid_out_sent_packets',
141            value=self._get_last_value(media_metrics_collector.
142                VIDEO_SENT_PACKETS),
143            units='packets',
144            higher_is_better=True)
145
146        # Video/Receiver metrics
147        self._writer_function(
148            description='vid_in_framerate_received',
149            value=self._get_jmi_data(media_metrics_collector.
150                FRAMERATE_NETWORK_RECEIVED),
151            units='fps',
152            higher_is_better=True)
153
154        self._writer_function(
155            description='vid_in_framerate_decoded',
156            value=self._get_jmi_data(media_metrics_collector.FRAMERATE_DECODED),
157            units='fps',
158            higher_is_better=True)
159
160        self._writer_function(
161            description='vid_in_framerate_to_renderer',
162            value=self._get_jmi_data(media_metrics_collector.
163                FRAMERATE_TO_RENDERER),
164            units='fps',
165            higher_is_better=True)
166
167        self._writer_function(
168            description='video_in_frame_heigth', # video_in_res
169            value=self._get_jmi_data(media_metrics_collector.
170                VIDEO_RECEIVED_FRAME_HEIGHT),
171            units='px',
172            higher_is_better=True)
173
174        self._writer_function(
175            description='vid_in_frame_width',
176            value=self._get_jmi_data(media_metrics_collector.
177                VIDEO_RECEIVED_FRAME_WIDTH),
178            units='px',
179            higher_is_better=True)
180
181        # Adaptation metrics
182        self._writer_function(
183            description='vid_out_adapt_changes',
184            value=self._get_last_value(media_metrics_collector.
185                ADAPTATION_CHANGES),
186            units='count',
187            higher_is_better=False)
188
189        self._writer_function(
190            description='vid_out_adapt_reasons',
191            value=self._get_jmi_data(media_metrics_collector.ADAPTATION_REASON),
192            units='reasons',
193            higher_is_better=False)
194
195        # System metrics
196        self._writer_function(
197            description='cpu_usage_jmi',
198            value=self._get_jmi_data(media_metrics_collector.
199                CPU_PERCENT_OF_TOTAL),
200            units='percent',
201            higher_is_better=False)
202
203        self._writer_function(
204            description='process_js_memory',
205            value=[(x / (1024 * 1024)) for x in self._get_jmi_data(
206                media_metrics_collector.PROCESS_JS_MEMORY_USED)],
207            units='MB',
208            higher_is_better=False)
209
210        self._writer_function(
211            description='renderer_cpu_usage',
212            value=self._get_jmi_data(
213                media_metrics_collector.RENDERER_CPU_PERCENT_OF_TOTAL),
214            units='percent',
215            higher_is_better=False)
216
217        self._writer_function(
218            description='browser_cpu_usage',
219            value=self._get_jmi_data(
220                media_metrics_collector.BROWSER_CPU_PERCENT_OF_TOTAL),
221            units='percent',
222            higher_is_better=False)
223
224        self._writer_function(
225            description='gpu_cpu_usage',
226            value=self._get_jmi_data(
227                media_metrics_collector.GPU_PERCENT_OF_TOTAL),
228            units='percent',
229            higher_is_better=False)
230
231        # Other
232        self._writer_function(
233            description='active_streams',
234            value=self._get_jmi_data(media_metrics_collector.
235                NUMBER_OF_ACTIVE_INCOMING_VIDEO_STREAMS),
236            units='count',
237            higher_is_better=True)
238