Home | History | Annotate | Download | only in enterprise_LongevityTrackerServer
      1 # Copyright 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 os
      6 import re
      7 import json
      8 import time
      9 import shutil
     10 import logging
     11 
     12 from autotest_lib.client.common_lib.cros import perf_stat_lib
     13 
     14 
     15 def get_cpu_usage(system_facade, measurement_duration_seconds):
     16     """
     17     Returns cpu usage in %.
     18 
     19     @param system_facade: A SystemFacadeRemoteAdapter to access
     20                           the CPU capture functionality from the DUT.
     21     @param measurement_duration_seconds: CPU metric capture duration.
     22 
     23     @returns current CPU usage percentage.
     24 
     25     """
     26     cpu_usage_start = system_facade.get_cpu_usage()
     27     time.sleep(measurement_duration_seconds)
     28     cpu_usage_end = system_facade.get_cpu_usage()
     29     return system_facade.compute_active_cpu_time(
     30             cpu_usage_start, cpu_usage_end) * 100
     31 
     32 
     33 def get_memory_usage(system_facade):
     34     """
     35     Returns total used memory in %.
     36 
     37     @param system_facade: A SystemFacadeRemoteAdapter to access
     38                           the memory capture functionality from the DUT.
     39 
     40     @returns current memory used.
     41 
     42     """
     43     total_memory = system_facade.get_mem_total()
     44     return ((total_memory - system_facade.get_mem_free())
     45             * 100 / total_memory)
     46 
     47 
     48 def get_temperature_data(client, system_facade):
     49     """
     50     Returns temperature sensor data in Celcius.
     51 
     52     @param system_facade: A SystemFacadeRemoteAdapter to access the temperature
     53                           capture functionality from the DUT.
     54 
     55     @returns current CPU temperature.
     56 
     57     """
     58     ectool = client.run('ectool version', ignore_status=True)
     59     if not ectool.exit_status:
     60         ec_temp = system_facade.get_ec_temperatures()
     61         return ec_temp[1]
     62     else:
     63         temp_sensor_name = 'temp0'
     64         if not temp_sensor_name:
     65             return 0
     66         MOSYS_OUTPUT_RE = re.compile('(\w+)="(.*?)"')
     67         values = {}
     68         cmd = 'mosys -k sensor print thermal %s' % temp_sensor_name
     69         for kv in MOSYS_OUTPUT_RE.finditer(client.run_output(cmd)):
     70             key, value = kv.groups()
     71             if key == 'reading':
     72                 value = int(value)
     73             values[key] = value
     74         return values['reading']
     75 
     76 
     77 #TODO(krishnargv): Replace _get_point_id with a call to the
     78 #                  _get_id_from_version method of the perf_uploader.py.
     79 def get_point_id(cros_version, epoch_minutes, version_pattern):
     80     """
     81     Compute point ID from ChromeOS version number and epoch minutes.
     82 
     83     @param cros_version: String of ChromeOS version number.
     84     @param epoch_minutes: String of minutes since 1970.
     85 
     86     @returns unique integer ID computed from given version and epoch.
     87 
     88     """
     89     # Number of digits from each part of the Chrome OS version string.
     90     cros_version_col_widths = [0, 4, 3, 2]
     91 
     92     def get_digits(version_num, column_widths):
     93         if re.match(version_pattern, version_num):
     94             computed_string = ''
     95             version_parts = version_num.split('.')
     96             for i, version_part in enumerate(version_parts):
     97                 if column_widths[i]:
     98                     computed_string += version_part.zfill(column_widths[i])
     99             return computed_string
    100         else:
    101             return None
    102 
    103     cros_digits = get_digits(cros_version, cros_version_col_widths)
    104     epoch_digits = epoch_minutes[-8:]
    105     if not cros_digits:
    106         return None
    107     return int(epoch_digits + cros_digits)
    108 
    109 
    110 def open_perf_file(file_path):
    111     """
    112     Open a perf file. Write header line if new. Return file object.
    113 
    114     If the file on |file_path| already exists, then open file for
    115     appending only. Otherwise open for writing only.
    116 
    117     @param file_path: file path for perf file.
    118 
    119     @returns file object for the perf file.
    120 
    121     """
    122     if os.path.isfile(file_path):
    123         perf_file = open(file_path, 'a+')
    124     else:
    125         perf_file = open(file_path, 'w')
    126         perf_file.write('Time,CPU,Memory,Temperature (C)\r\n')
    127     return perf_file
    128 
    129 
    130 def modulo_time(timer, interval):
    131     """
    132     Get time eplased on |timer| for the |interval| modulus.
    133 
    134     Value returned is used to adjust the timer so that it is
    135     synchronized with the current interval.
    136 
    137     @param timer: time on timer, in seconds.
    138     @param interval: period of time in seconds.
    139 
    140     @returns time elapsed from the start of the current interval.
    141 
    142     """
    143     return timer % int(interval)
    144 
    145 
    146 def syncup_time(timer, interval):
    147     """
    148     Get time remaining on |timer| for the |interval| modulus.
    149 
    150     Value returned is used to induce sleep just long enough to put the
    151     process back in sync with the timer.
    152 
    153     @param timer: time on timer, in seconds.
    154     @param interval: period of time in seconds.
    155 
    156     @returns time remaining till the end of the current interval.
    157 
    158     """
    159     return interval - (timer % int(interval))
    160 
    161 
    162 def append_to_aggregated_file(ts_file, ag_file):
    163     """
    164     Append contents of perf timestamp file to perf aggregated file.
    165 
    166     @param ts_file: file handle for performance timestamped file.
    167     @param ag_file: file handle for performance aggregated file.
    168 
    169     """
    170     next(ts_file)  # Skip fist line (the header) of timestamped file.
    171     for line in ts_file:
    172         ag_file.write(line)
    173 
    174 
    175 def copy_aggregated_to_resultsdir(resultsdir, aggregated_fpath, f_name):
    176     """Copy perf aggregated file to results dir for AutoTest results.
    177 
    178     Note: The AutoTest results default directory is located at /usr/local/
    179     autotest/results/default/longevity_Tracker/results
    180 
    181     @param resultsdir: Directory name where the perf results are stored.
    182     @param aggregated_fpath: file path to Aggregated performance values.
    183     @param f_name: Name of the perf File
    184     """
    185     results_fpath = os.path.join(resultsdir, f_name)
    186     shutil.copy(aggregated_fpath, results_fpath)
    187     logging.info('Copied %s to %s)', aggregated_fpath, results_fpath)
    188 
    189 
    190 def record_90th_metrics(perf_values, perf_metrics):
    191     """Record 90th percentile metric of attribute performance values.
    192 
    193     @param perf_values: dict attribute performance values.
    194     @param perf_metrics: dict attribute 90%-ile performance metrics.
    195     """
    196     # Calculate 90th percentile for each attribute.
    197     cpu_values = perf_values['cpu']
    198     mem_values = perf_values['mem']
    199     temp_values = perf_values['temp']
    200     cpu_metric = perf_stat_lib.get_kth_percentile(cpu_values, .90)
    201     mem_metric = perf_stat_lib.get_kth_percentile(mem_values, .90)
    202     temp_metric = perf_stat_lib.get_kth_percentile(temp_values, .90)
    203 
    204     logging.info('Performance values: %s', perf_values)
    205     logging.info('90th percentile: cpu: %s, mem: %s, temp: %s',
    206                  cpu_metric, mem_metric, temp_metric)
    207 
    208     # Append 90th percentile to each attribute performance metric.
    209     perf_metrics['cpu'].append(cpu_metric)
    210     perf_metrics['mem'].append(mem_metric)
    211     perf_metrics['temp'].append(temp_metric)
    212 
    213 
    214 def get_median_metrics(metrics):
    215     """
    216     Returns median of each attribute performance metric.
    217 
    218     If no metric values were recorded, return 0 for each metric.
    219 
    220     @param metrics: dict of attribute performance metric lists.
    221 
    222     @returns dict of attribute performance metric medians.
    223 
    224     """
    225     if len(metrics['cpu']):
    226         cpu_metric = perf_stat_lib.get_median(metrics['cpu'])
    227         mem_metric = perf_stat_lib.get_median(metrics['mem'])
    228         temp_metric = perf_stat_lib.get_median(metrics['temp'])
    229     else:
    230         cpu_metric = 0
    231         mem_metric = 0
    232         temp_metric = 0
    233     logging.info('Median of 90th percentile: cpu: %s, mem: %s, temp: %s',
    234                  cpu_metric, mem_metric, temp_metric)
    235     return {'cpu': cpu_metric, 'mem': mem_metric, 'temp': temp_metric}
    236 
    237 
    238 def read_perf_results(resultsdir, resultsfile):
    239     """
    240     Read perf results from results-chart.json file for Perf Dashboard.
    241 
    242     @returns dict of perf results, formatted as JSON chart data.
    243 
    244     """
    245     results_file = os.path.join(resultsdir, resultsfile)
    246     with open(results_file, 'r') as fp:
    247         contents = fp.read()
    248         chart_data = json.loads(contents)
    249     # TODO(krishnargv): refactor this with a better method to delete.
    250     open(results_file, 'w').close()
    251     return chart_data
    252 
    253 
    254 def verify_perf_params(expected_params, perf_params):
    255     """
    256     Verify that all the expected paramaters were passed to the test.
    257 
    258     Return True if the perf_params dict passed via the control file
    259     has all of the the expected parameters and have valid values.
    260 
    261     @param expected_params: list of expected parameters
    262     @param perf_params: dict of the paramaters passed via control file.
    263 
    264     @returns True if the perf_params dict is valid, else returns False.
    265 
    266     """
    267     for param in expected_params:
    268         if param not in perf_params or not perf_params[param]:
    269             return False
    270     return True