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