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 base64 12import collections 13import commands 14import errno 15import fnmatch 16import glob 17import json 18import logging 19import math 20import multiprocessing 21import os 22import pickle 23import platform 24import re 25import shutil 26import signal 27import string 28import tempfile 29import time 30import uuid 31 32from autotest_lib.client.common_lib import error 33from autotest_lib.client.common_lib import magic 34from autotest_lib.client.common_lib import utils 35from autotest_lib.client.common_lib.cros import cros_config 36 37from autotest_lib.client.common_lib.utils import * 38 39 40def grep(pattern, file): 41 """ 42 This is mainly to fix the return code inversion from grep 43 Also handles compressed files. 44 45 returns 1 if the pattern is present in the file, 0 if not. 46 """ 47 command = 'grep "%s" > /dev/null' % pattern 48 ret = cat_file_to_cmd(file, command, ignore_status=True) 49 return not ret 50 51 52def difflist(list1, list2): 53 """returns items in list2 that are not in list1""" 54 diff = []; 55 for x in list2: 56 if x not in list1: 57 diff.append(x) 58 return diff 59 60 61def cat_file_to_cmd(file, command, ignore_status=0, return_output=False): 62 """ 63 equivalent to 'cat file | command' but knows to use 64 zcat or bzcat if appropriate 65 """ 66 if not os.path.isfile(file): 67 raise NameError('invalid file %s to cat to command %s' 68 % (file, command)) 69 70 if return_output: 71 run_cmd = utils.system_output 72 else: 73 run_cmd = utils.system 74 75 if magic.guess_type(file) == 'application/x-bzip2': 76 cat = 'bzcat' 77 elif magic.guess_type(file) == 'application/x-gzip': 78 cat = 'zcat' 79 else: 80 cat = 'cat' 81 return run_cmd('%s %s | %s' % (cat, file, command), 82 ignore_status=ignore_status) 83 84 85def extract_tarball_to_dir(tarball, dir): 86 """ 87 Extract a tarball to a specified directory name instead of whatever 88 the top level of a tarball is - useful for versioned directory names, etc 89 """ 90 if os.path.exists(dir): 91 if os.path.isdir(dir): 92 shutil.rmtree(dir) 93 else: 94 os.remove(dir) 95 pwd = os.getcwd() 96 os.chdir(os.path.dirname(os.path.abspath(dir))) 97 newdir = extract_tarball(tarball) 98 os.rename(newdir, dir) 99 os.chdir(pwd) 100 101 102def extract_tarball(tarball): 103 """Returns the directory extracted by the tarball.""" 104 extracted = cat_file_to_cmd(tarball, 'tar xvf - 2>/dev/null', 105 return_output=True).splitlines() 106 107 dir = None 108 109 for line in extracted: 110 if line.startswith('./'): 111 line = line[2:] 112 if not line or line == '.': 113 continue 114 topdir = line.split('/')[0] 115 if os.path.isdir(topdir): 116 if dir: 117 assert(dir == topdir), 'tarball must be a a single directory' 118 else: 119 dir = topdir 120 if dir: 121 return dir 122 else: 123 raise NameError('extracting tarball produced no dir') 124 125 126def force_copy(src, dest): 127 """Replace dest with a new copy of src, even if it exists""" 128 if os.path.isfile(dest): 129 os.remove(dest) 130 if os.path.isdir(dest): 131 dest = os.path.join(dest, os.path.basename(src)) 132 shutil.copyfile(src, dest) 133 return dest 134 135 136def force_link(src, dest): 137 """Link src to dest, overwriting it if it exists""" 138 return utils.system("ln -sf %s %s" % (src, dest)) 139 140 141def file_contains_pattern(file, pattern): 142 """Return true if file contains the specified egrep pattern""" 143 if not os.path.isfile(file): 144 raise NameError('file %s does not exist' % file) 145 return not utils.system('egrep -q "' + pattern + '" ' + file, 146 ignore_status=True) 147 148 149def list_grep(list, pattern): 150 """True if any item in list matches the specified pattern.""" 151 compiled = re.compile(pattern) 152 for line in list: 153 match = compiled.search(line) 154 if (match): 155 return 1 156 return 0 157 158 159def get_os_vendor(): 160 """Try to guess what's the os vendor 161 """ 162 if os.path.isfile('/etc/SuSE-release'): 163 return 'SUSE' 164 165 issue = '/etc/issue' 166 167 if not os.path.isfile(issue): 168 return 'Unknown' 169 170 if file_contains_pattern(issue, 'Red Hat'): 171 return 'Red Hat' 172 elif file_contains_pattern(issue, 'Fedora'): 173 return 'Fedora Core' 174 elif file_contains_pattern(issue, 'SUSE'): 175 return 'SUSE' 176 elif file_contains_pattern(issue, 'Ubuntu'): 177 return 'Ubuntu' 178 elif file_contains_pattern(issue, 'Debian'): 179 return 'Debian' 180 else: 181 return 'Unknown' 182 183 184def get_cc(): 185 try: 186 return os.environ['CC'] 187 except KeyError: 188 return 'gcc' 189 190 191def get_vmlinux(): 192 """Return the full path to vmlinux 193 194 Ahem. This is crap. Pray harder. Bad Martin. 195 """ 196 vmlinux = '/boot/vmlinux-%s' % utils.system_output('uname -r') 197 if os.path.isfile(vmlinux): 198 return vmlinux 199 vmlinux = '/lib/modules/%s/build/vmlinux' % utils.system_output('uname -r') 200 if os.path.isfile(vmlinux): 201 return vmlinux 202 return None 203 204 205def get_systemmap(): 206 """Return the full path to System.map 207 208 Ahem. This is crap. Pray harder. Bad Martin. 209 """ 210 map = '/boot/System.map-%s' % utils.system_output('uname -r') 211 if os.path.isfile(map): 212 return map 213 map = '/lib/modules/%s/build/System.map' % utils.system_output('uname -r') 214 if os.path.isfile(map): 215 return map 216 return None 217 218 219def get_modules_dir(): 220 """Return the modules dir for the running kernel version""" 221 kernel_version = utils.system_output('uname -r') 222 return '/lib/modules/%s/kernel' % kernel_version 223 224 225_CPUINFO_RE = re.compile(r'^(?P<key>[^\t]*)\t*: ?(?P<value>.*)$') 226 227 228def get_cpuinfo(): 229 """Read /proc/cpuinfo and convert to a list of dicts.""" 230 cpuinfo = [] 231 with open('/proc/cpuinfo', 'r') as f: 232 cpu = {} 233 for line in f: 234 line = line.strip() 235 if not line: 236 cpuinfo.append(cpu) 237 cpu = {} 238 continue 239 match = _CPUINFO_RE.match(line) 240 cpu[match.group('key')] = match.group('value') 241 if cpu: 242 # cpuinfo usually ends in a blank line, so this shouldn't happen. 243 cpuinfo.append(cpu) 244 return cpuinfo 245 246 247def get_cpu_arch(): 248 """Work out which CPU architecture we're running on""" 249 250 # Using 'uname -m' should be a very portable way to do this since the 251 # format is pretty standard. 252 machine_name = utils.system_output('uname -m').strip() 253 254 # Apparently ARM64 and ARM have both historically returned the string 'arm' 255 # here so continue the tradition. Use startswith() because: 256 # - On most of our arm devices we'll actually see the string armv7l. 257 # - In theory the machine name could include a suffix for endianness. 258 if machine_name.startswith('aarch64') or machine_name.startswith('arm'): 259 return 'arm' 260 261 # Historically we _have_ treated x86_64 and i386 separately. 262 if machine_name in ('x86_64', 'i386'): 263 return machine_name 264 265 raise error.TestError('unsupported machine type %s' % machine_name) 266 267 268def get_arm_soc_family_from_devicetree(): 269 """ 270 Work out which ARM SoC we're running on based on the 'compatible' property 271 of the base node of devicetree, if it exists. 272 """ 273 devicetree_compatible = '/sys/firmware/devicetree/base/compatible' 274 if not os.path.isfile(devicetree_compatible): 275 return None 276 f = open(devicetree_compatible, 'r') 277 compatible = f.read().split(chr(0)) 278 f.close() 279 if list_grep(compatible, '^rockchip,'): 280 return 'rockchip' 281 elif list_grep(compatible, '^mediatek,'): 282 return 'mediatek' 283 elif list_grep(compatible, '^qcom,'): 284 return 'qualcomm' 285 return None 286 287 288def get_arm_soc_family(): 289 """Work out which ARM SoC we're running on""" 290 family = get_arm_soc_family_from_devicetree() 291 if family is not None: 292 return family 293 294 f = open('/proc/cpuinfo', 'r') 295 cpuinfo = f.readlines() 296 f.close() 297 if list_grep(cpuinfo, 'EXYNOS5'): 298 return 'exynos5' 299 elif list_grep(cpuinfo, 'Tegra'): 300 return 'tegra' 301 elif list_grep(cpuinfo, 'Rockchip'): 302 return 'rockchip' 303 return 'arm' 304 305 306def get_cpu_soc_family(): 307 """Like get_cpu_arch, but for ARM, returns the SoC family name""" 308 f = open('/proc/cpuinfo', 'r') 309 cpuinfo = f.readlines() 310 f.close() 311 family = get_cpu_arch() 312 if family == 'arm': 313 family = get_arm_soc_family() 314 if list_grep(cpuinfo, '^vendor_id.*:.*AMD'): 315 family = 'amd' 316 return family 317 318 319INTEL_UARCH_TABLE = { 320 '06_4C': 'Airmont', 321 '06_1C': 'Atom', 322 '06_26': 'Atom', 323 '06_27': 'Atom', 324 '06_35': 'Atom', 325 '06_36': 'Atom', 326 '06_3D': 'Broadwell', 327 '06_47': 'Broadwell', 328 '06_4F': 'Broadwell', 329 '06_56': 'Broadwell', 330 '06_A5': 'Comet Lake', 331 '06_A6': 'Comet Lake', 332 '06_0D': 'Dothan', 333 '06_5C': 'Goldmont', 334 '06_7A': 'Goldmont', 335 '06_3C': 'Haswell', 336 '06_45': 'Haswell', 337 '06_46': 'Haswell', 338 '06_3F': 'Haswell-E', 339 '06_7D': 'Ice Lake', 340 '06_7E': 'Ice Lake', 341 '06_3A': 'Ivy Bridge', 342 '06_3E': 'Ivy Bridge-E', 343 '06_8E': 'Kaby Lake', 344 '06_9E': 'Kaby Lake', 345 '06_0F': 'Merom', 346 '06_16': 'Merom', 347 '06_17': 'Nehalem', 348 '06_1A': 'Nehalem', 349 '06_1D': 'Nehalem', 350 '06_1E': 'Nehalem', 351 '06_1F': 'Nehalem', 352 '06_2E': 'Nehalem', 353 '0F_03': 'Prescott', 354 '0F_04': 'Prescott', 355 '0F_06': 'Presler', 356 '06_2A': 'Sandy Bridge', 357 '06_2D': 'Sandy Bridge', 358 '06_37': 'Silvermont', 359 '06_4A': 'Silvermont', 360 '06_4D': 'Silvermont', 361 '06_5A': 'Silvermont', 362 '06_5D': 'Silvermont', 363 '06_4E': 'Skylake', 364 '06_5E': 'Skylake', 365 '06_55': 'Skylake', 366 '06_8C': 'Tiger Lake', 367 '06_8D': 'Tiger Lake', 368 '06_25': 'Westmere', 369 '06_2C': 'Westmere', 370 '06_2F': 'Westmere', 371} 372 373 374def get_intel_cpu_uarch(numeric=False): 375 """Return the Intel microarchitecture we're running on, or None. 376 377 Returns None if this is not an Intel CPU. Returns the family and model as 378 underscore-separated hex (per Intel manual convention) if the uarch is not 379 known, or if numeric is True. 380 """ 381 if not get_current_kernel_arch().startswith('x86'): 382 return None 383 cpuinfo = get_cpuinfo()[0] 384 if cpuinfo['vendor_id'] != 'GenuineIntel': 385 return None 386 family_model = '%02X_%02X' % (int(cpuinfo['cpu family']), 387 int(cpuinfo['model'])) 388 if numeric: 389 return family_model 390 return INTEL_UARCH_TABLE.get(family_model, family_model) 391 392 393INTEL_SILVERMONT_BCLK_TABLE = [83333, 100000, 133333, 116667, 80000]; 394 395 396def get_intel_bclk_khz(): 397 """Return Intel CPU base clock. 398 399 This only worked with SandyBridge (released in 2011) or newer. Older CPU has 400 133 MHz bclk. See turbostat code for implementation that also works with 401 older CPU. https://git.io/vpyKT 402 """ 403 if get_intel_cpu_uarch() == 'Silvermont': 404 MSR_FSB_FREQ = 0xcd 405 return INTEL_SILVERMONT_BCLK_TABLE[utils.rdmsr(MSR_FSB_FREQ) & 0xf] 406 return 100000 407 408 409def get_current_kernel_arch(): 410 """Get the machine architecture, now just a wrap of 'uname -m'.""" 411 return os.popen('uname -m').read().rstrip() 412 413 414def get_file_arch(filename): 415 # -L means follow symlinks 416 file_data = utils.system_output('file -L ' + filename) 417 if file_data.count('80386'): 418 return 'i386' 419 return None 420 421 422def count_cpus(): 423 """number of CPUs in the local machine according to /proc/cpuinfo""" 424 try: 425 return multiprocessing.cpu_count() 426 except Exception: 427 logging.exception('can not get cpu count from' 428 ' multiprocessing.cpu_count()') 429 cpuinfo = get_cpuinfo() 430 # Returns at least one cpu. Check comment #1 in crosbug.com/p/9582. 431 return len(cpuinfo) or 1 432 433 434def cpu_online_map(): 435 """ 436 Check out the available cpu online map 437 """ 438 cpuinfo = get_cpuinfo() 439 cpus = [] 440 for cpu in cpuinfo: 441 cpus.append(cpu['processor']) # grab cpu number 442 return cpus 443 444 445def get_cpu_family(): 446 cpuinfo = get_cpuinfo()[0] 447 return int(cpuinfo['cpu_family']) 448 449 450def get_cpu_vendor(): 451 cpuinfo = get_cpuinfo() 452 vendors = [cpu['vendor_id'] for cpu in cpuinfo] 453 for v in vendors[1:]: 454 if v != vendors[0]: 455 raise error.TestError('multiple cpu vendors found: ' + str(vendors)) 456 return vendors[0] 457 458 459def probe_cpus(): 460 """ 461 This routine returns a list of cpu devices found under 462 /sys/devices/system/cpu. 463 """ 464 cmd = 'find /sys/devices/system/cpu/ -maxdepth 1 -type d -name cpu*' 465 return utils.system_output(cmd).splitlines() 466 467 468# Returns total memory in kb 469def read_from_meminfo(key): 470 meminfo = utils.system_output('grep %s /proc/meminfo' % key) 471 return int(re.search(r'\d+', meminfo).group(0)) 472 473 474def memtotal(): 475 return read_from_meminfo('MemTotal') 476 477 478def freememtotal(): 479 return read_from_meminfo('MemFree') 480 481def usable_memtotal(): 482 # Reserved 5% for OS use 483 return int(read_from_meminfo('MemFree') * 0.95) 484 485def swaptotal(): 486 return read_from_meminfo('SwapTotal') 487 488def rounded_memtotal(): 489 # Get total of all physical mem, in kbytes 490 usable_kbytes = memtotal() 491 # usable_kbytes is system's usable DRAM in kbytes, 492 # as reported by memtotal() from device /proc/meminfo memtotal 493 # after Linux deducts 1.5% to 5.1% for system table overhead 494 # Undo the unknown actual deduction by rounding up 495 # to next small multiple of a big power-of-two 496 # eg 12GB - 5.1% gets rounded back up to 12GB 497 mindeduct = 0.015 # 1.5 percent 498 maxdeduct = 0.055 # 5.5 percent 499 # deduction range 1.5% .. 5.5% supports physical mem sizes 500 # 6GB .. 12GB in steps of .5GB 501 # 12GB .. 24GB in steps of 1 GB 502 # 24GB .. 48GB in steps of 2 GB ... 503 # Finer granularity in physical mem sizes would require 504 # tighter spread between min and max possible deductions 505 506 # increase mem size by at least min deduction, without rounding 507 min_kbytes = int(usable_kbytes / (1.0 - mindeduct)) 508 # increase mem size further by 2**n rounding, by 0..roundKb or more 509 round_kbytes = int(usable_kbytes / (1.0 - maxdeduct)) - min_kbytes 510 # find least binary roundup 2**n that covers worst-cast roundKb 511 mod2n = 1 << int(math.ceil(math.log(round_kbytes, 2))) 512 # have round_kbytes <= mod2n < round_kbytes*2 513 # round min_kbytes up to next multiple of mod2n 514 phys_kbytes = min_kbytes + mod2n - 1 515 phys_kbytes = phys_kbytes - (phys_kbytes % mod2n) # clear low bits 516 return phys_kbytes 517 518 519_MEMINFO_RE = re.compile('^(\w+)(\(\w+\))?:\s+(\d+)') 520 521 522def get_meminfo(): 523 """Returns a namedtuple of pairs from /proc/meminfo. 524 525 Example /proc/meminfo snippets: 526 MemTotal: 2048000 kB 527 Active(anon): 409600 kB 528 Example usage: 529 meminfo = utils.get_meminfo() 530 print meminfo.Active_anon 531 """ 532 info = {} 533 with _open_file('/proc/meminfo') as f: 534 for line in f: 535 m = _MEMINFO_RE.match(line) 536 if m: 537 if m.group(2): 538 name = m.group(1) + '_' + m.group(2)[1:-1] 539 else: 540 name = m.group(1) 541 info[name] = int(m.group(3)) 542 return collections.namedtuple('MemInfo', info.keys())(**info) 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_nvme(disk_name): 900 """ 901 Return true if disk is a nvme device, return false otherwise 902 903 @param disk_name: disk name to check 904 """ 905 return re.match('/dev/nvme[0-9]+n[0-9]+', disk_name) 906 907 908def is_disk_scsi(disk_name): 909 """ 910 Return true if disk is a scsi device, return false otherwise 911 912 @param disk_name: disk name check 913 """ 914 return re.match('/dev/sd[a-z]+', disk_name) 915 916 917def is_disk_harddisk(disk_name): 918 """ 919 Return true if disk is a harddisk, return false otherwise 920 921 @param disk_name: disk name check 922 """ 923 cmd1 = 'udevadm info --query=property --name=%s' % disk_name 924 cmd2 = 'grep -E "ID_ATA_ROTATION_RATE_RPM="' 925 cmd3 = 'cut -f 2 -d"="' 926 cmd = ' | '.join([cmd1, cmd2, cmd3]) 927 928 rtt = utils.system_output(cmd) 929 930 # eMMC will not have this field; rtt == '' 931 # SSD will have zero rotation rate; rtt == '0' 932 # For harddisk rtt > 0 933 return rtt and int(rtt) > 0 934 935def concat_partition(disk_name, partition_number): 936 """ 937 Return the name of a partition: 938 sda, 3 --> sda3 939 mmcblk0, 3 --> mmcblk0p3 940 941 @param disk_name: diskname string 942 @param partition_number: integer 943 """ 944 if disk_name.endswith(tuple(str(i) for i in range(0, 10))): 945 sep = 'p' 946 else: 947 sep = '' 948 return disk_name + sep + str(partition_number) 949 950def verify_hdparm_feature(disk_name, feature): 951 """ 952 Check for feature support for SCSI disk using hdparm 953 954 @param disk_name: target disk 955 @param feature: hdparm output string of the feature 956 """ 957 cmd = 'hdparm -I %s | grep -q "%s"' % (disk_name, feature) 958 ret = utils.system(cmd, ignore_status=True) 959 if ret == 0: 960 return True 961 elif ret == 1: 962 return False 963 else: 964 raise error.TestFail('Error running command %s' % cmd) 965 966def get_nvme_id_ns_feature(disk_name, feature): 967 """ 968 Return feature value for NVMe disk using nvme id-ns 969 970 @param disk_name: target disk 971 @param feature: output string of the feature 972 """ 973 cmd = "nvme id-ns -n 1 %s | grep %s" % (disk_name, feature) 974 feat = utils.system_output(cmd, ignore_status=True) 975 if not feat: 976 return 'None' 977 start = feat.find(':') 978 value = feat[start+2:] 979 return value 980 981def get_storage_error_msg(disk_name, reason): 982 """ 983 Get Error message for storage test which include disk model. 984 and also include the firmware version for the SCSI disk 985 986 @param disk_name: target disk 987 @param reason: Reason of the error. 988 """ 989 990 msg = reason 991 992 model = get_disk_model(disk_name) 993 msg += ' Disk model: %s' % model 994 995 if is_disk_scsi(disk_name): 996 fw = get_disk_firmware_version(disk_name) 997 msg += ' firmware: %s' % fw 998 999 return msg 1000 1001 1002_IOSTAT_FIELDS = ('transfers_per_s', 'read_kb_per_s', 'written_kb_per_s', 1003 'read_kb', 'written_kb') 1004_IOSTAT_RE = re.compile('ALL' + len(_IOSTAT_FIELDS) * r'\s+([\d\.]+)') 1005 1006def get_storage_statistics(device=None): 1007 """ 1008 Fetches statistics for a storage device. 1009 1010 Using iostat(1) it retrieves statistics for a device since last boot. See 1011 the man page for iostat(1) for details on the different fields. 1012 1013 @param device: Path to a block device. Defaults to the device where root 1014 is mounted. 1015 1016 @returns a dict mapping each field to its statistic. 1017 1018 @raises ValueError: If the output from iostat(1) can not be parsed. 1019 """ 1020 if device is None: 1021 device = get_root_device() 1022 cmd = 'iostat -d -k -g ALL -H %s' % device 1023 output = utils.system_output(cmd, ignore_status=True) 1024 match = _IOSTAT_RE.search(output) 1025 if not match: 1026 raise ValueError('Unable to get iostat for %s' % device) 1027 return dict(zip(_IOSTAT_FIELDS, map(float, match.groups()))) 1028 1029 1030def load_module(module_name, params=None): 1031 # Checks if a module has already been loaded 1032 if module_is_loaded(module_name): 1033 return False 1034 1035 cmd = '/sbin/modprobe ' + module_name 1036 if params: 1037 cmd += ' ' + params 1038 utils.system(cmd) 1039 return True 1040 1041 1042def unload_module(module_name): 1043 """ 1044 Removes a module. Handles dependencies. If even then it's not possible 1045 to remove one of the modules, it will trhow an error.CmdError exception. 1046 1047 @param module_name: Name of the module we want to remove. 1048 """ 1049 l_raw = utils.system_output("/bin/lsmod").splitlines() 1050 lsmod = [x for x in l_raw if x.split()[0] == module_name] 1051 if len(lsmod) > 0: 1052 line_parts = lsmod[0].split() 1053 if len(line_parts) == 4: 1054 submodules = line_parts[3].split(",") 1055 for submodule in submodules: 1056 unload_module(submodule) 1057 utils.system("/sbin/modprobe -r %s" % module_name) 1058 logging.info("Module %s unloaded", module_name) 1059 else: 1060 logging.info("Module %s is already unloaded", module_name) 1061 1062 1063def module_is_loaded(module_name): 1064 module_name = module_name.replace('-', '_') 1065 modules = utils.system_output('/bin/lsmod').splitlines() 1066 for module in modules: 1067 if module.startswith(module_name) and module[len(module_name)] == ' ': 1068 return True 1069 return False 1070 1071 1072def get_loaded_modules(): 1073 lsmod_output = utils.system_output('/bin/lsmod').splitlines()[1:] 1074 return [line.split(None, 1)[0] for line in lsmod_output] 1075 1076 1077def get_huge_page_size(): 1078 output = utils.system_output('grep Hugepagesize /proc/meminfo') 1079 return int(output.split()[1]) # Assumes units always in kB. :( 1080 1081 1082def get_num_huge_pages(): 1083 raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages') 1084 return int(raw_hugepages.split()[2]) 1085 1086 1087def set_num_huge_pages(num): 1088 utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num) 1089 1090 1091def ping_default_gateway(): 1092 """Ping the default gateway.""" 1093 1094 network = open('/etc/sysconfig/network') 1095 m = re.search('GATEWAY=(\S+)', network.read()) 1096 1097 if m: 1098 gw = m.group(1) 1099 cmd = 'ping %s -c 5 > /dev/null' % gw 1100 return utils.system(cmd, ignore_status=True) 1101 1102 raise error.TestError('Unable to find default gateway') 1103 1104 1105def drop_caches(): 1106 """Writes back all dirty pages to disk and clears all the caches.""" 1107 utils.system("sync") 1108 # We ignore failures here as this will fail on 2.6.11 kernels. 1109 utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True) 1110 1111 1112def process_is_alive(name_pattern): 1113 """ 1114 'pgrep name' misses all python processes and also long process names. 1115 'pgrep -f name' gets all shell commands with name in args. 1116 So look only for command whose initial pathname ends with name. 1117 Name itself is an egrep pattern, so it can use | etc for variations. 1118 """ 1119 return utils.system("pgrep -f '^([^ /]*/)*(%s)([ ]|$)'" % name_pattern, 1120 ignore_status=True) == 0 1121 1122def set_hwclock(time='system', 1123 utc=True, 1124 rtc=None, 1125 noadjfile=False, 1126 ignore_status=False): 1127 """Uses the hwclock command to set time of an RTC. 1128 1129 @param time: Either 'system', meaning use the system time, or a string 1130 to be passed to the --date argument of hwclock. 1131 @param utc: Boolean of whether to use UTC or localtime. 1132 @param rtc: String to be passed to the --rtc arg of hwclock. 1133 @param noadjfile: Boolean of whether to use --noadjfile flag with hwclock. 1134 @param ignore_status: Boolean of whether to ignore exit code of hwclock. 1135 """ 1136 cmd = '/sbin/hwclock' 1137 if time == 'system': 1138 cmd += ' --systohc' 1139 else: 1140 cmd += ' --set --date "{}"'.format(time) 1141 if utc: 1142 cmd += ' --utc' 1143 else: 1144 cmd += ' --localtime' 1145 if rtc is not None: 1146 cmd += ' --rtc={}'.format(rtc) 1147 if noadjfile: 1148 cmd += ' --noadjfile' 1149 return utils.system(cmd, ignore_status=ignore_status) 1150 1151def get_hwclock_seconds(utc=True): 1152 """ 1153 Return the hardware clock in seconds as a floating point value. 1154 Use Coordinated Universal Time if utc is True, local time otherwise. 1155 Raise a ValueError if unable to read the hardware clock. 1156 """ 1157 cmd = '/sbin/hwclock --debug' 1158 if utc: 1159 cmd += ' --utc' 1160 hwclock_output = utils.system_output(cmd, ignore_status=True) 1161 match = re.search(r'= ([0-9]+) seconds since .+ (-?[0-9.]+) seconds$', 1162 hwclock_output, re.DOTALL) 1163 if match: 1164 seconds = int(match.group(1)) + float(match.group(2)) 1165 logging.debug('hwclock seconds = %f', seconds) 1166 return seconds 1167 1168 raise ValueError('Unable to read the hardware clock -- ' + 1169 hwclock_output) 1170 1171 1172def set_wake_alarm(alarm_time): 1173 """ 1174 Set the hardware RTC-based wake alarm to 'alarm_time'. 1175 """ 1176 utils.write_one_line('/sys/class/rtc/rtc0/wakealarm', str(alarm_time)) 1177 1178 1179def set_power_state(state): 1180 """ 1181 Set the system power state to 'state'. 1182 """ 1183 utils.write_one_line('/sys/power/state', state) 1184 1185 1186def standby(): 1187 """ 1188 Power-on suspend (S1) 1189 """ 1190 set_power_state('standby') 1191 1192 1193def suspend_to_ram(): 1194 """ 1195 Suspend the system to RAM (S3) 1196 """ 1197 set_power_state('mem') 1198 1199 1200def suspend_to_disk(): 1201 """ 1202 Suspend the system to disk (S4) 1203 """ 1204 set_power_state('disk') 1205 1206 1207_AUTOTEST_CLIENT_PATH = os.path.join(os.path.dirname(__file__), '..') 1208_AMD_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH, 1209 'bin/amd_pci_ids.json') 1210_INTEL_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH, 1211 'bin/intel_pci_ids.json') 1212_UI_USE_FLAGS_FILE_PATH = '/etc/ui_use_flags.txt' 1213 1214# Command to check if a package is installed. If the package is not installed 1215# the command shall fail. 1216_CHECK_PACKAGE_INSTALLED_COMMAND =( 1217 "dpkg-query -W -f='${Status}\n' %s | head -n1 | awk '{print $3;}' | " 1218 "grep -q '^installed$'") 1219 1220pciid_to_amd_architecture = {} 1221pciid_to_intel_architecture = {} 1222 1223class Crossystem(object): 1224 """A wrapper for the crossystem utility.""" 1225 1226 def __init__(self, client): 1227 self.cros_system_data = {} 1228 self._client = client 1229 1230 def init(self): 1231 self.cros_system_data = {} 1232 (_, fname) = tempfile.mkstemp() 1233 f = open(fname, 'w') 1234 self._client.run('crossystem', stdout_tee=f) 1235 f.close() 1236 text = utils.read_file(fname) 1237 for line in text.splitlines(): 1238 assignment_string = line.split('#')[0] 1239 if not assignment_string.count('='): 1240 continue 1241 (name, value) = assignment_string.split('=', 1) 1242 self.cros_system_data[name.strip()] = value.strip() 1243 os.remove(fname) 1244 1245 def __getattr__(self, name): 1246 """ 1247 Retrieve a crosssystem attribute. 1248 1249 The call crossystemobject.name() will return the crossystem reported 1250 string. 1251 """ 1252 return lambda: self.cros_system_data[name] 1253 1254 1255def get_oldest_pid_by_name(name): 1256 """ 1257 Return the oldest pid of a process whose name perfectly matches |name|. 1258 1259 name is an egrep expression, which will be matched against the entire name 1260 of processes on the system. For example: 1261 1262 get_oldest_pid_by_name('chrome') 1263 1264 on a system running 1265 8600 ? 00:00:04 chrome 1266 8601 ? 00:00:00 chrome 1267 8602 ? 00:00:00 chrome-sandbox 1268 1269 would return 8600, as that's the oldest process that matches. 1270 chrome-sandbox would not be matched. 1271 1272 Arguments: 1273 name: egrep expression to match. Will be anchored at the beginning and 1274 end of the match string. 1275 1276 Returns: 1277 pid as an integer, or None if one cannot be found. 1278 1279 Raises: 1280 ValueError if pgrep returns something odd. 1281 """ 1282 str_pid = utils.system_output('pgrep -o ^%s$' % name, 1283 ignore_status=True).rstrip() 1284 if str_pid: 1285 return int(str_pid) 1286 1287 1288def get_oldest_by_name(name): 1289 """Return pid and command line of oldest process whose name matches |name|. 1290 1291 @param name: egrep expression to match desired process name. 1292 @return: A tuple of (pid, command_line) of the oldest process whose name 1293 matches |name|. 1294 1295 """ 1296 pid = get_oldest_pid_by_name(name) 1297 if pid: 1298 command_line = utils.system_output('ps -p %i -o command=' % pid, 1299 ignore_status=True).rstrip() 1300 return (pid, command_line) 1301 1302 1303def get_chrome_remote_debugging_port(): 1304 """Returns remote debugging port for Chrome. 1305 1306 Parse chrome process's command line argument to get the remote debugging 1307 port. if it is 0, look at DevToolsActivePort for the ephemeral port. 1308 """ 1309 _, command = get_oldest_by_name('chrome') 1310 matches = re.search('--remote-debugging-port=([0-9]+)', command) 1311 if not matches: 1312 return 0 1313 port = int(matches.group(1)) 1314 if port: 1315 return port 1316 with open('/home/chronos/DevToolsActivePort') as f: 1317 return int(f.readline().rstrip()) 1318 1319 1320def get_process_list(name, command_line=None): 1321 """ 1322 Return the list of pid for matching process |name command_line|. 1323 1324 on a system running 1325 31475 ? 0:06 /opt/google/chrome/chrome --allow-webui-compositing - 1326 31478 ? 0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/ 1327 31485 ? 0:00 /opt/google/chrome/chrome --type=zygote --log-level=1 1328 31532 ? 1:05 /opt/google/chrome/chrome --type=renderer 1329 1330 get_process_list('chrome') 1331 would return ['31475', '31485', '31532'] 1332 1333 get_process_list('chrome', '--type=renderer') 1334 would return ['31532'] 1335 1336 Arguments: 1337 name: process name to search for. If command_line is provided, name is 1338 matched against full command line. If command_line is not provided, 1339 name is only matched against the process name. 1340 command line: when command line is passed, the full process command line 1341 is used for matching. 1342 1343 Returns: 1344 list of PIDs of the matching processes. 1345 1346 """ 1347 # TODO(rohitbm) crbug.com/268861 1348 flag = '-x' if not command_line else '-f' 1349 name = '\'%s.*%s\'' % (name, command_line) if command_line else name 1350 str_pid = utils.system_output('pgrep %s %s' % (flag, name), 1351 ignore_status=True).rstrip() 1352 return str_pid.split() 1353 1354 1355def nuke_process_by_name(name, with_prejudice=False): 1356 """Tell the oldest process specified by name to exit. 1357 1358 Arguments: 1359 name: process name specifier, as understood by pgrep. 1360 with_prejudice: if True, don't allow for graceful exit. 1361 1362 Raises: 1363 error.AutoservPidAlreadyDeadError: no existing process matches name. 1364 """ 1365 try: 1366 pid = get_oldest_pid_by_name(name) 1367 except Exception as e: 1368 logging.error(e) 1369 return 1370 if pid is None: 1371 raise error.AutoservPidAlreadyDeadError('No process matching %s.' % 1372 name) 1373 if with_prejudice: 1374 utils.nuke_pid(pid, [signal.SIGKILL]) 1375 else: 1376 utils.nuke_pid(pid) 1377 1378 1379def ensure_processes_are_dead_by_name(name, timeout_sec=10): 1380 """Terminate all processes specified by name and ensure they're gone. 1381 1382 Arguments: 1383 name: process name specifier, as understood by pgrep. 1384 timeout_sec: maximum number of seconds to wait for processes to die. 1385 1386 Raises: 1387 error.AutoservPidAlreadyDeadError: no existing process matches name. 1388 utils.TimeoutError: if processes still exist after timeout_sec. 1389 """ 1390 1391 def list_and_kill_processes(name): 1392 process_list = get_process_list(name) 1393 try: 1394 for pid in [int(str_pid) for str_pid in process_list]: 1395 utils.nuke_pid(pid) 1396 except error.AutoservPidAlreadyDeadError: 1397 pass 1398 return process_list 1399 1400 utils.poll_for_condition(lambda: list_and_kill_processes(name) == [], 1401 timeout=timeout_sec) 1402 1403 1404def is_virtual_machine(): 1405 if 'QEMU' in platform.processor(): 1406 return True 1407 1408 try: 1409 with open('/sys/devices/virtual/dmi/id/sys_vendor') as f: 1410 if 'QEMU' in f.read(): 1411 return True 1412 except IOError: 1413 pass 1414 1415 return False 1416 1417 1418def save_vm_state(checkpoint): 1419 """Saves the current state of the virtual machine. 1420 1421 This function is a NOOP if the test is not running under a virtual machine 1422 with the USB serial port redirected. 1423 1424 Arguments: 1425 checkpoint - Name used to identify this state 1426 1427 Returns: 1428 None 1429 """ 1430 # The QEMU monitor has been redirected to the guest serial port located at 1431 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm' 1432 # command to the serial port. 1433 if is_virtual_machine() and os.path.exists('/dev/ttyUSB0'): 1434 logging.info('Saving VM state "%s"', checkpoint) 1435 serial = open('/dev/ttyUSB0', 'w') 1436 serial.write('savevm %s\r\n' % checkpoint) 1437 logging.info('Done saving VM state "%s"', checkpoint) 1438 1439 1440def check_raw_dmesg(dmesg, message_level, whitelist): 1441 """Checks dmesg for unexpected warnings. 1442 1443 This function parses dmesg for message with message_level <= message_level 1444 which do not appear in the whitelist. 1445 1446 Arguments: 1447 dmesg - string containing raw dmesg buffer 1448 message_level - minimum message priority to check 1449 whitelist - messages to ignore 1450 1451 Returns: 1452 List of unexpected warnings 1453 """ 1454 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist)) 1455 unexpected = [] 1456 for line in dmesg.splitlines(): 1457 if int(line[1]) <= message_level: 1458 stripped_line = line.split('] ', 1)[1] 1459 if whitelist_re.search(stripped_line): 1460 continue 1461 unexpected.append(stripped_line) 1462 return unexpected 1463 1464 1465def verify_mesg_set(mesg, regex, whitelist): 1466 """Verifies that the exact set of messages are present in a text. 1467 1468 This function finds all strings in the text matching a certain regex, and 1469 then verifies that all expected strings are present in the set, and no 1470 unexpected strings are there. 1471 1472 Arguments: 1473 mesg - the mutiline text to be scanned 1474 regex - regular expression to match 1475 whitelist - messages to find in the output, a list of strings 1476 (potentially regexes) to look for in the filtered output. All these 1477 strings must be there, and no other strings should be present in the 1478 filtered output. 1479 1480 Returns: 1481 string of inconsistent findings (i.e. an empty string on success). 1482 """ 1483 1484 rv = [] 1485 1486 missing_strings = [] 1487 present_strings = [] 1488 for line in mesg.splitlines(): 1489 if not re.search(r'%s' % regex, line): 1490 continue 1491 present_strings.append(line.split('] ', 1)[1]) 1492 1493 for string in whitelist: 1494 for present_string in list(present_strings): 1495 if re.search(r'^%s$' % string, present_string): 1496 present_strings.remove(present_string) 1497 break 1498 else: 1499 missing_strings.append(string) 1500 1501 if present_strings: 1502 rv.append('unexpected strings:') 1503 rv.extend(present_strings) 1504 if missing_strings: 1505 rv.append('missing strings:') 1506 rv.extend(missing_strings) 1507 1508 return '\n'.join(rv) 1509 1510 1511def target_is_pie(): 1512 """Returns whether the toolchain produces a PIE (position independent 1513 executable) by default. 1514 1515 Arguments: 1516 None 1517 1518 Returns: 1519 True if the target toolchain produces a PIE by default. 1520 False otherwise. 1521 """ 1522 1523 command = 'echo | ${CC} -E -dD -P - | grep -i pie' 1524 result = utils.system_output(command, 1525 retain_output=True, 1526 ignore_status=True) 1527 if re.search('#define __PIE__', result): 1528 return True 1529 else: 1530 return False 1531 1532 1533def target_is_x86(): 1534 """Returns whether the toolchain produces an x86 object 1535 1536 Arguments: 1537 None 1538 1539 Returns: 1540 True if the target toolchain produces an x86 object 1541 False otherwise. 1542 """ 1543 1544 command = 'echo | ${CC} -E -dD -P - | grep -i 86' 1545 result = utils.system_output(command, 1546 retain_output=True, 1547 ignore_status=True) 1548 if re.search('__i386__', result) or re.search('__x86_64__', result): 1549 return True 1550 else: 1551 return False 1552 1553 1554def mounts(): 1555 ret = [] 1556 for line in file('/proc/mounts'): 1557 m = re.match( 1558 r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line) 1559 if m: 1560 ret.append(m.groupdict()) 1561 return ret 1562 1563 1564def is_mountpoint(path): 1565 return path in [m['dest'] for m in mounts()] 1566 1567 1568def require_mountpoint(path): 1569 """ 1570 Raises an exception if path is not a mountpoint. 1571 """ 1572 if not is_mountpoint(path): 1573 raise error.TestFail('Path not mounted: "%s"' % path) 1574 1575 1576def random_username(): 1577 return str(uuid.uuid4()) + '@example.com' 1578 1579 1580def get_signin_credentials(filepath): 1581 """Returns user_id, password tuple from credentials file at filepath. 1582 1583 File must have one line of the format user_id:password 1584 1585 @param filepath: path of credentials file. 1586 @return user_id, password tuple. 1587 """ 1588 user_id, password = None, None 1589 if os.path.isfile(filepath): 1590 with open(filepath) as f: 1591 user_id, password = f.read().rstrip().split(':') 1592 return user_id, password 1593 1594 1595def parse_cmd_output(command, run_method=utils.run): 1596 """Runs a command on a host object to retrieve host attributes. 1597 1598 The command should output to stdout in the format of: 1599 <key> = <value> # <optional_comment> 1600 1601 1602 @param command: Command to execute on the host. 1603 @param run_method: Function to use to execute the command. Defaults to 1604 utils.run so that the command will be executed locally. 1605 Can be replace with a host.run call so that it will 1606 execute on a DUT or external machine. Method must accept 1607 a command argument, stdout_tee and stderr_tee args and 1608 return a result object with a string attribute stdout 1609 which will be parsed. 1610 1611 @returns a dictionary mapping host attributes to their values. 1612 """ 1613 result = {} 1614 # Suppresses stdout so that the files are not printed to the logs. 1615 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None) 1616 for line in cmd_result.stdout.splitlines(): 1617 # Lines are of the format "<key> = <value> # <comment>" 1618 key_value = re.match(r'^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ' 1619 r']+)(?:\s*#.*)?$', line) 1620 if key_value: 1621 result[key_value.group('key')] = key_value.group('value') 1622 return result 1623 1624 1625def set_from_keyval_output(out, delimiter=' '): 1626 """Parse delimiter-separated key-val output into a set of tuples. 1627 1628 Output is expected to be multiline text output from a command. 1629 Stuffs the key-vals into tuples in a set to be later compared. 1630 1631 e.g. deactivated 0 1632 disableForceClear 0 1633 ==> set(('deactivated', '0'), ('disableForceClear', '0')) 1634 1635 @param out: multiple lines of space-separated key-val pairs. 1636 @param delimiter: character that separates key from val. Usually a 1637 space but may be '=' or something else. 1638 @return set of key-val tuples. 1639 """ 1640 results = set() 1641 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter) 1642 for linecr in out.splitlines(): 1643 match = kv_match_re.match(linecr.strip()) 1644 if match: 1645 results.add((match.group(1), match.group(2))) 1646 return results 1647 1648 1649def get_cpu_usage(): 1650 """Returns machine's CPU usage. 1651 1652 This function uses /proc/stat to identify CPU usage. 1653 Returns: 1654 A dictionary with values for all columns in /proc/stat 1655 Sample dictionary: 1656 { 1657 'user': 254544, 1658 'nice': 9, 1659 'system': 254768, 1660 'idle': 2859878, 1661 'iowait': 1, 1662 'irq': 2, 1663 'softirq': 3, 1664 'steal': 4, 1665 'guest': 5, 1666 'guest_nice': 6 1667 } 1668 If a column is missing or malformed in /proc/stat (typically on older 1669 systems), the value for that column is set to 0. 1670 """ 1671 with _open_file('/proc/stat') as proc_stat: 1672 cpu_usage_str = proc_stat.readline().split() 1673 columns = ('user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq', 1674 'steal', 'guest', 'guest_nice') 1675 d = {} 1676 for index, col in enumerate(columns, 1): 1677 try: 1678 d[col] = int(cpu_usage_str[index]) 1679 except: 1680 d[col] = 0 1681 return d 1682 1683def compute_active_cpu_time(cpu_usage_start, cpu_usage_end): 1684 """Computes the fraction of CPU time spent non-idling. 1685 1686 This function should be invoked using before/after values from calls to 1687 get_cpu_usage(). 1688 1689 See https://stackoverflow.com/a/23376195 and 1690 https://unix.stackexchange.com/a/303224 for some more context how 1691 to calculate usage given two /proc/stat snapshots. 1692 """ 1693 idle_cols = ('idle', 'iowait') # All other cols are calculated as active. 1694 time_active_start = sum([x[1] for x in cpu_usage_start.iteritems() 1695 if x[0] not in idle_cols]) 1696 time_active_end = sum([x[1] for x in cpu_usage_end.iteritems() 1697 if x[0] not in idle_cols]) 1698 total_time_start = sum(cpu_usage_start.values()) 1699 total_time_end = sum(cpu_usage_end.values()) 1700 # Avoid bogus division which has been observed on Tegra. 1701 if total_time_end <= total_time_start: 1702 logging.warning('compute_active_cpu_time observed bogus data') 1703 # We pretend to be busy, this will force a longer wait for idle CPU. 1704 return 1.0 1705 return ((float(time_active_end) - time_active_start) / 1706 (total_time_end - total_time_start)) 1707 1708 1709def is_pgo_mode(): 1710 return 'USE_PGO' in os.environ 1711 1712 1713def wait_for_idle_cpu(timeout, utilization): 1714 """Waits for the CPU to become idle (< utilization). 1715 1716 Args: 1717 timeout: The longest time in seconds to wait before throwing an error. 1718 utilization: The CPU usage below which the system should be considered 1719 idle (between 0 and 1.0 independent of cores/hyperthreads). 1720 """ 1721 time_passed = 0.0 1722 fraction_active_time = 1.0 1723 sleep_time = 1 1724 logging.info('Starting to wait up to %.1fs for idle CPU...', timeout) 1725 while fraction_active_time >= utilization: 1726 cpu_usage_start = get_cpu_usage() 1727 # Split timeout interval into not too many chunks to limit log spew. 1728 # Start at 1 second, increase exponentially 1729 time.sleep(sleep_time) 1730 time_passed += sleep_time 1731 sleep_time = min(16.0, 2.0 * sleep_time) 1732 cpu_usage_end = get_cpu_usage() 1733 fraction_active_time = compute_active_cpu_time(cpu_usage_start, 1734 cpu_usage_end) 1735 logging.info('After waiting %.1fs CPU utilization is %.3f.', 1736 time_passed, fraction_active_time) 1737 if time_passed > timeout: 1738 logging.warning('CPU did not become idle.') 1739 log_process_activity() 1740 # crosbug.com/37389 1741 if is_pgo_mode(): 1742 logging.info('Still continuing because we are in PGO mode.') 1743 return True 1744 1745 return False 1746 logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).', 1747 time_passed, fraction_active_time) 1748 return True 1749 1750 1751def log_process_activity(): 1752 """Logs the output of top. 1753 1754 Useful to debug performance tests and to find runaway processes. 1755 """ 1756 logging.info('Logging current process activity using top and ps.') 1757 cmd = 'top -b -n1 -c' 1758 output = utils.run(cmd) 1759 logging.info(output) 1760 output = utils.run('ps axl') 1761 logging.info(output) 1762 1763 1764def wait_for_cool_machine(): 1765 """ 1766 A simple heuristic to wait for a machine to cool. 1767 The code looks a bit 'magic', but we don't know ambient temperature 1768 nor machine characteristics and still would like to return the caller 1769 a machine that cooled down as much as reasonably possible. 1770 """ 1771 temperature = get_current_temperature_max() 1772 # We got here with a cold machine, return immediately. This should be the 1773 # most common case. 1774 if temperature < 45: 1775 return True 1776 logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature) 1777 # A modest wait should cool the machine. 1778 time.sleep(60.0) 1779 temperature = get_current_temperature_max() 1780 # Atoms idle below 60 and everyone else should be even lower. 1781 if temperature < 62: 1782 return True 1783 # This should be rare. 1784 logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature) 1785 time.sleep(120.0) 1786 temperature = get_current_temperature_max() 1787 # A temperature over 65'C doesn't give us much headroom to the critical 1788 # temperatures that start at 85'C (and PerfControl as of today will fail at 1789 # critical - 10'C). 1790 if temperature < 65: 1791 return True 1792 logging.warning('Did not cool down (%dC), giving up.', temperature) 1793 log_process_activity() 1794 return False 1795 1796 1797def report_temperature(test, keyname): 1798 """Report current max observed temperature with given keyname. 1799 1800 @param test: autotest_lib.client.bin.test.test instance 1801 @param keyname: key to be used when reporting perf value. 1802 """ 1803 temperature = get_current_temperature_max() 1804 logging.info('%s = %f degree Celsius', keyname, temperature) 1805 test.output_perf_value( 1806 description=keyname, 1807 value=temperature, 1808 units='Celsius', 1809 higher_is_better=False) 1810 1811 1812# System paths for machine performance state. 1813_CPUINFO = '/proc/cpuinfo' 1814_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs' 1815_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max' 1816_MEMINFO = '/proc/meminfo' 1817_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)' 1818 1819def _open_file(path): 1820 """ 1821 Opens a file and returns the file object. 1822 1823 This method is intended to be mocked by tests. 1824 @return The open file object. 1825 """ 1826 return open(path) 1827 1828def _get_line_from_file(path, line): 1829 """ 1830 line can be an integer or 1831 line can be a string that matches the beginning of the line 1832 """ 1833 with _open_file(path) as f: 1834 if isinstance(line, int): 1835 l = f.readline() 1836 for _ in range(0, line): 1837 l = f.readline() 1838 return l 1839 else: 1840 for l in f: 1841 if l.startswith(line): 1842 return l 1843 return None 1844 1845 1846def _get_match_from_file(path, line, prefix, postfix): 1847 """ 1848 Matches line in path and returns string between first prefix and postfix. 1849 """ 1850 match = _get_line_from_file(path, line) 1851 # Strip everything from front of line including prefix. 1852 if prefix: 1853 match = re.split(prefix, match)[1] 1854 # Strip everything from back of string including first occurence of postfix. 1855 if postfix: 1856 match = re.split(postfix, match)[0] 1857 return match 1858 1859 1860def _get_float_from_file(path, line, prefix, postfix): 1861 match = _get_match_from_file(path, line, prefix, postfix) 1862 return float(match) 1863 1864 1865def _get_int_from_file(path, line, prefix, postfix): 1866 match = _get_match_from_file(path, line, prefix, postfix) 1867 return int(match) 1868 1869 1870def _get_hex_from_file(path, line, prefix, postfix): 1871 match = _get_match_from_file(path, line, prefix, postfix) 1872 return int(match, 16) 1873 1874 1875def is_system_thermally_throttled(): 1876 """ 1877 Returns whether the system appears to be thermally throttled. 1878 """ 1879 for path in glob.glob('/sys/class/thermal/cooling_device*/type'): 1880 with _open_file(path) as f: 1881 cdev_type = f.read().strip() 1882 1883 if not (cdev_type == 'Processor' or 1884 cdev_type.startswith('thermal-devfreq') or 1885 cdev_type.startswith('thermal-cpufreq')): 1886 continue 1887 1888 cur_state_path = os.path.join(os.path.dirname(path), 'cur_state') 1889 if _get_int_from_file(cur_state_path, 0, None, None) > 0: 1890 return True 1891 1892 return False 1893 1894 1895# The paths don't change. Avoid running find all the time. 1896_hwmon_paths = {} 1897 1898def _get_hwmon_datas(file_pattern): 1899 """Returns a list of reading from hwmon.""" 1900 # Some systems like daisy_spring only have the virtual hwmon. 1901 # And other systems like rambi only have coretemp.0. See crbug.com/360249. 1902 # /sys/class/hwmon/hwmon*/ 1903 # /sys/devices/virtual/hwmon/hwmon*/ 1904 # /sys/devices/platform/coretemp.0/ 1905 if file_pattern not in _hwmon_paths: 1906 cmd = 'find /sys/class /sys/devices -name "' + file_pattern + '"' 1907 _hwmon_paths[file_pattern] = \ 1908 utils.run(cmd, verbose=False).stdout.splitlines() 1909 for _hwmon_path in _hwmon_paths[file_pattern]: 1910 try: 1911 yield _get_float_from_file(_hwmon_path, 0, None, None) * 0.001 1912 except IOError as err: 1913 # Files under /sys may get truncated and result in ENODATA. 1914 # Ignore those. 1915 if err.errno is not errno.ENODATA: 1916 raise 1917 1918 1919def _get_hwmon_temperatures(): 1920 """ 1921 Returns the currently observed temperatures from hwmon 1922 """ 1923 return list(_get_hwmon_datas('temp*_input')) 1924 1925 1926def _get_thermal_zone_temperatures(): 1927 """ 1928 Returns the maximum currently observered temperature in thermal_zones. 1929 """ 1930 temperatures = [] 1931 for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'): 1932 try: 1933 temperatures.append( 1934 _get_float_from_file(path, 0, None, None) * 0.001) 1935 except IOError: 1936 # Some devices (e.g. Veyron) may have reserved thermal zones that 1937 # are not active. Trying to read the temperature value would cause a 1938 # EINVAL IO error. 1939 continue 1940 return temperatures 1941 1942 1943def get_ec_temperatures(): 1944 """ 1945 Uses ectool to return a list of all sensor temperatures in Celsius. 1946 1947 Output from ectool is either '0: 300' or '0: 300 K' (newer ectool 1948 includes the unit). 1949 """ 1950 temperatures = [] 1951 try: 1952 full_cmd = 'ectool temps all' 1953 lines = utils.run(full_cmd, verbose=False).stdout.splitlines() 1954 pattern = re.compile('.*: (\d+)') 1955 for line in lines: 1956 matched = pattern.match(line) 1957 temperature = int(matched.group(1)) - 273 1958 temperatures.append(temperature) 1959 except Exception: 1960 logging.warning('Unable to read temperature sensors using ectool.') 1961 for temperature in temperatures: 1962 # Sanity check for real world values. 1963 assert ((temperature > 10.0) and 1964 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 1965 temperature) 1966 1967 return temperatures 1968 1969 1970def get_current_temperature_max(): 1971 """ 1972 Returns the highest reported board temperature (all sensors) in Celsius. 1973 """ 1974 all_temps = (_get_hwmon_temperatures() + 1975 _get_thermal_zone_temperatures() + 1976 get_ec_temperatures()) 1977 if all_temps: 1978 temperature = max(all_temps) 1979 else: 1980 temperature = -1 1981 # Sanity check for real world values. 1982 assert ((temperature > 10.0) and 1983 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' % 1984 temperature) 1985 return temperature 1986 1987 1988def get_cpu_cache_size(): 1989 """ 1990 Returns the last level CPU cache size in kBytes. 1991 """ 1992 cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB') 1993 # Sanity check. 1994 assert cache_size >= 64, 'Unreasonably small cache.' 1995 return cache_size 1996 1997 1998def get_cpu_model_frequency(): 1999 """ 2000 Returns the model frequency from the CPU model name on Intel only. This 2001 might be redundant with get_cpu_max_frequency. Unit is Hz. 2002 """ 2003 frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz') 2004 return 1.e9 * frequency 2005 2006 2007def get_cpu_max_frequency(): 2008 """ 2009 Returns the largest of the max CPU core frequencies. The unit is Hz. 2010 """ 2011 max_frequency = -1 2012 paths = utils._get_cpufreq_paths('cpuinfo_max_freq') 2013 if not paths: 2014 raise ValueError('Could not find max freq; is cpufreq supported?') 2015 for path in paths: 2016 try: 2017 # Convert from kHz to Hz. 2018 frequency = 1000 * _get_float_from_file(path, 0, None, None) 2019 # CPUs may come and go. A missing entry or two aren't critical. 2020 except IOError: 2021 continue 2022 max_frequency = max(frequency, max_frequency) 2023 # Sanity check. 2024 assert max_frequency > 1e8, ('Unreasonably low CPU frequency: %.1f' % 2025 max_frequency) 2026 return max_frequency 2027 2028 2029def get_cpu_model(): 2030 """ 2031 Returns the CPU model. 2032 Only works on Intel. 2033 """ 2034 cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None) 2035 return cpu_model 2036 2037 2038def get_cpu_family_intel(): 2039 """ 2040 Returns the CPU family. 2041 Only works on Intel. 2042 """ 2043 cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None) 2044 return cpu_family 2045 2046 2047def get_board_property(key): 2048 """ 2049 Get a specific property from /etc/lsb-release. 2050 2051 @param key: board property to return value for 2052 2053 @return the value or '' if not present 2054 """ 2055 with open('/etc/lsb-release') as f: 2056 pattern = '%s=(.*)' % key 2057 pat = re.search(pattern, f.read()) 2058 if pat: 2059 return pat.group(1) 2060 return '' 2061 2062 2063def get_board(): 2064 """ 2065 Get the ChromeOS release board name from /etc/lsb-release. 2066 """ 2067 return get_board_property('BOARD') 2068 2069 2070def get_board_type(): 2071 """ 2072 Get the ChromeOS board type from /etc/lsb-release. 2073 2074 @return device type. 2075 """ 2076 return get_board_property('DEVICETYPE') 2077 2078 2079def get_chromeos_version(): 2080 """ 2081 Get the ChromeOS build version from /etc/lsb-release. 2082 2083 @return chromeos release version. 2084 """ 2085 return get_board_property('CHROMEOS_RELEASE_VERSION') 2086 2087 2088def get_platform(): 2089 """ 2090 Get the ChromeOS platform name. 2091 2092 For unibuild this should be equal to model name. For non-unibuild 2093 it will either be board name or empty string. In the case of 2094 empty string return board name to match equivalent logic in 2095 server/hosts/cros_host.py 2096 2097 @returns platform name 2098 """ 2099 platform = cros_config.call_cros_config_get_output('/ name', utils.run) 2100 if platform == '': 2101 platform = get_board() 2102 return platform 2103 2104 2105def get_sku(): 2106 """ 2107 Get the SKU number. 2108 2109 @returns SKU number 2110 """ 2111 return cros_config.call_cros_config_get_output('/identity sku-id', 2112 utils.run) 2113 2114 2115def get_ec_version(): 2116 """Get the ec version as strings. 2117 2118 @returns a string representing this host's ec version. 2119 """ 2120 command = 'mosys ec info -s fw_version' 2121 result = utils.run(command, ignore_status=True) 2122 if result.exit_status != 0: 2123 return '' 2124 return result.stdout.strip() 2125 2126 2127def get_firmware_version(): 2128 """Get the firmware version as strings. 2129 2130 @returns a string representing this host's firmware version. 2131 """ 2132 return utils.run('crossystem fwid').stdout.strip() 2133 2134 2135def get_hardware_revision(): 2136 """Get the hardware revision as strings. 2137 2138 @returns a string representing this host's hardware revision. 2139 """ 2140 command = 'mosys platform version' 2141 result = utils.run(command, ignore_status=True) 2142 if result.exit_status != 0: 2143 return '' 2144 return result.stdout.strip() 2145 2146 2147def get_kernel_version(): 2148 """Get the kernel version as strings. 2149 2150 @returns a string representing this host's kernel version. 2151 """ 2152 return utils.run('uname -r').stdout.strip() 2153 2154 2155def get_cpu_name(): 2156 """Get the cpu name as strings. 2157 2158 @returns a string representing this host's cpu name. 2159 """ 2160 2161 # Try get cpu name from device tree first 2162 if os.path.exists("/proc/device-tree/compatible"): 2163 command = "sed -e 's/\\x0/\\n/g' /proc/device-tree/compatible | tail -1" 2164 return utils.run(command).stdout.strip().replace(',', ' ') 2165 2166 2167 # Get cpu name from uname -p 2168 command = "uname -p" 2169 ret = utils.run(command).stdout.strip() 2170 2171 # 'uname -p' return variant of unknown or amd64 or x86_64 or i686 2172 # Try get cpu name from /proc/cpuinfo instead 2173 if re.match("unknown|amd64|[ix][0-9]?86(_64)?", ret, re.IGNORECASE): 2174 command = "grep model.name /proc/cpuinfo | cut -f 2 -d: | head -1" 2175 ret = utils.run(command).stdout.strip() 2176 2177 # Remove bloat from CPU name, for example 2178 # 'Intel(R) Core(TM) i5-7Y57 CPU @ 1.20GHz' -> 'Intel Core i5-7Y57' 2179 # 'Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz' -> 'Intel Xeon E5-2690 v4' 2180 # 'AMD A10-7850K APU with Radeon(TM) R7 Graphics' -> 'AMD A10-7850K' 2181 # 'AMD GX-212JC SOC with Radeon(TM) R2E Graphics' -> 'AMD GX-212JC' 2182 trim_re = " (@|processor|apu|soc|radeon).*|\(.*?\)| cpu" 2183 return re.sub(trim_re, '', ret, flags=re.IGNORECASE) 2184 2185 2186def get_screen_resolution(): 2187 """Get the screen(s) resolution as strings. 2188 In case of more than 1 monitor, return resolution for each monitor separate 2189 with plus sign. 2190 2191 @returns a string representing this host's screen(s) resolution. 2192 """ 2193 command = 'for f in /sys/class/drm/*/*/modes; do head -1 $f; done' 2194 ret = utils.run(command, ignore_status=True) 2195 # We might have Chromebox without a screen 2196 if ret.exit_status != 0: 2197 return '' 2198 return ret.stdout.strip().replace('\n', '+') 2199 2200 2201def get_board_with_frequency_and_memory(): 2202 """ 2203 Returns a board name modified with CPU frequency and memory size to 2204 differentiate between different board variants. For instance 2205 link -> link_1.8GHz_4GB. 2206 """ 2207 board_name = get_board() 2208 if is_virtual_machine(): 2209 board = '%s_VM' % board_name 2210 else: 2211 memory = get_mem_total_gb() 2212 # Convert frequency to GHz with 1 digit accuracy after the 2213 # decimal point. 2214 frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1 2215 board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory) 2216 return board 2217 2218 2219def get_mem_total(): 2220 """ 2221 Returns the total memory available in the system in MBytes. 2222 """ 2223 mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB') 2224 # Sanity check, all Chromebooks have at least 1GB of memory. 2225 assert mem_total > 256 * 1024, 'Unreasonable amount of memory.' 2226 return mem_total / 1024 2227 2228 2229def get_mem_total_gb(): 2230 """ 2231 Returns the total memory available in the system in GBytes. 2232 """ 2233 return int(round(get_mem_total() / 1024.0)) 2234 2235 2236def get_mem_free(): 2237 """ 2238 Returns the currently free memory in the system in MBytes. 2239 """ 2240 mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB') 2241 return mem_free / 1024 2242 2243def get_mem_free_plus_buffers_and_cached(): 2244 """ 2245 Returns the free memory in MBytes, counting buffers and cached as free. 2246 2247 This is most often the most interesting number since buffers and cached 2248 memory can be reclaimed on demand. Note however, that there are cases 2249 where this as misleading as well, for example used tmpfs space 2250 count as Cached but can not be reclaimed on demand. 2251 See https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt. 2252 """ 2253 free_mb = get_mem_free() 2254 cached_mb = (_get_float_from_file( 2255 _MEMINFO, 'Cached:', 'Cached:', ' kB') / 1024) 2256 buffers_mb = (_get_float_from_file( 2257 _MEMINFO, 'Buffers:', 'Buffers:', ' kB') / 1024) 2258 return free_mb + buffers_mb + cached_mb 2259 2260def get_kernel_max(): 2261 """ 2262 Returns content of kernel_max. 2263 """ 2264 kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None) 2265 # Sanity check. 2266 assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.' 2267 return kernel_max 2268 2269 2270def get_dirty_writeback_centisecs(): 2271 """ 2272 Reads /proc/sys/vm/dirty_writeback_centisecs. 2273 """ 2274 time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None) 2275 return time 2276 2277 2278def set_dirty_writeback_centisecs(time=60000): 2279 """ 2280 In hundredths of a second, this is how often pdflush wakes up to write data 2281 to disk. The default wakes up the two (or more) active threads every five 2282 seconds. The ChromeOS default is 10 minutes. 2283 2284 We use this to set as low as 1 second to flush error messages in system 2285 logs earlier to disk. 2286 """ 2287 # Flush buffers first to make this function synchronous. 2288 utils.system('sync') 2289 if time >= 0: 2290 cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS) 2291 utils.system(cmd) 2292 2293 2294def wflinfo_cmd(): 2295 """ 2296 Returns a wflinfo command appropriate to the current graphics platform/api. 2297 """ 2298 return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api()) 2299 2300 2301def has_mali(): 2302 """ @return: True if system has a Mali GPU enabled.""" 2303 return os.path.exists('/dev/mali0') 2304 2305def get_gpu_family(): 2306 """Returns the GPU family name.""" 2307 global pciid_to_amd_architecture 2308 global pciid_to_intel_architecture 2309 2310 socfamily = get_cpu_soc_family() 2311 if socfamily == 'exynos5' or socfamily == 'rockchip' or has_mali(): 2312 cmd = wflinfo_cmd() 2313 wflinfo = utils.system_output(cmd, 2314 retain_output=True, 2315 ignore_status=False) 2316 version = re.findall(r'OpenGL renderer string: ' 2317 r'Mali-T([0-9]+)', wflinfo) 2318 if version: 2319 return 'mali-t%s' % version[0] 2320 return 'mali-unrecognized' 2321 if socfamily == 'tegra': 2322 return 'tegra' 2323 if socfamily == 'qualcomm': 2324 return 'qualcomm' 2325 if os.path.exists('/sys/kernel/debug/pvr'): 2326 return 'rogue' 2327 2328 pci_vga_device = utils.run("lspci | grep VGA").stdout.rstrip('\n') 2329 bus_device_function = pci_vga_device.partition(' ')[0] 2330 pci_path = '/sys/bus/pci/devices/0000:' + bus_device_function + '/device' 2331 2332 if not os.path.exists(pci_path): 2333 raise error.TestError('PCI device 0000:' + bus_device_function + ' not found') 2334 2335 device_id = utils.read_one_line(pci_path).lower() 2336 2337 if "Advanced Micro Devices" in pci_vga_device: 2338 if not pciid_to_amd_architecture: 2339 with open(_AMD_PCI_IDS_FILE_PATH, 'r') as in_f: 2340 pciid_to_amd_architecture = json.load(in_f) 2341 2342 return pciid_to_amd_architecture[device_id] 2343 2344 if "Intel Corporation" in pci_vga_device: 2345 # Only load Intel PCI ID file once and only if necessary. 2346 if not pciid_to_intel_architecture: 2347 with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f: 2348 pciid_to_intel_architecture = json.load(in_f) 2349 2350 return pciid_to_intel_architecture[device_id] 2351 2352# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE 2353# for sanity check, but usage seems a bit inconsistent. See 2354# src/third_party/chromiumos-overlay/eclass/appid.eclass 2355_BOARDS_WITHOUT_MONITOR = [ 2356 'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus', 2357 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 'veyron_rialto' 2358] 2359 2360 2361def has_no_monitor(): 2362 """Returns whether a machine doesn't have a built-in monitor.""" 2363 board_name = get_board() 2364 if board_name in _BOARDS_WITHOUT_MONITOR: 2365 return True 2366 2367 return False 2368 2369 2370def get_fixed_dst_drive(): 2371 """ 2372 Return device name for internal disk. 2373 Example: return /dev/sda for falco booted from usb 2374 """ 2375 cmd = ' '.join(['. /usr/sbin/write_gpt.sh;', 2376 '. /usr/share/misc/chromeos-common.sh;', 2377 'load_base_vars;', 2378 'get_fixed_dst_drive']) 2379 return utils.system_output(cmd) 2380 2381 2382def get_root_device(): 2383 """ 2384 Return root device. 2385 Will return correct disk device even system boot from /dev/dm-0 2386 Example: return /dev/sdb for falco booted from usb 2387 """ 2388 return utils.system_output('rootdev -s -d') 2389 2390 2391def get_other_device(): 2392 """ 2393 Return the non root devices. 2394 Will return a list of other block devices, that are not the root device. 2395 """ 2396 2397 cmd = 'lsblk -dpn -o NAME | grep -v loop | grep -v zram' 2398 devs = utils.system_output(cmd).splitlines() 2399 2400 for dev in devs[:]: 2401 if not re.match(r'/dev/(sd[a-z]|mmcblk[0-9]+|nvme[0-9]+)p?[0-9]*', dev): 2402 devs.remove(dev) 2403 if dev == get_root_device(): 2404 devs.remove(dev) 2405 return devs 2406 2407 2408def get_root_partition(): 2409 """ 2410 Return current root partition 2411 Example: return /dev/sdb3 for falco booted from usb 2412 """ 2413 return utils.system_output('rootdev -s') 2414 2415 2416def get_free_root_partition(root_part=None): 2417 """ 2418 Return currently unused root partion 2419 Example: return /dev/sdb5 for falco booted from usb 2420 2421 @param root_part: cuurent root partition 2422 """ 2423 spare_root_map = {'3': '5', '5': '3'} 2424 if not root_part: 2425 root_part = get_root_partition() 2426 return root_part[:-1] + spare_root_map[root_part[-1]] 2427 2428 2429def get_kernel_partition(root_part=None): 2430 """ 2431 Return current kernel partition 2432 Example: return /dev/sda2 for falco booted from usb 2433 2434 @param root_part: current root partition 2435 """ 2436 if not root_part: 2437 root_part = get_root_partition() 2438 current_kernel_map = {'3': '2', '5': '4'} 2439 return root_part[:-1] + current_kernel_map[root_part[-1]] 2440 2441 2442def get_free_kernel_partition(root_part=None): 2443 """ 2444 return currently unused kernel partition 2445 Example: return /dev/sda4 for falco booted from usb 2446 2447 @param root_part: current root partition 2448 """ 2449 kernel_part = get_kernel_partition(root_part) 2450 spare_kernel_map = {'2': '4', '4': '2'} 2451 return kernel_part[:-1] + spare_kernel_map[kernel_part[-1]] 2452 2453 2454def is_booted_from_internal_disk(): 2455 """Return True if boot from internal disk. False, otherwise.""" 2456 return get_root_device() == get_fixed_dst_drive() 2457 2458 2459def get_ui_use_flags(): 2460 """Parses the USE flags as listed in /etc/ui_use_flags.txt. 2461 2462 @return: A list of flag strings found in the ui use flags file. 2463 """ 2464 flags = [] 2465 for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines(): 2466 # Removes everything after the '#'. 2467 flag_before_comment = flag.split('#')[0].strip() 2468 if len(flag_before_comment) != 0: 2469 flags.append(flag_before_comment) 2470 2471 return flags 2472 2473 2474def graphics_platform(): 2475 """ 2476 Return a string identifying the graphics platform, 2477 e.g. 'glx' or 'x11_egl' or 'gbm' 2478 """ 2479 return 'null' 2480 2481 2482def graphics_api(): 2483 """Return a string identifying the graphics api, e.g. gl or gles2.""" 2484 use_flags = get_ui_use_flags() 2485 if 'opengles' in use_flags: 2486 return 'gles2' 2487 return 'gl' 2488 2489 2490def is_package_installed(package): 2491 """Check if a package is installed already. 2492 2493 @return: True if the package is already installed, otherwise return False. 2494 """ 2495 try: 2496 utils.run(_CHECK_PACKAGE_INSTALLED_COMMAND % package) 2497 return True 2498 except error.CmdError: 2499 logging.warn('Package %s is not installed.', package) 2500 return False 2501 2502 2503def is_python_package_installed(package): 2504 """Check if a Python package is installed already. 2505 2506 @return: True if the package is already installed, otherwise return False. 2507 """ 2508 try: 2509 __import__(package) 2510 return True 2511 except ImportError: 2512 logging.warn('Python package %s is not installed.', package) 2513 return False 2514 2515 2516def run_sql_cmd(server, user, password, command, database=''): 2517 """Run the given sql command against the specified database. 2518 2519 @param server: Hostname or IP address of the MySQL server. 2520 @param user: User name to log in the MySQL server. 2521 @param password: Password to log in the MySQL server. 2522 @param command: SQL command to run. 2523 @param database: Name of the database to run the command. Default to empty 2524 for command that does not require specifying database. 2525 2526 @return: The stdout of the command line. 2527 """ 2528 cmd = ('mysql -u%s -p%s --host %s %s -e "%s"' % 2529 (user, password, server, database, command)) 2530 # Set verbose to False so the command line won't be logged, as it includes 2531 # database credential. 2532 return utils.run(cmd, verbose=False).stdout 2533 2534 2535def strip_non_printable(s): 2536 """Strip non printable characters from string. 2537 2538 @param s: Input string 2539 2540 @return: The input string with only printable characters. 2541 """ 2542 return ''.join(x for x in s if x in string.printable) 2543 2544 2545def recursive_func(obj, func, types, sequence_types=(list, tuple, set), 2546 dict_types=(dict,), fix_num_key=False): 2547 """Apply func to obj recursively. 2548 2549 This function traverses recursively through any sequence-like and 2550 dict-like elements in obj. 2551 2552 @param obj: the object to apply the function func recursively. 2553 @param func: the function to invoke. 2554 @param types: the target types in the object to apply func. 2555 @param sequence_types: the sequence types in python. 2556 @param dict_types: the dict types in python. 2557 @param fix_num_key: to indicate if the key of a dict should be 2558 converted from str type to a number, int or float, type. 2559 It is a culprit of json that it always treats the key of 2560 a dict as string. 2561 Refer to https://docs.python.org/2/library/json.html 2562 for more information. 2563 2564 @return: the result object after applying the func recursively. 2565 """ 2566 def ancestors(obj, types): 2567 """Any ancestor of the object class is a subclass of the types? 2568 2569 @param obj: the object to apply the function func. 2570 @param types: the target types of the object. 2571 2572 @return: True if any ancestor class of the obj is found in types; 2573 False otherwise. 2574 """ 2575 return any([issubclass(anc, types) for anc in type(obj).__mro__]) 2576 2577 if isinstance(obj, sequence_types) or ancestors(obj, sequence_types): 2578 result_lst = [recursive_func(elm, func, types, fix_num_key=fix_num_key) 2579 for elm in obj] 2580 # Convert the result list to the object's original sequence type. 2581 return type(obj)(result_lst) 2582 elif isinstance(obj, dict_types) or ancestors(obj, dict_types): 2583 result_lst = [ 2584 (recursive_func(key, func, types, fix_num_key=fix_num_key), 2585 recursive_func(value, func, types, fix_num_key=fix_num_key)) 2586 for (key, value) in obj.items()] 2587 # Convert the result list to the object's original dict type. 2588 return type(obj)(result_lst) 2589 # Here are the basic types. 2590 elif isinstance(obj, types) or ancestors(obj, types): 2591 if fix_num_key: 2592 # Check if this is a int or float 2593 try: 2594 result_obj = int(obj) 2595 return result_obj 2596 except ValueError: 2597 try: 2598 result_obj = float(obj) 2599 return result_obj 2600 except ValueError: 2601 pass 2602 2603 result_obj = func(obj) 2604 return result_obj 2605 else: 2606 return obj 2607 2608 2609def base64_recursive_encode(obj): 2610 """Apply base64 encode recursively into the obj structure. 2611 2612 Most of the string-like types could be traced to basestring and bytearray 2613 as follows: 2614 str: basestring 2615 bytes: basestring 2616 dbus.String: basestring 2617 dbus.Signature: basestring 2618 dbus.ByteArray: basestring 2619 2620 Note that all the above types except dbus.String could be traced back to 2621 str. In order to cover dbus.String, basestring is used as the ancestor 2622 class for string-like types. 2623 2624 The other type that needs encoding with base64 in a structure includes 2625 bytearray: bytearray 2626 2627 The sequence types include (list, tuple, set). The dbus.Array is also 2628 covered as 2629 dbus.Array: list 2630 2631 The base dictionary type is dict. The dbus.Dictionary is also covered as 2632 dbus.Dictionary: dict 2633 2634 An example code and output look like 2635 obj = {'a': 10, 'b': 'hello', 2636 'c': [100, 200, bytearray(b'\xf0\xf1\xf2\xf3\xf4')], 2637 'd': {784: bytearray(b'@\x14\x01P'), 2638 78.0: bytearray(b'\x10\x05\x0b\x10\xb2\x1b\x00')}} 2639 encode_obj = base64_recursive_encode(obj) 2640 decode_obj = base64_recursive_decode(encode_obj) 2641 2642 print 'obj: ', obj 2643 print 'encode_obj: ', encode_obj 2644 print 'decode_obj: ', decode_obj 2645 print 'Equal?', obj == decode_obj 2646 2647 Output: 2648 obj: {'a': 10, 2649 'c': [100, 200, bytearray(b'\xf0\xf1\xf2\xf3\xf4')], 2650 'b': 'hello', 2651 'd': {784: bytearray(b'@\x14\x01P'), 2652 78.0: bytearray(b'\x10\x05\x0b\x10\xb2\x1b\x00')}} 2653 2654 encode_obj: {'YQ==': 10, 2655 'Yw==': [100, 200, '8PHy8/Q='], 2656 'Yg==': 'aGVsbG8=' 2657 'ZA==': {784: 'QBQBUA==', 78.0: 'EAULELIbAA=='}} 2658 decode_obj: {'a': 10, 2659 'c': [100, 200, '\xf0\xf1\xf2\xf3\xf4'], 2660 'b': 'hello', 2661 'd': {784: '@\x14\x01P', 2662 78.0: '\x10\x05\x0b\x10\xb2\x1b\x00'}} 2663 Equal? True 2664 2665 @param obj: the object to apply base64 encoding recursively. 2666 2667 @return: the base64 encoded object. 2668 """ 2669 encode_types = (basestring, bytearray) 2670 return recursive_func(obj, base64.standard_b64encode, encode_types) 2671 2672 2673def base64_recursive_decode(obj): 2674 """Apply base64 decode recursively into the obj structure. 2675 2676 @param obj: the object to apply base64 decoding recursively. 2677 2678 @return: the base64 decoded object. 2679 """ 2680 decode_types = (basestring,) 2681 return recursive_func(obj, base64.standard_b64decode, decode_types, 2682 fix_num_key=True) 2683