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