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