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