blob: 88e2384606f95e9dc506cbcb51f12433908e5b40 [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
120 if with_prejudice:
121 utils.nuke_pid(pid, [signal.SIGKILL])
122 else:
123 utils.nuke_pid(pid)
124
125
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700126def poll_for_condition(
rgindaf25f73f2010-04-07 14:55:25 -0700127 condition, exception=None, timeout=10, sleep_interval=0.1, desc=None):
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700128 """Poll until a condition becomes true.
129
Jon Salz4f646a32011-11-30 14:42:51 +0800130 Arguments:
131 condition: function taking no args and returning bool
132 exception: exception to throw if condition doesn't become true
133 timeout: maximum number of seconds to wait
134 sleep_interval: time to sleep between polls
135 desc: description of default TimeoutError used if 'exception' is None
136
137 Returns:
138 The true value that caused the poll loop to terminate.
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700139
140 Raises:
rgindaf25f73f2010-04-07 14:55:25 -0700141 'exception' arg if supplied; site_utils.TimeoutError otherwise
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700142 """
143 start_time = time.time()
144 while True:
Jon Salz4f646a32011-11-30 14:42:51 +0800145 value = condition()
146 if value:
147 return value
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700148 if time.time() + sleep_interval - start_time > timeout:
rgindaf25f73f2010-04-07 14:55:25 -0700149 if exception:
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700150 logging.error(exception)
rgindaf25f73f2010-04-07 14:55:25 -0700151 raise exception
152
153 if desc:
154 desc = 'Timed out waiting for condition: %s' % desc
155 else:
156 desc = 'Timed out waiting for unnamed condition'
Gaurav Shaha7fb8962011-08-16 15:06:32 -0700157 logging.error(desc)
Mitsuru Oshima5d3e4542010-08-18 13:46:06 -0700158 raise TimeoutError, desc
rgindaf25f73f2010-04-07 14:55:25 -0700159
Daniel Erat3e3f7f42010-03-29 17:19:14 -0700160 time.sleep(sleep_interval)
Thieu Le1904d002010-11-30 17:10:24 -0800161
162
163def save_vm_state(checkpoint):
164 """Saves the current state of the virtual machine.
165
166 This function is a NOOP if the test is not running under a virtual machine
167 with the USB serial port redirected.
168
169 Arguments:
170 checkpoint - Name used to identify this state
171
172 Returns:
173 None
174 """
175 # The QEMU monitor has been redirected to the guest serial port located at
176 # /dev/ttyUSB0. To save the state of the VM, we just send the 'savevm'
177 # command to the serial port.
178 proc = platform.processor()
179 if 'QEMU' in proc and os.path.exists('/dev/ttyUSB0'):
180 logging.info('Saving VM state "%s"' % checkpoint)
181 serial = open('/dev/ttyUSB0', 'w')
182 serial.write("savevm %s\r\n" % checkpoint)
183 logging.info('Done saving VM state "%s"' % checkpoint)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800184
185
186def check_raw_dmesg(dmesg, message_level, whitelist):
187 """Checks dmesg for unexpected warnings.
188
189 This function parses dmesg for message with message_level <= message_level
190 which do not appear in the whitelist.
191
192 Arguments:
193 dmesg - string containing raw dmesg buffer
194 message_level - minimum message priority to check
195 whitelist - messages to ignore
196
197 Returns:
198 List of unexpected warnings
199 """
Vadim Bendeburye7970922011-04-07 17:07:37 -0700200 whitelist_re = re.compile(r'(%s)' % '|'.join(whitelist))
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800201 unexpected = []
202 for line in dmesg.splitlines():
203 if int(line[1]) <= message_level:
Vadim Bendeburye7970922011-04-07 17:07:37 -0700204 stripped_line = line.split('] ', 1)[1]
205 if whitelist_re.search(stripped_line):
206 continue
207 unexpected.append(stripped_line)
Mandeep Singh Baines142ac8d2011-02-18 13:31:08 -0800208 return unexpected
Vadim Bendebury29916f22011-04-13 10:54:47 -0700209
210def verify_mesg_set(mesg, regex, whitelist):
211 """Verifies that the exact set of messages are present in a text.
212
213 This function finds all strings in the text matching a certain regex, and
214 then verifies that all expected strings are present in the set, and no
215 unexpected strings are there.
216
217 Arguments:
218 mesg - the mutiline text to be scanned
219 regex - regular expression to match
220 whitelist - messages to find in the output, a list of strings
221 (potentially regexes) to look for in the filtered output. All these
222 strings must be there, and no other strings should be present in the
223 filtered output.
224
225 Returns:
226 string of inconsistent findings (i.e. an empty string on success).
227 """
228
229 rv = []
230
231 missing_strings = []
232 present_strings = []
233 for line in mesg.splitlines():
234 if not re.search(r'%s' % regex, line):
235 continue
236 present_strings.append(line.split('] ', 1)[1])
237
238 for string in whitelist:
239 for present_string in list(present_strings):
240 if re.search(r'^%s$' % string, present_string):
241 present_strings.remove(present_string)
242 break
243 else:
244 missing_strings.append(string)
245
246 if present_strings:
247 rv.append('unexpected strings:')
248 rv.extend(present_strings)
249 if missing_strings:
250 rv.append('missing strings:')
251 rv.extend(missing_strings)
252
253 return '\n'.join(rv)
Ahmad Shariff8e92622011-05-24 12:37:39 -0700254
255
Mike Frysingere72f7e42012-03-16 14:49:11 -0400256def target_is_pie():
257 """Returns whether the toolchain produces a PIE (position independent
Ahmad Shariff8e92622011-05-24 12:37:39 -0700258 executable) by default.
259
260 Arguments:
261 None
262
263 Returns:
Mike Frysingere72f7e42012-03-16 14:49:11 -0400264 True if the target toolchain produces a PIE by default.
Ahmad Shariff8e92622011-05-24 12:37:39 -0700265 False otherwise.
266 """
267
268
Mike Frysingere72f7e42012-03-16 14:49:11 -0400269 command = 'echo | ${CC} -E -dD -P - | grep -i pie'
Ahmad Shariff8e92622011-05-24 12:37:39 -0700270 result = utils.system_output(command, retain_output=True,
271 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400272 if re.search('#define __PIE__', result):
Ahmad Shariff8e92622011-05-24 12:37:39 -0700273 return True
274 else:
275 return False
Elly Jones686c2f42011-10-24 16:45:07 -0400276
Sonny Rao172edad2012-02-07 23:23:58 +0000277def target_is_x86():
278 """Returns whether the toolchain produces an x86 object
279
280 Arguments:
281 None
282
283 Returns:
284 True if the target toolchain produces an x86 object
285 False otherwise.
286 """
287
288
Mike Frysingere72f7e42012-03-16 14:49:11 -0400289 command = 'echo | ${CC} -E -dD -P - | grep -i 86'
Sonny Rao172edad2012-02-07 23:23:58 +0000290 result = utils.system_output(command, retain_output=True,
291 ignore_status=True)
Mike Frysingere72f7e42012-03-16 14:49:11 -0400292 if re.search('__i386__', result) or re.search('__x86_64__', result):
Sonny Rao172edad2012-02-07 23:23:58 +0000293 return True
294 else:
295 return False
296
Elly Jones686c2f42011-10-24 16:45:07 -0400297def mounts():
298 ret = []
299 for line in file('/proc/mounts'):
300 m = re.match(r'(?P<src>\S+) (?P<dest>\S+) (?P<type>\S+) (?P<opts>\S+).*', line)
301 if m:
302 ret.append(m.groupdict())
303 return ret
304
305def is_mountpoint(path):
306 return path in [ m['dest'] for m in mounts() ]
307
308def require_mountpoint(path):
309 """
310 Raises an exception if path is not a mountpoint.
311 """
312 if not is_mountpoint(path):
313 raise error.TestFail('Path not mounted: "%s"' % path)
Elly Jones2f0ebba2011-10-27 13:43:20 -0400314
315def random_username():
316 return str(uuid.uuid4()) + '@example.com'
Simran Basic6f1f7a2012-10-16 10:47:46 -0700317
318
319def parse_cmd_output(command, run_method=utils.run):
320 """Runs a command on a host object to retrieve host attributes.
321
322 The command should output to stdout in the format of:
323 <key> = <value> # <optional_comment>
324
325
326 @param command: Command to execute on the host.
327 @param run_method: Function to use to execute the command. Defaults to
328 utils.run so that the command will be executed locally.
329 Can be replace with a host.run call so that it will
330 execute on a DUT or external machine. Method must accept
331 a command argument, stdout_tee and stderr_tee args and
332 return a result object with a string attribute stdout
333 which will be parsed.
334
335 @returns a dictionary mapping host attributes to their values.
336 """
337 result = {}
338 # Suppresses stdout so that the files are not printed to the logs.
339 cmd_result = run_method(command, stdout_tee=None, stderr_tee=None)
340 for line in cmd_result.stdout.splitlines():
341 # Lines are of the format "<key> = <value> # <comment>"
342 key_value = re.match('^\s*(?P<key>[^ ]+)\s*=\s*(?P<value>[^ ]+)'
343 '(?:\s*#.*)?$', line)
344 if key_value:
345 result[key_value.group('key')] = key_value.group('value')
Chris Masoneaf859092012-11-19 16:44:44 -0800346 return result
Mike Truty77f06bc2013-03-30 09:43:05 -0500347
348
349def set_from_keyval_output(out, delimiter=' '):
350 """Parse delimiter-separated key-val output into a set of tuples.
351
352 Output is expected to be multiline text output from a command.
353 Stuffs the key-vals into tuples in a set to be later compared.
354
355 e.g. deactivated 0
356 disableForceClear 0
357 ==> set(('deactivated', '0'), ('disableForceClear', '0'))
358
359 @param out: multiple lines of space-separated key-val pairs.
360 @param delimiter: character that separates key from val. Usually a
361 space but may be '=' or something else.
362 @return set of key-val tuples.
363 """
364 results = set()
365 kv_match_re = re.compile('([^ ]+)%s(.*)' % delimiter)
366 for linecr in out.splitlines():
367 match = kv_match_re.match(linecr.strip())
368 if match:
369 results.add((match.group(1), match.group(2)))
370 return results