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