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