site_utils.py revision 3f5d7687852d571a9e547b439f5d03deb776fa0a
1#pylint: disable-msg=C0111
2
3# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import logging, os, platform, re, signal, tempfile, time, uuid
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.common_lib import utils
10from autotest_lib.client.bin import base_utils
11
12class TimeoutError(error.TestError):
13    """Error raised when we time out when waiting on a condition."""
14    pass
15
16
17class Crossystem(object):
18    """A wrapper for the crossystem utility."""
19
20    def __init__(self, client):
21        self.cros_system_data = {}
22        self._client = client
23
24    def init(self):
25        self.cros_system_data = {}
26        (_, fname) = tempfile.mkstemp()
27        f = open(fname, 'w')
28        self._client.run('crossystem', stdout_tee=f)
29        f.close()
30        text = utils.read_file(fname)
31        for line in text.splitlines():
32            assignment_string = line.split('#')[0]
33            if not assignment_string.count('='):
34                continue
35            (name, value) = assignment_string.split('=', 1)
36            self.cros_system_data[name.strip()] = value.strip()
37        os.remove(fname)
38
39    def __getattr__(self, name):
40        """
41        Retrieve a crosssystem attribute.
42
43        The call crossystemobject.name() will return the crossystem reported
44        string.
45        """
46        return lambda : self.cros_system_data[name]
47
48
49def get_oldest_pid_by_name(name):
50    """
51    Return the oldest pid of a process whose name perfectly matches |name|.
52
53    name is an egrep expression, which will be matched against the entire name
54    of processes on the system.  For example:
55
56      get_oldest_pid_by_name('chrome')
57
58    on a system running
59      8600 ?        00:00:04 chrome
60      8601 ?        00:00:00 chrome
61      8602 ?        00:00:00 chrome-sandbox
62
63    would return 8600, as that's the oldest process that matches.
64    chrome-sandbox would not be matched.
65
66    Arguments:
67      name: egrep expression to match.  Will be anchored at the beginning and
68            end of the match string.
69
70    Returns:
71      pid as an integer, or None if one cannot be found.
72
73    Raises:
74      ValueError if pgrep returns something odd.
75    """
76    str_pid = utils.system_output(
77        'pgrep -o ^%s$' % name, ignore_status=True).rstrip()
78    if str_pid:
79        return int(str_pid)
80
81
82def get_oldest_by_name(name):
83    """Return pid and command line of oldest process whose name matches |name|.
84
85    @param name: egrep expression to match desired process name.
86    @return: A tuple of (pid, command_line) of the oldest process whose name
87             matches |name|.
88
89    """
90    pid = get_oldest_pid_by_name(name)
91    if pid:
92        command_line = utils.system_output('ps -p %i -o command=' % pid,
93                                           ignore_status=True).rstrip()
94        return (pid, command_line)
95
96
97def get_chrome_remote_debugging_port():
98    """Returns remote debugging port for Chrome.
99
100    Parse chrome process's command line argument to get the remote debugging
101    port.
102    """
103    pid, command = get_oldest_by_name('chrome')
104    matches = re.search('--remote-debugging-port=([0-9]+)', command)
105    if matches:
106        return int(matches.group(1))
107
108
109def get_process_list(name, command_line=None):
110    """
111    Return the list of pid for matching process |name command_line|.
112
113    on a system running
114      31475 ?    0:06 /opt/google/chrome/chrome --allow-webui-compositing -
115      31478 ?    0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/
116      31485 ?    0:00 /opt/google/chrome/chrome --type=zygote --log-level=1
117      31532 ?    1:05 /opt/google/chrome/chrome --type=renderer
118
119    get_process_list('chrome')
120    would return ['31475', '31485', '31532']
121
122    get_process_list('chrome', '--type=renderer')
123    would return ['31532']
124
125    Arguments:
126      name: process name to search for. If command_line is provided, name is
127            matched against full command line. If command_line is not provided,
128            name is only matched against the process name.
129      command line: when command line is passed, the full process command line
130                    is used for matching.
131
132    Returns:
133      list of PIDs of the matching processes.
134
135    """
136    # TODO(rohitbm) crbug.com/268861
137    flag = '-x' if not command_line else '-f'
138    name = '\'%s.*%s\'' % (name, command_line) if command_line else name
139    str_pid = utils.system_output(
140            'pgrep %s %s' % (flag, name), ignore_status=True).rstrip()
141    return str_pid
142
143
144def nuke_process_by_name(name, with_prejudice=False):
145    try:
146        pid = get_oldest_pid_by_name(name)
147    except Exception as e:
148        logging.error(e)
149        return
150    if pid is None:
151        raise error.AutoservPidAlreadyDeadError(
152            'No process matching %s.' % name)
153    if with_prejudice:
154        utils.nuke_pid(pid, [signal.SIGKILL])
155    else:
156        utils.nuke_pid(pid)
157
158
159def poll_for_condition(
160    condition, exception=None, timeout=10, sleep_interval=0.1, desc=None):
161    """Poll until a condition becomes true.
162
163    Arguments:
164      condition: function taking no args and returning bool
165      exception: exception to throw if condition doesn't become true
166      timeout: maximum number of seconds to wait
167      sleep_interval: time to sleep between polls
168      desc: description of default TimeoutError used if 'exception' is None
169
170    Returns:
171      The true value that caused the poll loop to terminate.
172
173    Raises:
174        'exception' arg if supplied; site_utils.TimeoutError otherwise
175    """
176    start_time = time.time()
177    while True:
178        value = condition()
179        if value:
180            return value
181        if time.time() + sleep_interval - start_time > timeout:
182            if exception:
183                logging.error(exception)
184                raise exception
185
186            if desc:
187                desc = 'Timed out waiting for condition: %s' % desc
188            else:
189                desc = 'Timed out waiting for unnamed condition'
190            logging.error(desc)
191            raise TimeoutError, desc
192
193        time.sleep(sleep_interval)
194
195
196def save_vm_state(checkpoint):
197    """Saves the current state of the virtual machine.
198
199    This function is a NOOP if the test is not running under a virtual machine
200    with the USB serial port redirected.
201
202    Arguments:
203      checkpoint - Name used to identify this state
204
205    Returns:
206      None
207    """
208    # The QEMU monitor has been redirected to the guest serial port located at
209    # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
210    # command to the serial port.
211    proc = platform.processor()
212    if 'QEMU' in proc and os.path.exists('/dev/ttyUSB0'):
213        logging.info('Saving VM state "%s"', checkpoint)
214        serial = open('/dev/ttyUSB0', 'w')
215        serial.write("savevm %s\r\n" % checkpoint)
216        logging.info('Done saving VM state "%s"', checkpoint)
217
218
219def check_raw_dmesg(dmesg, message_level, whitelist):
220    """Checks dmesg for unexpected warnings.
221
222    This function parses dmesg for message with message_level <= message_level
223    which do not appear in the whitelist.
224
225    Arguments:
226      dmesg - string containing raw dmesg buffer
227      message_level - minimum message priority to check
228      whitelist - messages to ignore
229
230    Returns:
231      List of unexpected warnings
232    """
233    whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
234    unexpected = []
235    for line in dmesg.splitlines():
236        if int(line[1]) <= message_level:
237            stripped_line = line.split('] ', 1)[1]
238            if whitelist_re.search(stripped_line):
239                continue
240            unexpected.append(stripped_line)
241    return unexpected
242
243def verify_mesg_set(mesg, regex, whitelist):
244    """Verifies that the exact set of messages are present in a text.
245
246    This function finds all strings in the text matching a certain regex, and
247    then verifies that all expected strings are present in the set, and no
248    unexpected strings are there.
249
250    Arguments:
251      mesg - the mutiline text to be scanned
252      regex - regular expression to match
253      whitelist - messages to find in the output, a list of strings
254          (potentially regexes) to look for in the filtered output. All these
255          strings must be there, and no other strings should be present in the
256          filtered output.
257
258    Returns:
259      string of inconsistent findings (i.e. an empty string on success).
260    """
261
262    rv = []
263
264    missing_strings = []
265    present_strings = []
266    for line in mesg.splitlines():
267        if not re.search(r'%s' % regex, line):
268            continue
269        present_strings.append(line.split('] ', 1)[1])
270
271    for string in whitelist:
272        for present_string in list(present_strings):
273            if re.search(r'^%s$' % string, present_string):
274                present_strings.remove(present_string)
275                break
276        else:
277            missing_strings.append(string)
278
279    if present_strings:
280        rv.append('unexpected strings:')
281        rv.extend(present_strings)
282    if missing_strings:
283        rv.append('missing strings:')
284        rv.extend(missing_strings)
285
286    return '\n'.join(rv)
287
288
289def target_is_pie():
290    """Returns whether the toolchain produces a PIE (position independent
291    executable) by default.
292
293    Arguments:
294      None
295
296    Returns:
297      True if the target toolchain produces a PIE by default.
298      False otherwise.
299    """
300
301
302    command = 'echo | ${CC} -E -dD -P - | grep -i pie'
303    result = utils.system_output(command, retain_output=True,
304                                 ignore_status=True)
305    if re.search('#define __PIE__', result):
306        return True
307    else:
308        return False
309
310def target_is_x86():
311    """Returns whether the toolchain produces an x86 object
312
313    Arguments:
314      None
315
316    Returns:
317      True if the target toolchain produces an x86 object
318      False otherwise.
319    """
320
321
322    command = 'echo | ${CC} -E -dD -P - | grep -i 86'
323    result = utils.system_output(command, retain_output=True,
324                                 ignore_status=True)
325    if re.search('__i386__', result) or re.search('__x86_64__', result):
326        return True
327    else:
328        return False
329
330def mounts():
331    ret = []
332    for line in file('/proc/mounts'):
333        m = re.match(r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
334        if m:
335            ret.append(m.groupdict())
336    return ret
337
338def is_mountpoint(path):
339    return path in [ m['dest'] for m in mounts() ]
340
341def require_mountpoint(path):
342    """
343    Raises an exception if path is not a mountpoint.
344    """
345    if not is_mountpoint(path):
346        raise error.TestFail('Path not mounted: "%s"' % path)
347
348def random_username():
349    return str(uuid.uuid4()) + '@example.com'
350
351
352def parse_cmd_output(command, run_method=utils.run):
353    """Runs a command on a host object to retrieve host attributes.
354
355    The command should output to stdout in the format of:
356    <key> = <value> # <optional_comment>
357
358
359    @param command: Command to execute on the host.
360    @param run_method: Function to use to execute the command. Defaults to
361                       utils.run so that the command will be executed locally.
362                       Can be replace with a host.run call so that it will
363                       execute on a DUT or external machine. Method must accept
364                       a command argument, stdout_tee and stderr_tee args and
365                       return a result object with a string attribute stdout
366                       which will be parsed.
367
368    @returns a dictionary mapping host attributes to their values.
369    """
370    result = {}
371    # Suppresses stdout so that the files are not printed to the logs.
372    cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
373    for line in cmd_result.stdout.splitlines():
374        # Lines are of the format "<key>     = <value>      # <comment>"
375        key_value = re.match('^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ]+)'
376                             '(?:\s*#.*)?$', line)
377        if key_value:
378            result[key_value.group('key')] = key_value.group('value')
379    return result
380
381
382def set_from_keyval_output(out, delimiter=' '):
383    """Parse delimiter-separated key-val output into a set of tuples.
384
385    Output is expected to be multiline text output from a command.
386    Stuffs the key-vals into tuples in a set to be later compared.
387
388    e.g.  deactivated 0
389          disableForceClear 0
390          ==>  set(('deactivated', '0'), ('disableForceClear', '0'))
391
392    @param out: multiple lines of space-separated key-val pairs.
393    @param delimiter: character that separates key from val. Usually a
394                      space but may be '=' or something else.
395    @return set of key-val tuples.
396    """
397    results = set()
398    kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
399    for linecr in out.splitlines():
400        match = kv_match_re.match(linecr.strip())
401        if match:
402            results.add((match.group(1), match.group(2)))
403    return results
404
405
406def get_cpu_usage():
407    """Returns machine's CPU usage.
408
409    This function uses /proc/stat to identify CPU usage.
410    Returns:
411        A dictionary with 'user', 'nice', 'system' and 'idle' values.
412        Sample dictionary:
413        {
414            'user': 254544,
415            'nice': 9,
416            'system': 254768,
417            'idle': 2859878,
418        }
419    """
420    proc_stat = open('/proc/stat')
421    cpu_usage_str = proc_stat.readline().split()
422    proc_stat.close()
423    return {
424        'user': int(cpu_usage_str[1]),
425        'nice': int(cpu_usage_str[2]),
426        'system': int(cpu_usage_str[3]),
427        'idle': int(cpu_usage_str[4])
428    }
429
430
431def compute_active_cpu_time(cpu_usage_start, cpu_usage_end):
432    """Computes the fraction of CPU time spent non-idling.
433
434    This function should be invoked using before/after values from calls to
435    get_cpu_usage().
436    """
437    time_active_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] +
438                                                  cpu_usage_end['system'])
439    time_active_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
440                                                      cpu_usage_start['system'])
441    total_time_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] +
442                      cpu_usage_end['system'] + cpu_usage_end['idle'])
443    total_time_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
444                        cpu_usage_start['system'] + cpu_usage_start['idle'])
445    return ((float(time_active_end) - time_active_start) /
446                    (total_time_end - total_time_start))
447
448
449def is_pgo_mode():
450    return 'USE_PGO' in os.environ
451
452
453def wait_for_idle_cpu(timeout, utilization):
454    """Waits for the CPU to become idle (< utilization).
455
456    Args:
457        timeout: The longest time in seconds to wait before throwing an error.
458        utilization: The CPU usage below which the system should be considered
459                idle (between 0 and 1.0 independent of cores/hyperthreads).
460    """
461    time_passed = 0.0
462    fraction_active_time = 1.0
463    sleep_time = 1
464    logging.info('Starting to wait up to %.1fs for idle CPU...', timeout)
465    while fraction_active_time >= utilization:
466        cpu_usage_start = get_cpu_usage()
467        # Split timeout interval into not too many chunks to limit log spew.
468        # Start at 1 second, increase exponentially
469        time.sleep(sleep_time)
470        time_passed += sleep_time
471        sleep_time = min(16.0, 2.0 * sleep_time)
472        cpu_usage_end = get_cpu_usage()
473        fraction_active_time = \
474                compute_active_cpu_time(cpu_usage_start, cpu_usage_end)
475        logging.info('After waiting %.1fs CPU utilization is %.3f.',
476                     time_passed, fraction_active_time)
477        if time_passed > timeout:
478            logging.warning('CPU did not become idle.')
479            log_process_activity()
480            # crosbug.com/37389
481            if is_pgo_mode():
482                logging.info('Still continuing because we are in PGO mode.')
483                return True
484
485            return False
486    logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).',
487                              time_passed, fraction_active_time)
488    return True
489
490
491def log_process_activity():
492    """Logs the output of top.
493
494    Useful to debug performance tests and to find runaway processes.
495    """
496    logging.info('Logging current process activity using top.')
497    cmd = 'top -b -n1 -c'
498    output = utils.run(cmd)
499    logging.info(output)
500
501
502def wait_for_cool_machine():
503    """
504    A simple heuristic to wait for a machine to cool.
505    The code looks a bit 'magic', but we don't know ambient temperature
506    nor machine characteristics and still would like to return the caller
507    a machine that cooled down as much as reasonably possible.
508    """
509    temperature = get_current_temperature_max()
510    # We got here with a cold machine, return immediately. This should be the
511    # most common case.
512    if temperature < 50:
513        return True
514    logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature)
515    # A modest wait should cool the machine.
516    time.sleep(60.0)
517    temperature = get_current_temperature_max()
518    # Atoms idle below 60 and everyone else should be even lower.
519    if temperature < 62:
520        return True
521    # This should be rare.
522    logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature)
523    time.sleep(120.0)
524    temperature = get_current_temperature_max()
525    # A temperature over 65'C doesn't give us much headroom to the critical
526    # temperatures that start at 85'C (and PerfControl as of today will fail at
527    # critical - 10'C).
528    if temperature < 65:
529        return True
530    logging.warning('Did not cool down (%dC), giving up.', temperature)
531    return False
532
533
534# System paths for machine performance state.
535_CPUINFO = '/proc/cpuinfo'
536_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
537_MEMINFO = '/proc/meminfo'
538_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)'
539
540
541def _get_line_from_file(path, line):
542    """
543    line can be an integer or
544    line can be a string that matches the beginning of the line
545    """
546    f = open(path)
547    if (isinstance(line, int)):
548        l = f.readline()
549        for _ in range(0, line):
550            l = f.readline()
551        return l
552    else:
553        for l in f:
554            if l.startswith(line):
555                return l
556    return None
557
558
559def _get_match_from_file(path, line, prefix, postfix):
560    """
561    Matches line in path and returns string between first prefix and postfix.
562    """
563    match = _get_line_from_file(path, line)
564    # Strip everything from front of line including prefix.
565    if prefix:
566        match = re.split(prefix, match)[1]
567    # Strip everything from back of string including first occurence of postfix.
568    if postfix:
569        match = re.split(postfix, match)[0]
570    return match
571
572
573def _get_float_from_file(path, line, prefix, postfix):
574    match = _get_match_from_file(path, line, prefix, postfix)
575    return float(match)
576
577
578def _get_int_from_file(path, line, prefix, postfix):
579    match = _get_match_from_file(path, line, prefix, postfix)
580    return int(match)
581
582
583def _get_hex_from_file(path, line, prefix, postfix):
584    match = _get_match_from_file(path, line, prefix, postfix)
585    return int(match, 16)
586
587
588# The paths don't change. Avoid running find all the time.
589_hwmon_paths = None
590
591def _get_hwmon_paths(file_pattern):
592    """
593    Returns a list of paths to the temperature sensors.
594    """
595    # Some systems like daisy_spring only have the virtual hwmon.
596    # And other systems like rambi only have coretemp.0. See crbug.com/360249.
597    #    /sys/class/hwmon/hwmon*/
598    #    /sys/devices/virtual/hwmon/hwmon*/
599    #    /sys/devices/platform/coretemp.0/
600    if not _hwmon_paths:
601       cmd = 'find /sys/ -name "' + file_pattern + '"'
602       _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines()
603    return _hwon_paths
604
605
606def get_temperature_critical():
607    """
608    Returns temperature at which we will see some throttling in the system.
609    """
610    min_temperature = 1000.0
611    paths = _get_hwmon_paths('temp*_crit')
612    for path in paths:
613        temperature = _get_float_from_file(path, 0, None, None) * 0.001
614        # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to
615        # the lowest known value.
616        if ((min_temperature < 60.0) or min_temperature > 150.0):
617            logging.warning('Critical temperature of %.1fC was reset to 85.0C.')
618            min_temperature = 85.0
619
620        min_temperature = min(temperature, min_temperature)
621    return min_temperature
622
623
624def get_temperature_input_max():
625    """
626    Returns the maximum currently observed temperature.
627    """
628    max_temperature = -1000.0
629    paths = _get_hwmon_paths('temp*_input')
630    for path in paths:
631        temperature = _get_float_from_file(path, 0, None, None) * 0.001
632        max_temperature = max(temperature, max_temperature)
633    # Sanity check for real world values.
634    assert ((max_temperature > 10.0) and
635            (max_temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
636                                         max_temperature)
637    return max_temperature
638
639
640def get_ec_temperatures():
641    """
642    Uses ectool to return a list of all sensor temperatures in Celsius.
643    """
644    temperatures = []
645    # TODO(ihf): On all ARM boards I tested 'ectool temps all' returns 200K
646    # for all sensors. Remove this check once crbug.com/358342 is fixed.
647    if 'arm' in utils.get_arch():
648        return temperatures
649    try:
650        full_cmd = 'ectool temps all'
651        lines = utils.run(full_cmd, verbose=False).stdout.splitlines()
652        for line in lines:
653            temperature = int(line.split(': ')[1]) - 273
654            temperatures.append(temperature)
655    except Exception:
656        logging.warning('Unable to read temperature sensors using ectool.')
657    for temperature in temperatures:
658        # Sanity check for real world values.
659        assert ((temperature > 10.0) and
660                (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
661                                         temperature)
662
663    return temperatures
664
665
666def get_current_temperature_max():
667    """
668    Returns the highest reported board temperature (all sensors) in Celsius.
669    """
670    temperature = get_temperature_input_max()
671    ec_temperatures = get_ec_temperatures()
672    if ec_temperatures:
673        temperature = max(max(ec_temperatures), temperature)
674    return temperature
675
676
677def get_cpu_cache_size():
678    """
679    Returns the last level CPU cache size in kBytes.
680    """
681    cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB')
682    # Sanity check.
683    assert cache_size >= 64, 'Unreasonably small cache.'
684    return cache_size
685
686
687def get_cpu_model_frequency():
688    """
689    Returns the model frequency from the CPU model name on Intel only. This
690    might be redundant with get_cpu_max_frequency. Unit is Hz.
691    """
692    frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz')
693    return 1.e9 * frequency
694
695
696def get_cpu_max_frequency():
697    """
698    Returns the largest of the max CPU core frequencies. The unit is Hz.
699    """
700    max_frequency = -1
701    paths = _get_cpufreq_paths('cpuinfo_max_freq')
702    for path in paths:
703        # Convert from kHz to Hz.
704        frequency = 1000 * _get_float_from_file(path, 0, None, None)
705        max_frequency = max(frequency, max_frequency)
706    # Sanity check.
707    assert max_frequency > 1e8, 'Unreasonably low CPU frequency.'
708    return max_frequency
709
710
711def get_cpu_min_frequency():
712    """
713    Returns the smallest of the minimum CPU core frequencies.
714    """
715    min_frequency = 1e20
716    paths = _get_cpufreq_paths('cpuinfo_min_freq')
717    for path in paths:
718        frequency = _get_float_from_file(path, 0, None, None)
719        min_frequency = min(frequency, min_frequency)
720    # Sanity check.
721    assert min_frequency > 1e8, 'Unreasonably low CPU frequency.'
722    return min_frequency
723
724
725def get_cpu_model():
726    """
727    Returns the CPU model.
728    Only works on Intel.
729    """
730    cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None)
731    return cpu_model
732
733
734def get_cpu_family():
735    """
736    Returns the CPU family.
737    Only works on Intel.
738    """
739    cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None)
740    return cpu_family
741
742
743def get_board():
744    """
745    Get the ChromeOS release board name from /etc/lsb-release.
746    """
747    f = open('/etc/lsb-release')
748    try:
749        return re.search('BOARD=(.*)', f.read()).group(1)
750    finally:
751        f.close()
752
753
754def get_board_with_frequency_and_memory():
755    """
756    Returns a board name modified with CPU frequency and memory size to
757    differentiate between different board variants. For instance
758    link -> link_1.8GHz_4GB.
759    """
760    board_name = get_board()
761    # Rounded to nearest GB and GHz.
762    memory = int(round(get_mem_total()/1024.0))
763    # Convert frequency to GHz with 1 digit accuracy after the decimal point.
764    frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1
765    board = "%s_%1.1fGHz_%dGB" % (board_name, frequency, memory)
766    return board
767
768
769def get_mem_total():
770    """
771    Returns the total memory available in the system in MBytes.
772    """
773    mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB')
774    # Sanity check, all Chromebooks have at least 1GB of memory.
775    assert mem_total > 1024*1024, 'Unreasonable amount of memory.'
776    return mem_total / 1024
777
778
779def get_mem_free():
780    """
781    Returns the currently free memory in the system in MBytes.
782    """
783    mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB')
784    return mem_free / 1024
785
786
787def get_kernel_max():
788    """
789    Returns content of kernel_max.
790    """
791    kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None)
792    # Sanity check.
793    assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.'
794    return kernel_max
795
796
797def set_high_performance_mode():
798    """
799    Sets the kernel governor mode to the highest setting.
800    Returns previous governor state.
801    """
802    original_governors = get_scaling_governor_states()
803    set_scaling_governors('performance')
804    return original_governors
805
806
807def set_scaling_governors(value):
808    """
809    Sets all scaling governor to string value.
810    Sample values: 'performance', 'interactive', 'ondemand', 'powersave'.
811    """
812    paths = _get_cpufreq_paths('scaling_governor')
813    for path in paths:
814        cmd = 'sudo echo %s > %s' % (value, path)
815        logging.info('Writing scaling governor mode \'%s\' -> %s', value, path)
816        utils.system(cmd)
817
818
819def _get_cpufreq_paths(filename):
820    """
821    Returns a list of paths to the governors.
822    """
823    cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename
824    paths = utils.run(cmd, verbose=False).stdout.splitlines()
825    return paths
826
827
828def get_scaling_governor_states():
829    """
830    Returns a list of (performance governor path, current state) tuples.
831    """
832    paths = _get_cpufreq_paths('scaling_governor')
833    path_value_list = []
834    for path in paths:
835        value = _get_line_from_file(path, 0)
836        path_value_list.append((path, value))
837    return path_value_list
838
839
840def restore_scaling_governor_states(path_value_list):
841    """
842    Restores governor states. Inverse operation to get_scaling_governor_states.
843    """
844    for (path, value) in path_value_list:
845        cmd = 'sudo echo %s > %s' % (value, path)
846        utils.system(cmd)
847
848
849def get_gpu_family():
850    """Return the GPU family name"""
851    cpuarch = base_utils.get_cpu_soc_family()
852    if cpuarch == 'exynos5':
853        return 'mali'
854    if cpuarch == 'tegra':
855        return 'tegra'
856
857    pci_path = '/sys/bus/pci/devices/0000:00:02.0/device'
858
859    if not os.path.exists(pci_path):
860        raise error.TestError('PCI device 0000:00:02.0 not found')
861
862    device_id = int(utils.read_one_line(pci_path), 16)
863    intel_architecture = {
864        0xa011: 'pinetrail',
865        0x0106: 'sandybridge',
866        0x0116: 'sandybridge',
867        0x0126: 'sandybridge',
868        0x0156: 'ivybridge',
869        0x0166: 'ivybridge',
870        0x0a06: 'haswell',
871        0x0a16: 'haswell',
872        0x0f31: 'baytrail',
873    }
874
875    return intel_architecture[device_id]
876
877
878def has_no_monitor():
879    """Return whether a machine doesn't have a built-in monitor"""
880    board_name = get_board()
881    if (board_name == 'stumpy' or board_name == 'panther' or
882        board_name == 'zako'):
883        return True
884
885    return False
886