Home | History | Annotate | Download | only in bin
      1 #pylint: disable=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 
      7 import glob
      8 import json
      9 import logging
     10 import os
     11 import platform
     12 import re
     13 import signal
     14 import tempfile
     15 import time
     16 import uuid
     17 
     18 from autotest_lib.client.common_lib import error
     19 from autotest_lib.client.common_lib import utils
     20 from autotest_lib.client.bin import base_utils
     21 
     22 _UI_USE_FLAGS_FILE_PATH = '/etc/ui_use_flags.txt'
     23 _INTEL_PCI_IDS_FILE_PATH = '/usr/local/autotest/bin/intel_pci_ids.json'
     24 
     25 pciid_to_intel_architecture = {}
     26 
     27 
     28 class TimeoutError(error.TestError):
     29     """Error raised when we time out when waiting on a condition."""
     30     pass
     31 
     32 
     33 class Crossystem(object):
     34     """A wrapper for the crossystem utility."""
     35 
     36     def __init__(self, client):
     37         self.cros_system_data = {}
     38         self._client = client
     39 
     40     def init(self):
     41         self.cros_system_data = {}
     42         (_, fname) = tempfile.mkstemp()
     43         f = open(fname, 'w')
     44         self._client.run('crossystem', stdout_tee=f)
     45         f.close()
     46         text = utils.read_file(fname)
     47         for line in text.splitlines():
     48             assignment_string = line.split('#')[0]
     49             if not assignment_string.count('='):
     50                 continue
     51             (name, value) = assignment_string.split('=', 1)
     52             self.cros_system_data[name.strip()] = value.strip()
     53         os.remove(fname)
     54 
     55     def __getattr__(self, name):
     56         """
     57         Retrieve a crosssystem attribute.
     58 
     59         The call crossystemobject.name() will return the crossystem reported
     60         string.
     61         """
     62         return lambda: self.cros_system_data[name]
     63 
     64 
     65 def get_oldest_pid_by_name(name):
     66     """
     67     Return the oldest pid of a process whose name perfectly matches |name|.
     68 
     69     name is an egrep expression, which will be matched against the entire name
     70     of processes on the system.  For example:
     71 
     72       get_oldest_pid_by_name('chrome')
     73 
     74     on a system running
     75       8600 ?        00:00:04 chrome
     76       8601 ?        00:00:00 chrome
     77       8602 ?        00:00:00 chrome-sandbox
     78 
     79     would return 8600, as that's the oldest process that matches.
     80     chrome-sandbox would not be matched.
     81 
     82     Arguments:
     83       name: egrep expression to match.  Will be anchored at the beginning and
     84             end of the match string.
     85 
     86     Returns:
     87       pid as an integer, or None if one cannot be found.
     88 
     89     Raises:
     90       ValueError if pgrep returns something odd.
     91     """
     92     str_pid = utils.system_output(
     93         'pgrep -o ^%s$' % name,
     94         ignore_status=True).rstrip()
     95     if str_pid:
     96         return int(str_pid)
     97 
     98 
     99 def get_oldest_by_name(name):
    100     """Return pid and command line of oldest process whose name matches |name|.
    101 
    102     @param name: egrep expression to match desired process name.
    103     @return: A tuple of (pid, command_line) of the oldest process whose name
    104              matches |name|.
    105 
    106     """
    107     pid = get_oldest_pid_by_name(name)
    108     if pid:
    109         command_line = utils.system_output('ps -p %i -o command=' % pid,
    110                                            ignore_status=True).rstrip()
    111         return (pid, command_line)
    112 
    113 
    114 def get_chrome_remote_debugging_port():
    115     """Returns remote debugging port for Chrome.
    116 
    117     Parse chrome process's command line argument to get the remote debugging
    118     port.
    119     """
    120     _, command = get_oldest_by_name('chrome')
    121     matches = re.search('--remote-debugging-port=([0-9]+)', command)
    122     if matches:
    123         return int(matches.group(1))
    124 
    125 
    126 def get_process_list(name, command_line=None):
    127     """
    128     Return the list of pid for matching process |name command_line|.
    129 
    130     on a system running
    131       31475 ?    0:06 /opt/google/chrome/chrome --allow-webui-compositing -
    132       31478 ?    0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/
    133       31485 ?    0:00 /opt/google/chrome/chrome --type=zygote --log-level=1
    134       31532 ?    1:05 /opt/google/chrome/chrome --type=renderer
    135 
    136     get_process_list('chrome')
    137     would return ['31475', '31485', '31532']
    138 
    139     get_process_list('chrome', '--type=renderer')
    140     would return ['31532']
    141 
    142     Arguments:
    143       name: process name to search for. If command_line is provided, name is
    144             matched against full command line. If command_line is not provided,
    145             name is only matched against the process name.
    146       command line: when command line is passed, the full process command line
    147                     is used for matching.
    148 
    149     Returns:
    150       list of PIDs of the matching processes.
    151 
    152     """
    153     # TODO(rohitbm) crbug.com/268861
    154     flag = '-x' if not command_line else '-f'
    155     name = '\'%s.*%s\'' % (name, command_line) if command_line else name
    156     str_pid = utils.system_output(
    157         'pgrep %s %s' % (flag, name),
    158         ignore_status=True).rstrip()
    159     return str_pid.split()
    160 
    161 
    162 def nuke_process_by_name(name, with_prejudice=False):
    163     """Tell the oldest process specified by name to exit.
    164 
    165     Arguments:
    166       name: process name specifier, as understood by pgrep.
    167       with_prejudice: if True, don't allow for graceful exit.
    168 
    169     Raises:
    170       error.AutoservPidAlreadyDeadError: no existing process matches name.
    171     """
    172     try:
    173         pid = get_oldest_pid_by_name(name)
    174     except Exception as e:
    175         logging.error(e)
    176         return
    177     if pid is None:
    178         raise error.AutoservPidAlreadyDeadError(
    179             'No process matching %s.' % name)
    180     if with_prejudice:
    181         utils.nuke_pid(pid, [signal.SIGKILL])
    182     else:
    183         utils.nuke_pid(pid)
    184 
    185 
    186 def ensure_processes_are_dead_by_name(name, timeout_sec=10):
    187     """Terminate all processes specified by name and ensure they're gone.
    188 
    189     Arguments:
    190       name: process name specifier, as understood by pgrep.
    191       timeout_sec: maximum number of seconds to wait for processes to die.
    192 
    193     Raises:
    194       error.AutoservPidAlreadyDeadError: no existing process matches name.
    195       site_utils.TimeoutError: if processes still exist after timeout_sec.
    196     """
    197     def list_and_kill_processes(name):
    198         process_list = get_process_list(name)
    199         try:
    200             for pid in [int(str_pid) for str_pid in process_list]:
    201                 utils.nuke_pid(pid)
    202         except error.AutoservPidAlreadyDeadError:
    203             pass
    204         return process_list
    205 
    206     poll_for_condition(lambda: list_and_kill_processes(name) == [],
    207                        timeout=timeout_sec)
    208 
    209 
    210 def poll_for_condition(condition, exception=None, timeout=10,
    211                        sleep_interval=0.1, desc=None):
    212     """Poll until a condition becomes true.
    213 
    214     Arguments:
    215       condition: function taking no args and returning bool
    216       exception: exception to throw if condition doesn't become true
    217       timeout: maximum number of seconds to wait
    218       sleep_interval: time to sleep between polls
    219       desc: description of default TimeoutError used if 'exception' is None
    220 
    221     Returns:
    222       The true value that caused the poll loop to terminate.
    223 
    224     Raises:
    225       'exception' arg if supplied; site_utils.TimeoutError otherwise
    226     """
    227     start_time = time.time()
    228     while True:
    229         value = condition()
    230         if value:
    231             return value
    232         if time.time() + sleep_interval - start_time > timeout:
    233             if exception:
    234                 logging.error(exception)
    235                 raise exception
    236 
    237             if desc:
    238                 desc = 'Timed out waiting for condition: %s' % desc
    239             else:
    240                 desc = 'Timed out waiting for unnamed condition'
    241             logging.error(desc)
    242             raise TimeoutError, desc
    243 
    244         time.sleep(sleep_interval)
    245 
    246 
    247 def save_vm_state(checkpoint):
    248     """Saves the current state of the virtual machine.
    249 
    250     This function is a NOOP if the test is not running under a virtual machine
    251     with the USB serial port redirected.
    252 
    253     Arguments:
    254       checkpoint - Name used to identify this state
    255 
    256     Returns:
    257       None
    258     """
    259     # The QEMU monitor has been redirected to the guest serial port located at
    260     # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
    261     # command to the serial port.
    262     proc = platform.processor()
    263     if 'QEMU' in proc and os.path.exists('/dev/ttyUSB0'):
    264         logging.info('Saving VM state "%s"', checkpoint)
    265         serial = open('/dev/ttyUSB0', 'w')
    266         serial.write("savevm %s\r\n" % checkpoint)
    267         logging.info('Done saving VM state "%s"', checkpoint)
    268 
    269 
    270 def check_raw_dmesg(dmesg, message_level, whitelist):
    271     """Checks dmesg for unexpected warnings.
    272 
    273     This function parses dmesg for message with message_level <= message_level
    274     which do not appear in the whitelist.
    275 
    276     Arguments:
    277       dmesg - string containing raw dmesg buffer
    278       message_level - minimum message priority to check
    279       whitelist - messages to ignore
    280 
    281     Returns:
    282       List of unexpected warnings
    283     """
    284     whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
    285     unexpected = []
    286     for line in dmesg.splitlines():
    287         if int(line[1]) <= message_level:
    288             stripped_line = line.split('] ', 1)[1]
    289             if whitelist_re.search(stripped_line):
    290                 continue
    291             unexpected.append(stripped_line)
    292     return unexpected
    293 
    294 def verify_mesg_set(mesg, regex, whitelist):
    295     """Verifies that the exact set of messages are present in a text.
    296 
    297     This function finds all strings in the text matching a certain regex, and
    298     then verifies that all expected strings are present in the set, and no
    299     unexpected strings are there.
    300 
    301     Arguments:
    302       mesg - the mutiline text to be scanned
    303       regex - regular expression to match
    304       whitelist - messages to find in the output, a list of strings
    305           (potentially regexes) to look for in the filtered output. All these
    306           strings must be there, and no other strings should be present in the
    307           filtered output.
    308 
    309     Returns:
    310       string of inconsistent findings (i.e. an empty string on success).
    311     """
    312 
    313     rv = []
    314 
    315     missing_strings = []
    316     present_strings = []
    317     for line in mesg.splitlines():
    318         if not re.search(r'%s' % regex, line):
    319             continue
    320         present_strings.append(line.split('] ', 1)[1])
    321 
    322     for string in whitelist:
    323         for present_string in list(present_strings):
    324             if re.search(r'^%s$' % string, present_string):
    325                 present_strings.remove(present_string)
    326                 break
    327         else:
    328             missing_strings.append(string)
    329 
    330     if present_strings:
    331         rv.append('unexpected strings:')
    332         rv.extend(present_strings)
    333     if missing_strings:
    334         rv.append('missing strings:')
    335         rv.extend(missing_strings)
    336 
    337     return '\n'.join(rv)
    338 
    339 
    340 def target_is_pie():
    341     """Returns whether the toolchain produces a PIE (position independent
    342     executable) by default.
    343 
    344     Arguments:
    345       None
    346 
    347     Returns:
    348       True if the target toolchain produces a PIE by default.
    349       False otherwise.
    350     """
    351 
    352     command = 'echo | ${CC} -E -dD -P - | grep -i pie'
    353     result = utils.system_output(command,
    354                                  retain_output=True,
    355                                  ignore_status=True)
    356     if re.search('#define __PIE__', result):
    357         return True
    358     else:
    359         return False
    360 
    361 
    362 def target_is_x86():
    363     """Returns whether the toolchain produces an x86 object
    364 
    365     Arguments:
    366       None
    367 
    368     Returns:
    369       True if the target toolchain produces an x86 object
    370       False otherwise.
    371     """
    372 
    373     command = 'echo | ${CC} -E -dD -P - | grep -i 86'
    374     result = utils.system_output(command,
    375                                  retain_output=True,
    376                                  ignore_status=True)
    377     if re.search('__i386__', result) or re.search('__x86_64__', result):
    378         return True
    379     else:
    380         return False
    381 
    382 
    383 def mounts():
    384     ret = []
    385     for line in file('/proc/mounts'):
    386         m = re.match(
    387             r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
    388         if m:
    389             ret.append(m.groupdict())
    390     return ret
    391 
    392 
    393 def is_mountpoint(path):
    394     return path in [m['dest'] for m in mounts()]
    395 
    396 
    397 def require_mountpoint(path):
    398     """
    399     Raises an exception if path is not a mountpoint.
    400     """
    401     if not is_mountpoint(path):
    402         raise error.TestFail('Path not mounted: "%s"' % path)
    403 
    404 
    405 def random_username():
    406     return str(uuid.uuid4()) + '@example.com'
    407 
    408 
    409 def get_signin_credentials(filepath):
    410     """Returns user_id, password tuple from credentials file at filepath.
    411 
    412     File must have one line of the format user_id:password
    413 
    414     @param filepath: path of credentials file.
    415     @return user_id, password tuple.
    416     """
    417     user_id, password = None, None
    418     if os.path.isfile(filepath):
    419         with open(filepath) as f:
    420             user_id, password = f.read().rstrip().split(':')
    421     return user_id, password
    422 
    423 
    424 def parse_cmd_output(command, run_method=utils.run):
    425     """Runs a command on a host object to retrieve host attributes.
    426 
    427     The command should output to stdout in the format of:
    428     <key> = <value> # <optional_comment>
    429 
    430 
    431     @param command: Command to execute on the host.
    432     @param run_method: Function to use to execute the command. Defaults to
    433                        utils.run so that the command will be executed locally.
    434                        Can be replace with a host.run call so that it will
    435                        execute on a DUT or external machine. Method must accept
    436                        a command argument, stdout_tee and stderr_tee args and
    437                        return a result object with a string attribute stdout
    438                        which will be parsed.
    439 
    440     @returns a dictionary mapping host attributes to their values.
    441     """
    442     result = {}
    443     # Suppresses stdout so that the files are not printed to the logs.
    444     cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
    445     for line in cmd_result.stdout.splitlines():
    446         # Lines are of the format "<key>     = <value>      # <comment>"
    447         key_value = re.match(r'^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ '
    448                              r']+)(?:\s*#.*)?$', line)
    449         if key_value:
    450             result[key_value.group('key')] = key_value.group('value')
    451     return result
    452 
    453 
    454 def set_from_keyval_output(out, delimiter=' '):
    455     """Parse delimiter-separated key-val output into a set of tuples.
    456 
    457     Output is expected to be multiline text output from a command.
    458     Stuffs the key-vals into tuples in a set to be later compared.
    459 
    460     e.g.  deactivated 0
    461           disableForceClear 0
    462           ==>  set(('deactivated', '0'), ('disableForceClear', '0'))
    463 
    464     @param out: multiple lines of space-separated key-val pairs.
    465     @param delimiter: character that separates key from val. Usually a
    466                       space but may be '=' or something else.
    467     @return set of key-val tuples.
    468     """
    469     results = set()
    470     kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
    471     for linecr in out.splitlines():
    472         match = kv_match_re.match(linecr.strip())
    473         if match:
    474             results.add((match.group(1), match.group(2)))
    475     return results
    476 
    477 
    478 def get_cpu_usage():
    479     """Returns machine's CPU usage.
    480 
    481     This function uses /proc/stat to identify CPU usage.
    482     Returns:
    483         A dictionary with 'user', 'nice', 'system' and 'idle' values.
    484         Sample dictionary:
    485         {
    486             'user': 254544,
    487             'nice': 9,
    488             'system': 254768,
    489             'idle': 2859878,
    490         }
    491     """
    492     proc_stat = open('/proc/stat')
    493     cpu_usage_str = proc_stat.readline().split()
    494     proc_stat.close()
    495     return {
    496         'user': int(cpu_usage_str[1]),
    497         'nice': int(cpu_usage_str[2]),
    498         'system': int(cpu_usage_str[3]),
    499         'idle': int(cpu_usage_str[4])
    500     }
    501 
    502 
    503 def compute_active_cpu_time(cpu_usage_start, cpu_usage_end):
    504     """Computes the fraction of CPU time spent non-idling.
    505 
    506     This function should be invoked using before/after values from calls to
    507     get_cpu_usage().
    508     """
    509     time_active_end = (
    510         cpu_usage_end['user'] + cpu_usage_end['nice'] + cpu_usage_end['system'])
    511     time_active_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
    512                          cpu_usage_start['system'])
    513     total_time_end = (cpu_usage_end['user'] + cpu_usage_end['nice'] +
    514                       cpu_usage_end['system'] + cpu_usage_end['idle'])
    515     total_time_start = (cpu_usage_start['user'] + cpu_usage_start['nice'] +
    516                         cpu_usage_start['system'] + cpu_usage_start['idle'])
    517     return ((float(time_active_end) - time_active_start) /
    518             (total_time_end - total_time_start))
    519 
    520 
    521 def is_pgo_mode():
    522     return 'USE_PGO' in os.environ
    523 
    524 
    525 def wait_for_idle_cpu(timeout, utilization):
    526     """Waits for the CPU to become idle (< utilization).
    527 
    528     Args:
    529         timeout: The longest time in seconds to wait before throwing an error.
    530         utilization: The CPU usage below which the system should be considered
    531                 idle (between 0 and 1.0 independent of cores/hyperthreads).
    532     """
    533     time_passed = 0.0
    534     fraction_active_time = 1.0
    535     sleep_time = 1
    536     logging.info('Starting to wait up to %.1fs for idle CPU...', timeout)
    537     while fraction_active_time >= utilization:
    538         cpu_usage_start = get_cpu_usage()
    539         # Split timeout interval into not too many chunks to limit log spew.
    540         # Start at 1 second, increase exponentially
    541         time.sleep(sleep_time)
    542         time_passed += sleep_time
    543         sleep_time = min(16.0, 2.0 * sleep_time)
    544         cpu_usage_end = get_cpu_usage()
    545         fraction_active_time = \
    546                 compute_active_cpu_time(cpu_usage_start, cpu_usage_end)
    547         logging.info('After waiting %.1fs CPU utilization is %.3f.',
    548                      time_passed, fraction_active_time)
    549         if time_passed > timeout:
    550             logging.warning('CPU did not become idle.')
    551             log_process_activity()
    552             # crosbug.com/37389
    553             if is_pgo_mode():
    554                 logging.info('Still continuing because we are in PGO mode.')
    555                 return True
    556 
    557             return False
    558     logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).',
    559                  time_passed, fraction_active_time)
    560     return True
    561 
    562 
    563 def log_process_activity():
    564     """Logs the output of top.
    565 
    566     Useful to debug performance tests and to find runaway processes.
    567     """
    568     logging.info('Logging current process activity using top.')
    569     cmd = 'top -b -n1 -c'
    570     output = utils.run(cmd)
    571     logging.info(output)
    572 
    573 
    574 def wait_for_cool_machine():
    575     """
    576     A simple heuristic to wait for a machine to cool.
    577     The code looks a bit 'magic', but we don't know ambient temperature
    578     nor machine characteristics and still would like to return the caller
    579     a machine that cooled down as much as reasonably possible.
    580     """
    581     temperature = get_current_temperature_max()
    582     # We got here with a cold machine, return immediately. This should be the
    583     # most common case.
    584     if temperature < 50:
    585         return True
    586     logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature)
    587     # A modest wait should cool the machine.
    588     time.sleep(60.0)
    589     temperature = get_current_temperature_max()
    590     # Atoms idle below 60 and everyone else should be even lower.
    591     if temperature < 62:
    592         return True
    593     # This should be rare.
    594     logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature)
    595     time.sleep(120.0)
    596     temperature = get_current_temperature_max()
    597     # A temperature over 65'C doesn't give us much headroom to the critical
    598     # temperatures that start at 85'C (and PerfControl as of today will fail at
    599     # critical - 10'C).
    600     if temperature < 65:
    601         return True
    602     logging.warning('Did not cool down (%dC), giving up.', temperature)
    603     log_process_activity()
    604     return False
    605 
    606 
    607 # System paths for machine performance state.
    608 _CPUINFO = '/proc/cpuinfo'
    609 _DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs'
    610 _KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
    611 _MEMINFO = '/proc/meminfo'
    612 _TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)'
    613 
    614 
    615 def _get_line_from_file(path, line):
    616     """
    617     line can be an integer or
    618     line can be a string that matches the beginning of the line
    619     """
    620     with open(path) as f:
    621         if isinstance(line, int):
    622             l = f.readline()
    623             for _ in range(0, line):
    624                 l = f.readline()
    625             return l
    626         else:
    627             for l in f:
    628                 if l.startswith(line):
    629                     return l
    630     return None
    631 
    632 
    633 def _get_match_from_file(path, line, prefix, postfix):
    634     """
    635     Matches line in path and returns string between first prefix and postfix.
    636     """
    637     match = _get_line_from_file(path, line)
    638     # Strip everything from front of line including prefix.
    639     if prefix:
    640         match = re.split(prefix, match)[1]
    641     # Strip everything from back of string including first occurence of postfix.
    642     if postfix:
    643         match = re.split(postfix, match)[0]
    644     return match
    645 
    646 
    647 def _get_float_from_file(path, line, prefix, postfix):
    648     match = _get_match_from_file(path, line, prefix, postfix)
    649     return float(match)
    650 
    651 
    652 def _get_int_from_file(path, line, prefix, postfix):
    653     match = _get_match_from_file(path, line, prefix, postfix)
    654     return int(match)
    655 
    656 
    657 def _get_hex_from_file(path, line, prefix, postfix):
    658     match = _get_match_from_file(path, line, prefix, postfix)
    659     return int(match, 16)
    660 
    661 
    662 # The paths don't change. Avoid running find all the time.
    663 _hwmon_paths = None
    664 
    665 def _get_hwmon_paths(file_pattern):
    666     """
    667     Returns a list of paths to the temperature sensors.
    668     """
    669     # Some systems like daisy_spring only have the virtual hwmon.
    670     # And other systems like rambi only have coretemp.0. See crbug.com/360249.
    671     #    /sys/class/hwmon/hwmon*/
    672     #    /sys/devices/virtual/hwmon/hwmon*/
    673     #    /sys/devices/platform/coretemp.0/
    674     if not _hwmon_paths:
    675         cmd = 'find /sys/ -name "' + file_pattern + '"'
    676         _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines()
    677     return _hwon_paths
    678 
    679 
    680 def get_temperature_critical():
    681     """
    682     Returns temperature at which we will see some throttling in the system.
    683     """
    684     min_temperature = 1000.0
    685     paths = _get_hwmon_paths('temp*_crit')
    686     for path in paths:
    687         temperature = _get_float_from_file(path, 0, None, None) * 0.001
    688         # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to
    689         # the lowest known value.
    690         if (min_temperature < 60.0) or min_temperature > 150.0:
    691             logging.warning('Critical temperature of %.1fC was reset to 85.0C.',
    692                             min_temperature)
    693             min_temperature = 85.0
    694 
    695         min_temperature = min(temperature, min_temperature)
    696     return min_temperature
    697 
    698 
    699 def get_temperature_input_max():
    700     """
    701     Returns the maximum currently observed temperature.
    702     """
    703     max_temperature = -1000.0
    704     paths = _get_hwmon_paths('temp*_input')
    705     for path in paths:
    706         temperature = _get_float_from_file(path, 0, None, None) * 0.001
    707         max_temperature = max(temperature, max_temperature)
    708     return max_temperature
    709 
    710 
    711 def get_thermal_zone_temperatures():
    712     """
    713     Returns the maximum currently observered temperature in thermal_zones.
    714     """
    715     temperatures = []
    716     for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'):
    717         try:
    718             temperatures.append(
    719                 _get_float_from_file(path, 0, None, None) * 0.001)
    720         except IOError:
    721             # Some devices (e.g. Veyron) may have reserved thermal zones that
    722             # are not active. Trying to read the temperature value would cause a
    723             # EINVAL IO error.
    724             continue
    725     return temperatures
    726 
    727 
    728 def get_ec_temperatures():
    729     """
    730     Uses ectool to return a list of all sensor temperatures in Celsius.
    731     """
    732     temperatures = []
    733     # TODO(ihf): On all ARM boards I tested 'ectool temps all' returns 200K
    734     # for all sensors. Remove this check once crbug.com/358342 is fixed.
    735     if 'arm' in utils.get_arch():
    736         return temperatures
    737     try:
    738         full_cmd = 'ectool temps all'
    739         lines = utils.run(full_cmd, verbose=False).stdout.splitlines()
    740         for line in lines:
    741             temperature = int(line.split(': ')[1]) - 273
    742             temperatures.append(temperature)
    743     except Exception:
    744         logging.warning('Unable to read temperature sensors using ectool.')
    745     for temperature in temperatures:
    746         # Sanity check for real world values.
    747         assert ((temperature > 10.0) and
    748                 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
    749                                          temperature)
    750 
    751     return temperatures
    752 
    753 
    754 def get_current_temperature_max():
    755     """
    756     Returns the highest reported board temperature (all sensors) in Celsius.
    757     """
    758     temperature = max([get_temperature_input_max()] +
    759                       get_thermal_zone_temperatures() +
    760                       get_ec_temperatures())
    761     # Sanity check for real world values.
    762     assert ((temperature > 10.0) and
    763             (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
    764                                      temperature)
    765     return temperature
    766 
    767 
    768 def get_cpu_cache_size():
    769     """
    770     Returns the last level CPU cache size in kBytes.
    771     """
    772     cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB')
    773     # Sanity check.
    774     assert cache_size >= 64, 'Unreasonably small cache.'
    775     return cache_size
    776 
    777 
    778 def get_cpu_model_frequency():
    779     """
    780     Returns the model frequency from the CPU model name on Intel only. This
    781     might be redundant with get_cpu_max_frequency. Unit is Hz.
    782     """
    783     frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz')
    784     return 1.e9 * frequency
    785 
    786 
    787 def get_cpu_max_frequency():
    788     """
    789     Returns the largest of the max CPU core frequencies. The unit is Hz.
    790     """
    791     max_frequency = -1
    792     paths = _get_cpufreq_paths('cpuinfo_max_freq')
    793     for path in paths:
    794         # Convert from kHz to Hz.
    795         frequency = 1000 * _get_float_from_file(path, 0, None, None)
    796         max_frequency = max(frequency, max_frequency)
    797     # Sanity check.
    798     assert max_frequency > 1e8, 'Unreasonably low CPU frequency.'
    799     return max_frequency
    800 
    801 
    802 def get_cpu_min_frequency():
    803     """
    804     Returns the smallest of the minimum CPU core frequencies.
    805     """
    806     min_frequency = 1e20
    807     paths = _get_cpufreq_paths('cpuinfo_min_freq')
    808     for path in paths:
    809         frequency = _get_float_from_file(path, 0, None, None)
    810         min_frequency = min(frequency, min_frequency)
    811     # Sanity check.
    812     assert min_frequency > 1e8, 'Unreasonably low CPU frequency.'
    813     return min_frequency
    814 
    815 
    816 def get_cpu_model():
    817     """
    818     Returns the CPU model.
    819     Only works on Intel.
    820     """
    821     cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None)
    822     return cpu_model
    823 
    824 
    825 def get_cpu_family():
    826     """
    827     Returns the CPU family.
    828     Only works on Intel.
    829     """
    830     cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None)
    831     return cpu_family
    832 
    833 
    834 def get_board():
    835     """
    836     Get the ChromeOS release board name from /etc/lsb-release.
    837     """
    838     f = open('/etc/lsb-release')
    839     try:
    840         return re.search('BOARD=(.*)', f.read()).group(1)
    841     finally:
    842         f.close()
    843 
    844 
    845 def get_board_type():
    846     """
    847     Get the ChromeOS board type from /etc/lsb-release.
    848 
    849     @return device type.
    850     """
    851     with open('/etc/lsb-release') as f:
    852         pat = re.search('DEVICETYPE=(.*)', f.read())
    853         if pat:
    854             return pat.group(1)
    855     return ''
    856 
    857 
    858 def get_board_with_frequency_and_memory():
    859     """
    860     Returns a board name modified with CPU frequency and memory size to
    861     differentiate between different board variants. For instance
    862     link -> link_1.8GHz_4GB.
    863     """
    864     board_name = get_board()
    865     # Rounded to nearest GB and GHz.
    866     memory = int(round(get_mem_total() / 1024.0))
    867     # Convert frequency to GHz with 1 digit accuracy after the decimal point.
    868     frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1
    869     board = "%s_%1.1fGHz_%dGB" % (board_name, frequency, memory)
    870     return board
    871 
    872 
    873 def get_mem_total():
    874     """
    875     Returns the total memory available in the system in MBytes.
    876     """
    877     mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB')
    878     # Sanity check, all Chromebooks have at least 1GB of memory.
    879     assert mem_total > 1024 * 1024, 'Unreasonable amount of memory.'
    880     return mem_total / 1024
    881 
    882 
    883 def get_mem_free():
    884     """
    885     Returns the currently free memory in the system in MBytes.
    886     """
    887     mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB')
    888     return mem_free / 1024
    889 
    890 
    891 def get_kernel_max():
    892     """
    893     Returns content of kernel_max.
    894     """
    895     kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None)
    896     # Sanity check.
    897     assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.'
    898     return kernel_max
    899 
    900 
    901 def set_high_performance_mode():
    902     """
    903     Sets the kernel governor mode to the highest setting.
    904     Returns previous governor state.
    905     """
    906     original_governors = get_scaling_governor_states()
    907     set_scaling_governors('performance')
    908     return original_governors
    909 
    910 
    911 def set_scaling_governors(value):
    912     """
    913     Sets all scaling governor to string value.
    914     Sample values: 'performance', 'interactive', 'ondemand', 'powersave'.
    915     """
    916     paths = _get_cpufreq_paths('scaling_governor')
    917     for path in paths:
    918         cmd = 'echo %s > %s' % (value, path)
    919         logging.info('Writing scaling governor mode \'%s\' -> %s', value, path)
    920         # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
    921         utils.system(cmd, ignore_status=True)
    922 
    923 
    924 def _get_cpufreq_paths(filename):
    925     """
    926     Returns a list of paths to the governors.
    927     """
    928     cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename
    929     paths = utils.run(cmd, verbose=False).stdout.splitlines()
    930     return paths
    931 
    932 
    933 def get_scaling_governor_states():
    934     """
    935     Returns a list of (performance governor path, current state) tuples.
    936     """
    937     paths = _get_cpufreq_paths('scaling_governor')
    938     path_value_list = []
    939     for path in paths:
    940         value = _get_line_from_file(path, 0)
    941         path_value_list.append((path, value))
    942     return path_value_list
    943 
    944 
    945 def restore_scaling_governor_states(path_value_list):
    946     """
    947     Restores governor states. Inverse operation to get_scaling_governor_states.
    948     """
    949     for (path, value) in path_value_list:
    950         cmd = 'echo %s > %s' % (value.rstrip('\n'), path)
    951         # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
    952         utils.system(cmd, ignore_status=True)
    953 
    954 
    955 def get_dirty_writeback_centisecs():
    956     """
    957     Reads /proc/sys/vm/dirty_writeback_centisecs.
    958     """
    959     time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None)
    960     return time
    961 
    962 
    963 def set_dirty_writeback_centisecs(time=60000):
    964     """
    965     In hundredths of a second, this is how often pdflush wakes up to write data
    966     to disk. The default wakes up the two (or more) active threads every five
    967     seconds. The ChromeOS default is 10 minutes.
    968 
    969     We use this to set as low as 1 second to flush error messages in system
    970     logs earlier to disk.
    971     """
    972     # Flush buffers first to make this function synchronous.
    973     utils.system('sync')
    974     if time >= 0:
    975         cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS)
    976         utils.system(cmd)
    977 
    978 
    979 def get_gpu_family():
    980     """Return the GPU family name"""
    981     global pciid_to_intel_architecture
    982 
    983     cpuarch = base_utils.get_cpu_soc_family()
    984     if cpuarch == 'exynos5' or cpuarch == 'rockchip':
    985         return 'mali'
    986     if cpuarch == 'tegra':
    987         return 'tegra'
    988     if os.path.exists('/sys/bus/platform/drivers/pvrsrvkm'):
    989         return 'rogue'
    990 
    991     pci_path = '/sys/bus/pci/devices/0000:00:02.0/device'
    992 
    993     if not os.path.exists(pci_path):
    994         raise error.TestError('PCI device 0000:00:02.0 not found')
    995 
    996     device_id = utils.read_one_line(pci_path).lower()
    997 
    998     # Only load Intel PCI ID file once and only if necessary.
    999     if not pciid_to_intel_architecture:
   1000         with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f:
   1001             pciid_to_intel_architecture = json.load(in_f)
   1002 
   1003     return pciid_to_intel_architecture[device_id]
   1004 
   1005 
   1006 _BOARDS_WITHOUT_MONITOR = [
   1007     'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus',
   1008     'veyron_brian', 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako'
   1009 ]
   1010 
   1011 
   1012 def has_no_monitor():
   1013     """Return whether a machine doesn't have a built-in monitor"""
   1014     board_name = get_board()
   1015     if (board_name in _BOARDS_WITHOUT_MONITOR):
   1016         return True
   1017 
   1018     return False
   1019 
   1020 
   1021 def get_fixed_dst_drive():
   1022     """
   1023     Return device name for internal disk.
   1024     Example: return /dev/sda for falco booted from usb
   1025     """
   1026     cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
   1027                     '. /usr/share/misc/chromeos-common.sh;',
   1028                     'load_base_vars;',
   1029                     'get_fixed_dst_drive'])
   1030     return utils.system_output(cmd)
   1031 
   1032 
   1033 def get_root_device():
   1034     """
   1035     Return root device.
   1036     Will return correct disk device even system boot from /dev/dm-0
   1037     Example: return /dev/sdb for falco booted from usb
   1038     """
   1039     return utils.system_output('rootdev -s -d')
   1040 
   1041 
   1042 def get_root_partition():
   1043     """
   1044     Return current root partition
   1045     Example: return /dev/sdb3 for falco booted from usb
   1046     """
   1047     return utils.system_output('rootdev -s')
   1048 
   1049 
   1050 def get_free_root_partition(root_part=None):
   1051     """
   1052     Return currently unused root partion
   1053     Example: return /dev/sdb5 for falco booted from usb
   1054 
   1055     @param root_part: cuurent root partition
   1056     """
   1057     spare_root_map = {'3': '5', '5': '3'}
   1058     if not root_part:
   1059         root_part = get_root_partition()
   1060     return root_part[:-1] + spare_root_map[root_part[-1]]
   1061 
   1062 
   1063 def is_booted_from_internal_disk():
   1064     """Return True if boot from internal disk. False, otherwise."""
   1065     return get_root_device() == get_fixed_dst_drive()
   1066 
   1067 
   1068 def get_ui_use_flags():
   1069     """Parses the USE flags as listed in /etc/ui_use_flags.txt.
   1070 
   1071     @return: A list of flag strings found in the ui use flags file.
   1072     """
   1073     flags = []
   1074     for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines():
   1075         # Removes everything after the '#'.
   1076         flag_before_comment = flag.split('#')[0].strip()
   1077         if len(flag_before_comment) != 0:
   1078             flags.append(flag_before_comment)
   1079 
   1080     return flags
   1081 
   1082 
   1083 def is_freon():
   1084     """Returns False if the system uses X, True otherwise."""
   1085     return 'X' not in get_ui_use_flags()
   1086 
   1087 
   1088 def graphics_platform():
   1089     """
   1090     Return a string identifying the graphics platform,
   1091     e.g. 'glx' or 'x11_egl' or 'gbm'
   1092     """
   1093     use_flags = get_ui_use_flags()
   1094     if 'X' not in use_flags:
   1095         return 'null'
   1096     elif 'opengles' in use_flags:
   1097         return 'x11_egl'
   1098     return 'glx'
   1099 
   1100 
   1101 def graphics_api():
   1102     """Return a string identifying the graphics api, e.g. gl or gles2."""
   1103     use_flags = get_ui_use_flags()
   1104     if 'opengles' in use_flags:
   1105         return 'gles2'
   1106     return 'gl'
   1107 
   1108 
   1109 def assert_has_X_server():
   1110     """Using X is soon to be deprecated. Print warning or raise error."""
   1111     if is_freon():
   1112         # TODO(ihf): Think about if we could support X for testing for a while.
   1113         raise error.TestFail('freon: can\'t use X server.')
   1114     logging.warning('freon: Using the X server will be deprecated soon.')
   1115 
   1116 
   1117 def is_vm():
   1118     """Check if the process is running in a virtual machine.
   1119 
   1120     @return: True if the process is running in a virtual machine, otherwise
   1121              return False.
   1122     """
   1123     try:
   1124         virt = utils.run('sudo -n virt-what').stdout.strip()
   1125         logging.debug('virt-what output: %s', virt)
   1126         return bool(virt)
   1127     except error.CmdError:
   1128         logging.warn('Package virt-what is not installed, default to assume '
   1129                      'it is not a virtual machine.')
   1130         return False
   1131 
   1132