blob: f82484f221aa74c7c7665931f3abe0626dd08cb2 [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
Kristoffer Erlandsson58776682017-11-06 11:32:49 +0100585def get_num_allocated_file_handles():
586 """
Kristoffer Erlandssona6928852017-11-09 10:04:31 +0100587 Returns the number of currently allocated file handles.
Kristoffer Erlandsson58776682017-11-06 11:32:49 +0100588
589 Gets this information by parsing /proc/sys/fs/file-nr.
590 See https://www.kernel.org/doc/Documentation/sysctl/fs.txt
591 for details on this file.
592 """
593 with _open_file('/proc/sys/fs/file-nr') as f:
594 line = f.readline()
595 allocated_handles = int(line.split()[0])
596 return allocated_handles
597
Allen Li2c32d6b2017-02-03 15:28:10 -0800598def print_to_tty(string):
599 """Output string straight to the tty"""
600 open('/dev/tty', 'w').write(string + '\n')
601
602
603def dump_object(object):
604 """Dump an object's attributes and methods
605
606 kind of like dir()
607 """
608 for item in object.__dict__.iteritems():
609 print item
610 try:
611 (key, value) = item
612 dump_object(value)
613 except:
614 continue
615
616
617def environ(env_key):
618 """return the requested environment variable, or '' if unset"""
619 if (os.environ.has_key(env_key)):
620 return os.environ[env_key]
621 else:
622 return ''
623
624
625def prepend_path(newpath, oldpath):
626 """prepend newpath to oldpath"""
627 if (oldpath):
628 return newpath + ':' + oldpath
629 else:
630 return newpath
631
632
633def append_path(oldpath, newpath):
634 """append newpath to oldpath"""
635 if (oldpath):
636 return oldpath + ':' + newpath
637 else:
638 return newpath
639
640
641_TIME_OUTPUT_RE = re.compile(
642 r'([\d\.]*)user ([\d\.]*)system '
643 r'(\d*):([\d\.]*)elapsed (\d*)%CPU')
644
645
646def avgtime_print(dir):
647 """ Calculate some benchmarking statistics.
648 Input is a directory containing a file called 'time'.
649 File contains one-per-line results of /usr/bin/time.
650 Output is average Elapsed, User, and System time in seconds,
651 and average CPU percentage.
652 """
653 user = system = elapsed = cpu = count = 0
654 with open(dir + "/time") as f:
655 for line in f:
656 try:
657 m = _TIME_OUTPUT_RE.match(line);
658 user += float(m.group(1))
659 system += float(m.group(2))
660 elapsed += (float(m.group(3)) * 60) + float(m.group(4))
661 cpu += float(m.group(5))
662 count += 1
663 except:
664 raise ValueError("badly formatted times")
665
666 return "Elapsed: %0.2fs User: %0.2fs System: %0.2fs CPU: %0.0f%%" % \
667 (elapsed / count, user / count, system / count, cpu / count)
668
669
670def to_seconds(time_string):
671 """Converts a string in M+:SS.SS format to S+.SS"""
672 elts = time_string.split(':')
673 if len(elts) == 1:
674 return time_string
675 return str(int(elts[0]) * 60 + float(elts[1]))
676
677
678_TIME_OUTPUT_RE_2 = re.compile(r'(.*?)user (.*?)system (.*?)elapsed')
679
680
681def extract_all_time_results(results_string):
682 """Extract user, system, and elapsed times into a list of tuples"""
683 results = []
684 for result in _TIME_OUTPUT_RE_2.findall(results_string):
685 results.append(tuple([to_seconds(elt) for elt in result]))
686 return results
687
688
689def running_config():
690 """
691 Return path of config file of the currently running kernel
692 """
693 version = utils.system_output('uname -r')
694 for config in ('/proc/config.gz', \
695 '/boot/config-%s' % version,
696 '/lib/modules/%s/build/.config' % version):
697 if os.path.isfile(config):
698 return config
699 return None
700
701
702def check_for_kernel_feature(feature):
703 config = running_config()
704
705 if not config:
706 raise TypeError("Can't find kernel config file")
707
708 if magic.guess_type(config) == 'application/x-gzip':
709 grep = 'zgrep'
710 else:
711 grep = 'grep'
712 grep += ' ^CONFIG_%s= %s' % (feature, config)
713
714 if not utils.system_output(grep, ignore_status=True):
715 raise ValueError("Kernel doesn't have a %s feature" % (feature))
716
717
718def check_glibc_ver(ver):
719 glibc_ver = commands.getoutput('ldd --version').splitlines()[0]
720 glibc_ver = re.search(r'(\d+\.\d+(\.\d+)?)', glibc_ver).group()
721 if utils.compare_versions(glibc_ver, ver) == -1:
722 raise error.TestError("Glibc too old (%s). Glibc >= %s is needed." %
723 (glibc_ver, ver))
724
725def check_kernel_ver(ver):
726 kernel_ver = utils.system_output('uname -r')
727 kv_tmp = re.split(r'[-]', kernel_ver)[0:3]
728 # In compare_versions, if v1 < v2, return value == -1
729 if utils.compare_versions(kv_tmp[0], ver) == -1:
730 raise error.TestError("Kernel too old (%s). Kernel > %s is needed." %
731 (kernel_ver, ver))
732
733
734def human_format(number):
735 # Convert number to kilo / mega / giga format.
736 if number < 1024:
737 return "%d" % number
738 kilo = float(number) / 1024.0
739 if kilo < 1024:
740 return "%.2fk" % kilo
741 meg = kilo / 1024.0
742 if meg < 1024:
743 return "%.2fM" % meg
744 gig = meg / 1024.0
745 return "%.2fG" % gig
746
747
748def numa_nodes():
749 node_paths = glob.glob('/sys/devices/system/node/node*')
750 nodes = [int(re.sub(r'.*node(\d+)', r'\1', x)) for x in node_paths]
751 return (sorted(nodes))
752
753
754def node_size():
755 nodes = max(len(numa_nodes()), 1)
756 return ((memtotal() * 1024) / nodes)
757
758
759def pickle_load(filename):
760 return pickle.load(open(filename, 'r'))
761
762
763# Return the kernel version and build timestamp.
764def running_os_release():
765 return os.uname()[2:4]
766
767
768def running_os_ident():
769 (version, timestamp) = running_os_release()
770 return version + '::' + timestamp
771
772
773def running_os_full_version():
774 (version, timestamp) = running_os_release()
775 return version
776
777
778# much like find . -name 'pattern'
779def locate(pattern, root=os.getcwd()):
780 for path, dirs, files in os.walk(root):
781 for f in files:
782 if fnmatch.fnmatch(f, pattern):
783 yield os.path.abspath(os.path.join(path, f))
784
785
786def freespace(path):
787 """Return the disk free space, in bytes"""
788 s = os.statvfs(path)
789 return s.f_bavail * s.f_bsize
790
791
792def disk_block_size(path):
793 """Return the disk block size, in bytes"""
794 return os.statvfs(path).f_bsize
795
796
797_DISK_PARTITION_3_RE = re.compile(r'^(/dev/hd[a-z]+)3', re.M)
798
799def get_disks():
800 df_output = utils.system_output('df')
801 return _DISK_PARTITION_3_RE.findall(df_output)
802
803
804def get_disk_size(disk_name):
805 """
806 Return size of disk in byte. Return 0 in Error Case
807
808 @param disk_name: disk name to find size
809 """
810 device = os.path.basename(disk_name)
811 for line in file('/proc/partitions'):
812 try:
813 _, _, blocks, name = re.split(r' +', line.strip())
814 except ValueError:
815 continue
816 if name == device:
817 return 1024 * int(blocks)
818 return 0
819
820
821def get_disk_size_gb(disk_name):
822 """
823 Return size of disk in GB (10^9). Return 0 in Error Case
824
825 @param disk_name: disk name to find size
826 """
827 return int(get_disk_size(disk_name) / (10.0 ** 9) + 0.5)
828
829
830def get_disk_model(disk_name):
831 """
832 Return model name for internal storage device
833
834 @param disk_name: disk name to find model
835 """
836 cmd1 = 'udevadm info --query=property --name=%s' % disk_name
837 cmd2 = 'grep -E "ID_(NAME|MODEL)="'
838 cmd3 = 'cut -f 2 -d"="'
839 cmd = ' | '.join([cmd1, cmd2, cmd3])
840 return utils.system_output(cmd)
841
842
Gwendal Grignou876e6912017-06-21 12:17:36 -0700843_DISK_DEV_RE = re.compile(r'/dev/sd[a-z]|'
844 r'/dev/mmcblk[0-9]+|'
845 r'/dev/nvme[0-9]+n[0-9]+')
Allen Li2c32d6b2017-02-03 15:28:10 -0800846
847
848def get_disk_from_filename(filename):
849 """
850 Return the disk device the filename is on.
851 If the file is on tmpfs or other special file systems,
852 return None.
853
854 @param filename: name of file, full path.
855 """
856
857 if not os.path.exists(filename):
858 raise error.TestError('file %s missing' % filename)
859
860 if filename[0] != '/':
861 raise error.TestError('This code works only with full path')
862
863 m = _DISK_DEV_RE.match(filename)
864 while not m:
865 if filename[0] != '/':
866 return None
867 if filename == '/dev/root':
868 cmd = 'rootdev -d -s'
869 elif filename.startswith('/dev/mapper'):
870 cmd = 'dmsetup table "%s"' % os.path.basename(filename)
871 dmsetup_output = utils.system_output(cmd).split(' ')
872 if dmsetup_output[2] == 'verity':
873 maj_min = dmsetup_output[4]
874 elif dmsetup_output[2] == 'crypt':
875 maj_min = dmsetup_output[6]
876 cmd = 'realpath "/dev/block/%s"' % maj_min
877 elif filename.startswith('/dev/loop'):
878 cmd = 'losetup -O BACK-FILE "%s" | tail -1' % filename
879 else:
880 cmd = 'df "%s" | tail -1 | cut -f 1 -d" "' % filename
881 filename = utils.system_output(cmd)
882 m = _DISK_DEV_RE.match(filename)
883 return m.group(0)
884
885
886def get_disk_firmware_version(disk_name):
887 """
888 Return firmware version for internal storage device. (empty string for eMMC)
889
890 @param disk_name: disk name to find model
891 """
892 cmd1 = 'udevadm info --query=property --name=%s' % disk_name
893 cmd2 = 'grep -E "ID_REVISION="'
894 cmd3 = 'cut -f 2 -d"="'
895 cmd = ' | '.join([cmd1, cmd2, cmd3])
896 return utils.system_output(cmd)
897
898
899def is_disk_scsi(disk_name):
900 """
901 Return true if disk is a scsi device, return false otherwise
902
903 @param disk_name: disk name check
904 """
905 return re.match('/dev/sd[a-z]+', disk_name)
906
907
908def is_disk_harddisk(disk_name):
909 """
910 Return true if disk is a harddisk, return false otherwise
911
912 @param disk_name: disk name check
913 """
914 cmd1 = 'udevadm info --query=property --name=%s' % disk_name
915 cmd2 = 'grep -E "ID_ATA_ROTATION_RATE_RPM="'
916 cmd3 = 'cut -f 2 -d"="'
917 cmd = ' | '.join([cmd1, cmd2, cmd3])
918
919 rtt = utils.system_output(cmd)
920
921 # eMMC will not have this field; rtt == ''
922 # SSD will have zero rotation rate; rtt == '0'
923 # For harddisk rtt > 0
924 return rtt and int(rtt) > 0
925
Gwendal Grignou427c7b22017-10-26 17:39:29 -0700926def concat_partition(disk_name, partition_number):
927 """
928 Return the name of a partition:
929 sda, 3 --> sda3
930 mmcblk0, 3 --> mmcblk0p3
931
932 @param disk_name: diskname string
933 @param partition_number: integer
934 """
935 if disk_name.endswith(tuple(str(i) for i in range(0, 10))):
936 sep = 'p'
937 else:
938 sep = ''
939 return disk_name + sep + str(partition_number)
Allen Li2c32d6b2017-02-03 15:28:10 -0800940
941def verify_hdparm_feature(disk_name, feature):
942 """
943 Check for feature support for SCSI disk using hdparm
944
945 @param disk_name: target disk
946 @param feature: hdparm output string of the feature
947 """
948 cmd = 'hdparm -I %s | grep -q "%s"' % (disk_name, feature)
949 ret = utils.system(cmd, ignore_status=True)
950 if ret == 0:
951 return True
952 elif ret == 1:
953 return False
954 else:
955 raise error.TestFail('Error running command %s' % cmd)
956
957
958def get_storage_error_msg(disk_name, reason):
959 """
960 Get Error message for storage test which include disk model.
961 and also include the firmware version for the SCSI disk
962
963 @param disk_name: target disk
964 @param reason: Reason of the error.
965 """
966
967 msg = reason
968
969 model = get_disk_model(disk_name)
970 msg += ' Disk model: %s' % model
971
972 if is_disk_scsi(disk_name):
973 fw = get_disk_firmware_version(disk_name)
974 msg += ' firmware: %s' % fw
975
976 return msg
977
978
979def load_module(module_name, params=None):
980 # Checks if a module has already been loaded
981 if module_is_loaded(module_name):
982 return False
983
984 cmd = '/sbin/modprobe ' + module_name
985 if params:
986 cmd += ' ' + params
987 utils.system(cmd)
988 return True
989
990
991def unload_module(module_name):
992 """
993 Removes a module. Handles dependencies. If even then it's not possible
994 to remove one of the modules, it will trhow an error.CmdError exception.
995
996 @param module_name: Name of the module we want to remove.
997 """
998 l_raw = utils.system_output("/bin/lsmod").splitlines()
999 lsmod = [x for x in l_raw if x.split()[0] == module_name]
1000 if len(lsmod) > 0:
1001 line_parts = lsmod[0].split()
1002 if len(line_parts) == 4:
1003 submodules = line_parts[3].split(",")
1004 for submodule in submodules:
1005 unload_module(submodule)
1006 utils.system("/sbin/modprobe -r %s" % module_name)
1007 logging.info("Module %s unloaded", module_name)
1008 else:
1009 logging.info("Module %s is already unloaded", module_name)
1010
1011
1012def module_is_loaded(module_name):
1013 module_name = module_name.replace('-', '_')
1014 modules = utils.system_output('/bin/lsmod').splitlines()
1015 for module in modules:
1016 if module.startswith(module_name) and module[len(module_name)] == ' ':
1017 return True
1018 return False
1019
1020
1021def get_loaded_modules():
1022 lsmod_output = utils.system_output('/bin/lsmod').splitlines()[1:]
1023 return [line.split(None, 1)[0] for line in lsmod_output]
1024
1025
1026def get_huge_page_size():
1027 output = utils.system_output('grep Hugepagesize /proc/meminfo')
1028 return int(output.split()[1]) # Assumes units always in kB. :(
1029
1030
1031def get_num_huge_pages():
1032 raw_hugepages = utils.system_output('/sbin/sysctl vm.nr_hugepages')
1033 return int(raw_hugepages.split()[2])
1034
1035
1036def set_num_huge_pages(num):
1037 utils.system('/sbin/sysctl vm.nr_hugepages=%d' % num)
1038
1039
1040def ping_default_gateway():
1041 """Ping the default gateway."""
1042
1043 network = open('/etc/sysconfig/network')
1044 m = re.search('GATEWAY=(\S+)', network.read())
1045
1046 if m:
1047 gw = m.group(1)
1048 cmd = 'ping %s -c 5 > /dev/null' % gw
1049 return utils.system(cmd, ignore_status=True)
1050
1051 raise error.TestError('Unable to find default gateway')
1052
1053
1054def drop_caches():
1055 """Writes back all dirty pages to disk and clears all the caches."""
1056 utils.system("sync")
1057 # We ignore failures here as this will fail on 2.6.11 kernels.
1058 utils.system("echo 3 > /proc/sys/vm/drop_caches", ignore_status=True)
1059
1060
1061def process_is_alive(name_pattern):
1062 """
1063 'pgrep name' misses all python processes and also long process names.
1064 'pgrep -f name' gets all shell commands with name in args.
1065 So look only for command whose initial pathname ends with name.
1066 Name itself is an egrep pattern, so it can use | etc for variations.
1067 """
1068 return utils.system("pgrep -f '^([^ /]*/)*(%s)([ ]|$)'" % name_pattern,
1069 ignore_status=True) == 0
1070
1071
1072def get_hwclock_seconds(utc=True):
1073 """
1074 Return the hardware clock in seconds as a floating point value.
1075 Use Coordinated Universal Time if utc is True, local time otherwise.
1076 Raise a ValueError if unable to read the hardware clock.
1077 """
1078 cmd = '/sbin/hwclock --debug'
1079 if utc:
1080 cmd += ' --utc'
1081 hwclock_output = utils.system_output(cmd, ignore_status=True)
1082 match = re.search(r'= ([0-9]+) seconds since .+ (-?[0-9.]+) seconds$',
1083 hwclock_output, re.DOTALL)
1084 if match:
1085 seconds = int(match.group(1)) + float(match.group(2))
1086 logging.debug('hwclock seconds = %f', seconds)
1087 return seconds
1088
1089 raise ValueError('Unable to read the hardware clock -- ' +
1090 hwclock_output)
1091
1092
1093def set_wake_alarm(alarm_time):
1094 """
1095 Set the hardware RTC-based wake alarm to 'alarm_time'.
1096 """
1097 utils.write_one_line('/sys/class/rtc/rtc0/wakealarm', str(alarm_time))
1098
1099
1100def set_power_state(state):
1101 """
1102 Set the system power state to 'state'.
1103 """
1104 utils.write_one_line('/sys/power/state', state)
1105
1106
1107def standby():
1108 """
1109 Power-on suspend (S1)
1110 """
1111 set_power_state('standby')
1112
1113
1114def suspend_to_ram():
1115 """
1116 Suspend the system to RAM (S3)
1117 """
1118 set_power_state('mem')
1119
1120
1121def suspend_to_disk():
1122 """
1123 Suspend the system to disk (S4)
1124 """
1125 set_power_state('disk')
1126
1127
Po-Hsien Wangc02992f2017-08-10 11:18:46 -07001128_AUTOTEST_CLIENT_PATH = os.path.join(os.path.dirname(__file__), '..')
1129_AMD_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH,
1130 'bin/amd_pci_ids.json')
1131_INTEL_PCI_IDS_FILE_PATH = os.path.join(_AUTOTEST_CLIENT_PATH,
1132 'bin/intel_pci_ids.json')
Allen Li2c32d6b2017-02-03 15:28:10 -08001133_UI_USE_FLAGS_FILE_PATH = '/etc/ui_use_flags.txt'
1134
1135# Command to check if a package is installed. If the package is not installed
1136# the command shall fail.
1137_CHECK_PACKAGE_INSTALLED_COMMAND =(
1138 "dpkg-query -W -f='${Status}\n' %s | head -n1 | awk '{print $3;}' | "
1139 "grep -q '^installed$'")
1140
1141pciid_to_amd_architecture = {}
1142pciid_to_intel_architecture = {}
1143
1144class Crossystem(object):
1145 """A wrapper for the crossystem utility."""
1146
1147 def __init__(self, client):
1148 self.cros_system_data = {}
1149 self._client = client
1150
1151 def init(self):
1152 self.cros_system_data = {}
1153 (_, fname) = tempfile.mkstemp()
1154 f = open(fname, 'w')
1155 self._client.run('crossystem', stdout_tee=f)
1156 f.close()
1157 text = utils.read_file(fname)
1158 for line in text.splitlines():
1159 assignment_string = line.split('#')[0]
1160 if not assignment_string.count('='):
1161 continue
1162 (name, value) = assignment_string.split('=', 1)
1163 self.cros_system_data[name.strip()] = value.strip()
1164 os.remove(fname)
1165
1166 def __getattr__(self, name):
1167 """
1168 Retrieve a crosssystem attribute.
1169
1170 The call crossystemobject.name() will return the crossystem reported
1171 string.
1172 """
1173 return lambda: self.cros_system_data[name]
1174
1175
1176def get_oldest_pid_by_name(name):
1177 """
1178 Return the oldest pid of a process whose name perfectly matches |name|.
1179
1180 name is an egrep expression, which will be matched against the entire name
1181 of processes on the system. For example:
1182
1183 get_oldest_pid_by_name('chrome')
1184
1185 on a system running
1186 8600 ? 00:00:04 chrome
1187 8601 ? 00:00:00 chrome
1188 8602 ? 00:00:00 chrome-sandbox
1189
1190 would return 8600, as that's the oldest process that matches.
1191 chrome-sandbox would not be matched.
1192
1193 Arguments:
1194 name: egrep expression to match. Will be anchored at the beginning and
1195 end of the match string.
1196
1197 Returns:
1198 pid as an integer, or None if one cannot be found.
1199
1200 Raises:
1201 ValueError if pgrep returns something odd.
1202 """
1203 str_pid = utils.system_output('pgrep -o ^%s$' % name,
1204 ignore_status=True).rstrip()
1205 if str_pid:
1206 return int(str_pid)
1207
1208
1209def get_oldest_by_name(name):
1210 """Return pid and command line of oldest process whose name matches |name|.
1211
1212 @param name: egrep expression to match desired process name.
1213 @return: A tuple of (pid, command_line) of the oldest process whose name
1214 matches |name|.
1215
1216 """
1217 pid = get_oldest_pid_by_name(name)
1218 if pid:
1219 command_line = utils.system_output('ps -p %i -o command=' % pid,
1220 ignore_status=True).rstrip()
1221 return (pid, command_line)
1222
1223
1224def get_chrome_remote_debugging_port():
1225 """Returns remote debugging port for Chrome.
1226
1227 Parse chrome process's command line argument to get the remote debugging
Achuith Bhandarkare3adb0a2017-08-10 14:15:57 -07001228 port. if it is 0, look at DevToolsActivePort for the ephemeral port.
Allen Li2c32d6b2017-02-03 15:28:10 -08001229 """
1230 _, command = get_oldest_by_name('chrome')
1231 matches = re.search('--remote-debugging-port=([0-9]+)', command)
Achuith Bhandarkare3adb0a2017-08-10 14:15:57 -07001232 if not matches:
1233 return 0
1234 port = int(matches.group(1))
1235 if port:
1236 return port
1237 with open('/home/chronos/DevToolsActivePort') as f:
1238 return int(f.readline().rstrip())
Allen Li2c32d6b2017-02-03 15:28:10 -08001239
1240
1241def get_process_list(name, command_line=None):
1242 """
1243 Return the list of pid for matching process |name command_line|.
1244
1245 on a system running
1246 31475 ? 0:06 /opt/google/chrome/chrome --allow-webui-compositing -
1247 31478 ? 0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/
1248 31485 ? 0:00 /opt/google/chrome/chrome --type=zygote --log-level=1
1249 31532 ? 1:05 /opt/google/chrome/chrome --type=renderer
1250
1251 get_process_list('chrome')
1252 would return ['31475', '31485', '31532']
1253
1254 get_process_list('chrome', '--type=renderer')
1255 would return ['31532']
1256
1257 Arguments:
1258 name: process name to search for. If command_line is provided, name is
1259 matched against full command line. If command_line is not provided,
1260 name is only matched against the process name.
1261 command line: when command line is passed, the full process command line
1262 is used for matching.
1263
1264 Returns:
1265 list of PIDs of the matching processes.
1266
1267 """
1268 # TODO(rohitbm) crbug.com/268861
1269 flag = '-x' if not command_line else '-f'
1270 name = '\'%s.*%s\'' % (name, command_line) if command_line else name
1271 str_pid = utils.system_output('pgrep %s %s' % (flag, name),
1272 ignore_status=True).rstrip()
1273 return str_pid.split()
1274
1275
1276def nuke_process_by_name(name, with_prejudice=False):
1277 """Tell the oldest process specified by name to exit.
1278
1279 Arguments:
1280 name: process name specifier, as understood by pgrep.
1281 with_prejudice: if True, don't allow for graceful exit.
1282
1283 Raises:
1284 error.AutoservPidAlreadyDeadError: no existing process matches name.
1285 """
1286 try:
1287 pid = get_oldest_pid_by_name(name)
1288 except Exception as e:
1289 logging.error(e)
1290 return
1291 if pid is None:
1292 raise error.AutoservPidAlreadyDeadError('No process matching %s.' %
1293 name)
1294 if with_prejudice:
1295 utils.nuke_pid(pid, [signal.SIGKILL])
1296 else:
1297 utils.nuke_pid(pid)
1298
1299
1300def ensure_processes_are_dead_by_name(name, timeout_sec=10):
1301 """Terminate all processes specified by name and ensure they're gone.
1302
1303 Arguments:
1304 name: process name specifier, as understood by pgrep.
1305 timeout_sec: maximum number of seconds to wait for processes to die.
1306
1307 Raises:
1308 error.AutoservPidAlreadyDeadError: no existing process matches name.
Allen Li5ed7e632017-02-03 16:31:33 -08001309 utils.TimeoutError: if processes still exist after timeout_sec.
Allen Li2c32d6b2017-02-03 15:28:10 -08001310 """
1311
1312 def list_and_kill_processes(name):
1313 process_list = get_process_list(name)
1314 try:
1315 for pid in [int(str_pid) for str_pid in process_list]:
1316 utils.nuke_pid(pid)
1317 except error.AutoservPidAlreadyDeadError:
1318 pass
1319 return process_list
1320
1321 utils.poll_for_condition(lambda: list_and_kill_processes(name) == [],
1322 timeout=timeout_sec)
1323
1324
1325def is_virtual_machine():
1326 return 'QEMU' in platform.processor()
1327
1328
1329def save_vm_state(checkpoint):
1330 """Saves the current state of the virtual machine.
1331
1332 This function is a NOOP if the test is not running under a virtual machine
1333 with the USB serial port redirected.
1334
1335 Arguments:
1336 checkpoint - Name used to identify this state
1337
1338 Returns:
1339 None
1340 """
1341 # The QEMU monitor has been redirected to the guest serial port located at
1342 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
1343 # command to the serial port.
1344 if is_virtual_machine() and os.path.exists('/dev/ttyUSB0'):
1345 logging.info('Saving VM state "%s"', checkpoint)
1346 serial = open('/dev/ttyUSB0', 'w')
1347 serial.write('savevm %s\r\n' % checkpoint)
1348 logging.info('Done saving VM state "%s"', checkpoint)
1349
1350
1351def check_raw_dmesg(dmesg, message_level, whitelist):
1352 """Checks dmesg for unexpected warnings.
1353
1354 This function parses dmesg for message with message_level <= message_level
1355 which do not appear in the whitelist.
1356
1357 Arguments:
1358 dmesg - string containing raw dmesg buffer
1359 message_level - minimum message priority to check
1360 whitelist - messages to ignore
1361
1362 Returns:
1363 List of unexpected warnings
1364 """
1365 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
1366 unexpected = []
1367 for line in dmesg.splitlines():
1368 if int(line[1]) <= message_level:
1369 stripped_line = line.split('] ', 1)[1]
1370 if whitelist_re.search(stripped_line):
1371 continue
1372 unexpected.append(stripped_line)
1373 return unexpected
1374
1375
1376def verify_mesg_set(mesg, regex, whitelist):
1377 """Verifies that the exact set of messages are present in a text.
1378
1379 This function finds all strings in the text matching a certain regex, and
1380 then verifies that all expected strings are present in the set, and no
1381 unexpected strings are there.
1382
1383 Arguments:
1384 mesg - the mutiline text to be scanned
1385 regex - regular expression to match
1386 whitelist - messages to find in the output, a list of strings
1387 (potentially regexes) to look for in the filtered output. All these
1388 strings must be there, and no other strings should be present in the
1389 filtered output.
1390
1391 Returns:
1392 string of inconsistent findings (i.e. an empty string on success).
1393 """
1394
1395 rv = []
1396
1397 missing_strings = []
1398 present_strings = []
1399 for line in mesg.splitlines():
1400 if not re.search(r'%s' % regex, line):
1401 continue
1402 present_strings.append(line.split('] ', 1)[1])
1403
1404 for string in whitelist:
1405 for present_string in list(present_strings):
1406 if re.search(r'^%s$' % string, present_string):
1407 present_strings.remove(present_string)
1408 break
1409 else:
1410 missing_strings.append(string)
1411
1412 if present_strings:
1413 rv.append('unexpected strings:')
1414 rv.extend(present_strings)
1415 if missing_strings:
1416 rv.append('missing strings:')
1417 rv.extend(missing_strings)
1418
1419 return '\n'.join(rv)
1420
1421
1422def target_is_pie():
1423 """Returns whether the toolchain produces a PIE (position independent
1424 executable) by default.
1425
1426 Arguments:
1427 None
1428
1429 Returns:
1430 True if the target toolchain produces a PIE by default.
1431 False otherwise.
1432 """
1433
1434 command = 'echo | ${CC} -E -dD -P - | grep -i pie'
1435 result = utils.system_output(command,
1436 retain_output=True,
1437 ignore_status=True)
1438 if re.search('#define __PIE__', result):
1439 return True
1440 else:
1441 return False
1442
1443
1444def target_is_x86():
1445 """Returns whether the toolchain produces an x86 object
1446
1447 Arguments:
1448 None
1449
1450 Returns:
1451 True if the target toolchain produces an x86 object
1452 False otherwise.
1453 """
1454
1455 command = 'echo | ${CC} -E -dD -P - | grep -i 86'
1456 result = utils.system_output(command,
1457 retain_output=True,
1458 ignore_status=True)
1459 if re.search('__i386__', result) or re.search('__x86_64__', result):
1460 return True
1461 else:
1462 return False
1463
1464
1465def mounts():
1466 ret = []
1467 for line in file('/proc/mounts'):
1468 m = re.match(
1469 r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
1470 if m:
1471 ret.append(m.groupdict())
1472 return ret
1473
1474
1475def is_mountpoint(path):
1476 return path in [m['dest'] for m in mounts()]
1477
1478
1479def require_mountpoint(path):
1480 """
1481 Raises an exception if path is not a mountpoint.
1482 """
1483 if not is_mountpoint(path):
1484 raise error.TestFail('Path not mounted: "%s"' % path)
1485
1486
1487def random_username():
1488 return str(uuid.uuid4()) + '@example.com'
1489
1490
1491def get_signin_credentials(filepath):
1492 """Returns user_id, password tuple from credentials file at filepath.
1493
1494 File must have one line of the format user_id:password
1495
1496 @param filepath: path of credentials file.
1497 @return user_id, password tuple.
1498 """
1499 user_id, password = None, None
1500 if os.path.isfile(filepath):
1501 with open(filepath) as f:
1502 user_id, password = f.read().rstrip().split(':')
1503 return user_id, password
1504
1505
1506def parse_cmd_output(command, run_method=utils.run):
1507 """Runs a command on a host object to retrieve host attributes.
1508
1509 The command should output to stdout in the format of:
1510 <key> = <value> # <optional_comment>
1511
1512
1513 @param command: Command to execute on the host.
1514 @param run_method: Function to use to execute the command. Defaults to
1515 utils.run so that the command will be executed locally.
1516 Can be replace with a host.run call so that it will
1517 execute on a DUT or external machine. Method must accept
1518 a command argument, stdout_tee and stderr_tee args and
1519 return a result object with a string attribute stdout
1520 which will be parsed.
1521
1522 @returns a dictionary mapping host attributes to their values.
1523 """
1524 result = {}
1525 # Suppresses stdout so that the files are not printed to the logs.
1526 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
1527 for line in cmd_result.stdout.splitlines():
1528 # Lines are of the format "<key> = <value> # <comment>"
1529 key_value = re.match(r'^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ '
1530 r']+)(?:\s*#.*)?$', line)
1531 if key_value:
1532 result[key_value.group('key')] = key_value.group('value')
1533 return result
1534
1535
1536def set_from_keyval_output(out, delimiter=' '):
1537 """Parse delimiter-separated key-val output into a set of tuples.
1538
1539 Output is expected to be multiline text output from a command.
1540 Stuffs the key-vals into tuples in a set to be later compared.
1541
1542 e.g. deactivated 0
1543 disableForceClear 0
1544 ==> set(('deactivated', '0'), ('disableForceClear', '0'))
1545
1546 @param out: multiple lines of space-separated key-val pairs.
1547 @param delimiter: character that separates key from val. Usually a
1548 space but may be '=' or something else.
1549 @return set of key-val tuples.
1550 """
1551 results = set()
1552 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
1553 for linecr in out.splitlines():
1554 match = kv_match_re.match(linecr.strip())
1555 if match:
1556 results.add((match.group(1), match.group(2)))
1557 return results
1558
1559
1560def get_cpu_usage():
1561 """Returns machine's CPU usage.
1562
1563 This function uses /proc/stat to identify CPU usage.
1564 Returns:
Kristoffer Erlandsson82b9f322017-11-07 12:20:38 +01001565 A dictionary with values for all columns in /proc/stat
Allen Li2c32d6b2017-02-03 15:28:10 -08001566 Sample dictionary:
1567 {
1568 'user': 254544,
1569 'nice': 9,
1570 'system': 254768,
1571 'idle': 2859878,
Kristoffer Erlandsson82b9f322017-11-07 12:20:38 +01001572 'iowait': 1,
1573 'irq': 2,
1574 'softirq': 3,
1575 'steal': 4,
1576 'guest': 5,
1577 'guest_nice': 6
Allen Li2c32d6b2017-02-03 15:28:10 -08001578 }
Kristoffer Erlandsson82b9f322017-11-07 12:20:38 +01001579 If a column is missing or malformed in /proc/stat (typically on older
1580 systems), the value for that column is set to 0.
Allen Li2c32d6b2017-02-03 15:28:10 -08001581 """
Kristoffer Erlandsson82b9f322017-11-07 12:20:38 +01001582 with _open_file('/proc/stat') as proc_stat:
1583 cpu_usage_str = proc_stat.readline().split()
1584 columns = ('user', 'nice', 'system', 'idle', 'iowait', 'irq', 'softirq',
1585 'steal', 'guest', 'guest_nice')
1586 d = {}
1587 for index, col in enumerate(columns, 1):
1588 try:
1589 d[col] = int(cpu_usage_str[index])
1590 except:
1591 d[col] = 0
1592 return d
Allen Li2c32d6b2017-02-03 15:28:10 -08001593
1594def compute_active_cpu_time(cpu_usage_start, cpu_usage_end):
1595 """Computes the fraction of CPU time spent non-idling.
1596
1597 This function should be invoked using before/after values from calls to
1598 get_cpu_usage().
Kristoffer Erlandsson82b9f322017-11-07 12:20:38 +01001599
1600 See https://stackoverflow.com/a/23376195 and
1601 https://unix.stackexchange.com/a/303224 for some more context how
1602 to calculate usage given two /proc/stat snapshots.
Allen Li2c32d6b2017-02-03 15:28:10 -08001603 """
Kristoffer Erlandsson82b9f322017-11-07 12:20:38 +01001604 idle_cols = ('idle', 'iowait') # All other cols are calculated as active.
1605 time_active_start = sum([x[1] for x in cpu_usage_start.iteritems()
1606 if x[0] not in idle_cols])
1607 time_active_end = sum([x[1] for x in cpu_usage_end.iteritems()
1608 if x[0] not in idle_cols])
1609 total_time_start = sum(cpu_usage_start.values())
1610 total_time_end = sum(cpu_usage_end.values())
Allen Li2c32d6b2017-02-03 15:28:10 -08001611 return ((float(time_active_end) - time_active_start) /
1612 (total_time_end - total_time_start))
1613
1614
1615def is_pgo_mode():
1616 return 'USE_PGO' in os.environ
1617
1618
1619def wait_for_idle_cpu(timeout, utilization):
1620 """Waits for the CPU to become idle (< utilization).
1621
1622 Args:
1623 timeout: The longest time in seconds to wait before throwing an error.
1624 utilization: The CPU usage below which the system should be considered
1625 idle (between 0 and 1.0 independent of cores/hyperthreads).
1626 """
1627 time_passed = 0.0
1628 fraction_active_time = 1.0
1629 sleep_time = 1
1630 logging.info('Starting to wait up to %.1fs for idle CPU...', timeout)
1631 while fraction_active_time >= utilization:
1632 cpu_usage_start = get_cpu_usage()
1633 # Split timeout interval into not too many chunks to limit log spew.
1634 # Start at 1 second, increase exponentially
1635 time.sleep(sleep_time)
1636 time_passed += sleep_time
1637 sleep_time = min(16.0, 2.0 * sleep_time)
1638 cpu_usage_end = get_cpu_usage()
1639 fraction_active_time = \
1640 compute_active_cpu_time(cpu_usage_start, cpu_usage_end)
1641 logging.info('After waiting %.1fs CPU utilization is %.3f.',
1642 time_passed, fraction_active_time)
1643 if time_passed > timeout:
1644 logging.warning('CPU did not become idle.')
1645 log_process_activity()
1646 # crosbug.com/37389
1647 if is_pgo_mode():
1648 logging.info('Still continuing because we are in PGO mode.')
1649 return True
1650
1651 return False
1652 logging.info('Wait for idle CPU took %.1fs (utilization = %.3f).',
1653 time_passed, fraction_active_time)
1654 return True
1655
1656
1657def log_process_activity():
1658 """Logs the output of top.
1659
1660 Useful to debug performance tests and to find runaway processes.
1661 """
1662 logging.info('Logging current process activity using top and ps.')
1663 cmd = 'top -b -n1 -c'
1664 output = utils.run(cmd)
1665 logging.info(output)
1666 output = utils.run('ps axl')
1667 logging.info(output)
1668
1669
1670def wait_for_cool_machine():
1671 """
1672 A simple heuristic to wait for a machine to cool.
1673 The code looks a bit 'magic', but we don't know ambient temperature
1674 nor machine characteristics and still would like to return the caller
1675 a machine that cooled down as much as reasonably possible.
1676 """
1677 temperature = get_current_temperature_max()
1678 # We got here with a cold machine, return immediately. This should be the
1679 # most common case.
1680 if temperature < 50:
1681 return True
1682 logging.info('Got a hot machine of %dC. Sleeping 1 minute.', temperature)
1683 # A modest wait should cool the machine.
1684 time.sleep(60.0)
1685 temperature = get_current_temperature_max()
1686 # Atoms idle below 60 and everyone else should be even lower.
1687 if temperature < 62:
1688 return True
1689 # This should be rare.
1690 logging.info('Did not cool down (%dC). Sleeping 2 minutes.', temperature)
1691 time.sleep(120.0)
1692 temperature = get_current_temperature_max()
1693 # A temperature over 65'C doesn't give us much headroom to the critical
1694 # temperatures that start at 85'C (and PerfControl as of today will fail at
1695 # critical - 10'C).
1696 if temperature < 65:
1697 return True
1698 logging.warning('Did not cool down (%dC), giving up.', temperature)
1699 log_process_activity()
1700 return False
1701
1702
1703# System paths for machine performance state.
1704_CPUINFO = '/proc/cpuinfo'
1705_DIRTY_WRITEBACK_CENTISECS = '/proc/sys/vm/dirty_writeback_centisecs'
1706_KERNEL_MAX = '/sys/devices/system/cpu/kernel_max'
1707_MEMINFO = '/proc/meminfo'
1708_TEMP_SENSOR_RE = 'Reading temperature...([0-9]*)'
1709
Kristoffer Erlandsson1ad7db02017-11-01 11:28:44 +01001710def _open_file(path):
1711 """
1712 Opens a file and returns the file object.
1713
1714 This method is intended to be mocked by tests.
1715 @return The open file object.
1716 """
1717 return open(path)
Allen Li2c32d6b2017-02-03 15:28:10 -08001718
1719def _get_line_from_file(path, line):
1720 """
1721 line can be an integer or
1722 line can be a string that matches the beginning of the line
1723 """
Kristoffer Erlandsson1ad7db02017-11-01 11:28:44 +01001724 with _open_file(path) as f:
Allen Li2c32d6b2017-02-03 15:28:10 -08001725 if isinstance(line, int):
1726 l = f.readline()
1727 for _ in range(0, line):
1728 l = f.readline()
1729 return l
1730 else:
1731 for l in f:
1732 if l.startswith(line):
1733 return l
1734 return None
1735
1736
1737def _get_match_from_file(path, line, prefix, postfix):
1738 """
1739 Matches line in path and returns string between first prefix and postfix.
1740 """
1741 match = _get_line_from_file(path, line)
1742 # Strip everything from front of line including prefix.
1743 if prefix:
1744 match = re.split(prefix, match)[1]
1745 # Strip everything from back of string including first occurence of postfix.
1746 if postfix:
1747 match = re.split(postfix, match)[0]
1748 return match
1749
1750
1751def _get_float_from_file(path, line, prefix, postfix):
1752 match = _get_match_from_file(path, line, prefix, postfix)
1753 return float(match)
1754
1755
1756def _get_int_from_file(path, line, prefix, postfix):
1757 match = _get_match_from_file(path, line, prefix, postfix)
1758 return int(match)
1759
1760
1761def _get_hex_from_file(path, line, prefix, postfix):
1762 match = _get_match_from_file(path, line, prefix, postfix)
1763 return int(match, 16)
1764
1765
1766# The paths don't change. Avoid running find all the time.
1767_hwmon_paths = None
1768
1769def _get_hwmon_paths(file_pattern):
1770 """
1771 Returns a list of paths to the temperature sensors.
1772 """
1773 # Some systems like daisy_spring only have the virtual hwmon.
1774 # And other systems like rambi only have coretemp.0. See crbug.com/360249.
1775 # /sys/class/hwmon/hwmon*/
1776 # /sys/devices/virtual/hwmon/hwmon*/
1777 # /sys/devices/platform/coretemp.0/
1778 if not _hwmon_paths:
1779 cmd = 'find /sys/ -name "' + file_pattern + '"'
1780 _hwon_paths = utils.run(cmd, verbose=False).stdout.splitlines()
1781 return _hwon_paths
1782
1783
1784def get_temperature_critical():
1785 """
1786 Returns temperature at which we will see some throttling in the system.
1787 """
1788 min_temperature = 1000.0
1789 paths = _get_hwmon_paths('temp*_crit')
1790 for path in paths:
1791 temperature = _get_float_from_file(path, 0, None, None) * 0.001
1792 # Today typical for Intel is 98'C to 105'C while ARM is 85'C. Clamp to
1793 # the lowest known value.
1794 if (min_temperature < 60.0) or min_temperature > 150.0:
1795 logging.warning('Critical temperature of %.1fC was reset to 85.0C.',
1796 min_temperature)
1797 min_temperature = 85.0
1798
1799 min_temperature = min(temperature, min_temperature)
1800 return min_temperature
1801
1802
1803def get_temperature_input_max():
1804 """
1805 Returns the maximum currently observed temperature.
1806 """
1807 max_temperature = -1000.0
1808 paths = _get_hwmon_paths('temp*_input')
1809 for path in paths:
1810 temperature = _get_float_from_file(path, 0, None, None) * 0.001
1811 max_temperature = max(temperature, max_temperature)
1812 return max_temperature
1813
1814
1815def get_thermal_zone_temperatures():
1816 """
1817 Returns the maximum currently observered temperature in thermal_zones.
1818 """
1819 temperatures = []
1820 for path in glob.glob('/sys/class/thermal/thermal_zone*/temp'):
1821 try:
1822 temperatures.append(
1823 _get_float_from_file(path, 0, None, None) * 0.001)
1824 except IOError:
1825 # Some devices (e.g. Veyron) may have reserved thermal zones that
1826 # are not active. Trying to read the temperature value would cause a
1827 # EINVAL IO error.
1828 continue
1829 return temperatures
1830
1831
1832def get_ec_temperatures():
1833 """
1834 Uses ectool to return a list of all sensor temperatures in Celsius.
1835 """
1836 temperatures = []
1837 try:
1838 full_cmd = 'ectool temps all'
1839 lines = utils.run(full_cmd, verbose=False).stdout.splitlines()
1840 for line in lines:
1841 temperature = int(line.split(': ')[1]) - 273
1842 temperatures.append(temperature)
1843 except Exception:
1844 logging.warning('Unable to read temperature sensors using ectool.')
1845 for temperature in temperatures:
1846 # Sanity check for real world values.
1847 assert ((temperature > 10.0) and
1848 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
1849 temperature)
1850
1851 return temperatures
1852
1853
1854def get_current_temperature_max():
1855 """
1856 Returns the highest reported board temperature (all sensors) in Celsius.
1857 """
1858 temperature = max([get_temperature_input_max()] +
1859 get_thermal_zone_temperatures() +
1860 get_ec_temperatures())
1861 # Sanity check for real world values.
1862 assert ((temperature > 10.0) and
1863 (temperature < 150.0)), ('Unreasonable temperature %.1fC.' %
1864 temperature)
1865 return temperature
1866
1867
1868def get_cpu_cache_size():
1869 """
1870 Returns the last level CPU cache size in kBytes.
1871 """
1872 cache_size = _get_int_from_file(_CPUINFO, 'cache size', ': ', ' KB')
1873 # Sanity check.
1874 assert cache_size >= 64, 'Unreasonably small cache.'
1875 return cache_size
1876
1877
1878def get_cpu_model_frequency():
1879 """
1880 Returns the model frequency from the CPU model name on Intel only. This
1881 might be redundant with get_cpu_max_frequency. Unit is Hz.
1882 """
1883 frequency = _get_float_from_file(_CPUINFO, 'model name', ' @ ', 'GHz')
1884 return 1.e9 * frequency
1885
1886
1887def get_cpu_max_frequency():
1888 """
1889 Returns the largest of the max CPU core frequencies. The unit is Hz.
1890 """
1891 max_frequency = -1
1892 paths = _get_cpufreq_paths('cpuinfo_max_freq')
1893 for path in paths:
1894 # Convert from kHz to Hz.
1895 frequency = 1000 * _get_float_from_file(path, 0, None, None)
1896 max_frequency = max(frequency, max_frequency)
1897 # Sanity check.
1898 assert max_frequency > 1e8, 'Unreasonably low CPU frequency.'
1899 return max_frequency
1900
1901
1902def get_cpu_min_frequency():
1903 """
1904 Returns the smallest of the minimum CPU core frequencies.
1905 """
1906 min_frequency = 1e20
1907 paths = _get_cpufreq_paths('cpuinfo_min_freq')
1908 for path in paths:
1909 frequency = _get_float_from_file(path, 0, None, None)
1910 min_frequency = min(frequency, min_frequency)
1911 # Sanity check.
1912 assert min_frequency > 1e8, 'Unreasonably low CPU frequency.'
1913 return min_frequency
1914
1915
1916def get_cpu_model():
1917 """
1918 Returns the CPU model.
1919 Only works on Intel.
1920 """
1921 cpu_model = _get_int_from_file(_CPUINFO, 'model\t', ': ', None)
1922 return cpu_model
1923
1924
1925def get_cpu_family():
1926 """
1927 Returns the CPU family.
1928 Only works on Intel.
1929 """
1930 cpu_family = _get_int_from_file(_CPUINFO, 'cpu family\t', ': ', None)
1931 return cpu_family
1932
1933
1934def get_board_property(key):
1935 """
1936 Get a specific property from /etc/lsb-release.
1937
1938 @param key: board property to return value for
1939
1940 @return the value or '' if not present
1941 """
1942 with open('/etc/lsb-release') as f:
1943 pattern = '%s=(.*)' % key
1944 pat = re.search(pattern, f.read())
1945 if pat:
1946 return pat.group(1)
1947 return ''
1948
1949
1950def get_board():
1951 """
1952 Get the ChromeOS release board name from /etc/lsb-release.
1953 """
1954 return get_board_property('BOARD')
1955
1956
1957def get_board_type():
1958 """
1959 Get the ChromeOS board type from /etc/lsb-release.
1960
1961 @return device type.
1962 """
1963 return get_board_property('DEVICETYPE')
1964
1965
1966def get_board_with_frequency_and_memory():
1967 """
1968 Returns a board name modified with CPU frequency and memory size to
1969 differentiate between different board variants. For instance
1970 link -> link_1.8GHz_4GB.
1971 """
1972 board_name = get_board()
1973 if is_virtual_machine():
1974 board = '%s_VM' % board_name
1975 else:
1976 # Rounded to nearest GB and GHz.
1977 memory = int(round(get_mem_total() / 1024.0))
1978 # Convert frequency to GHz with 1 digit accuracy after the
1979 # decimal point.
1980 frequency = int(round(get_cpu_max_frequency() * 1e-8)) * 0.1
1981 board = '%s_%1.1fGHz_%dGB' % (board_name, frequency, memory)
1982 return board
1983
1984
1985def get_mem_total():
1986 """
1987 Returns the total memory available in the system in MBytes.
1988 """
1989 mem_total = _get_float_from_file(_MEMINFO, 'MemTotal:', 'MemTotal:', ' kB')
1990 # Sanity check, all Chromebooks have at least 1GB of memory.
1991 assert mem_total > 256 * 1024, 'Unreasonable amount of memory.'
1992 return mem_total / 1024
1993
1994
1995def get_mem_free():
1996 """
1997 Returns the currently free memory in the system in MBytes.
1998 """
1999 mem_free = _get_float_from_file(_MEMINFO, 'MemFree:', 'MemFree:', ' kB')
2000 return mem_free / 1024
2001
Kristoffer Erlandssonb84da2b2017-11-01 21:29:24 +01002002def get_mem_free_plus_buffers_and_cached():
2003 """
2004 Returns the free memory in MBytes, counting buffers and cached as free.
2005
2006 This is most often the most interesting number since buffers and cached
2007 memory can be reclaimed on demand. Note however, that there are cases
2008 where this as misleading as well, for example used tmpfs space
2009 count as Cached but can not be reclaimed on demand.
2010 See https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt.
2011 """
2012 free_mb = get_mem_free()
2013 cached_mb = (_get_float_from_file(
2014 _MEMINFO, 'Cached:', 'Cached:', ' kB') / 1024)
2015 buffers_mb = (_get_float_from_file(
2016 _MEMINFO, 'Buffers:', 'Buffers:', ' kB') / 1024)
2017 return free_mb + buffers_mb + cached_mb
Allen Li2c32d6b2017-02-03 15:28:10 -08002018
2019def get_kernel_max():
2020 """
2021 Returns content of kernel_max.
2022 """
2023 kernel_max = _get_int_from_file(_KERNEL_MAX, 0, None, None)
2024 # Sanity check.
2025 assert ((kernel_max > 0) and (kernel_max < 257)), 'Unreasonable kernel_max.'
2026 return kernel_max
2027
2028
2029def set_high_performance_mode():
2030 """
2031 Sets the kernel governor mode to the highest setting.
2032 Returns previous governor state.
2033 """
2034 original_governors = get_scaling_governor_states()
2035 set_scaling_governors('performance')
2036 return original_governors
2037
2038
2039def set_scaling_governors(value):
2040 """
2041 Sets all scaling governor to string value.
2042 Sample values: 'performance', 'interactive', 'ondemand', 'powersave'.
2043 """
2044 paths = _get_cpufreq_paths('scaling_governor')
2045 for path in paths:
2046 cmd = 'echo %s > %s' % (value, path)
2047 logging.info('Writing scaling governor mode \'%s\' -> %s', value, path)
2048 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
2049 utils.system(cmd, ignore_status=True)
2050
2051
2052def _get_cpufreq_paths(filename):
2053 """
2054 Returns a list of paths to the governors.
2055 """
2056 cmd = 'ls /sys/devices/system/cpu/cpu*/cpufreq/' + filename
2057 paths = utils.run(cmd, verbose=False).stdout.splitlines()
2058 return paths
2059
2060
2061def get_scaling_governor_states():
2062 """
2063 Returns a list of (performance governor path, current state) tuples.
2064 """
2065 paths = _get_cpufreq_paths('scaling_governor')
2066 path_value_list = []
2067 for path in paths:
2068 value = _get_line_from_file(path, 0)
2069 path_value_list.append((path, value))
2070 return path_value_list
2071
2072
2073def restore_scaling_governor_states(path_value_list):
2074 """
2075 Restores governor states. Inverse operation to get_scaling_governor_states.
2076 """
2077 for (path, value) in path_value_list:
2078 cmd = 'echo %s > %s' % (value.rstrip('\n'), path)
2079 # On Tegra CPUs can be dynamically enabled/disabled. Ignore failures.
2080 utils.system(cmd, ignore_status=True)
2081
2082
2083def get_dirty_writeback_centisecs():
2084 """
2085 Reads /proc/sys/vm/dirty_writeback_centisecs.
2086 """
2087 time = _get_int_from_file(_DIRTY_WRITEBACK_CENTISECS, 0, None, None)
2088 return time
2089
2090
2091def set_dirty_writeback_centisecs(time=60000):
2092 """
2093 In hundredths of a second, this is how often pdflush wakes up to write data
2094 to disk. The default wakes up the two (or more) active threads every five
2095 seconds. The ChromeOS default is 10 minutes.
2096
2097 We use this to set as low as 1 second to flush error messages in system
2098 logs earlier to disk.
2099 """
2100 # Flush buffers first to make this function synchronous.
2101 utils.system('sync')
2102 if time >= 0:
2103 cmd = 'echo %d > %s' % (time, _DIRTY_WRITEBACK_CENTISECS)
2104 utils.system(cmd)
2105
2106
2107def wflinfo_cmd():
2108 """
2109 Returns a wflinfo command appropriate to the current graphics platform/api.
2110 """
2111 return 'wflinfo -p %s -a %s' % (graphics_platform(), graphics_api())
2112
2113
2114def has_mali():
2115 """ @return: True if system has a Mali GPU enabled."""
2116 return os.path.exists('/dev/mali0')
2117
2118def get_gpu_family():
2119 """Returns the GPU family name."""
2120 global pciid_to_amd_architecture
2121 global pciid_to_intel_architecture
2122
2123 socfamily = get_cpu_soc_family()
2124 if socfamily == 'exynos5' or socfamily == 'rockchip' or has_mali():
2125 cmd = wflinfo_cmd()
2126 wflinfo = utils.system_output(cmd,
2127 retain_output=True,
2128 ignore_status=False)
2129 version = re.findall(r'OpenGL renderer string: '
2130 r'Mali-T([0-9]+)', wflinfo)
2131 if version:
2132 return 'mali-t%s' % version[0]
2133 return 'mali-unrecognized'
2134 if socfamily == 'tegra':
2135 return 'tegra'
2136 if os.path.exists('/sys/kernel/debug/pvr'):
2137 return 'rogue'
2138
2139 pci_vga_device = utils.run("lspci | grep VGA").stdout.rstrip('\n')
2140 bus_device_function = pci_vga_device.partition(' ')[0]
2141 pci_path = '/sys/bus/pci/devices/0000:' + bus_device_function + '/device'
2142
2143 if not os.path.exists(pci_path):
2144 raise error.TestError('PCI device 0000:' + bus_device_function + ' not found')
2145
2146 device_id = utils.read_one_line(pci_path).lower()
2147
2148 if "Advanced Micro Devices" in pci_vga_device:
2149 if not pciid_to_amd_architecture:
2150 with open(_AMD_PCI_IDS_FILE_PATH, 'r') as in_f:
2151 pciid_to_amd_architecture = json.load(in_f)
2152
2153 return pciid_to_amd_architecture[device_id]
2154
2155 if "Intel Corporation" in pci_vga_device:
2156 # Only load Intel PCI ID file once and only if necessary.
2157 if not pciid_to_intel_architecture:
2158 with open(_INTEL_PCI_IDS_FILE_PATH, 'r') as in_f:
2159 pciid_to_intel_architecture = json.load(in_f)
2160
2161 return pciid_to_intel_architecture[device_id]
2162
2163# TODO(ihf): Consider using /etc/lsb-release DEVICETYPE != CHROMEBOOK/CHROMEBASE
2164# for sanity check, but usage seems a bit inconsistent. See
2165# src/third_party/chromiumos-overlay/eclass/appid.eclass
2166_BOARDS_WITHOUT_MONITOR = [
2167 'anglar', 'mccloud', 'monroe', 'ninja', 'rikku', 'guado', 'jecht', 'tidus',
Brian Norrisbb745382017-06-30 11:49:27 -07002168 'beltino', 'panther', 'stumpy', 'panther', 'tricky', 'zako', 'veyron_rialto'
Allen Li2c32d6b2017-02-03 15:28:10 -08002169]
2170
2171
2172def has_no_monitor():
2173 """Returns whether a machine doesn't have a built-in monitor."""
2174 board_name = get_board()
2175 if board_name in _BOARDS_WITHOUT_MONITOR:
2176 return True
2177
2178 return False
2179
2180
2181def get_fixed_dst_drive():
2182 """
2183 Return device name for internal disk.
2184 Example: return /dev/sda for falco booted from usb
2185 """
2186 cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
2187 '. /usr/share/misc/chromeos-common.sh;',
2188 'load_base_vars;',
2189 'get_fixed_dst_drive'])
2190 return utils.system_output(cmd)
2191
2192
2193def get_root_device():
2194 """
2195 Return root device.
2196 Will return correct disk device even system boot from /dev/dm-0
2197 Example: return /dev/sdb for falco booted from usb
2198 """
2199 return utils.system_output('rootdev -s -d')
2200
2201
2202def get_root_partition():
2203 """
2204 Return current root partition
2205 Example: return /dev/sdb3 for falco booted from usb
2206 """
2207 return utils.system_output('rootdev -s')
2208
2209
2210def get_free_root_partition(root_part=None):
2211 """
2212 Return currently unused root partion
2213 Example: return /dev/sdb5 for falco booted from usb
2214
2215 @param root_part: cuurent root partition
2216 """
2217 spare_root_map = {'3': '5', '5': '3'}
2218 if not root_part:
2219 root_part = get_root_partition()
2220 return root_part[:-1] + spare_root_map[root_part[-1]]
2221
2222
2223def get_kernel_partition(root_part=None):
2224 """
2225 Return current kernel partition
2226 Example: return /dev/sda2 for falco booted from usb
2227
2228 @param root_part: current root partition
2229 """
2230 if not root_part:
2231 root_part = get_root_partition()
2232 current_kernel_map = {'3': '2', '5': '4'}
2233 return root_part[:-1] + current_kernel_map[root_part[-1]]
2234
2235
2236def get_free_kernel_partition(root_part=None):
2237 """
2238 return currently unused kernel partition
2239 Example: return /dev/sda4 for falco booted from usb
2240
2241 @param root_part: current root partition
2242 """
2243 kernel_part = get_kernel_partition(root_part)
2244 spare_kernel_map = {'2': '4', '4': '2'}
2245 return kernel_part[:-1] + spare_kernel_map[kernel_part[-1]]
2246
2247
2248def is_booted_from_internal_disk():
2249 """Return True if boot from internal disk. False, otherwise."""
2250 return get_root_device() == get_fixed_dst_drive()
2251
2252
2253def get_ui_use_flags():
2254 """Parses the USE flags as listed in /etc/ui_use_flags.txt.
2255
2256 @return: A list of flag strings found in the ui use flags file.
2257 """
2258 flags = []
2259 for flag in utils.read_file(_UI_USE_FLAGS_FILE_PATH).splitlines():
2260 # Removes everything after the '#'.
2261 flag_before_comment = flag.split('#')[0].strip()
2262 if len(flag_before_comment) != 0:
2263 flags.append(flag_before_comment)
2264
2265 return flags
2266
2267
Allen Li2c32d6b2017-02-03 15:28:10 -08002268def graphics_platform():
2269 """
2270 Return a string identifying the graphics platform,
2271 e.g. 'glx' or 'x11_egl' or 'gbm'
2272 """
Po-Hsien Wangdab8e182017-05-03 10:25:41 -07002273 return 'null'
Allen Li2c32d6b2017-02-03 15:28:10 -08002274
2275
2276def graphics_api():
2277 """Return a string identifying the graphics api, e.g. gl or gles2."""
2278 use_flags = get_ui_use_flags()
2279 if 'opengles' in use_flags:
2280 return 'gles2'
2281 return 'gl'
2282
2283
Allen Li2c32d6b2017-02-03 15:28:10 -08002284def is_vm():
2285 """Check if the process is running in a virtual machine.
2286
2287 @return: True if the process is running in a virtual machine, otherwise
2288 return False.
2289 """
2290 try:
2291 virt = utils.run('sudo -n virt-what').stdout.strip()
2292 logging.debug('virt-what output: %s', virt)
2293 return bool(virt)
2294 except error.CmdError:
2295 logging.warn('Package virt-what is not installed, default to assume '
2296 'it is not a virtual machine.')
2297 return False
2298
2299
2300def is_package_installed(package):
2301 """Check if a package is installed already.
2302
2303 @return: True if the package is already installed, otherwise return False.
2304 """
2305 try:
2306 utils.run(_CHECK_PACKAGE_INSTALLED_COMMAND % package)
2307 return True
2308 except error.CmdError:
2309 logging.warn('Package %s is not installed.', package)
2310 return False
2311
2312
2313def is_python_package_installed(package):
2314 """Check if a Python package is installed already.
2315
2316 @return: True if the package is already installed, otherwise return False.
2317 """
2318 try:
2319 __import__(package)
2320 return True
2321 except ImportError:
2322 logging.warn('Python package %s is not installed.', package)
2323 return False
2324
2325
2326def run_sql_cmd(server, user, password, command, database=''):
2327 """Run the given sql command against the specified database.
2328
2329 @param server: Hostname or IP address of the MySQL server.
2330 @param user: User name to log in the MySQL server.
2331 @param password: Password to log in the MySQL server.
2332 @param command: SQL command to run.
2333 @param database: Name of the database to run the command. Default to empty
2334 for command that does not require specifying database.
2335
2336 @return: The stdout of the command line.
2337 """
2338 cmd = ('mysql -u%s -p%s --host %s %s -e "%s"' %
2339 (user, password, server, database, command))
2340 # Set verbose to False so the command line won't be logged, as it includes
2341 # database credential.