blob: 679d88f453c70ab4265b7bc692b2927191e2d648 [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
David Jamesd51ac9c2011-09-10 00:45:24 -070079def nuke_process_by_name(name, with_prejudice=False):
80 try:
Chris Masoneaf859092012-11-19 16:44:44 -080081 pid = get_oldest_pid_by_name(name)
David Jamesd51ac9c2011-09-10 00:45:24 -070082 except Exception as e:
83 logging.error(e)
84 return
85 if with_prejudice:
86 utils.nuke_pid(pid, [signal.SIGKILL])
87 else:
88 utils.nuke_pid(pid)
89
90
Daniel Erat3e3f7f42010-03-29 17:19:14 -070091def poll_for_condition(
rgindaf25f73f2010-04-07 14:55:25 -070092 condition, exception=None, timeout=10, sleep_interval=0.1, desc=None):
Daniel Erat3e3f7f42010-03-29 17:19:14 -070093 """Poll until a condition becomes true.
94
Jon Salz4f646a32011-11-30 14:42:51 +080095 Arguments:
96 condition: function taking no args and returning bool
97 exception: exception to throw if condition doesn't become true
98 timeout: maximum number of seconds to wait
99 sleep_interval: time to sleep between polls
100 desc: description of default TimeoutError used if 'exception' is None
101
102 Returns:
103 The true value that caused the poll loop to terminate.
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700104
105 Raises:
rgindaf25f73f2010-04-07 14:55:25 -0700106 'exception' arg if supplied; site_utils.TimeoutError otherwise
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700107 """
108 start_time = time.time()
109 while True:
Jon Salz4f646a32011-11-30 14:42:51 +0800110 value = condition()
111 if value:
112 return value
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700113 if time.time() + sleep_interval - start_time > timeout:
rgindaf25f73f2010-04-07 14:55:25 -0700114 if exception:
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700115 logging.error(exception)
rgindaf25f73f2010-04-07 14:55:25 -0700116 raise exception
117
118 if desc:
119 desc = 'Timed out waiting for condition: %s' % desc
120 else:
121 desc = 'Timed out waiting for unnamed condition'
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700122 logging.error(desc)
Mitsuru Oshima5d3e4542010-08-18 13:46:06 -0700123 raise TimeoutError, desc
rgindaf25f73f2010-04-07 14:55:25 -0700124
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700125 time.sleep(sleep_interval)
Thieu Le1904d002010-11-30 17:10:24 -0800126
127
128def save_vm_state(checkpoint):
129 """Saves the current state of the virtual machine.
130
131 This function is a NOOP if the test is not running under a virtual machine
132 with the USB serial port redirected.
133
134 Arguments:
135 checkpoint - Name used to identify this state
136
137 Returns:
138 None
139 """
140 # The QEMU monitor has been redirected to the guest serial port located at
141 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
142 # command to the serial port.
143 proc = platform.processor()
144 if 'QEMU' in proc and os.path.exists('/dev/ttyUSB0'):
145 logging.info('Saving VM state "%s"' % checkpoint)
146 serial = open('/dev/ttyUSB0', 'w')
147 serial.write("savevm %s\r\n" % checkpoint)
148 logging.info('Done saving VM state "%s"' % checkpoint)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800149
150
151def check_raw_dmesg(dmesg, message_level, whitelist):
152 """Checks dmesg for unexpected warnings.
153
154 This function parses dmesg for message with message_level <= message_level
155 which do not appear in the whitelist.
156
157 Arguments:
158 dmesg - string containing raw dmesg buffer
159 message_level - minimum message priority to check
160 whitelist - messages to ignore
161
162 Returns:
163 List of unexpected warnings
164 """
Vadim Bendeburye7970922011-04-07 17:07:37 -0700165 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800166 unexpected = []
167 for line in dmesg.splitlines():
168 if int(line[1]) <= message_level:
Vadim Bendeburye7970922011-04-07 17:07:37 -0700169 stripped_line = line.split('] ', 1)[1]
170 if whitelist_re.search(stripped_line):
171 continue
172 unexpected.append(stripped_line)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800173 return unexpected
Vadim Bendebury29916f22011-04-13 10:54:47 -0700174
175def verify_mesg_set(mesg, regex, whitelist):
176 """Verifies that the exact set of messages are present in a text.
177
178 This function finds all strings in the text matching a certain regex, and
179 then verifies that all expected strings are present in the set, and no
180 unexpected strings are there.
181
182 Arguments:
183 mesg - the mutiline text to be scanned
184 regex - regular expression to match
185 whitelist - messages to find in the output, a list of strings
186 (potentially regexes) to look for in the filtered output. All these
187 strings must be there, and no other strings should be present in the
188 filtered output.
189
190 Returns:
191 string of inconsistent findings (i.e. an empty string on success).
192 """
193
194 rv = []
195
196 missing_strings = []
197 present_strings = []
198 for line in mesg.splitlines():
199 if not re.search(r'%s' % regex, line):
200 continue
201 present_strings.append(line.split('] ', 1)[1])
202
203 for string in whitelist:
204 for present_string in list(present_strings):
205 if re.search(r'^%s$' % string, present_string):
206 present_strings.remove(present_string)
207 break
208 else:
209 missing_strings.append(string)
210
211 if present_strings:
212 rv.append('unexpected strings:')
213 rv.extend(present_strings)
214 if missing_strings:
215 rv.append('missing strings:')
216 rv.extend(missing_strings)
217
218 return '\n'.join(rv)
Ahmad Shariff8e92622011-05-24 12:37:39 -0700219
220
Mike Frysingere72f7e42012-03-16 14:49:11 -0400221def target_is_pie():
222 """Returns whether the toolchain produces a PIE (position independent
Ahmad Shariff8e92622011-05-24 12:37:39 -0700223 executable) by default.
224
225 Arguments:
226 None
227
228 Returns:
Mike Frysingere72f7e42012-03-16 14:49:11 -0400229 True if the target toolchain produces a PIE by default.
Ahmad Shariff8e92622011-05-24 12:37:39 -0700230 False otherwise.
231 """
232
233
Mike Frysingere72f7e42012-03-16 14:49:11 -0400234 command = 'echo | ${CC} -E -dD -P - | grep -i pie'
Ahmad Shariff8e92622011-05-24 12:37:39 -0700235 result = utils.system_output(command, retain_output=True,
236 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400237 if re.search('#define __PIE__', result):
Ahmad Shariff8e92622011-05-24 12:37:39 -0700238 return True
239 else:
240 return False
Elly Jones686c2f42011-10-24 16:45:07 -0400241
Sonny Rao172edad2012-02-07 23:23:58 +0000242def target_is_x86():
243 """Returns whether the toolchain produces an x86 object
244
245 Arguments:
246 None
247
248 Returns:
249 True if the target toolchain produces an x86 object
250 False otherwise.
251 """
252
253
Mike Frysingere72f7e42012-03-16 14:49:11 -0400254 command = 'echo | ${CC} -E -dD -P - | grep -i 86'
Sonny Rao172edad2012-02-07 23:23:58 +0000255 result = utils.system_output(command, retain_output=True,
256 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400257 if re.search('__i386__', result) or re.search('__x86_64__', result):
Sonny Rao172edad2012-02-07 23:23:58 +0000258 return True
259 else:
260 return False
261
Elly Jones686c2f42011-10-24 16:45:07 -0400262def mounts():
263 ret = []
264 for line in file('/proc/mounts'):
265 m = re.match(r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
266 if m:
267 ret.append(m.groupdict())
268 return ret
269
270def is_mountpoint(path):
271 return path in [ m['dest'] for m in mounts() ]
272
273def require_mountpoint(path):
274 """
275 Raises an exception if path is not a mountpoint.
276 """
277 if not is_mountpoint(path):
278 raise error.TestFail('Path not mounted: "%s"' % path)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400279
280def random_username():
281 return str(uuid.uuid4()) + '@example.com'
Simran Basic6f1f7a2012-10-16 10:47:46 -0700282
283
284def parse_cmd_output(command, run_method=utils.run):
285 """Runs a command on a host object to retrieve host attributes.
286
287 The command should output to stdout in the format of:
288 <key> = <value> # <optional_comment>
289
290
291 @param command: Command to execute on the host.
292 @param run_method: Function to use to execute the command. Defaults to
293 utils.run so that the command will be executed locally.
294 Can be replace with a host.run call so that it will
295 execute on a DUT or external machine. Method must accept
296 a command argument, stdout_tee and stderr_tee args and
297 return a result object with a string attribute stdout
298 which will be parsed.
299
300 @returns a dictionary mapping host attributes to their values.
301 """
302 result = {}
303 # Suppresses stdout so that the files are not printed to the logs.
304 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
305 for line in cmd_result.stdout.splitlines():
306 # Lines are of the format "<key> = <value> # <comment>"
307 key_value = re.match('^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ]+)'
308 '(?:\s*#.*)?$', line)
309 if key_value:
310 result[key_value.group('key')] = key_value.group('value')
Chris Masoneaf859092012-11-19 16:44:44 -0800311 return result
Mike Truty77f06bc2013-03-30 09:43:05 -0500312
313
314def set_from_keyval_output(out, delimiter=' '):
315 """Parse delimiter-separated key-val output into a set of tuples.
316
317 Output is expected to be multiline text output from a command.
318 Stuffs the key-vals into tuples in a set to be later compared.
319
320 e.g. deactivated 0
321 disableForceClear 0
322 ==> set(('deactivated', '0'), ('disableForceClear', '0'))
323
324 @param out: multiple lines of space-separated key-val pairs.
325 @param delimiter: character that separates key from val. Usually a
326 space but may be '=' or something else.
327 @return set of key-val tuples.
328 """
329 results = set()
330 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
331 for linecr in out.splitlines():
332 match = kv_match_re.match(linecr.strip())
333 if match:
334 results.add((match.group(1), match.group(2)))
335 return results