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