blob: e79986cf41ead79cd32d6c9dc20df7c7f5435f86 [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
Rohit Makasanac018b972013-07-29 21:25:39 +053079def get_process_list(name, command_line=None):
80 """
81 Return the list of pid for matching process |name command_line|.
82
83 on a system running
84 31475 ? 0:06 /opt/google/chrome/chrome --allow-webui-compositing -
85 31478 ? 0:00 /opt/google/chrome/chrome-sandbox /opt/google/chrome/
86 31485 ? 0:00 /opt/google/chrome/chrome --type=zygote --log-level=1
87 31532 ? 1:05 /opt/google/chrome/chrome --type=renderer
88
89 get_process_list('chrome')
90 would return ['31475', '31485', '31532']
91
92 get_process_list('chrome', '--type=renderer')
93 would return ['31532']
94
95 Arguments:
96 name: process name to search for. If command_line is provided, name is
97 matched against full command line. If command_line is not provided,
98 name is only matched against the process name.
99 command line: when command line is passed, the full process command line
100 is used for matching.
101
102 Returns:
103 list of PIDs of the matching processes.
104
105 """
106 # TODO(rohitbm) crbug.com/268861
107 flag = '-x' if not command_line else '-f'
108 name = '\'%s.*%s\'' % (name, command_line) if command_line else name
109 str_pid = utils.system_output(
110 'pgrep %s %s' % (flag, name), ignore_status=True).rstrip()
111 return str_pid
112
113
David Jamesd51ac9c2011-09-10 00:45:24 -0700114def nuke_process_by_name(name, with_prejudice=False):
115 try:
Chris Masoneaf859092012-11-19 16:44:44 -0800116 pid = get_oldest_pid_by_name(name)
David Jamesd51ac9c2011-09-10 00:45:24 -0700117 except Exception as e:
118 logging.error(e)
119 return
Chris Masone7d6af682013-08-06 13:47:44 -0700120 if pid is None:
121 raise error.AutoservPidAlreadyDeadError(
122 'No process matching %s.' % name)
David Jamesd51ac9c2011-09-10 00:45:24 -0700123 if with_prejudice:
124 utils.nuke_pid(pid, [signal.SIGKILL])
125 else:
126 utils.nuke_pid(pid)
127
128
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700129def poll_for_condition(
rgindaf25f73f2010-04-07 14:55:25 -0700130 condition, exception=None, timeout=10, sleep_interval=0.1, desc=None):
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700131 """Poll until a condition becomes true.
132
Jon Salz4f646a32011-11-30 14:42:51 +0800133 Arguments:
134 condition: function taking no args and returning bool
135 exception: exception to throw if condition doesn't become true
136 timeout: maximum number of seconds to wait
137 sleep_interval: time to sleep between polls
138 desc: description of default TimeoutError used if 'exception' is None
139
140 Returns:
141 The true value that caused the poll loop to terminate.
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700142
143 Raises:
rgindaf25f73f2010-04-07 14:55:25 -0700144 'exception' arg if supplied; site_utils.TimeoutError otherwise
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700145 """
146 start_time = time.time()
147 while True:
Jon Salz4f646a32011-11-30 14:42:51 +0800148 value = condition()
149 if value:
150 return value
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700151 if time.time() + sleep_interval - start_time > timeout:
rgindaf25f73f2010-04-07 14:55:25 -0700152 if exception:
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700153 logging.error(exception)
rgindaf25f73f2010-04-07 14:55:25 -0700154 raise exception
155
156 if desc:
157 desc = 'Timed out waiting for condition: %s' % desc
158 else:
159 desc = 'Timed out waiting for unnamed condition'
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700160 logging.error(desc)
Mitsuru Oshima5d3e4542010-08-18 13:46:06 -0700161 raise TimeoutError, desc
rgindaf25f73f2010-04-07 14:55:25 -0700162
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700163 time.sleep(sleep_interval)
Thieu Le1904d002010-11-30 17:10:24 -0800164
165
166def save_vm_state(checkpoint):
167 """Saves the current state of the virtual machine.
168
169 This function is a NOOP if the test is not running under a virtual machine
170 with the USB serial port redirected.
171
172 Arguments:
173 checkpoint - Name used to identify this state
174
175 Returns:
176 None
177 """
178 # The QEMU monitor has been redirected to the guest serial port located at
179 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
180 # command to the serial port.
181 proc = platform.processor()
182 if 'QEMU' in proc and os.path.exists('/dev/ttyUSB0'):
183 logging.info('Saving VM state "%s"' % checkpoint)
184 serial = open('/dev/ttyUSB0', 'w')
185 serial.write("savevm %s\r\n" % checkpoint)
186 logging.info('Done saving VM state "%s"' % checkpoint)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800187
188
189def check_raw_dmesg(dmesg, message_level, whitelist):
190 """Checks dmesg for unexpected warnings.
191
192 This function parses dmesg for message with message_level <= message_level
193 which do not appear in the whitelist.
194
195 Arguments:
196 dmesg - string containing raw dmesg buffer
197 message_level - minimum message priority to check
198 whitelist - messages to ignore
199
200 Returns:
201 List of unexpected warnings
202 """
Vadim Bendeburye7970922011-04-07 17:07:37 -0700203 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800204 unexpected = []
205 for line in dmesg.splitlines():
206 if int(line[1]) <= message_level:
Vadim Bendeburye7970922011-04-07 17:07:37 -0700207 stripped_line = line.split('] ', 1)[1]
208 if whitelist_re.search(stripped_line):
209 continue
210 unexpected.append(stripped_line)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800211 return unexpected
Vadim Bendebury29916f22011-04-13 10:54:47 -0700212
213def verify_mesg_set(mesg, regex, whitelist):
214 """Verifies that the exact set of messages are present in a text.
215
216 This function finds all strings in the text matching a certain regex, and
217 then verifies that all expected strings are present in the set, and no
218 unexpected strings are there.
219
220 Arguments:
221 mesg - the mutiline text to be scanned
222 regex - regular expression to match
223 whitelist - messages to find in the output, a list of strings
224 (potentially regexes) to look for in the filtered output. All these
225 strings must be there, and no other strings should be present in the
226 filtered output.
227
228 Returns:
229 string of inconsistent findings (i.e. an empty string on success).
230 """
231
232 rv = []
233
234 missing_strings = []
235 present_strings = []
236 for line in mesg.splitlines():
237 if not re.search(r'%s' % regex, line):
238 continue
239 present_strings.append(line.split('] ', 1)[1])
240
241 for string in whitelist:
242 for present_string in list(present_strings):
243 if re.search(r'^%s$' % string, present_string):
244 present_strings.remove(present_string)
245 break
246 else:
247 missing_strings.append(string)
248
249 if present_strings:
250 rv.append('unexpected strings:')
251 rv.extend(present_strings)
252 if missing_strings:
253 rv.append('missing strings:')
254 rv.extend(missing_strings)
255
256 return '\n'.join(rv)
Ahmad Shariff8e92622011-05-24 12:37:39 -0700257
258
Mike Frysingere72f7e42012-03-16 14:49:11 -0400259def target_is_pie():
260 """Returns whether the toolchain produces a PIE (position independent
Ahmad Shariff8e92622011-05-24 12:37:39 -0700261 executable) by default.
262
263 Arguments:
264 None
265
266 Returns:
Mike Frysingere72f7e42012-03-16 14:49:11 -0400267 True if the target toolchain produces a PIE by default.
Ahmad Shariff8e92622011-05-24 12:37:39 -0700268 False otherwise.
269 """
270
271
Mike Frysingere72f7e42012-03-16 14:49:11 -0400272 command = 'echo | ${CC} -E -dD -P - | grep -i pie'
Ahmad Shariff8e92622011-05-24 12:37:39 -0700273 result = utils.system_output(command, retain_output=True,
274 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400275 if re.search('#define __PIE__', result):
Ahmad Shariff8e92622011-05-24 12:37:39 -0700276 return True
277 else:
278 return False
Elly Jones686c2f42011-10-24 16:45:07 -0400279
Sonny Rao172edad2012-02-07 23:23:58 +0000280def target_is_x86():
281 """Returns whether the toolchain produces an x86 object
282
283 Arguments:
284 None
285
286 Returns:
287 True if the target toolchain produces an x86 object
288 False otherwise.
289 """
290
291
Mike Frysingere72f7e42012-03-16 14:49:11 -0400292 command = 'echo | ${CC} -E -dD -P - | grep -i 86'
Sonny Rao172edad2012-02-07 23:23:58 +0000293 result = utils.system_output(command, retain_output=True,
294 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400295 if re.search('__i386__', result) or re.search('__x86_64__', result):
Sonny Rao172edad2012-02-07 23:23:58 +0000296 return True
297 else:
298 return False
299
Elly Jones686c2f42011-10-24 16:45:07 -0400300def mounts():
301 ret = []
302 for line in file('/proc/mounts'):
303 m = re.match(r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
304 if m:
305 ret.append(m.groupdict())
306 return ret
307
308def is_mountpoint(path):
309 return path in [ m['dest'] for m in mounts() ]
310
311def require_mountpoint(path):
312 """
313 Raises an exception if path is not a mountpoint.
314 """
315 if not is_mountpoint(path):
316 raise error.TestFail('Path not mounted: "%s"' % path)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400317
318def random_username():
319 return str(uuid.uuid4()) + '@example.com'
Simran Basic6f1f7a2012-10-16 10:47:46 -0700320
321
322def parse_cmd_output(command, run_method=utils.run):
323 """Runs a command on a host object to retrieve host attributes.
324
325 The command should output to stdout in the format of:
326 <key> = <value> # <optional_comment>
327
328
329 @param command: Command to execute on the host.
330 @param run_method: Function to use to execute the command. Defaults to
331 utils.run so that the command will be executed locally.
332 Can be replace with a host.run call so that it will
333 execute on a DUT or external machine. Method must accept
334 a command argument, stdout_tee and stderr_tee args and
335 return a result object with a string attribute stdout
336 which will be parsed.
337
338 @returns a dictionary mapping host attributes to their values.
339 """
340 result = {}
341 # Suppresses stdout so that the files are not printed to the logs.
342 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
343 for line in cmd_result.stdout.splitlines():
344 # Lines are of the format "<key> = <value> # <comment>"
345 key_value = re.match('^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ]+)'
346 '(?:\s*#.*)?$', line)
347 if key_value:
348 result[key_value.group('key')] = key_value.group('value')
Chris Masoneaf859092012-11-19 16:44:44 -0800349 return result
Mike Truty77f06bc2013-03-30 09:43:05 -0500350
351
352def set_from_keyval_output(out, delimiter=' '):
353 """Parse delimiter-separated key-val output into a set of tuples.
354
355 Output is expected to be multiline text output from a command.
356 Stuffs the key-vals into tuples in a set to be later compared.
357
358 e.g. deactivated 0
359 disableForceClear 0
360 ==> set(('deactivated', '0'), ('disableForceClear', '0'))
361
362 @param out: multiple lines of space-separated key-val pairs.
363 @param delimiter: character that separates key from val. Usually a
364 space but may be '=' or something else.
365 @return set of key-val tuples.
366 """
367 results = set()
368 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
369 for linecr in out.splitlines():
370 match = kv_match_re.match(linecr.strip())
371 if match:
372 results.add((match.group(1), match.group(2)))
373 return results