blob: 615e8b2ad19aa7d5dfdb323eba78ae8928fb1a05 [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.
1738 if temperature < 50:
1739 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
1761# System paths for machine performance state.
1762_CPUINFO = '/proc/cpuinfo'
1763_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs'
1764_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
1765_MEMINFO = '/proc/meminfo'
1766_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)'
1767
Kristoffer Erlandsson1ad7db02017-11-01 11:28:44 +01001768def _open_file(path):
1769 """
1770 Opens a file and returns the file object.
1771
1772 This method is intended to be mocked by tests.
1773 @return The open file object.
1774 """
1775 return open(path)
Allen Li2c32d6b2017-02-03 15:28:10 -08001776
1777def _get_line_from_file(path, line):
1778 """
1779 line can be an integer or
1780 line can be a string that matches the beginning of the line
1781 """
Kristoffer Erlandsson1ad7db02017-11-01 11:28:44 +01001782 with _open_file(path) as f:
Allen Li2c32d6b2017-02-03 15:28:10 -08001783 if isinstance(line, int):
1784 l = f.readline()
1785 for _ in range(0, line):
1786 l = f.readline()
1787 return l
1788 else:
1789 for l in f:
1790 if l.startswith(line):
1791 return l
1792 return None
1793
1794
1795def _get_match_from_file(path, line, prefix, postfix):
1796 """
1797 Matches line in path and returns string between first prefix and postfix.
1798 """
1799 match = _get_line_from_file(path, line)
1800 # Strip everything from front of line including prefix.
1801 if prefix:
1802 match = re.split(prefix, match)[1]
1803 # Strip everything from back of string including first occurence of postfix.
1804 if postfix:
1805 match = re.split(postfix, match)[0]
1806 return match
1807
1808
1809def _get_float_from_file(path, line, prefix, postfix):
1810 match = _get_match_from_file(path, line, prefix, postfix)
1811 return float(match)
1812
1813
1814def _get_int_from_file(path, line, prefix, postfix):
1815 match = _get_match_from_file(path, line, prefix, postfix)
1816 return int(match)
1817
1818
1819def _get_hex_from_file(path, line, prefix, postfix):
1820 match = _get_match_from_file(path, line, prefix, postfix)
1821 return int(match, 16)
1822
1823
1824# The paths don't change. Avoid running find all the time.
1825_hwmon_paths = None
1826
1827def _get_hwmon_paths(file_pattern):
1828 """
1829 Returns a list of paths to the temperature sensors.
1830 """
1831 # Some systems like daisy_spring only have the virtual hwmon.
1832 # And other systems like rambi only have coretemp.0. See crbug.com/360249.
1833 # /sys/class/hwmon/hwmon*/
1834 # /sys/devices/virtual/hwmon/hwmon*/
1835 # /sys/devices/platform/coretemp.0/
1836 if not _hwmon_paths:
1837 cmd = 'find /sys/ -name "' + file_pattern + '"'
1838 _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines()
1839 return _hwon_paths
1840
1841
1842def get_temperature_critical():
1843 """
1844 Returns temperature at which we will see some throttling in the system.
1845 """
1846 min_temperature = 1000.0
1847 paths = _get_hwmon_paths('temp*_crit')
1848 for path in paths:
1849 temperature = _get_float_from_file(path, 0, None, None) * 0.001
Pallavi Gulavaniba8f57e2018-10-05 16:33:13 +05301850 # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to 98
1851 # if Intel device or the lowest known value otherwise.
1852 result = utils.system_output('crossystem arch', retain_output=True,
1853 ignore_status=True)
Allen Li2c32d6b2017-02-03 15:28:10 -08001854 if (min_temperature < 60.0) or min_temperature > 150.0:
Pallavi Gulavaniba8f57e2018-10-05 16:33:13 +05301855 if 'x86' in result:
1856 min_temperature = 98.0
1857 else:
1858 min_temperature = 85.0
1859 logging.warning('Critical temperature was reset to %.1fC.',
Allen Li2c32d6b2017-02-03 15:28:10 -08001860 min_temperature)
Allen Li2c32d6b2017-02-03 15:28:10 -08001861
1862 min_temperature = min(temperature, min_temperature)
1863 return min_temperature
1864
1865
1866def get_temperature_input_max():
1867 """
1868 Returns the maximum currently observed temperature.
1869 """
1870 max_temperature = -1000.0
1871 paths = _get_hwmon_paths('temp*_input')
1872 for path in paths:
1873 temperature = _get_float_from_file(path, 0, None, None) * 0.001
1874 max_temperature = max(temperature, max_temperature)
1875 return max_temperature
1876
1877
1878def get_thermal_zone_temperatures():
1879 """
1880 Returns the maximum currently observered temperature in thermal_zones.
1881 """
1882 temperatures = []
1883 for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'):
1884 try:
1885 temperatures.append(
1886 _get_float_from_file(path, 0, None, None) * 0.001)
1887 except IOError:
1888 # Some devices (e.g. Veyron) may have reserved thermal zones that
1889 # are not active. Trying to read the temperature value would cause a
1890 # EINVAL IO error.
1891 continue
1892 return temperatures
1893
1894
1895def get_ec_temperatures():
1896 """
1897 Uses ectool to return a list of all sensor temperatures in Celsius.
Nicolas Boichateebd1222017-11-10 10:21:42 -08001898
1899 Output from ectool is either '0: 300' or '0: 300 K' (newer ectool
1900 includes the unit).
Allen Li2c32d6b2017-02-03 15:28:10 -08001901 """
1902 temperatures = []
1903 try:
1904 full_cmd = 'ectool temps all'
1905 lines = utils.run(full_cmd, verbose=False).stdout.splitlines()
Nicolas Boichateebd1222017-11-10 10:21:42 -08001906 pattern = re.compile('.*: (\d+)')
Allen Li2c32d6b2017-02-03 15:28:10 -08001907 for line in lines:
Nicolas Boichateebd1222017-11-10 10:21:42 -08001908 matched = pattern.match(line)
1909 temperature = int(matched.group(1)) - 273
Allen Li2c32d6b2017-02-03 15:28:10 -08001910 temperatures.append(temperature)
1911 except Exception:
1912 logging.warning('Unable to read temperature sensors using ectool.')
1913 for temperature in temperatures:
1914 # Sanity check for real world values.
1915 assert ((temperature > 10.0) and
1916 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
1917 temperature)
1918
1919 return temperatures
1920
1921
1922def get_current_temperature_max():
1923 """
1924 Returns the highest reported board temperature (all sensors) in Celsius.
1925 """
1926 temperature = max([get_temperature_input_max()] +
1927 get_thermal_zone_temperatures() +
1928 get_ec_temperatures())
1929 # Sanity check for real world values.
1930 assert ((temperature > 10.0) and
1931 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
1932 temperature)
1933 return temperature
1934
1935
1936def get_cpu_cache_size():
1937 """
1938 Returns the last level CPU cache size in kBytes.
1939 """
1940 cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB')
1941 # Sanity check.
1942 assert cache_size >= 64, 'Unreasonably small cache.'
1943 return cache_size
1944
1945
1946def get_cpu_model_frequency():
1947 """
1948 Returns the model frequency from the CPU model name on Intel only. This
1949 might be redundant with get_cpu_max_frequency. Unit is Hz.
1950 """
1951 frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz')
1952 return 1.e9 * frequency
1953
1954
1955def get_cpu_max_frequency():
1956 """
1957 Returns the largest of the max CPU core frequencies. The unit is Hz.
1958 """
1959 max_frequency = -1
Alex Khouderchah19e0ab32018-07-06 17:03:21 -07001960 paths = utils._get_cpufreq_paths('cpuinfo_max_freq')
Brian Norris951f3572019-01-16 10:13:03 -08001961 if not paths:
1962 raise ValueError('Could not find max freq; is cpufreq supported?')
Allen Li2c32d6b2017-02-03 15:28:10 -08001963 for path in paths:
Brian Norris951f3572019-01-16 10:13:03 -08001964 try:
1965 # Convert from kHz to Hz.
1966 frequency = 1000 * _get_float_from_file(path, 0, None, None)
1967 # CPUs may come and go. A missing entry or two aren't critical.
1968 except IOError:
1969 continue
Allen Li2c32d6b2017-02-03 15:28:10 -08001970 max_frequency = max(frequency, max_frequency)
1971 # Sanity check.
Brian Norris951f3572019-01-16 10:13:03 -08001972 assert max_frequency > 1e8, ('Unreasonably low CPU frequency: %.1f' %
1973 max_frequency)
Allen Li2c32d6b2017-02-03 15:28:10 -08001974 return max_frequency
1975
1976
Allen Li2c32d6b2017-02-03 15:28:10 -08001977def get_cpu_model():
1978 """
1979 Returns the CPU model.
1980 Only works on Intel.
1981 """
1982 cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None)
1983 return cpu_model
1984
1985
Tom Hughese3bf5d62019-02-27 14:08:57 -08001986def get_cpu_family_intel():
Allen Li2c32d6b2017-02-03 15:28:10 -08001987 """
1988 Returns the CPU family.
1989 Only works on Intel.
1990 """
1991 cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None)
1992 return cpu_family
1993
1994
1995def get_board_property(key):
1996 """
1997 Get a specific property from /etc/lsb-release.
1998
1999 @param key: board property to return value for
2000
2001 @return the value or '' if not present
2002 """
2003 with open('/etc/lsb-release') as f:
2004 pattern = '%s=(.*)' % key
2005 pat = re.search(pattern, f.read())
2006 if pat:
2007 return pat.group(1)
2008 return ''
2009
2010
2011def get_board():
2012 """
2013 Get the ChromeOS release board name from /etc/lsb-release.
2014 """
2015 return get_board_property('BOARD')
2016
2017
2018def get_board_type():
2019 """
2020 Get the ChromeOS board type from /etc/lsb-release.
2021
2022 @return device type.
2023 """
2024 return get_board_property('DEVICETYPE')
2025
2026
Todd Broch2017ffc2018-07-10 08:49:26 -07002027def get_platform():
2028 """
2029 Get the ChromeOS platform name.
2030
2031 For unibuild this should be equal to model name. For non-unibuild
2032 it will either be board name or empty string. In the case of
2033 empty string return board name to match equivalent logic in
2034 server/hosts/cros_host.py
2035
2036 @returns platform name
2037 """
2038 platform = ''
2039 command = 'mosys platform model'
2040 result = utils.run(command, ignore_status=True)
2041 if result.exit_status == 0:
2042 platform = result.stdout.strip()
2043
2044 if platform == '':
2045 platform = get_board()
2046 return platform
2047
2048
Puthikorn Voravootivat0f6d2302017-11-27 16:31:00 -08002049def get_ec_version():
2050 """Get the ec version as strings.
2051
2052 @returns a string representing this host's ec version.
2053 """
Puthikorn Voravootivat65ad7672018-01-30 14:00:01 -08002054 command = 'mosys ec info -s fw_version'
Puthikorn Voravootivat720640d2018-02-16 17:32:38 -08002055 result = utils.run(command, ignore_status=True)
2056 if result.exit_status != 0:
2057 return ''
2058 return result.stdout.strip()
Puthikorn Voravootivat0f6d2302017-11-27 16:31:00 -08002059
2060
2061def get_firmware_version():
2062 """Get the firmware version as strings.
2063
2064 @returns a string representing this host's firmware version.
2065 """
2066 return utils.run('crossystem fwid').stdout.strip()
2067
2068
2069def get_hardware_revision():
2070 """Get the hardware revision as strings.
2071
2072 @returns a string representing this host's hardware revision.
2073 """
Puthikorn Voravootivat65ad7672018-01-30 14:00:01 -08002074 command = 'mosys platform version'
Puthikorn Voravootivat720640d2018-02-16 17:32:38 -08002075 result = utils.run(command, ignore_status=True)
2076 if result.exit_status != 0:
2077 return ''
2078 return result.stdout.strip()
Puthikorn Voravootivat0f6d2302017-11-27 16:31:00 -08002079
2080
2081def get_kernel_version():
2082 """Get the kernel version as strings.
2083
2084 @returns a string representing this host's kernel version.
2085 """
2086 return utils.run('uname -r').stdout.strip()
2087
2088
Puthikorn Voravootivata83e82c2018-02-15 17:49:45 -08002089def get_cpu_name():
2090 """Get the cpu name as strings.
2091
2092 @returns a string representing this host's cpu name.
2093 """
2094
2095 # Try get cpu name from device tree first
2096 if os.path.exists("/proc/device-tree/compatible"):
2097 command = "sed -e 's/\\x0/\\n/g' /proc/device-tree/compatible | tail -1"
2098 return utils.run(command).stdout.strip().replace(',', ' ')
2099
2100
2101 # Get cpu name from uname -p
2102 command = "uname -p"
2103 ret = utils.run(command).stdout.strip()
2104
2105 # 'uname -p' return variant of unknown or amd64 or x86_64 or i686
2106 # Try get cpu name from /proc/cpuinfo instead
2107 if re.match("unknown|amd64|[ix][0-9]?86(_64)?", ret, re.IGNORECASE):
2108 command = "grep model.name /proc/cpuinfo | cut -f 2 -d: | head -1"
2109 ret = utils.run(command).stdout.strip()
2110
2111 # Remove bloat from CPU name, for example
2112 # 'Intel(R) Core(TM) i5-7Y57 CPU @ 1.20GHz' -> 'Intel Core i5-7Y57'
2113 # 'Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60GHz' -> 'Intel Xeon E5-2690 v4'
2114 # 'AMD A10-7850K APU with Radeon(TM) R7 Graphics' -> 'AMD A10-7850K'
2115 # 'AMD GX-212JC SOC with Radeon(TM) R2E Graphics' -> 'AMD GX-212JC'
2116 trim_re = " (@|processor|apu|soc|radeon).*|\(.*?\)| cpu"
2117 return re.sub(trim_re, '', ret, flags=re.IGNORECASE)
2118
2119
2120def get_screen_resolution():
2121 """Get the screen(s) resolution as strings.
2122 In case of more than 1 monitor, return resolution for each monitor separate
2123 with plus sign.
2124
2125 @returns a string representing this host's screen(s) resolution.
2126 """
2127 command = 'for f in /sys/class/drm/*/*/modes; do head -1 $f; done'
2128 ret = utils.run(command, ignore_status=True)
2129 # We might have Chromebox without a screen
2130 if ret.exit_status != 0:
2131 return ''
2132 return ret.stdout.strip().replace('\n', '+')
2133
2134
Allen Li2c32d6b2017-02-03 15:28:10 -08002135def get_board_with_frequency_and_memory():
2136 """
2137 Returns a board name modified with CPU frequency and memory size to
2138 differentiate between different board variants. For instance
2139 link -> link_1.8GHz_4GB.
2140 """
2141 board_name = get_board()
2142 if is_virtual_machine():
2143 board = '%s_VM' % board_name
2144 else:
Puthikorn Voravootivata83e82c2018-02-15 17:49:45 -08002145 memory = get_mem_total_gb()
Allen Li2c32d6b2017-02-03 15:28:10 -08002146 # Convert frequency to GHz with 1 digit accuracy after the
2147 # decimal point.
2148 frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1
2149 board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory)
2150 return board
2151
2152
2153def get_mem_total():
2154 """
2155 Returns the total memory available in the system in MBytes.
2156 """
2157 mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB')
2158 # Sanity check, all Chromebooks have at least 1GB of memory.
2159 assert mem_total > 256 * 1024, 'Unreasonable amount of memory.'
2160 return mem_total / 1024
2161
2162
Puthikorn Voravootivata83e82c2018-02-15 17:49:45 -08002163def get_mem_total_gb():
2164 """
2165 Returns the total memory available in the system in GBytes.
2166 """
2167 return int(round(get_mem_total() / 1024.0))
2168
2169
Allen Li2c32d6b2017-02-03 15:28:10 -08002170def get_mem_free():
2171 """
2172 Returns the currently free memory in the system in MBytes.
2173 """
2174 mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB')
2175 return mem_free / 1024
2176
Kristoffer Erlandssonb84da2b2017-11-01 21:29:24 +01002177def get_mem_free_plus_buffers_and_cached():
2178 """
2179 Returns the free memory in MBytes, counting buffers and cached as free.
2180
2181 This is most often the most interesting number since buffers and cached
2182 memory can be reclaimed on demand. Note however, that there are cases
2183 where this as misleading as well, for example used tmpfs space
2184 count as Cached but can not be reclaimed on demand.
2185 See https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt.
2186 """
2187 free_mb = get_mem_free()
2188 cached_mb = (_get_float_from_file(
2189 _MEMINFO, 'Cached:', 'Cached:', ' kB') / 1024)
2190 buffers_mb = (_get_float_from_file(
2191 _MEMINFO, 'Buffers:', 'Buffers:', ' kB') / 1024)
2192 return free_mb + buffers_mb + cached_mb
Allen Li2c32d6b2017-02-03 15:28:10 -08002193
2194def get_kernel_max():
2195 """
2196 Returns content of kernel_max.
2197 """
2198 kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None)
2199 # Sanity check.
2200 assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.'
2201 return kernel_max
2202
2203
Allen Li2c32d6b2017-02-03 15:28:10 -08002204def get_dirty_writeback_centisecs():
2205 """
2206 Reads /proc/sys/vm/dirty_writeback_centisecs.
2207 """
2208 time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None)
2209 return time
2210
2211
2212def set_dirty_writeback_centisecs(time=60000):
2213 """
2214 In hundredths of a second, this is how often pdflush wakes up to write data
2215 to disk. The default wakes up the two (or more) active threads every five
2216 seconds. The ChromeOS default is 10 minutes.
2217
2218 We use this to set as low as 1 second to flush error messages in system
2219 logs earlier to disk.
2220 """
2221 # Flush buffers first to make this function synchronous.
2222 utils.system('sync')
2223 if time >= 0:
2224 cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS)
2225 utils.system(cmd)
2226
2227
2228def wflinfo_cmd():
2229 """
2230 Returns a wflinfo command appropriate to the current graphics platform/api.
2231 """
2232 return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api())
2233
2234
2235def has_mali():
2236 """ @return: True if system has a Mali GPU enabled."""
2237 return os.path.exists('/dev/mali0')
2238
2239def get_gpu_family():
2240 """Returns the GPU family name."""
2241 global pciid_to_amd_architecture
2242 global pciid_to_intel_architecture
2243
2244 socfamily = get_cpu_soc_family()
2245 if socfamily == 'exynos5' or socfamily == 'rockchip' or has_mali():
2246 cmd = wflinfo_cmd()
2247 wflinfo = utils.system_output(cmd,
2248 retain_output=True,
2249 ignore_status=False)
2250 version = re.findall(r'OpenGL renderer string: '
2251 r'Mali-T([0-9]+)', wflinfo)
2252 if version:
2253 return 'mali-t%s' % version[0]
2254 return 'mali-unrecognized'
2255 if socfamily == 'tegra':
2256 return 'tegra'
Douglas Anderson7978d022018-10-02 14:38:49 -07002257 if socfamily == 'qualcomm':
2258 return 'qualcomm'
Allen Li2c32d6b2017-02-03 15:28:10 -08002259 if os.path.exists('/sys/kernel/debug/pvr'):
2260 return 'rogue'
2261
2262 pci_vga_device = utils.run("lspci | grep VGA").stdout.rstrip('\n')
2263 bus_device_function = pci_vga_device.partition(' ')[0]
2264 pci_path = '/sys/bus/pci/devices/0000:' + bus_device_function + '/device'
2265
2266 if not os.path.exists(pci_path):
2267 raise error.TestError('PCI device 0000:' + bus_device_function + ' not found')
2268
2269 device_id = utils.read_one_line(pci_path).lower()
2270
2271 if "Advanced Micro Devices" in pci_vga_device:
2272 if not pciid_to_amd_architecture:
2273 with open(_AMD_PCI_IDS_FILE_PATH, 'r') as in_f:
2274 pciid_to_amd_architecture = json.load(in_f)
2275
2276 return pciid_to_amd_architecture[device_id]
2277
2278 if "Intel Corporation" in pci_vga_device:
2279 # Only load Intel PCI ID file once and only if necessary.
2280 if not pciid_to_intel_architecture:
2281 with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f:
2282 pciid_to_intel_architecture = json.load(in_f)
2283
2284 return pciid_to_intel_architecture[device_id]
2285
2286# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE
2287# for sanity check, but usage seems a bit inconsistent. See
2288# src/third_party/chromiumos-overlay/eclass/appid.eclass
2289_BOARDS_WITHOUT_MONITOR = [
2290 'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus',
Brian Norrisbb745382017-06-30 11:49:27 -07002291 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 'veyron_rialto'
Allen Li2c32d6b2017-02-03 15:28:10 -08002292]
2293
2294
2295def has_no_monitor():
2296 """Returns whether a machine doesn't have a built-in monitor."""
2297 board_name = get_board()
2298 if board_name in _BOARDS_WITHOUT_MONITOR:
2299 return True
2300
2301 return False
2302
2303
2304def get_fixed_dst_drive():
2305 """
2306 Return device name for internal disk.
2307 Example: return /dev/sda for falco booted from usb
2308 """
2309 cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
2310 '. /usr/share/misc/chromeos-common.sh;',
2311 'load_base_vars;',
2312 'get_fixed_dst_drive'])
2313 return utils.system_output(cmd)
2314
2315
2316def get_root_device():
2317 """
2318 Return root device.
2319 Will return correct disk device even system boot from /dev/dm-0
2320 Example: return /dev/sdb for falco booted from usb
2321 """
2322 return utils.system_output('rootdev -s -d')
2323
2324
2325def get_root_partition():
2326 """
2327 Return current root partition
2328 Example: return /dev/sdb3 for falco booted from usb
2329 """
2330 return utils.system_output('rootdev -s')
2331
2332
2333def get_free_root_partition(root_part=None):
2334 """
2335 Return currently unused root partion
2336 Example: return /dev/sdb5 for falco booted from usb
2337
2338 @param root_part: cuurent root partition
2339 """
2340 spare_root_map = {'3': '5', '5': '3'}
2341 if not root_part:
2342 root_part = get_root_partition()
2343 return root_part[:-1] + spare_root_map[root_part[-1]]
2344
2345
2346def get_kernel_partition(root_part=None):
2347 """
2348 Return current kernel partition
2349 Example: return /dev/sda2 for falco booted from usb
2350
2351 @param root_part: current root partition
2352 """
2353 if not root_part:
2354 root_part = get_root_partition()
2355 current_kernel_map = {'3': '2', '5': '4'}
2356 return root_part[:-1] + current_kernel_map[root_part[-1]]
2357
2358
2359def get_free_kernel_partition(root_part=None):
2360 """
2361 return currently unused kernel partition
2362 Example: return /dev/sda4 for falco booted from usb
2363
2364 @param root_part: current root partition
2365 """
2366 kernel_part = get_kernel_partition(root_part)
2367 spare_kernel_map = {'2': '4', '4': '2'}
2368 return kernel_part[:-1] + spare_kernel_map[kernel_part[-1]]
2369
2370
2371def is_booted_from_internal_disk():
2372 """Return True if boot from internal disk. False, otherwise."""
2373 return get_root_device() == get_fixed_dst_drive()
2374
2375
2376def get_ui_use_flags():
2377 """Parses the USE flags as listed in /etc/ui_use_flags.txt.
2378
2379 @return: A list of flag strings found in the ui use flags file.
2380 """
2381 flags = []
2382 for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines():
2383 # Removes everything after the '#'.
2384 flag_before_comment = flag.split('#')[0].strip()
2385 if len(flag_before_comment) != 0:
2386 flags.append(flag_before_comment)
2387
2388 return flags
2389
2390
Allen Li2c32d6b2017-02-03 15:28:10 -08002391def graphics_platform():
2392 """
2393 Return a string identifying the graphics platform,
2394 e.g. 'glx' or 'x11_egl' or 'gbm'
2395 """
Po-Hsien Wangdab8e182017-05-03 10:25:41 -07002396 return 'null'
Allen Li2c32d6b2017-02-03 15:28:10 -08002397
2398
2399def graphics_api():
2400 """Return a string identifying the graphics api, e.g. gl or gles2."""
2401 use_flags = get_ui_use_flags()
2402 if 'opengles' in use_flags:
2403 return 'gles2'
2404 return 'gl'
2405
2406
Allen Li2c32d6b2017-02-03 15:28:10 -08002407def is_package_installed(package):
2408 """Check if a package is installed already.
2409
2410 @return: True if the package is already installed, otherwise return False.
2411 """
2412 try:
2413 utils.run(_CHECK_PACKAGE_INSTALLED_COMMAND % package)
2414 return True
2415 except error.CmdError:
2416 logging.warn('Package %s is not installed.', package)
2417 return False
2418
2419
2420def is_python_package_installed(package):
2421 """Check if a Python package is installed already.
2422
2423 @return: True if the package is already installed, otherwise return False.
2424 """
2425 try:
2426 __import__(package)
2427 return True
2428 except ImportError:
2429 logging.warn('Python package %s is not installed.', package)
2430 return False
2431
2432
2433def run_sql_cmd(server, user, password, command, database=''):
2434 """Run the given sql command against the specified database.
2435
2436 @param server: Hostname or IP address of the MySQL server.
2437 @param user: User name to log in the MySQL server.
2438 @param password: Password to log in the MySQL server.
2439 @param command: SQL command to run.
2440 @param database: Name of the database to run the command. Default to empty
2441 for command that does not require specifying database.
2442
2443 @return: The stdout of the command line.
2444 """
2445 cmd = ('mysql -u%s -p%s --host %s %s -e "%s"' %
2446 (user, password, server, database, command))
2447 # Set verbose to False so the command line won't be logged, as it includes
2448 # database credential.
Aviv Keshetb3950442018-01-24 15:32:03 -08002449 return utils.run(cmd, verbose=False).stdout
Puthikorn Voravootivatc9f7f5f2018-10-23 18:24:34 -07002450
2451
2452def strip_non_printable(s):
2453 """Strip non printable characters from string.
2454
2455 @param s: Input string
2456
2457 @return: The input string with only printable characters.
2458 """
2459 return ''.join(x for x in s if x in string.printable)
Joseph Hwange3cb1b02018-10-26 17:50:05 +08002460
2461
2462def recursive_func(obj, func, types, sequence_types=(list, tuple, set),
2463 dict_types=(dict,), fix_num_key=False):
2464 """Apply func to obj recursively.
2465
2466 This function traverses recursively through any sequence-like and
2467 dict-like elements in obj.
2468
2469 @param obj: the object to apply the function func recursively.
2470 @param func: the function to invoke.
2471 @param types: the target types in the object to apply func.
2472 @param sequence_types: the sequence types in python.
2473 @param dict_types: the dict types in python.
2474 @param fix_num_key: to indicate if the key of a dict should be
2475 converted from str type to a number, int or float, type.
2476 It is a culprit of json that it always treats the key of
2477 a dict as string.
2478 Refer to https://docs.python.org/2/library/json.html
2479 for more information.
2480
2481 @return: the result object after applying the func recursively.
2482 """
2483 def ancestors(obj, types):
2484 """Any ancestor of the object class is a subclass of the types?
2485
2486 @param obj: the object to apply the function func.
2487 @param types: the target types of the object.
2488
2489 @return: True if any ancestor class of the obj is found in types;
2490 False otherwise.
2491 """
2492 return any([issubclass(anc, types) for anc in type(obj).__mro__])
2493
2494 if isinstance(obj, sequence_types) or ancestors(obj, sequence_types):
2495 result_lst = [recursive_func(elm, func, types, fix_num_key=fix_num_key)
2496 for elm in obj]
2497 # Convert the result list to the object's original sequence type.
2498 return type(obj)(result_lst)
2499 elif isinstance(obj, dict_types) or ancestors(obj, dict_types):
2500 result_lst = [
2501 (recursive_func(key, func, types, fix_num_key=fix_num_key),
2502 recursive_func(value, func, types, fix_num_key=fix_num_key))
2503 for (key, value) in obj.items()]
2504 # Convert the result list to the object's original dict type.
2505 return type(obj)(result_lst)
2506 # Here are the basic types.
2507 elif isinstance(obj, types) or ancestors(obj, types):
2508 if fix_num_key:
2509 # Check if this is a int or float
2510 try:
2511 result_obj = int(obj)
2512 return result_obj
2513 except ValueError:
2514 try:
2515 result_obj = float(obj)
2516 return result_obj
2517 except ValueError:
2518 pass
2519
2520 result_obj = func(obj)
2521 return result_obj
2522 else:
2523 return obj
2524
2525
2526def base64_recursive_encode(obj):
2527 """Apply base64 encode recursively into the obj structure.
2528
2529 Most of the string-like types could be traced to basestring and bytearray
2530 as follows:
2531 str: basestring
2532 bytes: basestring
2533 dbus.String: basestring
2534 dbus.Signature: basestring
2535 dbus.ByteArray: basestring
2536
2537 Note that all the above types except dbus.String could be traced back to
2538 str. In order to cover dbus.String, basestring is used as the ancestor
2539 class for string-like types.
2540
2541 The other type that needs encoding with base64 in a structure includes
2542 bytearray: bytearray
2543
2544 The sequence types include (list, tuple, set). The dbus.Array is also
2545 covered as
2546 dbus.Array: list
2547
2548 The base dictionary type is dict. The dbus.Dictionary is also covered as
2549 dbus.Dictionary: dict
2550
2551 An example code and output look like
2552 obj = {'a': 10, 'b': 'hello',
2553 'c': [100, 200, bytearray(b'\xf0\xf1\xf2\xf3\xf4')],
2554 'd': {784: bytearray(b'@\x14\x01P'),
2555 78.0: bytearray(b'\x10\x05\x0b\x10\xb2\x1b\x00')}}
2556 encode_obj = base64_recursive_encode(obj)
2557 decode_obj = base64_recursive_decode(encode_obj)
2558
2559 print 'obj: ', obj
2560 print 'encode_obj: ', encode_obj
2561 print 'decode_obj: ', decode_obj
2562 print 'Equal?', obj == decode_obj
2563
2564 Output:
2565 obj: {'a': 10,
2566 'c': [100, 200, bytearray(b'\xf0\xf1\xf2\xf3\xf4')],
2567 'b': 'hello',
2568 'd': {784: bytearray(b'@\x14\x01P'),
2569 78.0: bytearray(b'\x10\x05\x0b\x10\xb2\x1b\x00')}}
2570
2571 encode_obj: {'YQ==': 10,
2572 'Yw==': [100, 200, '8PHy8/Q='],
2573 'Yg==': 'aGVsbG8='
2574 'ZA==': {784: 'QBQBUA==', 78.0: 'EAULELIbAA=='}}
2575 decode_obj: {'a': 10,
2576 'c': [100, 200, '\xf0\xf1\xf2\xf3\xf4'],
2577 'b': 'hello',
2578 'd': {784: '@\x14\x01P',
2579 78.0: '\x10\x05\x0b\x10\xb2\x1b\x00'}}
2580 Equal? True
2581
2582 @param obj: the object to apply base64 encoding recursively.
2583
2584 @return: the base64 encoded object.
2585 """
2586 encode_types = (basestring, bytearray)
2587 return recursive_func(obj, base64.standard_b64encode, encode_types)
2588
2589
2590def base64_recursive_decode(obj):
2591 """Apply base64 decode recursively into the obj structure.
2592
2593 @param obj: the object to apply base64 decoding recursively.
2594
2595 @return: the base64 decoded object.
2596 """
2597 decode_types = (basestring,)
2598 return recursive_func(obj, base64.standard_b64decode, decode_types,
2599 fix_num_key=True)