blob: 5c82c61210de2e889e4dabf1868e09a323e6d5eb [file] [log] [blame]
Torne (Richard Coles)58218062012-11-14 11:43:16 +00001#!/usr/bin/python2.4
2#
3#
4# Copyright 2007, The Android Open Source Project
5#
6# 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
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# 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
16# limitations under the License.
17
18# System imports
19import os
20import signal
21import subprocess
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010022import tempfile
Torne (Richard Coles)58218062012-11-14 11:43:16 +000023import threading
24import time
Ben Murdochbb1529c2013-08-08 10:24:53 +010025import sys
Torne (Richard Coles)58218062012-11-14 11:43:16 +000026
27# local imports
28import errors
29import logger
30
31_abort_on_error = False
32
33def SetAbortOnError(abort=True):
34 """Sets behavior of RunCommand to throw AbortError if command process returns
35 a negative error code"""
36 global _abort_on_error
37 _abort_on_error = abort
38
39def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True,
40 stdin_input=None):
41 """Spawn and retry a subprocess to run the given shell command.
42
43 Args:
44 cmd: shell command to run
45 timeout_time: time in seconds to wait for command to run before aborting.
46 retry_count: number of times to retry command
47 return_output: if True return output of command as string. Otherwise,
48 direct output of command to stdout.
49 stdin_input: data to feed to stdin
50 Returns:
51 output of command
52 """
53 result = None
54 while True:
55 try:
56 result = RunOnce(cmd, timeout_time=timeout_time,
57 return_output=return_output, stdin_input=stdin_input)
58 except errors.WaitForResponseTimedOutError:
59 if retry_count == 0:
60 raise
61 retry_count -= 1
62 logger.Log("No response for %s, retrying" % cmd)
63 else:
64 # Success
65 return result
66
67def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None):
68 """Spawns a subprocess to run the given shell command.
69
70 Args:
71 cmd: shell command to run
72 timeout_time: time in seconds to wait for command to run before aborting.
73 return_output: if True return output of command as string. Otherwise,
74 direct output of command to stdout.
75 stdin_input: data to feed to stdin
76 Returns:
77 output of command
78 Raises:
79 errors.WaitForResponseTimedOutError if command did not complete within
80 timeout_time seconds.
81 errors.AbortError is command returned error code and SetAbortOnError is on.
82 """
83 start_time = time.time()
84 so = []
Torne (Richard Coles)58218062012-11-14 11:43:16 +000085 global _abort_on_error, error_occurred
86 error_occurred = False
87
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000088 if return_output:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010089 output_dest = tempfile.TemporaryFile(bufsize=0)
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000090 else:
91 # None means direct to stdout
92 output_dest = None
93 if stdin_input:
94 stdin_dest = subprocess.PIPE
95 else:
96 stdin_dest = None
Ben Murdochbb1529c2013-08-08 10:24:53 +010097 stderr_dest = subprocess.STDOUT
98 if os.environ.get('ADB_TRACE'):
99 stderr_dest = sys.stdout
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000100 pipe = subprocess.Popen(
101 cmd,
102 executable='/bin/bash',
103 stdin=stdin_dest,
104 stdout=output_dest,
Ben Murdochbb1529c2013-08-08 10:24:53 +0100105 stderr=stderr_dest,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100106 shell=True, close_fds=True,
107 preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL))
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000108
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000109 def Run():
110 global error_occurred
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000111 try:
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100112 pipe.communicate(input=stdin_input)
113 output = None
114 if return_output:
115 output_dest.seek(0)
116 output = output_dest.read()
117 output_dest.close()
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000118 if output is not None and len(output) > 0:
119 so.append(output)
120 except OSError, e:
121 logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
122 logger.Log(e)
123 so.append("ERROR")
124 error_occurred = True
125 if pipe.returncode:
126 logger.SilentLog("Error: %s returned %d error code" %(cmd,
127 pipe.returncode))
128 error_occurred = True
129
130 t = threading.Thread(target=Run)
131 t.start()
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000132 t.join(timeout_time)
133 if t.isAlive():
134 try:
135 pipe.kill()
136 except OSError:
137 # Can't kill a dead process.
138 pass
139 finally:
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000140 logger.SilentLog("about to raise a timeout for: %s" % cmd)
141 raise errors.WaitForResponseTimedOutError
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000142
Torne (Richard Coles)58218062012-11-14 11:43:16 +0000143 output = "".join(so)
144 if _abort_on_error and error_occurred:
145 raise errors.AbortError(msg=output)
146
147 return "".join(so)
148
149
150def RunHostCommand(binary, valgrind=False):
151 """Run a command on the host (opt using valgrind).
152
153 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.
158
159 Args:
160 binary: full path of the file to be run.
161 valgrind: If True the command will be run under valgrind.
162
163 Returns:
164 The command exit code (int)
165 """
166 if not valgrind:
167 subproc = subprocess.Popen(binary, stdout=subprocess.PIPE,
168 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
173 else:
174 # Need the full path to valgrind to avoid other versions on the system.
175 subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
176 "--leak-check=yes", "-q", binary],
177 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
178 # 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
185
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")