site_utils.py revision d5972948ef1e4c173eb3e2a37e47832d1a06f468
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.') 530 cmd = 'top -b -n1 -c' 531 output = utils.run(cmd) 532 logging.info(output) 533 534 535def wait_for_cool_machine(): 536 """ 537 A simple heuristic to wait for a machine to cool. 538 The code looks a bit 'magic', but we don't know ambient temperature 539 nor machine characteristics and still would like to return the caller 540 a machine that cooled down as much as reasonably possible. 541 """ 542 temperature = get_current_temperature_max() 543 # We got here with a cold machine, return immediately. This should be the 544 # most common case. 545 if temperature < 50: 546 return True 547 logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature) 548 # A modest wait should cool the machine. 549 time.sleep(60.0) 550 temperature = get_current_temperature_max() 551 # Atoms idle below 60 and everyone else should be even lower. 552 if temperature < 62: 553 return True 554 # This should be rare. 555 logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature) 556 time.sleep(120.0) 557 temperature = get_current_temperature_max() 558 # A temperature over 65'C doesn't give us much headroom to the critical 559 # temperatures that start at 85'C (and PerfControl as of today will fail at 560 # critical - 10'C). 561 if temperature < 65: 562 return True 563 logging.warning('Did not cool down (%dC), giving up.', temperature) 564 log_process_activity() 565 return False 566 567 568# System paths for machine performance state. 569_CPUINFO = '/proc/cpuinfo' 570_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs' 571_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' 572_MEMINFO = '/proc/meminfo' 573_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)' 574 575 576def _get_line_from_file(path, line): 577 """ 578 line can be an integer or 579 line can be a string that matches the beginning of the line 580 """ 581 with open(path) as f: 582 if isinstance(line, int): 583 l = f.readline() 584 for _ in range(0, line): 585 l = f.readline() 586 return l 587 else: 588 for l in f: 589 if l.startswith(line): 590 return l 591 return None 592 593 594def _get_match_from_file(path, line, prefix, postfix): 595 """ 596 Matches line in path and returns string between first prefix and postfix. 597 """ 598 match = _get_line_from_file(path, line) 599 # Strip everything from front of line including prefix. 600 if prefix: 601 match = re.split(prefix, match)[1] 602 # Strip everything from back of string including first occurence of postfix. 603 if postfix: 604 match = re.split(postfix, match)[0] 605 return match 606 607 608def _get_float_from_file(path, line, prefix, postfix): 609 match = _get_match_from_file(path, line, prefix, postfix) 610 return float(match) 611 612 613def _get_int_from_file(path, line, prefix, postfix): 614 match = _get_match_from_file(path, line, prefix, postfix) 615 return int(match) 616 617 618def _get_hex_from_file(path, line, prefix, postfix): 619 match = _get_match_from_file(path, line, prefix, postfix) 620 return int(match, 16) 621 622 623# The paths don't change. Avoid running find all the time. 624_hwmon_paths = None 625 626def _get_hwmon_paths(file_pattern): 627 """ 628 Returns a list of paths to the temperature sensors. 629 """ 630 # Some systems like daisy_spring only have the virtual hwmon. 631 # And other systems like rambi only have coretemp.0. See crbug.com/360249. 632 # /sys/class/hwmon/hwmon*/ 633 # /sys/devices/virtual/hwmon/hwmon*/ 634 # /sys/devices/platform/coretemp.0/ 635 if not _hwmon_paths: 636 cmd = 'find /sys/ -name "' + file_pattern + '"' 637 _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines() 638 return _hwon_paths 639 640 641def get_temperature_critical(): 642 """ 643 Returns temperature at which we will see some throttling in the system. 644 """ 645 min_temperature = 1000.0 646 paths = _get_hwmon_paths('temp*_crit') 647 for path in paths: 648 temperature = _get_float_from_file(path, 0, None, None) * 0.001 649 # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to 650 # the lowest known value. 651 if (min_temperature < 60.0) or min_temperature > 150.0: 652 logging.warning('Critical temperature of %.1fC was reset to 85.0C.', 653 min_temperature) 654 min_temperature = 85.0 655 656 min_temperature = min(temperature, min_temperature) 657 return min_temperature 658 659 660def get_temperature_input_max(): 661 """ 662 Returns the maximum currently observed temperature. 663 """ 664 max_temperature = -1000.0 665 paths = _get_hwmon_paths('temp*_input') 666 for path in paths: 667 temperature = _get_float_from_file(path, 0, None, None) * 0.001 668 max_temperature = max(temperature, max_temperature) 669 return max_temperature 670 671 672def get_thermal_zone_temperatures(): 673 """ 674 Returns the maximum currently observered temperature in thermal_zones. 675 """ 676 temperatures = [] 677 for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'): 678 try: 679 temperatures.append( 680 _get_float_from_file(path, 0, None, None) * 0.001) 681 except IOError: 682 # Some devices (e.g. Veyron) may have reserved thermal zones that 683 # are not active. Trying to read the temperature value would cause a 684 # EINVAL IO error. 685 continue 686 return temperatures 687 688 689def get_ec_temperatures(): 690 """ 691 Uses ectool to return a list of all sensor temperatures in Celsius. 692 """ 693 temperatures = [] 694 try: 695 full_cmd = 'ectool temps all' 696 lines = utils.run(full_cmd, verbose=False).stdout.splitlines() 697 for line in lines: 698 temperature = int(line.split(': ')[1]) - 273 699 temperatures.append(temperature) 700 except Exception: 701 logging.warning('Unable to read temperature sensors using ectool.') 702 for temperature in temperatures: 703 # Sanity check for real world values. 704 assert ((temperature > 10.0) and 705 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 706 temperature) 707 708 return temperatures 709 710 711def get_current_temperature_max(): 712 """ 713 Returns the highest reported board temperature (all sensors) in Celsius. 714 """ 715 temperature = max([get_temperature_input_max()] + 716 get_thermal_zone_temperatures() + 717 get_ec_temperatures()) 718 # Sanity check for real world values. 719 assert ((temperature > 10.0) and 720 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 721 temperature) 722 return temperature 723 724 725def get_cpu_cache_size(): 726 """ 727 Returns the last level CPU cache size in kBytes. 728 """ 729 cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB') 730 # Sanity check. 731 assert cache_size >= 64, 'Unreasonably small cache.' 732 return cache_size 733 734 735def get_cpu_model_frequency(): 736 """ 737 Returns the model frequency from the CPU model name on Intel only. This 738 might be redundant with get_cpu_max_frequency. Unit is Hz. 739 """ 740 frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz') 741 return 1.e9 * frequency 742 743 744def get_cpu_max_frequency(): 745 """ 746 Returns the largest of the max CPU core frequencies. The unit is Hz. 747 """ 748 max_frequency = -1 749 paths = _get_cpufreq_paths('cpuinfo_max_freq') 750 for path in paths: 751 # Convert from kHz to Hz. 752 frequency = 1000 * _get_float_from_file(path, 0, None, None) 753 max_frequency = max(frequency, max_frequency) 754 # Sanity check. 755 assert max_frequency > 1e8, 'Unreasonably low CPU frequency.' 756 return max_frequency 757 758 759def get_cpu_min_frequency(): 760 """ 761 Returns the smallest of the minimum CPU core frequencies. 762 """ 763 min_frequency = 1e20 764 paths = _get_cpufreq_paths('cpuinfo_min_freq') 765 for path in paths: 766 frequency = _get_float_from_file(path, 0, None, None) 767 min_frequency = min(frequency, min_frequency) 768 # Sanity check. 769 assert min_frequency > 1e8, 'Unreasonably low CPU frequency.' 770 return min_frequency 771 772 773def get_cpu_model(): 774 """ 775 Returns the CPU model. 776 Only works on Intel. 777 """ 778 cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None) 779 return cpu_model 780 781 782def get_cpu_family(): 783 """ 784 Returns the CPU family. 785 Only works on Intel. 786 """ 787 cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None) 788 return cpu_family 789 790 791def get_board_property(key): 792 """ 793 Get a specific property from /etc/lsb-release. 794 795 @param key: board property to return value for 796 797 @return the value or '' if not present 798 """ 799 with open('/etc/lsb-release') as f: 800 pattern = '%s=(.*)' % key 801 pat = re.search(pattern, f.read()) 802 if pat: 803 return pat.group(1) 804 return '' 805 806 807def get_board(): 808 """ 809 Get the ChromeOS release board name from /etc/lsb-release. 810 """ 811 return get_board_property('BOARD') 812 813 814def get_board_type(): 815 """ 816 Get the ChromeOS board type from /etc/lsb-release. 817 818 @return device type. 819 """ 820 return get_board_property('DEVICETYPE') 821 822 823def get_board_with_frequency_and_memory(): 824 """ 825 Returns a board name modified with CPU frequency and memory size to 826 differentiate between different board variants. For instance 827 link -> link_1.8GHz_4GB. 828 """ 829 board_name = get_board() 830 if is_virtual_machine(): 831 board = '%s_VM' % board_name 832 else: 833 # Rounded to nearest GB and GHz. 834 memory = int(round(get_mem_total() / 1024.0)) 835 # Convert frequency to GHz with 1 digit accuracy after the decimal point. 836 frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1 837 board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory) 838 return board 839 840 841def get_mem_total(): 842 """ 843 Returns the total memory available in the system in MBytes. 844 """ 845 mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB') 846 # Sanity check, all Chromebooks have at least 1GB of memory. 847 assert mem_total > 256 * 1024, 'Unreasonable amount of memory.' 848 return mem_total / 1024 849 850 851def get_mem_free(): 852 """ 853 Returns the currently free memory in the system in MBytes. 854 """ 855 mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB') 856 return mem_free / 1024 857 858 859def get_kernel_max(): 860 """ 861 Returns content of kernel_max. 862 """ 863 kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None) 864 # Sanity check. 865 assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.' 866 return kernel_max 867 868 869def set_high_performance_mode(): 870 """ 871 Sets the kernel governor mode to the highest setting. 872 Returns previous governor state. 873 """ 874 original_governors = get_scaling_governor_states() 875 set_scaling_governors('performance') 876 return original_governors 877 878 879def set_scaling_governors(value): 880 """ 881 Sets all scaling governor to string value. 882 Sample values: 'performance', 'interactive', 'ondemand', 'powersave'. 883 """ 884 paths = _get_cpufreq_paths('scaling_governor') 885 for path in paths: 886 cmd = 'echo %s > %s' % (value, path) 887 logging.info('Writing scaling governor mode \'%s\' -> %s', value, path) 888 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures. 889 utils.system(cmd, ignore_status=True) 890 891 892def _get_cpufreq_paths(filename): 893 """ 894 Returns a list of paths to the governors. 895 """ 896 cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename 897 paths = utils.run(cmd, verbose=False).stdout.splitlines() 898 return paths 899 900 901def get_scaling_governor_states(): 902 """ 903 Returns a list of (performance governor path, current state) tuples. 904 """ 905 paths = _get_cpufreq_paths('scaling_governor') 906 path_value_list = [] 907 for path in paths: 908 value = _get_line_from_file(path, 0) 909 path_value_list.append((path, value)) 910 return path_value_list 911 912 913def restore_scaling_governor_states(path_value_list): 914 """ 915 Restores governor states. Inverse operation to get_scaling_governor_states. 916 """ 917 for (path, value) in path_value_list: 918 cmd = 'echo %s > %s' % (value.rstrip('\n'), path) 919 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures. 920 utils.system(cmd, ignore_status=True) 921 922 923def get_dirty_writeback_centisecs(): 924 """ 925 Reads /proc/sys/vm/dirty_writeback_centisecs. 926 """ 927 time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None) 928 return time 929 930 931def set_dirty_writeback_centisecs(time=60000): 932 """ 933 In hundredths of a second, this is how often pdflush wakes up to write data 934 to disk. The default wakes up the two (or more) active threads every five 935 seconds. The ChromeOS default is 10 minutes. 936 937 We use this to set as low as 1 second to flush error messages in system 938 logs earlier to disk. 939 """ 940 # Flush buffers first to make this function synchronous. 941 utils.system('sync') 942 if time >= 0: 943 cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS) 944 utils.system(cmd) 945 946 947def wflinfo_cmd(): 948 """ 949 Return a wflinfo command appropriate to the current graphics platform/api. 950 """ 951 return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api()) 952 953 954def get_gpu_family(): 955 """Return the GPU family name""" 956 global pciid_to_intel_architecture 957 958 cpuarch = base_utils.get_cpu_soc_family() 959 if cpuarch == 'exynos5' or cpuarch == 'rockchip': 960 cmd = wflinfo_cmd() 961 wflinfo = utils.system_output(cmd, 962 retain_output=True, 963 ignore_status=False) 964 version = re.findall(r'OpenGL renderer string: ' 965 r'Mali-T([0-9]+)', wflinfo) 966 if version: 967 return 'mali-t%s' % version[0] 968 return 'mali-unrecognized' 969 if cpuarch == 'tegra': 970 return 'tegra' 971 if os.path.exists('/sys/bus/platform/drivers/pvrsrvkm'): 972 return 'rogue' 973 974 pci_path = '/sys/bus/pci/devices/0000:00:02.0/device' 975 976 if not os.path.exists(pci_path): 977 raise error.TestError('PCI device 0000:00:02.0 not found') 978 979 device_id = utils.read_one_line(pci_path).lower() 980 981 # Only load Intel PCI ID file once and only if necessary. 982 if not pciid_to_intel_architecture: 983 with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f: 984 pciid_to_intel_architecture = json.load(in_f) 985 986 return pciid_to_intel_architecture[device_id] 987 988# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE 989# for sanity check, but usage seems a bit inconsistent. See 990# src/third_party/chromiumos-overlay/eclass/appid.eclass 991_BOARDS_WITHOUT_MONITOR = [ 992 'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus', 993 'veyron_brian', 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 994 'veyron_rialto' 995] 996 997 998def has_no_monitor(): 999 """Return whether a machine doesn't have a built-in monitor""" 1000 board_name = get_board() 1001 if board_name in _BOARDS_WITHOUT_MONITOR: 1002 return True 1003 1004 return False 1005 1006 1007def get_fixed_dst_drive(): 1008 """ 1009 Return device name for internal disk. 1010 Example: return /dev/sda for falco booted from usb 1011 """ 1012 cmd = ' '.join(['. /usr/sbin/write_gpt.sh;', 1013 '. /usr/share/misc/chromeos-common.sh;', 1014 'load_base_vars;', 1015 'get_fixed_dst_drive']) 1016 return utils.system_output(cmd) 1017 1018 1019def get_root_device(): 1020 """ 1021 Return root device. 1022 Will return correct disk device even system boot from /dev/dm-0 1023 Example: return /dev/sdb for falco booted from usb 1024 """ 1025 return utils.system_output('rootdev -s -d') 1026 1027 1028def get_root_partition(): 1029 """ 1030 Return current root partition 1031 Example: return /dev/sdb3 for falco booted from usb 1032 """ 1033 return utils.system_output('rootdev -s') 1034 1035 1036def get_free_root_partition(root_part=None): 1037 """ 1038 Return currently unused root partion 1039 Example: return /dev/sdb5 for falco booted from usb 1040 1041 @param root_part: cuurent root partition 1042 """ 1043 spare_root_map = {'3': '5', '5': '3'} 1044 if not root_part: 1045 root_part = get_root_partition() 1046 return root_part[:-1] + spare_root_map[root_part[-1]] 1047 1048 1049def is_booted_from_internal_disk(): 1050 """Return True if boot from internal disk. False, otherwise.""" 1051 return get_root_device() == get_fixed_dst_drive() 1052 1053 1054def get_ui_use_flags(): 1055 """Parses the USE flags as listed in /etc/ui_use_flags.txt. 1056 1057 @return: A list of flag strings found in the ui use flags file. 1058 """ 1059 flags = [] 1060 for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines(): 1061 # Removes everything after the '#'. 1062 flag_before_comment = flag.split('#')[0].strip() 1063 if len(flag_before_comment) != 0: 1064 flags.append(flag_before_comment) 1065 1066 return flags 1067 1068 1069def is_freon(): 1070 """Returns False if the system uses X, True otherwise.""" 1071 return 'X' not in get_ui_use_flags() 1072 1073 1074def graphics_platform(): 1075 """ 1076 Return a string identifying the graphics platform, 1077 e.g. 'glx' or 'x11_egl' or 'gbm' 1078 """ 1079 use_flags = get_ui_use_flags() 1080 if 'X' not in use_flags: 1081 return 'null' 1082 elif 'opengles' in use_flags: 1083 return 'x11_egl' 1084 return 'glx' 1085 1086 1087def graphics_api(): 1088 """Return a string identifying the graphics api, e.g. gl or gles2.""" 1089 use_flags = get_ui_use_flags() 1090 if 'opengles' in use_flags: 1091 return 'gles2' 1092 return 'gl' 1093 1094 1095def assert_has_X_server(): 1096 """Using X is soon to be deprecated. Print warning or raise error.""" 1097 if is_freon(): 1098 # TODO(ihf): Think about if we could support X for testing for a while. 1099 raise error.TestFail('freon: can\'t use X server.') 1100 logging.warning('freon: Using the X server will be deprecated soon.') 1101 1102 1103def is_vm(): 1104 """Check if the process is running in a virtual machine. 1105 1106 @return: True if the process is running in a virtual machine, otherwise 1107 return False. 1108 """ 1109 try: 1110 virt = utils.run('sudo -n virt-what').stdout.strip() 1111 logging.debug('virt-what output: %s', virt) 1112 return bool(virt) 1113 except error.CmdError: 1114 logging.warn('Package virt-what is not installed, default to assume ' 1115 'it is not a virtual machine.') 1116 return False 1117