blob: d398daa283b10ffe5baeadde29574d320ca72ce6 [file] [log] [blame]
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -08001#!/usr/bin/python2.4
2#
3#
4# Copyright 2007, The Android Open Source Project
5#
Nicolas Catania97b24c42009-04-22 11:08:32 -07006# Licensed under the Apache License, Version 2.0 (the "License");
7# you may not use this file except in compliance with the License.
8# You may obtain a copy of the License at
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -08009#
Nicolas Catania97b24c42009-04-22 11:08:32 -070010# http://www.apache.org/licenses/LICENSE-2.0
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080011#
Nicolas Catania97b24c42009-04-22 11:08:32 -070012# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS,
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080016# limitations under the License.
17
18# System imports
19import os
20import signal
21import subprocess
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080022import threading
Nicolas Catania97b24c42009-04-22 11:08:32 -070023import time
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080024
25# local imports
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080026import errors
Nicolas Catania97b24c42009-04-22 11:08:32 -070027import logger
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080028
29_abort_on_error = False
30
31def SetAbortOnError(abort=True):
Nicolas Catania97b24c42009-04-22 11:08:32 -070032 """Sets behavior of RunCommand to throw AbortError if command process returns
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080033 a negative error code"""
34 global _abort_on_error
35 _abort_on_error = abort
Nicolas Catania97b24c42009-04-22 11:08:32 -070036
Brett Chabot1d9928a2009-09-29 15:14:05 -070037def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True,
38 stdin_input=None):
39 """Spawn and retry a subprocess to run the given shell command.
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080040
Brett Chabot1d9928a2009-09-29 15:14:05 -070041 Args:
42 cmd: shell command to run
43 timeout_time: time in seconds to wait for command to run before aborting.
44 retry_count: number of times to retry command
45 return_output: if True return output of command as string. Otherwise,
46 direct output of command to stdout.
47 stdin_input: data to feed to stdin
48 Returns:
49 output of command
50 """
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080051 result = None
52 while True:
53 try:
Nicolas Catania97b24c42009-04-22 11:08:32 -070054 result = RunOnce(cmd, timeout_time=timeout_time,
Brett Chabot1d9928a2009-09-29 15:14:05 -070055 return_output=return_output, stdin_input=stdin_input)
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080056 except errors.WaitForResponseTimedOutError:
57 if retry_count == 0:
58 raise
59 retry_count -= 1
60 logger.Log("No response for %s, retrying" % cmd)
61 else:
62 # Success
63 return result
64
Brett Chabot1d9928a2009-09-29 15:14:05 -070065def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None):
66 """Spawns a subprocess to run the given shell command.
67
68 Args:
69 cmd: shell command to run
70 timeout_time: time in seconds to wait for command to run before aborting.
71 return_output: if True return output of command as string. Otherwise,
72 direct output of command to stdout.
73 stdin_input: data to feed to stdin
74 Returns:
75 output of command
76 Raises:
77 errors.WaitForResponseTimedOutError if command did not complete within
78 timeout_time seconds.
79 errors.AbortError is command returned error code and SetAbortOnError is on.
80 """
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080081 start_time = time.time()
82 so = []
83 pid = []
Brett Chabot8a101cb2009-05-05 12:56:39 -070084 global _abort_on_error, error_occurred
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080085 error_occurred = False
Nicolas Catania97b24c42009-04-22 11:08:32 -070086
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080087 def Run():
Brett Chabot8a101cb2009-05-05 12:56:39 -070088 global error_occurred
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080089 if return_output:
90 output_dest = subprocess.PIPE
91 else:
92 # None means direct to stdout
Nicolas Catania97b24c42009-04-22 11:08:32 -070093 output_dest = None
Brett Chabot1d9928a2009-09-29 15:14:05 -070094 if stdin_input:
95 stdin_dest = subprocess.PIPE
96 else:
97 stdin_dest = None
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080098 pipe = subprocess.Popen(
99 cmd,
100 executable='/bin/bash',
Brett Chabot1d9928a2009-09-29 15:14:05 -0700101 stdin=stdin_dest,
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800102 stdout=output_dest,
103 stderr=subprocess.STDOUT,
104 shell=True)
105 pid.append(pipe.pid)
106 try:
Brett Chabot1d9928a2009-09-29 15:14:05 -0700107 output = pipe.communicate(input=stdin_input)[0]
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800108 if output is not None and len(output) > 0:
109 so.append(output)
110 except OSError, e:
111 logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
112 logger.Log(e)
113 so.append("ERROR")
114 error_occurred = True
Brett Chabot99e5f912009-10-26 14:51:45 -0700115 if pipe.returncode:
Brett Chabot1d9928a2009-09-29 15:14:05 -0700116 logger.SilentLog("Error: %s returned %d error code" %(cmd,
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800117 pipe.returncode))
Nicolas Catania97b24c42009-04-22 11:08:32 -0700118 error_occurred = True
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800119
120 t = threading.Thread(target=Run)
121 t.start()
122
123 break_loop = False
124 while not break_loop:
125 if not t.isAlive():
126 break_loop = True
127
128 # Check the timeout
129 if (not break_loop and timeout_time is not None
130 and time.time() > start_time + timeout_time):
131 try:
132 os.kill(pid[0], signal.SIGKILL)
133 except OSError:
134 # process already dead. No action required.
135 pass
136
137 logger.SilentLog("about to raise a timeout for: %s" % cmd)
138 raise errors.WaitForResponseTimedOutError
139 if not break_loop:
140 time.sleep(0.1)
141
142 t.join()
Brett Chabot8a101cb2009-05-05 12:56:39 -0700143 output = "".join(so)
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800144 if _abort_on_error and error_occurred:
Brett Chabot8a101cb2009-05-05 12:56:39 -0700145 raise errors.AbortError(msg=output)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700146
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800147 return "".join(so)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700148
149
150def RunHostCommand(binary, valgrind=False):
151 """Run a command on the host (opt using valgrind).
152
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700153 Runs the host binary and returns the exit code.
154 If successfull, the output (stdout and stderr) are discarded,
155 but printed in case of error.
156 The command can be run under valgrind in which case all the
157 output are always discarded.
Nicolas Catania97b24c42009-04-22 11:08:32 -0700158
159 Args:
Brett Chabotbf7863e2009-06-29 17:40:45 -0700160 binary: full path of the file to be run.
Nicolas Catania97b24c42009-04-22 11:08:32 -0700161 valgrind: If True the command will be run under valgrind.
162
163 Returns:
164 The command exit code (int)
165 """
Nicolas Catania97b24c42009-04-22 11:08:32 -0700166 if not valgrind:
Brett Chabotbf7863e2009-06-29 17:40:45 -0700167 subproc = subprocess.Popen(binary, stdout=subprocess.PIPE,
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700168 stderr=subprocess.STDOUT)
169 subproc.wait()
170 if subproc.returncode != 0: # In case of error print the output
171 print subproc.communicate()[0]
172 return subproc.returncode
Nicolas Catania97b24c42009-04-22 11:08:32 -0700173 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700174 # Need the full path to valgrind to avoid other versions on the system.
Nicolas Cataniaab80b392009-06-04 09:42:03 -0700175 subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
niko2dafd062009-07-07 13:22:33 -0700176 "--leak-check=yes", "-q", binary],
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700177 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
Nicolas Cataniaab80b392009-06-04 09:42:03 -0700178 # Cannot rely on the retcode of valgrind. Instead look for an empty output.
179 valgrind_out = subproc.communicate()[0].strip()
180 if valgrind_out:
181 print valgrind_out
182 return 1
183 else:
184 return 0
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700185
186
187def HasValgrind():
188 """Check that /usr/bin/valgrind exists.
189
190 We look for the fullpath to avoid picking up 'alternative' valgrind
191 on the system.
192
193 Returns:
194 True if a system valgrind was found.
195 """
196 return os.path.exists("/usr/bin/valgrind")