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