blob: c14416c2ae6642fa920ea8cba19cde71698f91dd [file] [log] [blame]
Gaurav Shaha7fb8962011-08-16 15:06:32 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
Daniel Erat3e3f7f42010-03-29 17:19:14 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Elly Jones2f0ebba2011-10-27 13:43:20 -04005import logging, os, platform, re, signal, tempfile, time, uuid
Daniel Erat3e3f7f42010-03-29 17:19:14 -07006from autotest_lib.client.common_lib import error
Vadim Bendeburye7970922011-04-07 17:07:37 -07007from autotest_lib.client.common_lib import utils
Daniel Erat3e3f7f42010-03-29 17:19:14 -07008
rgindaf25f73f2010-04-07 14:55:25 -07009class TimeoutError(error.TestError):
10 """Error raised when we time out when waiting on a condition."""
David Jamesd51ac9c2011-09-10 00:45:24 -070011 pass
rgindaf25f73f2010-04-07 14:55:25 -070012
13
Vadim Bendeburye7970922011-04-07 17:07:37 -070014class Crossystem(object):
15 """A wrapper for the crossystem utility."""
16
17 def __init__(self, client):
18 self.cros_system_data = {}
19 self._client = client
20
21 def init(self):
22 self.cros_system_data = {}
23 (_, fname) = tempfile.mkstemp()
24 f = open(fname, 'w')
25 self._client.run('crossystem', stdout_tee=f)
26 f.close()
27 text = utils.read_file(fname)
28 for line in text.splitlines():
29 assignment_string = line.split('#')[0]
30 if not assignment_string.count('='):
31 continue
32 (name, value) = assignment_string.split('=', 1)
33 self.cros_system_data[name.strip()] = value.strip()
34 os.remove(fname)
35
36 def __getattr__(self, name):
37 """
38 Retrieve a crosssystem attribute.
39
40 The call crossystemobject.name() will return the crossystem reported
41 string.
42 """
43 return lambda : self.cros_system_data[name]
44
45
Chris Masoneaf859092012-11-19 16:44:44 -080046def get_oldest_pid_by_name(name):
47 """
48 Return the oldest pid of a process whose name perfectly matches |name|.
49
50 name is an egrep expression, which will be matched against the entire name
51 of processes on the system. For example:
52
53 get_oldest_pid_by_name('chrome')
54
55 on a system running
56 8600 ? 00:00:04 chrome
57 8601 ? 00:00:00 chrome
58 8602 ? 00:00:00 chrome-sandbox
59
60 would return 8600, as that's the oldest process that matches.
61 chrome-sandbox would not be matched.
62
63 Arguments:
64 name: egrep expression to match. Will be anchored at the beginning and
65 end of the match string.
66
67 Returns:
68 pid as an integer, or None if one cannot be found.
69
70 Raises:
71 ValueError if pgrep returns something odd.
72 """
73 str_pid = utils.system_output(
74 'pgrep -o ^%s$' % name, ignore_status=True).rstrip()
75 if str_pid:
76 return int(str_pid)
77
78
Dan Shi03c3e442013-10-22 14:41:23 -070079def get_oldest_by_name(name):
80 """Return pid and command line of oldest process whose name matches |name|.
81
82 @param name: egrep expression to match desired process name.
83 @return: A tuple of (pid, command_line) of the oldest process whose name
84 matches |name|.
85
86 """
87 pid = get_oldest_pid_by_name(name)
88 if pid:
89 command_line = utils.system_output('ps -p %i -o command=' % pid,
90 ignore_status=True).rstrip()
91 return (pid, command_line)
92
93
Rohit Makasanac018b972013-07-29 21:25:39 +053094def get_process_list(name, command_line=None):
95 """
96 Return the list of pid for matching process |name command_line|.
97
98 on a system running
99 31475 ? 0:06 /opt/google/chrome/chrome --allow-webui-compositing -
100 31478 ? 0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/
101 31485 ? 0:00 /opt/google/chrome/chrome --type=zygote --log-level=1
102 31532 ? 1:05 /opt/google/chrome/chrome --type=renderer
103
104 get_process_list('chrome')
105 would return ['31475', '31485', '31532']
106
107 get_process_list('chrome', '--type=renderer')
108 would return ['31532']
109
110 Arguments:
111 name: process name to search for. If command_line is provided, name is
112 matched against full command line. If command_line is not provided,
113 name is only matched against the process name.
114 command line: when command line is passed, the full process command line
115 is used for matching.
116
117 Returns:
118 list of PIDs of the matching processes.
119
120 """
121 # TODO(rohitbm) crbug.com/268861
122 flag = '-x' if not command_line else '-f'
123 name = '\'%s.*%s\'' % (name, command_line) if command_line else name
124 str_pid = utils.system_output(
125 'pgrep %s %s' % (flag, name), ignore_status=True).rstrip()
126 return str_pid
127
128
David Jamesd51ac9c2011-09-10 00:45:24 -0700129def nuke_process_by_name(name, with_prejudice=False):
130 try:
Chris Masoneaf859092012-11-19 16:44:44 -0800131 pid = get_oldest_pid_by_name(name)
David Jamesd51ac9c2011-09-10 00:45:24 -0700132 except Exception as e:
133 logging.error(e)
134 return
Chris Masone7d6af682013-08-06 13:47:44 -0700135 if pid is None:
136 raise error.AutoservPidAlreadyDeadError(
137 'No process matching %s.' % name)
David Jamesd51ac9c2011-09-10 00:45:24 -0700138 if with_prejudice:
139 utils.nuke_pid(pid, [signal.SIGKILL])
140 else:
141 utils.nuke_pid(pid)
142
143
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700144def poll_for_condition(
rgindaf25f73f2010-04-07 14:55:25 -0700145 condition, exception=None, timeout=10, sleep_interval=0.1, desc=None):
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700146 """Poll until a condition becomes true.
147
Jon Salz4f646a32011-11-30 14:42:51 +0800148 Arguments:
149 condition: function taking no args and returning bool
150 exception: exception to throw if condition doesn't become true
151 timeout: maximum number of seconds to wait
152 sleep_interval: time to sleep between polls
153 desc: description of default TimeoutError used if 'exception' is None
154
155 Returns:
156 The true value that caused the poll loop to terminate.
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700157
158 Raises:
rgindaf25f73f2010-04-07 14:55:25 -0700159 'exception' arg if supplied; site_utils.TimeoutError otherwise
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700160 """
161 start_time = time.time()
162 while True:
Jon Salz4f646a32011-11-30 14:42:51 +0800163 value = condition()
164 if value:
165 return value
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700166 if time.time() + sleep_interval - start_time > timeout:
rgindaf25f73f2010-04-07 14:55:25 -0700167 if exception:
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700168 logging.error(exception)
rgindaf25f73f2010-04-07 14:55:25 -0700169 raise exception
170
171 if desc:
172 desc = 'Timed out waiting for condition: %s' % desc
173 else:
174 desc = 'Timed out waiting for unnamed condition'
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700175 logging.error(desc)
Mitsuru Oshima5d3e4542010-08-18 13:46:06 -0700176 raise TimeoutError, desc
rgindaf25f73f2010-04-07 14:55:25 -0700177
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700178 time.sleep(sleep_interval)
Thieu Le1904d002010-11-30 17:10:24 -0800179
180
181def save_vm_state(checkpoint):
182 """Saves the current state of the virtual machine.
183
184 This function is a NOOP if the test is not running under a virtual machine
185 with the USB serial port redirected.
186
187 Arguments:
188 checkpoint - Name used to identify this state
189
190 Returns:
191 None
192 """
193 # The QEMU monitor has been redirected to the guest serial port located at
194 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
195 # command to the serial port.
196 proc = platform.processor()
197 if 'QEMU' in proc and os.path.exists('/dev/ttyUSB0'):
198 logging.info('Saving VM state "%s"' % checkpoint)
199 serial = open('/dev/ttyUSB0', 'w')
200 serial.write("savevm %s\r\n" % checkpoint)
201 logging.info('Done saving VM state "%s"' % checkpoint)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800202
203
204def check_raw_dmesg(dmesg, message_level, whitelist):
205 """Checks dmesg for unexpected warnings.
206
207 This function parses dmesg for message with message_level <= message_level
208 which do not appear in the whitelist.
209
210 Arguments:
211 dmesg - string containing raw dmesg buffer
212 message_level - minimum message priority to check
213 whitelist - messages to ignore
214
215 Returns:
216 List of unexpected warnings
217 """
Vadim Bendeburye7970922011-04-07 17:07:37 -0700218 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800219 unexpected = []
220 for line in dmesg.splitlines():
221 if int(line[1]) <= message_level:
Vadim Bendeburye7970922011-04-07 17:07:37 -0700222 stripped_line = line.split('] ', 1)[1]
223 if whitelist_re.search(stripped_line):
224 continue
225 unexpected.append(stripped_line)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800226 return unexpected
Vadim Bendebury29916f22011-04-13 10:54:47 -0700227
228def verify_mesg_set(mesg, regex, whitelist):
229 """Verifies that the exact set of messages are present in a text.
230
231 This function finds all strings in the text matching a certain regex, and
232 then verifies that all expected strings are present in the set, and no
233 unexpected strings are there.
234
235 Arguments:
236 mesg - the mutiline text to be scanned
237 regex - regular expression to match
238 whitelist - messages to find in the output, a list of strings
239 (potentially regexes) to look for in the filtered output. All these
240 strings must be there, and no other strings should be present in the
241 filtered output.
242
243 Returns:
244 string of inconsistent findings (i.e. an empty string on success).
245 """
246
247 rv = []
248
249 missing_strings = []
250 present_strings = []
251 for line in mesg.splitlines():
252 if not re.search(r'%s' % regex, line):
253 continue
254 present_strings.append(line.split('] ', 1)[1])
255
256 for string in whitelist:
257 for present_string in list(present_strings):
258 if re.search(r'^%s$' % string, present_string):
259 present_strings.remove(present_string)
260 break
261 else:
262 missing_strings.append(string)
263
264 if present_strings:
265 rv.append('unexpected strings:')
266 rv.extend(present_strings)
267 if missing_strings:
268 rv.append('missing strings:')
269 rv.extend(missing_strings)
270
271 return '\n'.join(rv)
Ahmad Shariff8e92622011-05-24 12:37:39 -0700272
273
Mike Frysingere72f7e42012-03-16 14:49:11 -0400274def target_is_pie():
275 """Returns whether the toolchain produces a PIE (position independent
Ahmad Shariff8e92622011-05-24 12:37:39 -0700276 executable) by default.
277
278 Arguments:
279 None
280
281 Returns:
Mike Frysingere72f7e42012-03-16 14:49:11 -0400282 True if the target toolchain produces a PIE by default.
Ahmad Shariff8e92622011-05-24 12:37:39 -0700283 False otherwise.
284 """
285
286
Mike Frysingere72f7e42012-03-16 14:49:11 -0400287 command = 'echo | ${CC} -E -dD -P - | grep -i pie'
Ahmad Shariff8e92622011-05-24 12:37:39 -0700288 result = utils.system_output(command, retain_output=True,
289 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400290 if re.search('#define __PIE__', result):
Ahmad Shariff8e92622011-05-24 12:37:39 -0700291 return True
292 else:
293 return False
Elly Jones686c2f42011-10-24 16:45:07 -0400294
Sonny Rao172edad2012-02-07 23:23:58 +0000295def target_is_x86():
296 """Returns whether the toolchain produces an x86 object
297
298 Arguments:
299 None
300
301 Returns:
302 True if the target toolchain produces an x86 object
303 False otherwise.
304 """
305
306
Mike Frysingere72f7e42012-03-16 14:49:11 -0400307 command = 'echo | ${CC} -E -dD -P - | grep -i 86'
Sonny Rao172edad2012-02-07 23:23:58 +0000308 result = utils.system_output(command, retain_output=True,
309 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400310 if re.search('__i386__', result) or re.search('__x86_64__', result):
Sonny Rao172edad2012-02-07 23:23:58 +0000311 return True
312 else:
313 return False
314
Elly Jones686c2f42011-10-24 16:45:07 -0400315def mounts():
316 ret = []
317 for line in file('/proc/mounts'):
318 m = re.match(r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
319 if m:
320 ret.append(m.groupdict())
321 return ret
322
323def is_mountpoint(path):
324 return path in [ m['dest'] for m in mounts() ]
325
326def require_mountpoint(path):
327 """
328 Raises an exception if path is not a mountpoint.
329 """
330 if not is_mountpoint(path):
331 raise error.TestFail('Path not mounted: "%s"' % path)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400332
333def random_username():
334 return str(uuid.uuid4()) + '@example.com'
Simran Basic6f1f7a2012-10-16 10:47:46 -0700335
336
337def parse_cmd_output(command, run_method=utils.run):
338 """Runs a command on a host object to retrieve host attributes.
339
340 The command should output to stdout in the format of:
341 <key> = <value> # <optional_comment>
342
343
344 @param command: Command to execute on the host.
345 @param run_method: Function to use to execute the command. Defaults to
346 utils.run so that the command will be executed locally.
347 Can be replace with a host.run call so that it will
348 execute on a DUT or external machine. Method must accept
349 a command argument, stdout_tee and stderr_tee args and
350 return a result object with a string attribute stdout
351 which will be parsed.
352
353 @returns a dictionary mapping host attributes to their values.
354 """
355 result = {}
356 # Suppresses stdout so that the files are not printed to the logs.
357 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
358 for line in cmd_result.stdout.splitlines():
359 # Lines are of the format "<key> = <value> # <comment>"
360 key_value = re.match('^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ]+)'
361 '(?:\s*#.*)?$', line)
362 if key_value:
363 result[key_value.group('key')] = key_value.group('value')
Chris Masoneaf859092012-11-19 16:44:44 -0800364 return result
Mike Truty77f06bc2013-03-30 09:43:05 -0500365
366
367def set_from_keyval_output(out, delimiter=' '):
368 """Parse delimiter-separated key-val output into a set of tuples.
369
370 Output is expected to be multiline text output from a command.
371 Stuffs the key-vals into tuples in a set to be later compared.
372
373 e.g. deactivated 0
374 disableForceClear 0
375 ==> set(('deactivated', '0'), ('disableForceClear', '0'))
376
377 @param out: multiple lines of space-separated key-val pairs.
378 @param delimiter: character that separates key from val. Usually a
379 space but may be '=' or something else.
380 @return set of key-val tuples.
381 """
382 results = set()
383 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
384 for linecr in out.splitlines():
385 match = kv_match_re.match(linecr.strip())
386 if match:
387 results.add((match.group(1), match.group(2)))
388 return results