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