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