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