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 7import glob 8import json 9import logging 10import os 11import platform 12import re 13import signal 14import tempfile 15import time 16import uuid 17 18from autotest_lib.client.common_lib import error 19from autotest_lib.client.common_lib import utils 20from 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 32pciid_to_amd_architecture = {} 33pciid_to_intel_architecture = {} 34 35class 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 67def 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 100def 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 115def 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 127def 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 162def 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 186def 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 211def is_virtual_machine(): 212 return 'QEMU' in platform.processor() 213 214 215def 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 237def 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 262def 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 308def 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 330def 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 351def 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 361def is_mountpoint(path): 362 return path in [m['dest'] for m in mounts()] 363 364 365def 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 373def random_username(): 374 return str(uuid.uuid4()) + '@example.com' 375 376 377def 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 392def 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 422def 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 446def 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 471def 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 489def is_pgo_mode(): 490 return 'USE_PGO' in os.environ 491 492 493def 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 531def 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 544def 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 585def _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 603def _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 617def _get_float_from_file(path, line, prefix, postfix): 618 match = _get_match_from_file(path, line, prefix, postfix) 619 return float(match) 620 621 622def _get_int_from_file(path, line, prefix, postfix): 623 match = _get_match_from_file(path, line, prefix, postfix) 624 return int(match) 625 626 627def _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 635def _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 650def 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 669def 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 681def 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 698def 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 720def 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 734def 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 744def 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 753def 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 768def 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 782def 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 791def 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 800def 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 816def get_board(): 817 """ 818 Get the ChromeOS release board name from /etc/lsb-release. 819 """ 820 return get_board_property('BOARD') 821 822 823def 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 832def 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 850def 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 860def 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 868def 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 878def 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 888def 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 901def _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 910def 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 922def 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 932def 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 940def 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 956def 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 963def has_mali(): 964 """ @return: True if system has a Mali GPU enabled.""" 965 return os.path.exists('/dev/mali0') 966 967def 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 1022def 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 1031def 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 1043def 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 1052def 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 1060def 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 1073def 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 1086def 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 1098def 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 1103def 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 1118def is_freon(): 1119 """Returns False if the system uses X, True otherwise.""" 1120 return 'X' not in get_ui_use_flags() 1121 1122 1123def 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 1136def 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 1144def 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 1152def 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 1168def 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 1181def 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 1194def 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