utils.py revision eebd1225a4b967e98ca417eb4e373ece67670f49
1# Copyright 2017 The Chromium OS Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5""" 6Convenience functions for use by tests or whomever. 7""" 8 9# pylint: disable=missing-docstring 10 11import commands 12import fnmatch 13import glob 14import json 15import logging 16import math 17import multiprocessing 18import os 19import pickle 20import platform 21import re 22import shutil 23import signal 24import tempfile 25import time 26import uuid 27 28from autotest_lib.client.common_lib import error 29from autotest_lib.client.common_lib import magic 30from autotest_lib.client.common_lib import utils 31 32from autotest_lib.client.common_lib.utils import * 33 34 35def grep(pattern, file): 36 """ 37 This is mainly to fix the return code inversion from grep 38 Also handles compressed files. 39 40 returns 1 if the pattern is present in the file, 0 if not. 41 """ 42 command = 'grep "%s" > /dev/null' % pattern 43 ret = cat_file_to_cmd(file, command, ignore_status=True) 44 return not ret 45 46 47def difflist(list1, list2): 48 """returns items in list2 that are not in list1""" 49 diff = []; 50 for x in list2: 51 if x not in list1: 52 diff.append(x) 53 return diff 54 55 56def cat_file_to_cmd(file, command, ignore_status=0, return_output=False): 57 """ 58 equivalent to 'cat file | command' but knows to use 59 zcat or bzcat if appropriate 60 """ 61 if not os.path.isfile(file): 62 raise NameError('invalid file %s to cat to command %s' 63 % (file, command)) 64 65 if return_output: 66 run_cmd = utils.system_output 67 else: 68 run_cmd = utils.system 69 70 if magic.guess_type(file) == 'application/x-bzip2': 71 cat = 'bzcat' 72 elif magic.guess_type(file) == 'application/x-gzip': 73 cat = 'zcat' 74 else: 75 cat = 'cat' 76 return run_cmd('%s %s | %s' % (cat, file, command), 77 ignore_status=ignore_status) 78 79 80def extract_tarball_to_dir(tarball, dir): 81 """ 82 Extract a tarball to a specified directory name instead of whatever 83 the top level of a tarball is - useful for versioned directory names, etc 84 """ 85 if os.path.exists(dir): 86 if os.path.isdir(dir): 87 shutil.rmtree(dir) 88 else: 89 os.remove(dir) 90 pwd = os.getcwd() 91 os.chdir(os.path.dirname(os.path.abspath(dir))) 92 newdir = extract_tarball(tarball) 93 os.rename(newdir, dir) 94 os.chdir(pwd) 95 96 97def extract_tarball(tarball): 98 """Returns the directory extracted by the tarball.""" 99 extracted = cat_file_to_cmd(tarball, 'tar xvf - 2>/dev/null', 100 return_output=True).splitlines() 101 102 dir = None 103 104 for line in extracted: 105 if line.startswith('./'): 106 line = line[2:] 107 if not line or line == '.': 108 continue 109 topdir = line.split('/')[0] 110 if os.path.isdir(topdir): 111 if dir: 112 assert(dir == topdir) 113 else: 114 dir = topdir 115 if dir: 116 return dir 117 else: 118 raise NameError('extracting tarball produced no dir') 119 120 121def unmap_url_cache(cachedir, url, expected_hash, method="md5"): 122 """ 123 Downloads a file from a URL to a cache directory. If the file is already 124 at the expected position and has the expected hash, let's not download it 125 again. 126 127 @param cachedir: Directory that might hold a copy of the file we want to 128 download. 129 @param url: URL for the file we want to download. 130 @param expected_hash: Hash string that we expect the file downloaded to 131 have. 132 @param method: Method used to calculate the hash string (md5, sha1). 133 """ 134 # Let's convert cachedir to a canonical path, if it's not already 135 cachedir = os.path.realpath(cachedir) 136 if not os.path.isdir(cachedir): 137 try: 138 os.makedirs(cachedir) 139 except: 140 raise ValueError('Could not create cache directory %s' % cachedir) 141 file_from_url = os.path.basename(url) 142 file_local_path = os.path.join(cachedir, file_from_url) 143 144 file_hash = None 145 failure_counter = 0 146 while not file_hash == expected_hash: 147 if os.path.isfile(file_local_path): 148 file_hash = hash_file(file_local_path, method) 149 if file_hash == expected_hash: 150 # File is already at the expected position and ready to go 151 src = file_from_url 152 else: 153 # Let's download the package again, it's corrupted... 154 logging.error("Seems that file %s is corrupted, trying to " 155 "download it again", file_from_url) 156 src = url 157 failure_counter += 1 158 else: 159 # File is not there, let's download it 160 src = url 161 if failure_counter > 1: 162 raise EnvironmentError("Consistently failed to download the " 163 "package %s. Aborting further download " 164 "attempts. This might mean either the " 165 "network connection has problems or the " 166 "expected hash string that was determined " 167 "for this file is wrong", file_from_url) 168 file_path = utils.unmap_url(cachedir, src, cachedir) 169 170 return file_path 171 172 173def force_copy(src, dest): 174 """Replace dest with a new copy of src, even if it exists""" 175 if os.path.isfile(dest): 176 os.remove(dest) 177 if os.path.isdir(dest): 178 dest = os.path.join(dest, os.path.basename(src)) 179 shutil.copyfile(src, dest) 180 return dest 181 182 183def force_link(src, dest): 184 """Link src to dest, overwriting it if it exists""" 185 return utils.system("ln -sf %s %s" % (src, dest)) 186 187 188def file_contains_pattern(file, pattern): 189 """Return true if file contains the specified egrep pattern""" 190 if not os.path.isfile(file): 191 raise NameError('file %s does not exist' % file) 192 return not utils.system('egrep -q "' + pattern + '" ' + file, 193 ignore_status=True) 194 195 196def list_grep(list, pattern): 197 """True if any item in list matches the specified pattern.""" 198 compiled = re.compile(pattern) 199 for line in list: 200 match = compiled.search(line) 201 if (match): 202 return 1 203 return 0 204 205 206def get_os_vendor(): 207 """Try to guess what's the os vendor 208 """ 209 if os.path.isfile('/etc/SuSE-release'): 210 return 'SUSE' 211 212 issue = '/etc/issue' 213 214 if not os.path.isfile(issue): 215 return 'Unknown' 216 217 if file_contains_pattern(issue, 'Red Hat'): 218 return 'Red Hat' 219 elif file_contains_pattern(issue, 'Fedora'): 220 return 'Fedora Core' 221 elif file_contains_pattern(issue, 'SUSE'): 222 return 'SUSE' 223 elif file_contains_pattern(issue, 'Ubuntu'): 224 return 'Ubuntu' 225 elif file_contains_pattern(issue, 'Debian'): 226 return 'Debian' 227 else: 228 return 'Unknown' 229 230 231def get_cc(): 232 try: 233 return os.environ['CC'] 234 except KeyError: 235 return 'gcc' 236 237 238def get_vmlinux(): 239 """Return the full path to vmlinux 240 241 Ahem. This is crap. Pray harder. Bad Martin. 242 """ 243 vmlinux = '/boot/vmlinux-%s' % utils.system_output('uname -r') 244 if os.path.isfile(vmlinux): 245 return vmlinux 246 vmlinux = '/lib/modules/%s/build/vmlinux' % utils.system_output('uname -r') 247 if os.path.isfile(vmlinux): 248 return vmlinux 249 return None 250 251 252def get_systemmap(): 253 """Return the full path to System.map 254 255 Ahem. This is crap. Pray harder. Bad Martin. 256 """ 257 map = '/boot/System.map-%s' % utils.system_output('uname -r') 258 if os.path.isfile(map): 259 return map 260 map = '/lib/modules/%s/build/System.map' % utils.system_output('uname -r') 261 if os.path.isfile(map): 262 return map 263 return None 264 265 266def get_modules_dir(): 267 """Return the modules dir for the running kernel version""" 268 kernel_version = utils.system_output('uname -r') 269 return '/lib/modules/%s/kernel' % kernel_version 270 271 272_CPUINFO_RE = re.compile(r'^(?P<key>[^\t]*)\t*: ?(?P<value>.*)$') 273 274 275def get_cpuinfo(): 276 """Read /proc/cpuinfo and convert to a list of dicts.""" 277 cpuinfo = [] 278 with open('/proc/cpuinfo', 'r') as f: 279 cpu = {} 280 for line in f: 281 line = line.strip() 282 if not line: 283 cpuinfo.append(cpu) 284 cpu = {} 285 continue 286 match = _CPUINFO_RE.match(line) 287 cpu[match.group('key')] = match.group('value') 288 if cpu: 289 # cpuinfo usually ends in a blank line, so this shouldn't happen. 290 cpuinfo.append(cpu) 291 return cpuinfo 292 293 294def get_cpu_arch(): 295 """Work out which CPU architecture we're running on""" 296 f = open('/proc/cpuinfo', 'r') 297 cpuinfo = f.readlines() 298 f.close() 299 if list_grep(cpuinfo, '^cpu.*(RS64|POWER3|Broadband Engine)'): 300 return 'power' 301 elif list_grep(cpuinfo, '^cpu.*POWER4'): 302 return 'power4' 303 elif list_grep(cpuinfo, '^cpu.*POWER5'): 304 return 'power5' 305 elif list_grep(cpuinfo, '^cpu.*POWER6'): 306 return 'power6' 307 elif list_grep(cpuinfo, '^cpu.*POWER7'): 308 return 'power7' 309 elif list_grep(cpuinfo, '^cpu.*PPC970'): 310 return 'power970' 311 elif list_grep(cpuinfo, 'ARM'): 312 return 'arm' 313 elif list_grep(cpuinfo, '^flags.*:.* lm .*'): 314 return 'x86_64' 315 elif list_grep(cpuinfo, 'CPU.*implementer.*0x41'): 316 return 'arm' 317 else: 318 return 'i386' 319 320 321def get_arm_soc_family_from_devicetree(): 322 """ 323 Work out which ARM SoC we're running on based on the 'compatible' property 324 of the base node of devicetree, if it exists. 325 """ 326 devicetree_compatible = '/sys/firmware/devicetree/base/compatible' 327 if not os.path.isfile(devicetree_compatible): 328 return None 329 f = open(devicetree_compatible, 'r') 330 compatible = f.readlines() 331 f.close() 332 if list_grep(compatible, 'rk3399'): 333 return 'rockchip' 334 elif list_grep(compatible, 'mt8173'): 335 return 'mediatek' 336 return None 337 338 339def get_arm_soc_family(): 340 """Work out which ARM SoC we're running on""" 341 family = get_arm_soc_family_from_devicetree() 342 if family is not None: 343 return family 344 345 f = open('/proc/cpuinfo', 'r') 346 cpuinfo = f.readlines() 347 f.close() 348 if list_grep(cpuinfo, 'EXYNOS5'): 349 return 'exynos5' 350 elif list_grep(cpuinfo, 'Tegra'): 351 return 'tegra' 352 elif list_grep(cpuinfo, 'Rockchip'): 353 return 'rockchip' 354 return 'arm' 355 356 357def get_cpu_soc_family(): 358 """Like get_cpu_arch, but for ARM, returns the SoC family name""" 359 f = open('/proc/cpuinfo', 'r') 360 cpuinfo = f.readlines() 361 f.close() 362 family = get_cpu_arch() 363 if family == 'arm': 364 family = get_arm_soc_family() 365 if list_grep(cpuinfo, '^vendor_id.*:.*AMD'): 366 family = 'amd' 367 return family 368 369 370INTEL_UARCH_TABLE = { 371 '06_4C': 'Airmont', 372 '06_1C': 'Atom', 373 '06_26': 'Atom', 374 '06_27': 'Atom', 375 '06_35': 'Atom', 376 '06_36': 'Atom', 377 '06_3D': 'Broadwell', 378 '06_47': 'Broadwell', 379 '06_4F': 'Broadwell', 380 '06_56': 'Broadwell', 381 '06_0D': 'Dothan', 382 '06_5C': 'Goldmont', 383 '06_3C': 'Haswell', 384 '06_45': 'Haswell', 385 '06_46': 'Haswell', 386 '06_3F': 'Haswell-E', 387 '06_3A': 'Ivy Bridge', 388 '06_3E': 'Ivy Bridge-E', 389 '06_8E': 'Kaby Lake', 390 '06_9E': 'Kaby Lake', 391 '06_0F': 'Merom', 392 '06_16': 'Merom', 393 '06_17': 'Nehalem', 394 '06_1A': 'Nehalem', 395 '06_1D': 'Nehalem', 396 '06_1E': 'Nehalem', 397 '06_1F': 'Nehalem', 398 '06_2E': 'Nehalem', 399 '0F_03': 'Prescott', 400 '0F_04': 'Prescott', 401 '0F_06': 'Presler', 402 '06_2A': 'Sandy Bridge', 403 '06_2D': 'Sandy Bridge', 404 '06_37': 'Silvermont', 405 '06_4A': 'Silvermont', 406 '06_4D': 'Silvermont', 407 '06_5A': 'Silvermont', 408 '06_5D': 'Silvermont', 409 '06_4E': 'Skylake', 410 '06_5E': 'Skylake', 411 '06_55': 'Skylake', 412 '06_25': 'Westmere', 413 '06_2C': 'Westmere', 414 '06_2F': 'Westmere', 415} 416 417 418def get_intel_cpu_uarch(numeric=False): 419 """Return the Intel microarchitecture we're running on, or None. 420 421 Returns None if this is not an Intel CPU. Returns the family and model as 422 underscore-separated hex (per Intel manual convention) if the uarch is not 423 known, or if numeric is True. 424 """ 425 if not get_current_kernel_arch().startswith('x86'): 426 return None 427 cpuinfo = get_cpuinfo()[0] 428 if cpuinfo['vendor_id'] != 'GenuineIntel': 429 return None 430 family_model = '%02X_%02X' % (int(cpuinfo['cpu family']), 431 int(cpuinfo['model'])) 432 if numeric: 433 return family_model 434 return INTEL_UARCH_TABLE.get(family_model, family_model) 435 436 437def get_current_kernel_arch(): 438 """Get the machine architecture, now just a wrap of 'uname -m'.""" 439 return os.popen('uname -m').read().rstrip() 440 441 442def get_file_arch(filename): 443 # -L means follow symlinks 444 file_data = utils.system_output('file -L ' + filename) 445 if file_data.count('80386'): 446 return 'i386' 447 return None 448 449 450def count_cpus(): 451 """number of CPUs in the local machine according to /proc/cpuinfo""" 452 try: 453 return multiprocessing.cpu_count() 454 except Exception: 455 logging.exception('can not get cpu count from' 456 ' multiprocessing.cpu_count()') 457 cpuinfo = get_cpuinfo() 458 # Returns at least one cpu. Check comment #1 in crosbug.com/p/9582. 459 return len(cpuinfo) or 1 460 461 462def cpu_online_map(): 463 """ 464 Check out the available cpu online map 465 """ 466 cpuinfo = get_cpuinfo() 467 cpus = [] 468 for cpu in cpuinfo: 469 cpus.append(cpu['processor']) # grab cpu number 470 return cpus 471 472 473def get_cpu_family(): 474 cpuinfo = get_cpuinfo()[0] 475 return int(cpuinfo['cpu_family']) 476 477 478def get_cpu_vendor(): 479 cpuinfo = get_cpuinfo() 480 vendors = [cpu['vendor_id'] for cpu in cpuinfo] 481 for v in vendors[1:]: 482 if v != vendors[0]: 483 raise error.TestError('multiple cpu vendors found: ' + str(vendors)) 484 return vendors[0] 485 486 487def probe_cpus(): 488 """ 489 This routine returns a list of cpu devices found under 490 /sys/devices/system/cpu. 491 """ 492 cmd = 'find /sys/devices/system/cpu/ -maxdepth 1 -type d -name cpu*' 493 return utils.system_output(cmd).splitlines() 494 495 496# Returns total memory in kb 497def read_from_meminfo(key): 498 meminfo = utils.system_output('grep %s /proc/meminfo' % key) 499 return int(re.search(r'\d+', meminfo).group(0)) 500 501 502def memtotal(): 503 return read_from_meminfo('MemTotal') 504 505 506def freememtotal(): 507 return read_from_meminfo('MemFree') 508 509def usable_memtotal(): 510 # Reserved 5% for OS use 511 return int(read_from_meminfo('MemFree') * 0.95) 512 513 514def rounded_memtotal(): 515 # Get total of all physical mem, in kbytes 516 usable_kbytes = memtotal() 517 # usable_kbytes is system's usable DRAM in kbytes, 518 # as reported by memtotal() from device /proc/meminfo memtotal 519 # after Linux deducts 1.5% to 5.1% for system table overhead 520 # Undo the unknown actual deduction by rounding up 521 # to next small multiple of a big power-of-two 522 # eg 12GB - 5.1% gets rounded back up to 12GB 523 mindeduct = 0.015 # 1.5 percent 524 maxdeduct = 0.055 # 5.5 percent 525 # deduction range 1.5% .. 5.5% supports physical mem sizes 526 # 6GB .. 12GB in steps of .5GB 527 # 12GB .. 24GB in steps of 1 GB 528 # 24GB .. 48GB in steps of 2 GB ... 529 # Finer granularity in physical mem sizes would require 530 # tighter spread between min and max possible deductions 531 532 # increase mem size by at least min deduction, without rounding 533 min_kbytes = int(usable_kbytes / (1.0 - mindeduct)) 534 # increase mem size further by 2**n rounding, by 0..roundKb or more 535 round_kbytes = int(usable_kbytes / (1.0 - maxdeduct)) - min_kbytes 536 # find least binary roundup 2**n that covers worst-cast roundKb 537 mod2n = 1 << int(math.ceil(math.log(round_kbytes, 2))) 538 # have round_kbytes <= mod2n < round_kbytes*2 539 # round min_kbytes up to next multiple of mod2n 540 phys_kbytes = min_kbytes + mod2n - 1 541 phys_kbytes = phys_kbytes - (phys_kbytes % mod2n) # clear low bits 542 return phys_kbytes 543 544 545def sysctl(key, value=None): 546 """Generic implementation of sysctl, to read and write. 547 548 @param key: A location under /proc/sys 549 @param value: If not None, a value to write into the sysctl. 550 551 @return The single-line sysctl value as a string. 552 """ 553 path = '/proc/sys/%s' % key 554 if value is not None: 555 utils.write_one_line(path, str(value)) 556 return utils.read_one_line(path) 557 558 559def sysctl_kernel(key, value=None): 560 """(Very) partial implementation of sysctl, for kernel params""" 561 if value is not None: 562 # write 563 utils.write_one_line('/proc/sys/kernel/%s' % key, str(value)) 564 else: 565 # read 566 out = utils.read_one_line('/proc/sys/kernel/%s' % key) 567 return int(re.search(r'\d+', out).group(0)) 568 569 570def _convert_exit_status(sts): 571 if os.WIFSIGNALED(sts): 572 return -os.WTERMSIG(sts) 573 elif os.WIFEXITED(sts): 574 return os.WEXITSTATUS(sts) 575 else: 576 # impossible? 577 raise RuntimeError("Unknown exit status %d!" % sts) 578 579 580def where_art_thy_filehandles(): 581 """Dump the current list of filehandles""" 582 os.system("ls -l /proc/%d/fd >> /dev/tty" % os.getpid()) 583 584 585def get_num_allocated_file_handles(): 586 """ 587 Returns the number of currently allocated file handles. 588 589 Gets this information by parsing /proc/sys/fs/file-nr. 590 See https://www.kernel.org/doc/Documentation/sysctl/fs.txt 591 for details on this file. 592 """ 593 with _open_file('/proc/sys/fs/file-nr') as f: 594 line = f.readline() 595 allocated_handles = int(line.split()[0]) 596 return allocated_handles 597 598def print_to_tty(string): 599 """Output string straight to the tty""" 600 open('/dev/tty', 'w').write(string + '\n') 601 602 603def dump_object(object): 604 """Dump an object's attributes and methods 605 606 kind of like dir() 607 """ 608 for item in object.__dict__.iteritems(): 609 print item 610 try: 611 (key, value) = item 612 dump_object(value) 613 except: 614 continue 615 616 617def environ(env_key): 618 """return the requested environment variable, or '' if unset""" 619 if (os.environ.has_key(env_key)): 620 return os.environ[env_key] 621 else: 622 return '' 623 624 625def prepend_path(newpath, oldpath): 626 """prepend newpath to oldpath""" 627 if (oldpath): 628 return newpath + ':' + oldpath 629 else: 630 return newpath 631 632 633def append_path(oldpath, newpath): 634 """append newpath to oldpath""" 635 if (oldpath): 636 return oldpath + ':' + newpath 637 else: 638 return newpath 639 640 641_TIME_OUTPUT_RE = re.compile( 642 r'([\d\.]*)user ([\d\.]*)system ' 643 r'(\d*):([\d\.]*)elapsed (\d*)%CPU') 644 645 646def avgtime_print(dir): 647 """ Calculate some benchmarking statistics. 648 Input is a directory containing a file called 'time'. 649 File contains one-per-line results of /usr/bin/time. 650 Output is average Elapsed, User, and System time in seconds, 651 and average CPU percentage. 652 """ 653 user = system = elapsed = cpu = count = 0 654 with open(dir + "/time") as f: 655 for line in f: 656 try: 657 m = _TIME_OUTPUT_RE.match(line); 658 user += float(m.group(1)) 659 system += float(m.group(2)) 660 elapsed += (float(m.group(3)) * 60) + float(m.group(4)) 661 cpu += float(m.group(5)) 662 count += 1 663 except: 664 raise ValueError("badly formatted times") 665 666 return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \ 667 (elapsed / count, user / count, system / count, cpu / count) 668 669 670def to_seconds(time_string): 671 """Converts a string in M+:SS.SS format to S+.SS""" 672 elts = time_string.split(':') 673 if len(elts) == 1: 674 return time_string 675 return str(int(elts[0]) * 60 + float(elts[1])) 676 677 678_TIME_OUTPUT_RE_2 = re.compile(r'(.*?)user (.*?)system (.*?)elapsed') 679 680 681def extract_all_time_results(results_string): 682 """Extract user, system, and elapsed times into a list of tuples""" 683 results = [] 684 for result in _TIME_OUTPUT_RE_2.findall(results_string): 685 results.append(tuple([to_seconds(elt) for elt in result])) 686 return results 687 688 689def running_config(): 690 """ 691 Return path of config file of the currently running kernel 692 """ 693 version = utils.system_output('uname -r') 694 for config in ('/proc/config.gz', \ 695 '/boot/config-%s' % version, 696 '/lib/modules/%s/build/.config' % version): 697 if os.path.isfile(config): 698 return config 699 return None 700 701 702def check_for_kernel_feature(feature): 703 config = running_config() 704 705 if not config: 706 raise TypeError("Can't find kernel config file") 707 708 if magic.guess_type(config) == 'application/x-gzip': 709 grep = 'zgrep' 710 else: 711 grep = 'grep' 712 grep += ' ^CONFIG_%s= %s' % (feature, config) 713 714 if not utils.system_output(grep, ignore_status=True): 715 raise ValueError("Kernel doesn't have a %s feature" % (feature)) 716 717 718def check_glibc_ver(ver): 719 glibc_ver = commands.getoutput('ldd --version').splitlines()[0] 720 glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group() 721 if utils.compare_versions(glibc_ver, ver) == -1: 722 raise error.TestError("Glibc too old (%s). Glibc >= %s is needed." % 723 (glibc_ver, ver)) 724 725def check_kernel_ver(ver): 726 kernel_ver = utils.system_output('uname -r') 727 kv_tmp = re.split(r'[-]', kernel_ver)[0:3] 728 # In compare_versions, if v1 < v2, return value == -1 729 if utils.compare_versions(kv_tmp[0], ver) == -1: 730 raise error.TestError("Kernel too old (%s). Kernel > %s is needed." % 731 (kernel_ver, ver)) 732 733 734def human_format(number): 735 # Convert number to kilo / mega / giga format. 736 if number < 1024: 737 return "%d" % number 738 kilo = float(number) / 1024.0 739 if kilo < 1024: 740 return "%.2fk" % kilo 741 meg = kilo / 1024.0 742 if meg < 1024: 743 return "%.2fM" % meg 744 gig = meg / 1024.0 745 return "%.2fG" % gig 746 747 748def numa_nodes(): 749 node_paths = glob.glob('/sys/devices/system/node/node*') 750 nodes = [int(re.sub(r'.*node(\d+)', r'\1', x)) for x in node_paths] 751 return (sorted(nodes)) 752 753 754def node_size(): 755 nodes = max(len(numa_nodes()), 1) 756 return ((memtotal() * 1024) / nodes) 757 758 759def pickle_load(filename): 760 return pickle.load(open(filename, 'r')) 761 762 763# Return the kernel version and build timestamp. 764def running_os_release(): 765 return os.uname()[2:4] 766 767 768def running_os_ident(): 769 (version, timestamp) = running_os_release() 770 return version + '::' + timestamp 771 772 773def running_os_full_version(): 774 (version, timestamp) = running_os_release() 775 return version 776 777 778# much like find . -name 'pattern' 779def locate(pattern, root=os.getcwd()): 780 for path, dirs, files in os.walk(root): 781 for f in files: 782 if fnmatch.fnmatch(f, pattern): 783 yield os.path.abspath(os.path.join(path, f)) 784 785 786def freespace(path): 787 """Return the disk free space, in bytes""" 788 s = os.statvfs(path) 789 return s.f_bavail * s.f_bsize 790 791 792def disk_block_size(path): 793 """Return the disk block size, in bytes""" 794 return os.statvfs(path).f_bsize 795 796 797_DISK_PARTITION_3_RE = re.compile(r'^(/dev/hd[a-z]+)3', re.M) 798 799def get_disks(): 800 df_output = utils.system_output('df') 801 return _DISK_PARTITION_3_RE.findall(df_output) 802 803 804def get_disk_size(disk_name): 805 """ 806 Return size of disk in byte. Return 0 in Error Case 807 808 @param disk_name: disk name to find size 809 """ 810 device = os.path.basename(disk_name) 811 for line in file('/proc/partitions'): 812 try: 813 _, _, blocks, name = re.split(r' +', line.strip()) 814 except ValueError: 815 continue 816 if name == device: 817 return 1024 * int(blocks) 818 return 0 819 820 821def get_disk_size_gb(disk_name): 822 """ 823 Return size of disk in GB (10^9). Return 0 in Error Case 824 825 @param disk_name: disk name to find size 826 """ 827 return int(get_disk_size(disk_name) / (10.0 ** 9) + 0.5) 828 829 830def get_disk_model(disk_name): 831 """ 832 Return model name for internal storage device 833 834 @param disk_name: disk name to find model 835 """ 836 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 837 cmd2 = 'grep -E "ID_(NAME|MODEL)="' 838 cmd3 = 'cut -f 2 -d"="' 839 cmd = ' | '.join([cmd1, cmd2, cmd3]) 840 return utils.system_output(cmd) 841 842 843_DISK_DEV_RE = re.compile(r'/dev/sd[a-z]|' 844 r'/dev/mmcblk[0-9]+|' 845 r'/dev/nvme[0-9]+n[0-9]+') 846 847 848def get_disk_from_filename(filename): 849 """ 850 Return the disk device the filename is on. 851 If the file is on tmpfs or other special file systems, 852 return None. 853 854 @param filename: name of file, full path. 855 """ 856 857 if not os.path.exists(filename): 858 raise error.TestError('file %s missing' % filename) 859 860 if filename[0] != '/': 861 raise error.TestError('This code works only with full path') 862 863 m = _DISK_DEV_RE.match(filename) 864 while not m: 865 if filename[0] != '/': 866 return None 867 if filename == '/dev/root': 868 cmd = 'rootdev -d -s' 869 elif filename.startswith('/dev/mapper'): 870 cmd = 'dmsetup table "%s"' % os.path.basename(filename) 871 dmsetup_output = utils.system_output(cmd).split(' ') 872 if dmsetup_output[2] == 'verity': 873 maj_min = dmsetup_output[4] 874 elif dmsetup_output[2] == 'crypt': 875 maj_min = dmsetup_output[6] 876 cmd = 'realpath "/dev/block/%s"' % maj_min 877 elif filename.startswith('/dev/loop'): 878 cmd = 'losetup -O BACK-FILE "%s" | tail -1' % filename 879 else: 880 cmd = 'df "%s" | tail -1 | cut -f 1 -d" "' % filename 881 filename = utils.system_output(cmd) 882 m = _DISK_DEV_RE.match(filename) 883 return m.group(0) 884 885 886def get_disk_firmware_version(disk_name): 887 """ 888 Return firmware version for internal storage device. (empty string for eMMC) 889 890 @param disk_name: disk name to find model 891 """ 892 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 893 cmd2 = 'grep -E "ID_REVISION="' 894 cmd3 = 'cut -f 2 -d"="' 895 cmd = ' | '.join([cmd1, cmd2, cmd3]) 896 return utils.system_output(cmd) 897 898 899def is_disk_scsi(disk_name): 900 """ 901 Return true if disk is a scsi device, return false otherwise 902 903 @param disk_name: disk name check 904 """ 905 return re.match('/dev/sd[a-z]+', disk_name) 906 907 908def is_disk_harddisk(disk_name): 909 """ 910 Return true if disk is a harddisk, return false otherwise 911 912 @param disk_name: disk name check 913 """ 914 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 915 cmd2 = 'grep -E "ID_ATA_ROTATION_RATE_RPM="' 916 cmd3 = 'cut -f 2 -d"="' 917 cmd = ' | '.join([cmd1, cmd2, cmd3]) 918 919 rtt = utils.system_output(cmd) 920 921 # eMMC will not have this field; rtt == '' 922 # SSD will have zero rotation rate; rtt == '0' 923 # For harddisk rtt > 0 924 return rtt and int(rtt) > 0 925 926def concat_partition(disk_name, partition_number): 927 """ 928 Return the name of a partition: 929 sda, 3 --> sda3 930 mmcblk0, 3 --> mmcblk0p3 931 932 @param disk_name: diskname string 933 @param partition_number: integer 934 """ 935 if disk_name.endswith(tuple(str(i) for i in range(0, 10))): 936 sep = 'p' 937 else: 938 sep = '' 939 return disk_name + sep + str(partition_number) 940 941def verify_hdparm_feature(disk_name, feature): 942 """ 943 Check for feature support for SCSI disk using hdparm 944 945 @param disk_name: target disk 946 @param feature: hdparm output string of the feature 947 """ 948 cmd = 'hdparm -I %s | grep -q "%s"' % (disk_name, feature) 949 ret = utils.system(cmd, ignore_status=True) 950 if ret == 0: 951 return True 952 elif ret == 1: 953 return False 954 else: 955 raise error.TestFail('Error running command %s' % cmd) 956 957 958def get_storage_error_msg(disk_name, reason): 959 """ 960 Get Error message for storage test which include disk model. 961 and also include the firmware version for the SCSI disk 962 963 @param disk_name: target disk 964 @param reason: Reason of the error. 965 """ 966 967 msg = reason 968 969 model = get_disk_model(disk_name) 970 msg += ' Disk model: %s' % model 971 972 if is_disk_scsi(disk_name): 973 fw = get_disk_firmware_version(disk_name) 974 msg += ' firmware: %s' % fw 975 976 return msg 977 978 979def load_module(module_name, params=None): 980 # Checks if a module has already been loaded 981 if module_is_loaded(module_name): 982 return False 983 984 cmd = '/sbin/modprobe ' + module_name 985 if params: 986 cmd += ' ' + params 987 utils.system(cmd) 988 return True 989 990 991def unload_module(module_name): 992 """ 993 Removes a module. Handles dependencies. If even then it's not possible 994 to remove one of the modules, it will trhow an error.CmdError exception. 995 996 @param module_name: Name of the module we want to remove. 997 """ 998 l_raw = utils.system_output("/bin/lsmod").splitlines() 999 lsmod = [x for x in l_raw if x.split()[0] == module_name] 1000 if len(lsmod) > 0: 1001 line_parts = lsmod[0].split() 1002 if len(line_parts) == 4: 1003 submodules = line_parts[3].split(",") 1004 for submodule in submodules: 1005 unload_module(submodule) 1006 utils.system("/sbin/modprobe -r %s" % module_name) 1007 logging.info("Module %s unloaded", module_name) 1008 else: 1009 logging.info("Module %s is already unloaded", module_name) 1010 1011 1012def module_is_loaded(module_name): 1013 module_name = module_name.replace('-', '_') 1014 modules = utils.system_output('/bin/lsmod').splitlines() 1015 for module in modules: 1016 if module.startswith(module_name) and module[len(module_name)] == ' ': 1017 return True 1018 return False 1019 1020 1021def get_loaded_modules(): 1022 lsmod_output = utils.system_output('/bin/lsmod').splitlines()[1:] 1023 return [line.split(None, 1)[0] for line in lsmod_output] 1024 1025 1026def get_huge_page_size(): 1027 output = utils.system_output('grep Hugepagesize /proc/meminfo') 1028 return int(output.split()[1]) # Assumes units always in kB. :( 1029 1030 1031def get_num_huge_pages(): 1032 raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages') 1033 return int(raw_hugepages.split()[2]) 1034 1035 1036def set_num_huge_pages(num): 1037 utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num) 1038 1039 1040def ping_default_gateway(): 1041 """Ping the default gateway.""" 1042 1043 network = open('/etc/sysconfig/network') 1044 m = re.search('GATEWAY=(\S+)', network.read()) 1045 1046 if m: 1047 gw = m.group(1) 1048 cmd = 'ping %s -c 5 > /dev/null' % gw 1049 return utils.system(cmd, ignore_status=True) 1050 1051 raise error.TestError('Unable to find default gateway') 1052 1053 1054def drop_caches(): 1055 """Writes back all dirty pages to disk and clears all the caches.""" 1056 utils.system("sync") 1057 # We ignore failures here as this will fail on 2.6.11 kernels. 1058 utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True) 1059 1060 1061def process_is_alive(name_pattern): 1062 """ 1063 'pgrep name' misses all python processes and also long process names. 1064 'pgrep -f name' gets all shell commands with name in args. 1065 So look only for command whose initial pathname ends with name. 1066 Name itself is an egrep pattern, so it can use | etc for variations. 1067 """ 1068 return utils.system("pgrep -f '^([^ /]*/)*(%s)([ ]|$)'" % name_pattern, 1069 ignore_status=True) == 0 1070 1071 1072def get_hwclock_seconds(utc=True): 1073 """ 1074 Return the hardware clock in seconds as a floating point value. 1075 Use Coordinated Universal Time if utc is True, local time otherwise. 1076 Raise a ValueError if unable to read the hardware clock. 1077 """ 1078 cmd = '/sbin/hwclock --debug' 1079 if utc: 1080 cmd += ' --utc' 1081 hwclock_output = utils.system_output(cmd, ignore_status=True) 1082 match = re.search(r'= ([0-9]+) seconds since .+ (-?[0-9.]+) seconds$', 1083 hwclock_output, re.DOTALL) 1084 if match: 1085 seconds = int(match.group(1)) + float(match.group(2)) 1086 logging.debug('hwclock seconds = %f', seconds) 1087 return seconds 1088 1089 raise ValueError('Unable to read the hardware clock -- ' + 1090 hwclock_output) 1091 1092 1093def set_wake_alarm(alarm_time): 1094 """ 1095 Set the hardware RTC-based wake alarm to 'alarm_time'. 1096 """ 1097 utils.write_one_line('/sys/class/rtc/rtc0/wakealarm', str(alarm_time)) 1098 1099 1100def set_power_state(state): 1101 """ 1102 Set the system power state to 'state'. 1103 """ 1104 utils.write_one_line('/sys/power/state', state) 1105 1106 1107def standby(): 1108 """ 1109 Power-on suspend (S1) 1110 """ 1111 set_power_state('standby') 1112 1113 1114def suspend_to_ram(): 1115 """ 1116 Suspend the system to RAM (S3) 1117 """ 1118 set_power_state('mem') 1119 1120 1121def suspend_to_disk(): 1122 """ 1123 Suspend the system to disk (S4) 1124 """ 1125 set_power_state('disk') 1126 1127 1128_AUTOTEST_CLIENT_PATH = os.path.join(os.path.dirname(__file__), '..') 1129_AMD_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH, 1130 'bin/amd_pci_ids.json') 1131_INTEL_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH, 1132 'bin/intel_pci_ids.json') 1133_UI_USE_FLAGS_FILE_PATH = '/etc/ui_use_flags.txt' 1134 1135# Command to check if a package is installed. If the package is not installed 1136# the command shall fail. 1137_CHECK_PACKAGE_INSTALLED_COMMAND =( 1138 "dpkg-query -W -f='${Status}\n' %s | head -n1 | awk '{print $3;}' | " 1139 "grep -q '^installed$'") 1140 1141pciid_to_amd_architecture = {} 1142pciid_to_intel_architecture = {} 1143 1144class Crossystem(object): 1145 """A wrapper for the crossystem utility.""" 1146 1147 def __init__(self, client): 1148 self.cros_system_data = {} 1149 self._client = client 1150 1151 def init(self): 1152 self.cros_system_data = {} 1153 (_, fname) = tempfile.mkstemp() 1154 f = open(fname, 'w') 1155 self._client.run('crossystem', stdout_tee=f) 1156 f.close() 1157 text = utils.read_file(fname) 1158 for line in text.splitlines(): 1159 assignment_string = line.split('#')[0] 1160 if not assignment_string.count('='): 1161 continue 1162 (name, value) = assignment_string.split('=', 1) 1163 self.cros_system_data[name.strip()] = value.strip() 1164 os.remove(fname) 1165 1166 def __getattr__(self, name): 1167 """ 1168 Retrieve a crosssystem attribute. 1169 1170 The call crossystemobject.name() will return the crossystem reported 1171 string. 1172 """ 1173 return lambda: self.cros_system_data[name] 1174 1175 1176def get_oldest_pid_by_name(name): 1177 """ 1178 Return the oldest pid of a process whose name perfectly matches |name|. 1179 1180 name is an egrep expression, which will be matched against the entire name 1181 of processes on the system. For example: 1182 1183 get_oldest_pid_by_name('chrome') 1184 1185 on a system running 1186 8600 ? 00:00:04 chrome 1187 8601 ? 00:00:00 chrome 1188 8602 ? 00:00:00 chrome-sandbox 1189 1190 would return 8600, as that's the oldest process that matches. 1191 chrome-sandbox would not be matched. 1192 1193 Arguments: 1194 name: egrep expression to match. Will be anchored at the beginning and 1195 end of the match string. 1196 1197 Returns: 1198 pid as an integer, or None if one cannot be found. 1199 1200 Raises: 1201 ValueError if pgrep returns something odd. 1202 """ 1203 str_pid = utils.system_output('pgrep -o ^%s$' % name, 1204 ignore_status=True).rstrip() 1205 if str_pid: 1206 return int(str_pid) 1207 1208 1209def get_oldest_by_name(name): 1210 """Return pid and command line of oldest process whose name matches |name|. 1211 1212 @param name: egrep expression to match desired process name. 1213 @return: A tuple of (pid, command_line) of the oldest process whose name 1214 matches |name|. 1215 1216 """ 1217 pid = get_oldest_pid_by_name(name) 1218 if pid: 1219 command_line = utils.system_output('ps -p %i -o command=' % pid, 1220 ignore_status=True).rstrip() 1221 return (pid, command_line) 1222 1223 1224def get_chrome_remote_debugging_port(): 1225 """Returns remote debugging port for Chrome. 1226 1227 Parse chrome process's command line argument to get the remote debugging 1228 port. if it is 0, look at DevToolsActivePort for the ephemeral port. 1229 """ 1230 _, command = get_oldest_by_name('chrome') 1231 matches = re.search('--remote-debugging-port=([0-9]+)', command) 1232 if not matches: 1233 return 0 1234 port = int(matches.group(1)) 1235 if port: 1236 return port 1237 with open('/home/chronos/DevToolsActivePort') as f: 1238 return int(f.readline().rstrip()) 1239 1240 1241def get_process_list(name, command_line=None): 1242 """ 1243 Return the list of pid for matching process |name command_line|. 1244 1245 on a system running 1246 31475 ? 0:06 /opt/google/chrome/chrome --allow-webui-compositing - 1247 31478 ? 0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/ 1248 31485 ? 0:00 /opt/google/chrome/chrome --type=zygote --log-level=1 1249 31532 ? 1:05 /opt/google/chrome/chrome --type=renderer 1250 1251 get_process_list('chrome') 1252 would return ['31475', '31485', '31532'] 1253 1254 get_process_list('chrome', '--type=renderer') 1255 would return ['31532'] 1256 1257 Arguments: 1258 name: process name to search for. If command_line is provided, name is 1259 matched against full command line. If command_line is not provided, 1260 name is only matched against the process name. 1261 command line: when command line is passed, the full process command line 1262 is used for matching. 1263 1264 Returns: 1265 list of PIDs of the matching processes. 1266 1267 """ 1268 # TODO(rohitbm) crbug.com/268861 1269 flag = '-x' if not command_line else '-f' 1270 name = '\'%s.*%s\'' % (name, command_line) if command_line else name 1271 str_pid = utils.system_output('pgrep %s %s' % (flag, name), 1272 ignore_status=True).rstrip() 1273 return str_pid.split() 1274 1275 1276def nuke_process_by_name(name, with_prejudice=False): 1277 """Tell the oldest process specified by name to exit. 1278 1279 Arguments: 1280 name: process name specifier, as understood by pgrep. 1281 with_prejudice: if True, don't allow for graceful exit. 1282 1283 Raises: 1284 error.AutoservPidAlreadyDeadError: no existing process matches name. 1285 """ 1286 try: 1287 pid = get_oldest_pid_by_name(name) 1288 except Exception as e: 1289 logging.error(e) 1290 return 1291 if pid is None: 1292 raise error.AutoservPidAlreadyDeadError('No process matching %s.' % 1293 name) 1294 if with_prejudice: 1295 utils.nuke_pid(pid, [signal.SIGKILL]) 1296 else: 1297 utils.nuke_pid(pid) 1298 1299 1300def ensure_processes_are_dead_by_name(name, timeout_sec=10): 1301 """Terminate all processes specified by name and ensure they're gone. 1302 1303 Arguments: 1304 name: process name specifier, as understood by pgrep. 1305 timeout_sec: maximum number of seconds to wait for processes to die. 1306 1307 Raises: 1308 error.AutoservPidAlreadyDeadError: no existing process matches name. 1309 utils.TimeoutError: if processes still exist after timeout_sec. 1310 """ 1311 1312 def list_and_kill_processes(name): 1313 process_list = get_process_list(name) 1314 try: 1315 for pid in [int(str_pid) for str_pid in process_list]: 1316 utils.nuke_pid(pid) 1317 except error.AutoservPidAlreadyDeadError: 1318 pass 1319 return process_list 1320 1321 utils.poll_for_condition(lambda: list_and_kill_processes(name) == [], 1322 timeout=timeout_sec) 1323 1324 1325def is_virtual_machine(): 1326 return 'QEMU' in platform.processor() 1327 1328 1329def save_vm_state(checkpoint): 1330 """Saves the current state of the virtual machine. 1331 1332 This function is a NOOP if the test is not running under a virtual machine 1333 with the USB serial port redirected. 1334 1335 Arguments: 1336 checkpoint - Name used to identify this state 1337 1338 Returns: 1339 None 1340 """ 1341 # The QEMU monitor has been redirected to the guest serial port located at 1342 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm' 1343 # command to the serial port. 1344 if is_virtual_machine() and os.path.exists('/dev/ttyUSB0'): 1345 logging.info('Saving VM state "%s"', checkpoint) 1346 serial = open('/dev/ttyUSB0', 'w') 1347 serial.write('savevm %s\r\n' % checkpoint) 1348 logging.info('Done saving VM state "%s"', checkpoint) 1349 1350 1351def check_raw_dmesg(dmesg, message_level, whitelist): 1352 """Checks dmesg for unexpected warnings. 1353 1354 This function parses dmesg for message with message_level <= message_level 1355 which do not appear in the whitelist. 1356 1357 Arguments: 1358 dmesg - string containing raw dmesg buffer 1359 message_level - minimum message priority to check 1360 whitelist - messages to ignore 1361 1362 Returns: 1363 List of unexpected warnings 1364 """ 1365 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist)) 1366 unexpected = [] 1367 for line in dmesg.splitlines(): 1368 if int(line[1]) <= message_level: 1369 stripped_line = line.split('] ', 1)[1] 1370 if whitelist_re.search(stripped_line): 1371 continue 1372 unexpected.append(stripped_line) 1373 return unexpected 1374 1375 1376def verify_mesg_set(mesg, regex, whitelist): 1377 """Verifies that the exact set of messages are present in a text. 1378 1379 This function finds all strings in the text matching a certain regex, and 1380 then verifies that all expected strings are present in the set, and no 1381 unexpected strings are there. 1382 1383 Arguments: 1384 mesg - the mutiline text to be scanned 1385 regex - regular expression to match 1386 whitelist - messages to find in the output, a list of strings 1387 (potentially regexes) to look for in the filtered output. All these 1388 strings must be there, and no other strings should be present in the 1389 filtered output. 1390 1391 Returns: 1392 string of inconsistent findings (i.e. an empty string on success). 1393 """ 1394 1395 rv = [] 1396 1397 missing_strings = [] 1398 present_strings = [] 1399 for line in mesg.splitlines(): 1400 if not re.search(r'%s' % regex, line): 1401 continue 1402 present_strings.append(line.split('] ', 1)[1]) 1403 1404 for string in whitelist: 1405 for present_string in list(present_strings): 1406 if re.search(r'^%s$' % string, present_string): 1407 present_strings.remove(present_string) 1408 break 1409 else: 1410 missing_strings.append(string) 1411 1412 if present_strings: 1413 rv.append('unexpected strings:') 1414 rv.extend(present_strings) 1415 if missing_strings: 1416 rv.append('missing strings:') 1417 rv.extend(missing_strings) 1418 1419 return '\n'.join(rv) 1420 1421 1422def target_is_pie(): 1423 """Returns whether the toolchain produces a PIE (position independent 1424 executable) by default. 1425 1426 Arguments: 1427 None 1428 1429 Returns: 1430 True if the target toolchain produces a PIE by default. 1431 False otherwise. 1432 """ 1433 1434 command = 'echo | ${CC} -E -dD -P - | grep -i pie' 1435 result = utils.system_output(command, 1436 retain_output=True, 1437 ignore_status=True) 1438 if re.search('#define __PIE__', result): 1439 return True 1440 else: 1441 return False 1442 1443 1444def target_is_x86(): 1445 """Returns whether the toolchain produces an x86 object 1446 1447 Arguments: 1448 None 1449 1450 Returns: 1451 True if the target toolchain produces an x86 object 1452 False otherwise. 1453 """ 1454 1455 command = 'echo | ${CC} -E -dD -P - | grep -i 86' 1456 result = utils.system_output(command, 1457 retain_output=True, 1458 ignore_status=True) 1459 if re.search('__i386__', result) or re.search('__x86_64__', result): 1460 return True 1461 else: 1462 return False 1463 1464 1465def mounts(): 1466 ret = [] 1467 for line in file('/proc/mounts'): 1468 m = re.match( 1469 r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line) 1470 if m: 1471 ret.append(m.groupdict()) 1472 return ret 1473 1474 1475def is_mountpoint(path): 1476 return path in [m['dest'] for m in mounts()] 1477 1478 1479def require_mountpoint(path): 1480 """ 1481 Raises an exception if path is not a mountpoint. 1482 """ 1483 if not is_mountpoint(path): 1484 raise error.TestFail('Path not mounted: "%s"' % path) 1485 1486 1487def random_username(): 1488 return str(uuid.uuid4()) + '@example.com' 1489 1490 1491def get_signin_credentials(filepath): 1492 """Returns user_id, password tuple from credentials file at filepath. 1493 1494 File must have one line of the format user_id:password 1495 1496 @param filepath: path of credentials file. 1497 @return user_id, password tuple. 1498 """ 1499 user_id, password = None, None 1500 if os.path.isfile(filepath): 1501 with open(filepath) as f: 1502 user_id, password = f.read().rstrip().split(':') 1503 return user_id, password 1504 1505 1506def parse_cmd_output(command, run_method=utils.run): 1507 """Runs a command on a host object to retrieve host attributes. 1508 1509 The command should output to stdout in the format of: 1510 <key> = <value> # <optional_comment> 1511 1512 1513 @param command: Command to execute on the host. 1514 @param run_method: Function to use to execute the command. Defaults to 1515 utils.run so that the command will be executed locally. 1516 Can be replace with a host.run call so that it will 1517 execute on a DUT or external machine. Method must accept 1518 a command argument, stdout_tee and stderr_tee args and 1519 return a result object with a string attribute stdout 1520 which will be parsed. 1521 1522 @returns a dictionary mapping host attributes to their values. 1523 """ 1524 result = {} 1525 # Suppresses stdout so that the files are not printed to the logs. 1526 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None) 1527 for line in cmd_result.stdout.splitlines(): 1528 # Lines are of the format "<key> = <value> # <comment>" 1529 key_value = re.match(r'^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ' 1530 r']+)(?:\s*#.*)?$', line) 1531 if key_value: 1532 result[key_value.group('key')] = key_value.group('value') 1533 return result 1534 1535 1536def set_from_keyval_output(out, delimiter=' '): 1537 """Parse delimiter-separated key-val output into a set of tuples. 1538 1539 Output is expected to be multiline text output from a command. 1540 Stuffs the key-vals into tuples in a set to be later compared. 1541 1542 e.g. deactivated 0 1543 disableForceClear 0 1544 ==> set(('deactivated', '0'), ('disableForceClear', '0')) 1545 1546 @param out: multiple lines of space-separated key-val pairs. 1547 @param delimiter: character that separates key from val. Usually a 1548 space but may be '=' or something else. 1549 @return set of key-val tuples. 1550 """ 1551 results = set() 1552 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter) 1553 for linecr in out.splitlines(): 1554 match = kv_match_re.match(linecr.strip()) 1555 if match: 1556 results.add((match.group(1), match.group(2))) 1557 return results 1558 1559 1560def get_cpu_usage(): 1561 """Returns machine's CPU usage. 1562 1563 This function uses /proc/stat to identify CPU usage. 1564 Returns: 1565 A dictionary with values for all columns in /proc/stat 1566 Sample dictionary: 1567 { 1568 'user': 254544, 1569 'nice': 9, 1570 'system': 254768, 1571 'idle': 2859878, 1572 'iowait': 1, 1573 'irq': 2, 1574 'softirq': 3, 1575 'steal': 4, 1576 'guest': 5, 1577 'guest_nice': 6 1578 } 1579 If a column is missing or malformed in /proc/stat (typically on older 1580 systems), the value for that column is set to 0. 1581 """ 1582 with _open_file('/proc/stat') as proc_stat: 1583 cpu_usage_str = proc_stat.readline().split() 1584 columns = ('user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq', 1585 'steal', 'guest', 'guest_nice') 1586 d = {} 1587 for index, col in enumerate(columns, 1): 1588 try: 1589 d[col] = int(cpu_usage_str[index]) 1590 except: 1591 d[col] = 0 1592 return d 1593 1594def compute_active_cpu_time(cpu_usage_start, cpu_usage_end): 1595 """Computes the fraction of CPU time spent non-idling. 1596 1597 This function should be invoked using before/after values from calls to 1598 get_cpu_usage(). 1599 1600 See https://stackoverflow.com/a/23376195 and 1601 https://unix.stackexchange.com/a/303224 for some more context how 1602 to calculate usage given two /proc/stat snapshots. 1603 """ 1604 idle_cols = ('idle', 'iowait') # All other cols are calculated as active. 1605 time_active_start = sum([x[1] for x in cpu_usage_start.iteritems() 1606 if x[0] not in idle_cols]) 1607 time_active_end = sum([x[1] for x in cpu_usage_end.iteritems() 1608 if x[0] not in idle_cols]) 1609 total_time_start = sum(cpu_usage_start.values()) 1610 total_time_end = sum(cpu_usage_end.values()) 1611 return ((float(time_active_end) - time_active_start) / 1612 (total_time_end - total_time_start)) 1613 1614 1615def is_pgo_mode(): 1616 return 'USE_PGO' in os.environ 1617 1618 1619def wait_for_idle_cpu(timeout, utilization): 1620 """Waits for the CPU to become idle (< utilization). 1621 1622 Args: 1623 timeout: The longest time in seconds to wait before throwing an error. 1624 utilization: The CPU usage below which the system should be considered 1625 idle (between 0 and 1.0 independent of cores/hyperthreads). 1626 """ 1627 time_passed = 0.0 1628 fraction_active_time = 1.0 1629 sleep_time = 1 1630 logging.info('Starting to wait up to %.1fs for idle CPU...', timeout) 1631 while fraction_active_time >= utilization: 1632 cpu_usage_start = get_cpu_usage() 1633 # Split timeout interval into not too many chunks to limit log spew. 1634 # Start at 1 second, increase exponentially 1635 time.sleep(sleep_time) 1636 time_passed += sleep_time 1637 sleep_time = min(16.0, 2.0 * sleep_time) 1638 cpu_usage_end = get_cpu_usage() 1639 fraction_active_time = \ 1640 compute_active_cpu_time(cpu_usage_start, cpu_usage_end) 1641 logging.info('After waiting %.1fs CPU utilization is %.3f.', 1642 time_passed, fraction_active_time) 1643 if time_passed > timeout: 1644 logging.warning('CPU did not become idle.') 1645 log_process_activity() 1646 # crosbug.com/37389 1647 if is_pgo_mode(): 1648 logging.info('Still continuing because we are in PGO mode.') 1649 return True 1650 1651 return False 1652 logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).', 1653 time_passed, fraction_active_time) 1654 return True 1655 1656 1657def log_process_activity(): 1658 """Logs the output of top. 1659 1660 Useful to debug performance tests and to find runaway processes. 1661 """ 1662 logging.info('Logging current process activity using top and ps.') 1663 cmd = 'top -b -n1 -c' 1664 output = utils.run(cmd) 1665 logging.info(output) 1666 output = utils.run('ps axl') 1667 logging.info(output) 1668 1669 1670def wait_for_cool_machine(): 1671 """ 1672 A simple heuristic to wait for a machine to cool. 1673 The code looks a bit 'magic', but we don't know ambient temperature 1674 nor machine characteristics and still would like to return the caller 1675 a machine that cooled down as much as reasonably possible. 1676 """ 1677 temperature = get_current_temperature_max() 1678 # We got here with a cold machine, return immediately. This should be the 1679 # most common case. 1680 if temperature < 50: 1681 return True 1682 logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature) 1683 # A modest wait should cool the machine. 1684 time.sleep(60.0) 1685 temperature = get_current_temperature_max() 1686 # Atoms idle below 60 and everyone else should be even lower. 1687 if temperature < 62: 1688 return True 1689 # This should be rare. 1690 logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature) 1691 time.sleep(120.0) 1692 temperature = get_current_temperature_max() 1693 # A temperature over 65'C doesn't give us much headroom to the critical 1694 # temperatures that start at 85'C (and PerfControl as of today will fail at 1695 # critical - 10'C). 1696 if temperature < 65: 1697 return True 1698 logging.warning('Did not cool down (%dC), giving up.', temperature) 1699 log_process_activity() 1700 return False 1701 1702 1703# System paths for machine performance state. 1704_CPUINFO = '/proc/cpuinfo' 1705_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs' 1706_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' 1707_MEMINFO = '/proc/meminfo' 1708_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)' 1709 1710def _open_file(path): 1711 """ 1712 Opens a file and returns the file object. 1713 1714 This method is intended to be mocked by tests. 1715 @return The open file object. 1716 """ 1717 return open(path) 1718 1719def _get_line_from_file(path, line): 1720 """ 1721 line can be an integer or 1722 line can be a string that matches the beginning of the line 1723 """ 1724 with _open_file(path) as f: 1725 if isinstance(line, int): 1726 l = f.readline() 1727 for _ in range(0, line): 1728 l = f.readline() 1729 return l 1730 else: 1731 for l in f: 1732 if l.startswith(line): 1733 return l 1734 return None 1735 1736 1737def _get_match_from_file(path, line, prefix, postfix): 1738 """ 1739 Matches line in path and returns string between first prefix and postfix. 1740 """ 1741 match = _get_line_from_file(path, line) 1742 # Strip everything from front of line including prefix. 1743 if prefix: 1744 match = re.split(prefix, match)[1] 1745 # Strip everything from back of string including first occurence of postfix. 1746 if postfix: 1747 match = re.split(postfix, match)[0] 1748 return match 1749 1750 1751def _get_float_from_file(path, line, prefix, postfix): 1752 match = _get_match_from_file(path, line, prefix, postfix) 1753 return float(match) 1754 1755 1756def _get_int_from_file(path, line, prefix, postfix): 1757 match = _get_match_from_file(path, line, prefix, postfix) 1758 return int(match) 1759 1760 1761def _get_hex_from_file(path, line, prefix, postfix): 1762 match = _get_match_from_file(path, line, prefix, postfix) 1763 return int(match, 16) 1764 1765 1766# The paths don't change. Avoid running find all the time. 1767_hwmon_paths = None 1768 1769def _get_hwmon_paths(file_pattern): 1770 """ 1771 Returns a list of paths to the temperature sensors. 1772 """ 1773 # Some systems like daisy_spring only have the virtual hwmon. 1774 # And other systems like rambi only have coretemp.0. See crbug.com/360249. 1775 # /sys/class/hwmon/hwmon*/ 1776 # /sys/devices/virtual/hwmon/hwmon*/ 1777 # /sys/devices/platform/coretemp.0/ 1778 if not _hwmon_paths: 1779 cmd = 'find /sys/ -name "' + file_pattern + '"' 1780 _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines() 1781 return _hwon_paths 1782 1783 1784def get_temperature_critical(): 1785 """ 1786 Returns temperature at which we will see some throttling in the system. 1787 """ 1788 min_temperature = 1000.0 1789 paths = _get_hwmon_paths('temp*_crit') 1790 for path in paths: 1791 temperature = _get_float_from_file(path, 0, None, None) * 0.001 1792 # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to 1793 # the lowest known value. 1794 if (min_temperature < 60.0) or min_temperature > 150.0: 1795 logging.warning('Critical temperature of %.1fC was reset to 85.0C.', 1796 min_temperature) 1797 min_temperature = 85.0 1798 1799 min_temperature = min(temperature, min_temperature) 1800 return min_temperature 1801 1802 1803def get_temperature_input_max(): 1804 """ 1805 Returns the maximum currently observed temperature. 1806 """ 1807 max_temperature = -1000.0 1808 paths = _get_hwmon_paths('temp*_input') 1809 for path in paths: 1810 temperature = _get_float_from_file(path, 0, None, None) * 0.001 1811 max_temperature = max(temperature, max_temperature) 1812 return max_temperature 1813 1814 1815def get_thermal_zone_temperatures(): 1816 """ 1817 Returns the maximum currently observered temperature in thermal_zones. 1818 """ 1819 temperatures = [] 1820 for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'): 1821 try: 1822 temperatures.append( 1823 _get_float_from_file(path, 0, None, None) * 0.001) 1824 except IOError: 1825 # Some devices (e.g. Veyron) may have reserved thermal zones that 1826 # are not active. Trying to read the temperature value would cause a 1827 # EINVAL IO error. 1828 continue 1829 return temperatures 1830 1831 1832def get_ec_temperatures(): 1833 """ 1834 Uses ectool to return a list of all sensor temperatures in Celsius. 1835 1836 Output from ectool is either '0: 300' or '0: 300 K' (newer ectool 1837 includes the unit). 1838 """ 1839 temperatures = [] 1840 try: 1841 full_cmd = 'ectool temps all' 1842 lines = utils.run(full_cmd, verbose=False).stdout.splitlines() 1843 pattern = re.compile('.*: (\d+)') 1844 for line in lines: 1845 matched = pattern.match(line) 1846 temperature = int(matched.group(1)) - 273 1847 temperatures.append(temperature) 1848 except Exception: 1849 logging.warning('Unable to read temperature sensors using ectool.') 1850 for temperature in temperatures: 1851 # Sanity check for real world values. 1852 assert ((temperature > 10.0) and 1853 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 1854 temperature) 1855 1856 return temperatures 1857 1858 1859def get_current_temperature_max(): 1860 """ 1861 Returns the highest reported board temperature (all sensors) in Celsius. 1862 """ 1863 temperature = max([get_temperature_input_max()] + 1864 get_thermal_zone_temperatures() + 1865 get_ec_temperatures()) 1866 # Sanity check for real world values. 1867 assert ((temperature > 10.0) and 1868 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 1869 temperature) 1870 return temperature 1871 1872 1873def get_cpu_cache_size(): 1874 """ 1875 Returns the last level CPU cache size in kBytes. 1876 """ 1877 cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB') 1878 # Sanity check. 1879 assert cache_size >= 64, 'Unreasonably small cache.' 1880 return cache_size 1881 1882 1883def get_cpu_model_frequency(): 1884 """ 1885 Returns the model frequency from the CPU model name on Intel only. This 1886 might be redundant with get_cpu_max_frequency. Unit is Hz. 1887 """ 1888 frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz') 1889 return 1.e9 * frequency 1890 1891 1892def get_cpu_max_frequency(): 1893 """ 1894 Returns the largest of the max CPU core frequencies. The unit is Hz. 1895 """ 1896 max_frequency = -1 1897 paths = _get_cpufreq_paths('cpuinfo_max_freq') 1898 for path in paths: 1899 # Convert from kHz to Hz. 1900 frequency = 1000 * _get_float_from_file(path, 0, None, None) 1901 max_frequency = max(frequency, max_frequency) 1902 # Sanity check. 1903 assert max_frequency > 1e8, 'Unreasonably low CPU frequency.' 1904 return max_frequency 1905 1906 1907def get_cpu_min_frequency(): 1908 """ 1909 Returns the smallest of the minimum CPU core frequencies. 1910 """ 1911 min_frequency = 1e20 1912 paths = _get_cpufreq_paths('cpuinfo_min_freq') 1913 for path in paths: 1914 frequency = _get_float_from_file(path, 0, None, None) 1915 min_frequency = min(frequency, min_frequency) 1916 # Sanity check. 1917 assert min_frequency > 1e8, 'Unreasonably low CPU frequency.' 1918 return min_frequency 1919 1920 1921def get_cpu_model(): 1922 """ 1923 Returns the CPU model. 1924 Only works on Intel. 1925 """ 1926 cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None) 1927 return cpu_model 1928 1929 1930def get_cpu_family(): 1931 """ 1932 Returns the CPU family. 1933 Only works on Intel. 1934 """ 1935 cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None) 1936 return cpu_family 1937 1938 1939def get_board_property(key): 1940 """ 1941 Get a specific property from /etc/lsb-release. 1942 1943 @param key: board property to return value for 1944 1945 @return the value or '' if not present 1946 """ 1947 with open('/etc/lsb-release') as f: 1948 pattern = '%s=(.*)' % key 1949 pat = re.search(pattern, f.read()) 1950 if pat: 1951 return pat.group(1) 1952 return '' 1953 1954 1955def get_board(): 1956 """ 1957 Get the ChromeOS release board name from /etc/lsb-release. 1958 """ 1959 return get_board_property('BOARD') 1960 1961 1962def get_board_type(): 1963 """ 1964 Get the ChromeOS board type from /etc/lsb-release. 1965 1966 @return device type. 1967 """ 1968 return get_board_property('DEVICETYPE') 1969 1970 1971def get_board_with_frequency_and_memory(): 1972 """ 1973 Returns a board name modified with CPU frequency and memory size to 1974 differentiate between different board variants. For instance 1975 link -> link_1.8GHz_4GB. 1976 """ 1977 board_name = get_board() 1978 if is_virtual_machine(): 1979 board = '%s_VM' % board_name 1980 else: 1981 # Rounded to nearest GB and GHz. 1982 memory = int(round(get_mem_total() / 1024.0)) 1983 # Convert frequency to GHz with 1 digit accuracy after the 1984 # decimal point. 1985 frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1 1986 board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory) 1987 return board 1988 1989 1990def get_mem_total(): 1991 """ 1992 Returns the total memory available in the system in MBytes. 1993 """ 1994 mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB') 1995 # Sanity check, all Chromebooks have at least 1GB of memory. 1996 assert mem_total > 256 * 1024, 'Unreasonable amount of memory.' 1997 return mem_total / 1024 1998 1999 2000def get_mem_free(): 2001 """ 2002 Returns the currently free memory in the system in MBytes. 2003 """ 2004 mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB') 2005 return mem_free / 1024 2006 2007def get_mem_free_plus_buffers_and_cached(): 2008 """ 2009 Returns the free memory in MBytes, counting buffers and cached as free. 2010 2011 This is most often the most interesting number since buffers and cached 2012 memory can be reclaimed on demand. Note however, that there are cases 2013 where this as misleading as well, for example used tmpfs space 2014 count as Cached but can not be reclaimed on demand. 2015 See https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt. 2016 """ 2017 free_mb = get_mem_free() 2018 cached_mb = (_get_float_from_file( 2019 _MEMINFO, 'Cached:', 'Cached:', ' kB') / 1024) 2020 buffers_mb = (_get_float_from_file( 2021 _MEMINFO, 'Buffers:', 'Buffers:', ' kB') / 1024) 2022 return free_mb + buffers_mb + cached_mb 2023 2024def get_kernel_max(): 2025 """ 2026 Returns content of kernel_max. 2027 """ 2028 kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None) 2029 # Sanity check. 2030 assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.' 2031 return kernel_max 2032 2033 2034def set_high_performance_mode(): 2035 """ 2036 Sets the kernel governor mode to the highest setting. 2037 Returns previous governor state. 2038 """ 2039 original_governors = get_scaling_governor_states() 2040 set_scaling_governors('performance') 2041 return original_governors 2042 2043 2044def set_scaling_governors(value): 2045 """ 2046 Sets all scaling governor to string value. 2047 Sample values: 'performance', 'interactive', 'ondemand', 'powersave'. 2048 """ 2049 paths = _get_cpufreq_paths('scaling_governor') 2050 for path in paths: 2051 cmd = 'echo %s > %s' % (value, path) 2052 logging.info('Writing scaling governor mode \'%s\' -> %s', value, path) 2053 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures. 2054 utils.system(cmd, ignore_status=True) 2055 2056 2057def _get_cpufreq_paths(filename): 2058 """ 2059 Returns a list of paths to the governors. 2060 """ 2061 cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename 2062 paths = utils.run(cmd, verbose=False).stdout.splitlines() 2063 return paths 2064 2065 2066def get_scaling_governor_states(): 2067 """ 2068 Returns a list of (performance governor path, current state) tuples. 2069 """ 2070 paths = _get_cpufreq_paths('scaling_governor') 2071 path_value_list = [] 2072 for path in paths: 2073 value = _get_line_from_file(path, 0) 2074 path_value_list.append((path, value)) 2075 return path_value_list 2076 2077 2078def restore_scaling_governor_states(path_value_list): 2079 """ 2080 Restores governor states. Inverse operation to get_scaling_governor_states. 2081 """ 2082 for (path, value) in path_value_list: 2083 cmd = 'echo %s > %s' % (value.rstrip('\n'), path) 2084 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures. 2085 utils.system(cmd, ignore_status=True) 2086 2087 2088def get_dirty_writeback_centisecs(): 2089 """ 2090 Reads /proc/sys/vm/dirty_writeback_centisecs. 2091 """ 2092 time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None) 2093 return time 2094 2095 2096def set_dirty_writeback_centisecs(time=60000): 2097 """ 2098 In hundredths of a second, this is how often pdflush wakes up to write data 2099 to disk. The default wakes up the two (or more) active threads every five 2100 seconds. The ChromeOS default is 10 minutes. 2101 2102 We use this to set as low as 1 second to flush error messages in system 2103 logs earlier to disk. 2104 """ 2105 # Flush buffers first to make this function synchronous. 2106 utils.system('sync') 2107 if time >= 0: 2108 cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS) 2109 utils.system(cmd) 2110 2111 2112def wflinfo_cmd(): 2113 """ 2114 Returns a wflinfo command appropriate to the current graphics platform/api. 2115 """ 2116 return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api()) 2117 2118 2119def has_mali(): 2120 """ @return: True if system has a Mali GPU enabled.""" 2121 return os.path.exists('/dev/mali0') 2122 2123def get_gpu_family(): 2124 """Returns the GPU family name.""" 2125 global pciid_to_amd_architecture 2126 global pciid_to_intel_architecture 2127 2128 socfamily = get_cpu_soc_family() 2129 if socfamily == 'exynos5' or socfamily == 'rockchip' or has_mali(): 2130 cmd = wflinfo_cmd() 2131 wflinfo = utils.system_output(cmd, 2132 retain_output=True, 2133 ignore_status=False) 2134 version = re.findall(r'OpenGL renderer string: ' 2135 r'Mali-T([0-9]+)', wflinfo) 2136 if version: 2137 return 'mali-t%s' % version[0] 2138 return 'mali-unrecognized' 2139 if socfamily == 'tegra': 2140 return 'tegra' 2141 if os.path.exists('/sys/kernel/debug/pvr'): 2142 return 'rogue' 2143 2144 pci_vga_device = utils.run("lspci | grep VGA").stdout.rstrip('\n') 2145 bus_device_function = pci_vga_device.partition(' ')[0] 2146 pci_path = '/sys/bus/pci/devices/0000:' + bus_device_function + '/device' 2147 2148 if not os.path.exists(pci_path): 2149 raise error.TestError('PCI device 0000:' + bus_device_function + ' not found') 2150 2151 device_id = utils.read_one_line(pci_path).lower() 2152 2153 if "Advanced Micro Devices" in pci_vga_device: 2154 if not pciid_to_amd_architecture: 2155 with open(_AMD_PCI_IDS_FILE_PATH, 'r') as in_f: 2156 pciid_to_amd_architecture = json.load(in_f) 2157 2158 return pciid_to_amd_architecture[device_id] 2159 2160 if "Intel Corporation" in pci_vga_device: 2161 # Only load Intel PCI ID file once and only if necessary. 2162 if not pciid_to_intel_architecture: 2163 with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f: 2164 pciid_to_intel_architecture = json.load(in_f) 2165 2166 return pciid_to_intel_architecture[device_id] 2167 2168# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE 2169# for sanity check, but usage seems a bit inconsistent. See 2170# src/third_party/chromiumos-overlay/eclass/appid.eclass 2171_BOARDS_WITHOUT_MONITOR = [ 2172 'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus', 2173 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 'veyron_rialto' 2174] 2175 2176 2177def has_no_monitor(): 2178 """Returns whether a machine doesn't have a built-in monitor.""" 2179 board_name = get_board() 2180 if board_name in _BOARDS_WITHOUT_MONITOR: 2181 return True 2182 2183 return False 2184 2185 2186def get_fixed_dst_drive(): 2187 """ 2188 Return device name for internal disk. 2189 Example: return /dev/sda for falco booted from usb 2190 """ 2191 cmd = ' '.join(['. /usr/sbin/write_gpt.sh;', 2192 '. /usr/share/misc/chromeos-common.sh;', 2193 'load_base_vars;', 2194 'get_fixed_dst_drive']) 2195 return utils.system_output(cmd) 2196 2197 2198def get_root_device(): 2199 """ 2200 Return root device. 2201 Will return correct disk device even system boot from /dev/dm-0 2202 Example: return /dev/sdb for falco booted from usb 2203 """ 2204 return utils.system_output('rootdev -s -d') 2205 2206 2207def get_root_partition(): 2208 """ 2209 Return current root partition 2210 Example: return /dev/sdb3 for falco booted from usb 2211 """ 2212 return utils.system_output('rootdev -s') 2213 2214 2215def get_free_root_partition(root_part=None): 2216 """ 2217 Return currently unused root partion 2218 Example: return /dev/sdb5 for falco booted from usb 2219 2220 @param root_part: cuurent root partition 2221 """ 2222 spare_root_map = {'3': '5', '5': '3'} 2223 if not root_part: 2224 root_part = get_root_partition() 2225 return root_part[:-1] + spare_root_map[root_part[-1]] 2226 2227 2228def get_kernel_partition(root_part=None): 2229 """ 2230 Return current kernel partition 2231 Example: return /dev/sda2 for falco booted from usb 2232 2233 @param root_part: current root partition 2234 """ 2235 if not root_part: 2236 root_part = get_root_partition() 2237 current_kernel_map = {'3': '2', '5': '4'} 2238 return root_part[:-1] + current_kernel_map[root_part[-1]] 2239 2240 2241def get_free_kernel_partition(root_part=None): 2242 """ 2243 return currently unused kernel partition 2244 Example: return /dev/sda4 for falco booted from usb 2245 2246 @param root_part: current root partition 2247 """ 2248 kernel_part = get_kernel_partition(root_part) 2249 spare_kernel_map = {'2': '4', '4': '2'} 2250 return kernel_part[:-1] + spare_kernel_map[kernel_part[-1]] 2251 2252 2253def is_booted_from_internal_disk(): 2254 """Return True if boot from internal disk. False, otherwise.""" 2255 return get_root_device() == get_fixed_dst_drive() 2256 2257 2258def get_ui_use_flags(): 2259 """Parses the USE flags as listed in /etc/ui_use_flags.txt. 2260 2261 @return: A list of flag strings found in the ui use flags file. 2262 """ 2263 flags = [] 2264 for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines(): 2265 # Removes everything after the '#'. 2266 flag_before_comment = flag.split('#')[0].strip() 2267 if len(flag_before_comment) != 0: 2268 flags.append(flag_before_comment) 2269 2270 return flags 2271 2272 2273def graphics_platform(): 2274 """ 2275 Return a string identifying the graphics platform, 2276 e.g. 'glx' or 'x11_egl' or 'gbm' 2277 """ 2278 return 'null' 2279 2280 2281def graphics_api(): 2282 """Return a string identifying the graphics api, e.g. gl or gles2.""" 2283 use_flags = get_ui_use_flags() 2284 if 'opengles' in use_flags: 2285 return 'gles2' 2286 return 'gl' 2287 2288 2289def is_vm(): 2290 """Check if the process is running in a virtual machine. 2291 2292 @return: True if the process is running in a virtual machine, otherwise 2293 return False. 2294 """ 2295 try: 2296 virt = utils.run('sudo -n virt-what').stdout.strip() 2297 logging.debug('virt-what output: %s', virt) 2298 return bool(virt) 2299 except error.CmdError: 2300 logging.warn('Package virt-what is not installed, default to assume ' 2301 'it is not a virtual machine.') 2302 return False 2303 2304 2305def is_package_installed(package): 2306 """Check if a package is installed already. 2307 2308 @return: True if the package is already installed, otherwise return False. 2309 """ 2310 try: 2311 utils.run(_CHECK_PACKAGE_INSTALLED_COMMAND % package) 2312 return True 2313 except error.CmdError: 2314 logging.warn('Package %s is not installed.', package) 2315 return False 2316 2317 2318def is_python_package_installed(package): 2319 """Check if a Python package is installed already. 2320 2321 @return: True if the package is already installed, otherwise return False. 2322 """ 2323 try: 2324 __import__(package) 2325 return True 2326 except ImportError: 2327 logging.warn('Python package %s is not installed.', package) 2328 return False 2329 2330 2331def run_sql_cmd(server, user, password, command, database=''): 2332 """Run the given sql command against the specified database. 2333 2334 @param server: Hostname or IP address of the MySQL server. 2335 @param user: User name to log in the MySQL server. 2336 @param password: Password to log in the MySQL server. 2337 @param command: SQL command to run. 2338 @param database: Name of the database to run the command. Default to empty 2339 for command that does not require specifying database. 2340 2341 @return: The stdout of the command line. 2342 """ 2343 cmd = ('mysql -u%s -p%s --host %s %s -e "%s"' % 2344 (user, password, server, database, command)) 2345 # Set verbose to False so the command line won't be logged, as it includes 2346 # database credential. 2347