Home | History | Annotate | Download | only in enterprise_CFM_Perf
      1 # Copyright (c) 2017 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import datetime
      6 import glob
      7 import os
      8 import time
      9 
     10 from autotest_lib.client.common_lib import error
     11 from autotest_lib.client.common_lib.cros import system_metrics_collector
     12 from autotest_lib.client.common_lib.cros.cfm.metrics import (
     13         media_metrics_collector)
     14 from autotest_lib.server.cros import cfm_jmidata_log_collector
     15 from autotest_lib.server.cros.cfm import cfm_base_test
     16 
     17 _SHORT_TIMEOUT = 5
     18 _MEASUREMENT_DURATION_SECONDS = 10
     19 _TOTAL_TEST_DURATION_SECONDS = 900
     20 
     21 _BASE_DIR = '/home/chronos/user/Storage/ext/'
     22 _EXT_ID = 'ikfcpmgefdpheiiomgmhlmmkihchmdlj'
     23 _JMI_DIR = '/0*/File\ System/000/t/00/*'
     24 _JMI_SOURCE_DIR = _BASE_DIR + _EXT_ID + _JMI_DIR
     25 
     26 class ParticipantCountMetric(system_metrics_collector.Metric):
     27     """
     28     Metric for getting the current participant count in a call.
     29     """
     30     def __init__(self, cfm_facade):
     31         """
     32         Initializes with a cfm_facade.
     33 
     34         @param cfm_facade object having a get_participant_count() method.
     35         """
     36         super(ParticipantCountMetric, self).__init__(
     37                 'participant_count',
     38                 'participants',
     39                 higher_is_better=True)
     40         self.cfm_facade = cfm_facade
     41 
     42     def collect_metric(self):
     43         """
     44         Collects one metric value.
     45         """
     46         self.values.append(self.cfm_facade.get_participant_count())
     47 
     48 
     49 class enterprise_CFM_Perf(cfm_base_test.CfmBaseTest):
     50     """This is a server test which clears device TPM and runs
     51     enterprise_RemoraRequisition client test to enroll the device in to hotrod
     52     mode. After enrollment is successful, it collects and logs cpu, memory and
     53     temperature data from the device under test."""
     54     version = 1
     55 
     56     def start_hangout(self):
     57         """Waits for the landing page and starts a hangout session."""
     58         self.cfm_facade.wait_for_hangouts_telemetry_commands()
     59         current_date = datetime.datetime.now().strftime("%Y-%m-%d")
     60         hangout_name = current_date + '-cfm-perf'
     61         self.cfm_facade.start_new_hangout_session(hangout_name)
     62 
     63 
     64     def join_meeting(self):
     65         """Waits for the landing page and joins a meeting session."""
     66         self.cfm_facade.wait_for_meetings_landing_page()
     67         # Daily meeting for perf testing with 9 remote participants.
     68         meeting_code = 'nis-rhmz-dyh'
     69         self.cfm_facade.join_meeting_session(meeting_code)
     70 
     71 
     72     def collect_perf_data(self):
     73         """
     74         Collects run time data from the DUT using system_metrics_collector.
     75         Writes the data to the chrome perf dashboard.
     76         """
     77         start_time = time.time()
     78         while (time.time() - start_time) < _TOTAL_TEST_DURATION_SECONDS:
     79             time.sleep(_MEASUREMENT_DURATION_SECONDS)
     80             self.metrics_collector.collect_snapshot()
     81             if self.is_meeting:
     82                 # Media metrics collector is only available for Meet.
     83                 self.media_metrics_collector.collect_snapshot()
     84         self.metrics_collector.write_metrics(self.output_perf_value)
     85 
     86     def _get_average(self, data_type, jmidata):
     87         """Computes mean of a list of numbers.
     88 
     89         @param data_type: Type of data to be retrieved from jmi data log.
     90         @param jmidata: Raw jmi data log to parse.
     91         @return Mean computed from the list of numbers.
     92         """
     93         data = self._get_data_from_jmifile(data_type, jmidata)
     94         if not data:
     95             return 0
     96         return float(sum(data)) / len(data)
     97 
     98 
     99     def _get_max_value(self, data_type, jmidata):
    100         """Computes maximum value of a list of numbers.
    101 
    102         @param data_type: Type of data to be retrieved from jmi data log.
    103         @param jmidata: Raw jmi data log to parse.
    104         @return Maxium value from the list of numbers.
    105         """
    106         data = self._get_data_from_jmifile(data_type, jmidata)
    107         if not data:
    108             return 0
    109         return max(data)
    110 
    111 
    112     def _get_sum(self, data_type, jmidata):
    113         """Computes sum of a list of numbers.
    114 
    115         @param data_type: Type of data to be retrieved from jmi data log.
    116         @param jmidata: Raw jmi data log to parse.
    117         @return Sum computed from the list of numbers.
    118         """
    119         data = self._get_data_from_jmifile(data_type, jmidata)
    120         if not data:
    121             return 0
    122         return sum(data)
    123 
    124 
    125     def _get_last_value(self, data_type, jmidata):
    126         """Gets last value of a list of numbers.
    127 
    128         @param data_type: Type of data to be retrieved from jmi data log.
    129         @param jmidata: Raw jmi data log to parse.
    130         @return The last value in the jmidata for the specified data_type. 0 if
    131                 there are no values in the jmidata for this data_type.
    132         """
    133         data = self._get_data_from_jmifile(data_type, jmidata)
    134         if not data:
    135             return 0
    136         return data[-1]
    137 
    138 
    139     def _get_data_from_jmifile(self, data_type, jmidata):
    140         """Gets data from jmidata log for given data type.
    141 
    142         @param data_type: Type of data to be retrieved from jmi data log.
    143         @param jmidata: Raw jmi data log to parse.
    144         @return Data for given data type from jmidata log.
    145         """
    146         if self.is_meeting:
    147             try:
    148                 timestamped_values = self.media_metrics_collector.get_metric(
    149                         data_type)
    150             except KeyError:
    151                 # Ensure we always return at least one element, or perf uploads
    152                 # will be sad.
    153                 return [0]
    154             # Strip timestamps.
    155             values = [x[1] for x in timestamped_values]
    156             # Each entry in values is a list, extract the raw values:
    157             res = []
    158             for value_list in values:
    159                 res.extend(value_list)
    160             # Ensure we always return at least one element, or perf uploads will
    161             # be sad.
    162             return res or [0]
    163         else:
    164             return cfm_jmidata_log_collector.GetDataFromLogs(
    165                     self, data_type, jmidata)
    166 
    167 
    168     def _get_file_to_parse(self):
    169         """Copy jmi logs from client to test's results directory.
    170 
    171         @return The newest jmi log file.
    172         """
    173         self._host.get_file(_JMI_SOURCE_DIR, self.resultsdir)
    174         source_jmi_files = self.resultsdir + '/0*'
    175         if not source_jmi_files:
    176             raise error.TestNAError('JMI data file not found.')
    177         newest_file = max(glob.iglob(source_jmi_files), key=os.path.getctime)
    178         return newest_file
    179 
    180     def upload_jmidata(self):
    181         """
    182         Write jmidata results to results-chart.json file for Perf Dashboard.
    183         """
    184         jmi_file = self._get_file_to_parse()
    185         jmifile_to_parse = open(jmi_file, 'r')
    186         jmidata = jmifile_to_parse.read()
    187 
    188         # Compute and save aggregated stats from JMI.
    189         self.output_perf_value(description='sum_vid_in_frames_decoded',
    190                 value=self._get_sum('frames_decoded', jmidata), units='frames',
    191                 higher_is_better=True)
    192 
    193         self.output_perf_value(description='sum_vid_out_frames_encoded',
    194                 value=self._get_sum('frames_encoded', jmidata), units='frames',
    195                 higher_is_better=True)
    196 
    197         self.output_perf_value(description='vid_out_adapt_changes',
    198                 value=self._get_last_value('adaptation_changes', jmidata),
    199                 units='count', higher_is_better=False)
    200 
    201         self.output_perf_value(description='video_out_encode_time',
    202                 value=self._get_data_from_jmifile(
    203                         'average_encode_time', jmidata),
    204                 units='ms', higher_is_better=False)
    205 
    206         self.output_perf_value(description='max_video_out_encode_time',
    207                 value=self._get_max_value('average_encode_time', jmidata),
    208                 units='ms', higher_is_better=False)
    209 
    210         self.output_perf_value(description='vid_out_bandwidth_adapt',
    211                 value=self._get_average('bandwidth_adaptation', jmidata),
    212                 units='bool', higher_is_better=False)
    213 
    214         self.output_perf_value(description='vid_out_cpu_adapt',
    215                 value=self._get_average('cpu_adaptation', jmidata),
    216                 units='bool', higher_is_better=False)
    217 
    218         self.output_perf_value(description='video_in_res',
    219                 value=self._get_data_from_jmifile(
    220                         'video_received_frame_height', jmidata),
    221                 units='px', higher_is_better=True)
    222 
    223         self.output_perf_value(description='video_out_res',
    224                 value=self._get_data_from_jmifile(
    225                         'video_sent_frame_height', jmidata),
    226                 units='resolution', higher_is_better=True)
    227 
    228         self.output_perf_value(description='vid_in_framerate_decoded',
    229                 value=self._get_data_from_jmifile(
    230                         'framerate_decoded', jmidata),
    231                 units='fps', higher_is_better=True)
    232 
    233         self.output_perf_value(description='vid_out_framerate_input',
    234                 value=self._get_data_from_jmifile(
    235                         'framerate_outgoing', jmidata),
    236                 units='fps', higher_is_better=True)
    237 
    238         self.output_perf_value(description='vid_in_framerate_to_renderer',
    239                 value=self._get_data_from_jmifile(
    240                         'framerate_to_renderer', jmidata),
    241                 units='fps', higher_is_better=True)
    242 
    243         self.output_perf_value(description='vid_in_framerate_received',
    244                 value=self._get_data_from_jmifile(
    245                         'framerate_received', jmidata),
    246                 units='fps', higher_is_better=True)
    247 
    248         self.output_perf_value(description='vid_out_framerate_sent',
    249                 value=self._get_data_from_jmifile('framerate_sent', jmidata),
    250                 units='fps', higher_is_better=True)
    251 
    252         self.output_perf_value(description='vid_in_frame_width',
    253                 value=self._get_data_from_jmifile(
    254                         'video_received_frame_width', jmidata),
    255                 units='px', higher_is_better=True)
    256 
    257         self.output_perf_value(description='vid_out_frame_width',
    258                 value=self._get_data_from_jmifile(
    259                         'video_sent_frame_width', jmidata),
    260                 units='px', higher_is_better=True)
    261 
    262         self.output_perf_value(description='vid_out_encode_cpu_usage',
    263                 value=self._get_data_from_jmifile(
    264                         'video_encode_cpu_usage', jmidata),
    265                 units='percent', higher_is_better=False)
    266 
    267         total_vid_packets_sent = self._get_sum('video_packets_sent', jmidata)
    268         total_vid_packets_lost = self._get_sum('video_packets_lost', jmidata)
    269         lost_packet_percentage = float(total_vid_packets_lost)*100/ \
    270                                  float(total_vid_packets_sent) if \
    271                                  total_vid_packets_sent else 0
    272 
    273         self.output_perf_value(description='lost_packet_percentage',
    274                 value=lost_packet_percentage, units='percent',
    275                 higher_is_better=False)
    276         self.output_perf_value(description='cpu_usage_jmi',
    277                 value=self._get_data_from_jmifile('cpu_percent', jmidata),
    278                 units='percent', higher_is_better=False)
    279         self.output_perf_value(description='renderer_cpu_usage',
    280                 value=self._get_data_from_jmifile(
    281                     'renderer_cpu_percent', jmidata),
    282                 units='percent', higher_is_better=False)
    283         self.output_perf_value(description='browser_cpu_usage',
    284                 value=self._get_data_from_jmifile(
    285                         'browser_cpu_percent', jmidata),
    286                 units='percent', higher_is_better=False)
    287 
    288         self.output_perf_value(description='gpu_cpu_usage',
    289                 value=self._get_data_from_jmifile(
    290                         'gpu_cpu_percent', jmidata),
    291                 units='percent', higher_is_better=False)
    292 
    293         self.output_perf_value(description='active_streams',
    294                 value=self._get_data_from_jmifile(
    295                         'num_active_vid_in_streams', jmidata),
    296                 units='count', higher_is_better=True)
    297 
    298 
    299     def initialize(self, host, run_test_only=False):
    300         """
    301         Initializes common test properties.
    302 
    303         @param host: a host object representing the DUT.
    304         @param run_test_only: Wheter to run only the test or to also perform
    305             deprovisioning, enrollment and system reboot. See cfm_base_test.
    306         """
    307         super(enterprise_CFM_Perf, self).initialize(host, run_test_only)
    308         self.system_facade = self._facade_factory.create_system_facade()
    309         metrics = system_metrics_collector.create_default_metric_set(
    310                 self.system_facade)
    311         metrics.append(ParticipantCountMetric(self.cfm_facade))
    312         self.metrics_collector = (system_metrics_collector.
    313                                   SystemMetricsCollector(self.system_facade,
    314                                                          metrics))
    315         data_point_collector = media_metrics_collector.DataPointCollector(
    316                 self.cfm_facade)
    317         self.media_metrics_collector = (media_metrics_collector
    318                                         .MetricsCollector(data_point_collector))
    319 
    320     def run_once(self, is_meeting=False):
    321         """Stays in a meeting/hangout and collects perf data."""
    322         self.is_meeting = is_meeting
    323         if is_meeting:
    324             self.join_meeting()
    325         else:
    326             self.start_hangout()
    327 
    328         self.collect_perf_data()
    329 
    330         if is_meeting:
    331             self.cfm_facade.end_meeting_session()
    332         else:
    333             self.cfm_facade.end_hangout_session()
    334 
    335         self.upload_jmidata()
    336 
    337