• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import os
6import re
7import json
8import time
9import shutil
10import logging
11
12from autotest_lib.client.common_lib.cros import perf_stat_lib
13
14
15def 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
33def 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
48def 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.
79def 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
110def 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
130def 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
146def 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
162def 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
175def 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
190def 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
214def 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
238def 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
254def 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