| # Copyright (c) 2017 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import datetime |
| import glob |
| import os |
| import time |
| |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib.cros import system_metrics_collector |
| from autotest_lib.server.cros import cfm_jmidata_log_collector |
| from autotest_lib.server.cros.cfm import cfm_base_test |
| |
| _SHORT_TIMEOUT = 5 |
| _MEASUREMENT_DURATION_SECONDS = 10 |
| _TOTAL_TEST_DURATION_SECONDS = 900 |
| |
| _BASE_DIR = '/home/chronos/user/Storage/ext/' |
| _EXT_ID = 'ikfcpmgefdpheiiomgmhlmmkihchmdlj' |
| _JMI_DIR = '/0*/File\ System/000/t/00/*' |
| _JMI_SOURCE_DIR = _BASE_DIR + _EXT_ID + _JMI_DIR |
| |
| class ParticipantCountMetric(system_metrics_collector.Metric): |
| """ |
| Metric for getting the current participant count in a call. |
| """ |
| def __init__(self, cfm_facade): |
| """ |
| Initializes with a cfm_facade. |
| |
| @param cfm_facade object having a get_participant_count() method. |
| """ |
| super(ParticipantCountMetric, self).__init__( |
| 'participant_count', |
| 'participants', |
| higher_is_better=True) |
| self.cfm_facade = cfm_facade |
| |
| def collect_metric(self): |
| """ |
| Collects one metric value. |
| """ |
| self.values.append(self.cfm_facade.get_participant_count()) |
| |
| |
| class enterprise_CFM_Perf(cfm_base_test.CfmBaseTest): |
| """This is a server test which clears device TPM and runs |
| enterprise_RemoraRequisition client test to enroll the device in to hotrod |
| mode. After enrollment is successful, it collects and logs cpu, memory and |
| temperature data from the device under test.""" |
| version = 1 |
| |
| def start_hangout(self): |
| """Waits for the landing page and starts a hangout session.""" |
| self.cfm_facade.wait_for_hangouts_telemetry_commands() |
| current_date = datetime.datetime.now().strftime("%Y-%m-%d") |
| hangout_name = current_date + '-cfm-perf' |
| self.cfm_facade.start_new_hangout_session(hangout_name) |
| |
| |
| def join_meeting(self): |
| """Waits for the landing page and joins a meeting session.""" |
| self.cfm_facade.wait_for_meetings_landing_page() |
| # Daily meeting for perf testing with 9 remote participants. |
| meeting_code = 'nis-rhmz-dyh' |
| self.cfm_facade.join_meeting_session(meeting_code) |
| |
| |
| def collect_perf_data(self): |
| """ |
| Collects run time data from the DUT using system_metrics_collector. |
| Writes the data to the chrome perf dashboard. |
| """ |
| start_time = time.time() |
| while (time.time() - start_time) < _TOTAL_TEST_DURATION_SECONDS: |
| time.sleep(_MEASUREMENT_DURATION_SECONDS) |
| self.metrics_collector.collect_snapshot() |
| self.metrics_collector.write_metrics(self.output_perf_value) |
| |
| def _get_average(self, data_type, jmidata): |
| """Computes mean of a list of numbers. |
| |
| @param data_type: Type of data to be retrieved from jmi data log. |
| @param jmidata: Raw jmi data log to parse. |
| @return Mean computed from the list of numbers. |
| """ |
| data = self._get_data_from_jmifile(data_type, jmidata) |
| if not data: |
| return 0 |
| return float(sum(data)) / len(data) |
| |
| |
| def _get_max_value(self, data_type, jmidata): |
| """Computes maximum value of a list of numbers. |
| |
| @param data_type: Type of data to be retrieved from jmi data log. |
| @param jmidata: Raw jmi data log to parse. |
| @return Maxium value from the list of numbers. |
| """ |
| data = self._get_data_from_jmifile(data_type, jmidata) |
| if not data: |
| return 0 |
| return max(data) |
| |
| |
| def _get_sum(self, data_type, jmidata): |
| """Computes sum of a list of numbers. |
| |
| @param data_type: Type of data to be retrieved from jmi data log. |
| @param jmidata: Raw jmi data log to parse. |
| @return Sum computed from the list of numbers. |
| """ |
| data = self._get_data_from_jmifile(data_type, jmidata) |
| if not data: |
| return 0 |
| return sum(data) |
| |
| |
| def _get_last_value(self, data_type, jmidata): |
| """Gets last value of a list of numbers. |
| |
| @param data_type: Type of data to be retrieved from jmi data log. |
| @param jmidata: Raw jmi data log to parse. |
| @return The last value in the jmidata for the specified data_type. 0 if |
| there are no values in the jmidata for this data_type. |
| """ |
| data = self._get_data_from_jmifile(data_type, jmidata) |
| if not data: |
| return 0 |
| return data[-1] |
| |
| |
| def _get_data_from_jmifile(self, data_type, jmidata): |
| """Gets data from jmidata log for given data type. |
| |
| @param data_type: Type of data to be retrieved from jmi data log. |
| @param jmidata: Raw jmi data log to parse. |
| @return Data for given data type from jmidata log. |
| """ |
| return cfm_jmidata_log_collector.GetDataFromLogs( |
| self, data_type, jmidata) |
| |
| |
| def _get_file_to_parse(self): |
| """Copy jmi logs from client to test's results directory. |
| |
| @return The newest jmi log file. |
| """ |
| self._host.get_file(_JMI_SOURCE_DIR, self.resultsdir) |
| source_jmi_files = self.resultsdir + '/0*' |
| if not source_jmi_files: |
| raise error.TestNAError('JMI data file not found.') |
| newest_file = max(glob.iglob(source_jmi_files), key=os.path.getctime) |
| return newest_file |
| |
| def upload_jmidata(self): |
| """ |
| Write jmidata results to results-chart.json file for Perf Dashboard. |
| """ |
| jmi_file = self._get_file_to_parse() |
| jmifile_to_parse = open(jmi_file, 'r') |
| jmidata = jmifile_to_parse.read() |
| |
| # Compute and save aggregated stats from JMI. |
| self.output_perf_value(description='sum_vid_in_frames_decoded', |
| value=self._get_sum('frames_decoded', jmidata), units='frames', |
| higher_is_better=True) |
| |
| self.output_perf_value(description='sum_vid_out_frames_encoded', |
| value=self._get_sum('frames_encoded', jmidata), units='frames', |
| higher_is_better=True) |
| |
| self.output_perf_value(description='vid_out_adapt_changes', |
| value=self._get_last_value('adaptation_changes', jmidata), |
| units='count', higher_is_better=False) |
| |
| self.output_perf_value(description='video_out_encode_time', |
| value=self._get_data_from_jmifile( |
| 'average_encode_time', jmidata), |
| units='ms', higher_is_better=False) |
| |
| self.output_perf_value(description='max_video_out_encode_time', |
| value=self._get_max_value('average_encode_time', jmidata), |
| units='ms', higher_is_better=False) |
| |
| self.output_perf_value(description='vid_out_bandwidth_adapt', |
| value=self._get_average('bandwidth_adaptation', jmidata), |
| units='bool', higher_is_better=False) |
| |
| self.output_perf_value(description='vid_out_cpu_adapt', |
| value=self._get_average('cpu_adaptation', jmidata), |
| units='bool', higher_is_better=False) |
| |
| self.output_perf_value(description='video_in_res', |
| value=self._get_data_from_jmifile( |
| 'video_received_frame_height', jmidata), |
| units='px', higher_is_better=True) |
| |
| self.output_perf_value(description='video_out_res', |
| value=self._get_data_from_jmifile( |
| 'video_sent_frame_height', jmidata), |
| units='resolution', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_in_framerate_decoded', |
| value=self._get_data_from_jmifile( |
| 'framerate_decoded', jmidata), |
| units='fps', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_out_framerate_input', |
| value=self._get_data_from_jmifile( |
| 'framerate_outgoing', jmidata), |
| units='fps', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_in_framerate_to_renderer', |
| value=self._get_data_from_jmifile( |
| 'framerate_to_renderer', jmidata), |
| units='fps', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_in_framerate_received', |
| value=self._get_data_from_jmifile( |
| 'framerate_received', jmidata), |
| units='fps', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_out_framerate_sent', |
| value=self._get_data_from_jmifile('framerate_sent', jmidata), |
| units='fps', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_in_frame_width', |
| value=self._get_data_from_jmifile( |
| 'video_received_frame_width', jmidata), |
| units='px', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_out_frame_width', |
| value=self._get_data_from_jmifile( |
| 'video_sent_frame_width', jmidata), |
| units='px', higher_is_better=True) |
| |
| self.output_perf_value(description='vid_out_encode_cpu_usage', |
| value=self._get_data_from_jmifile( |
| 'video_encode_cpu_usage', jmidata), |
| units='percent', higher_is_better=False) |
| |
| total_vid_packets_sent = self._get_sum('video_packets_sent', jmidata) |
| total_vid_packets_lost = self._get_sum('video_packets_lost', jmidata) |
| lost_packet_percentage = float(total_vid_packets_lost)*100/ \ |
| float(total_vid_packets_sent) if \ |
| total_vid_packets_sent else 0 |
| |
| self.output_perf_value(description='lost_packet_percentage', |
| value=lost_packet_percentage, units='percent', |
| higher_is_better=False) |
| self.output_perf_value(description='cpu_usage_jmi', |
| value=self._get_data_from_jmifile('cpu_percent', jmidata), |
| units='percent', higher_is_better=False) |
| self.output_perf_value(description='renderer_cpu_usage', |
| value=self._get_data_from_jmifile( |
| 'renderer_cpu_percent', jmidata), |
| units='percent', higher_is_better=False) |
| self.output_perf_value(description='browser_cpu_usage', |
| value=self._get_data_from_jmifile( |
| 'browser_cpu_percent', jmidata), |
| units='percent', higher_is_better=False) |
| |
| self.output_perf_value(description='gpu_cpu_usage', |
| value=self._get_data_from_jmifile( |
| 'gpu_cpu_percent', jmidata), |
| units='percent', higher_is_better=False) |
| |
| self.output_perf_value(description='active_streams', |
| value=self._get_data_from_jmifile( |
| 'num_active_vid_in_streams', jmidata), |
| units='count', higher_is_better=True) |
| |
| |
| def initialize(self, host): |
| """ |
| Initializes common test properties. |
| |
| @param host: a host object representing the DUT. |
| """ |
| super(enterprise_CFM_Perf, self).initialize(host) |
| self.system_facade = self._facade_factory.create_system_facade() |
| metrics = system_metrics_collector.create_default_metric_set( |
| self.system_facade) |
| metrics.append(ParticipantCountMetric(self.cfm_facade)) |
| self.metrics_collector = (system_metrics_collector. |
| SystemMetricsCollector(self.system_facade, |
| metrics)) |
| |
| def run_once(self, is_meeting=False): |
| """Stays in a meeting/hangout and collects perf data.""" |
| if is_meeting: |
| self.join_meeting() |
| else: |
| self.start_hangout() |
| |
| self.collect_perf_data() |
| |
| if is_meeting: |
| self.cfm_facade.end_meeting_session() |
| else: |
| self.cfm_facade.end_hangout_session() |
| |
| self.upload_jmidata() |
| |