OLD | NEW |
| (Empty) |
1 #!/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 | |
19 import os | |
20 import signal | |
21 import subprocess | |
22 import tempfile | |
23 import threading | |
24 import time | |
25 | |
26 # local imports | |
27 import errors | |
28 import logger | |
29 | |
30 _abort_on_error = False | |
31 | |
32 def SetAbortOnError(abort=True): | |
33 """Sets behavior of RunCommand to throw AbortError if command process returns | |
34 a negative error code""" | |
35 global _abort_on_error | |
36 _abort_on_error = abort | |
37 | |
38 def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True, | |
39 stdin_input=None): | |
40 """Spawn and retry a subprocess to run the given shell command. | |
41 | |
42 Args: | |
43 cmd: shell command to run | |
44 timeout_time: time in seconds to wait for command to run before aborting. | |
45 retry_count: number of times to retry command | |
46 return_output: if True return output of command as string. Otherwise, | |
47 direct output of command to stdout. | |
48 stdin_input: data to feed to stdin | |
49 Returns: | |
50 output of command | |
51 """ | |
52 result = None | |
53 while True: | |
54 try: | |
55 result = RunOnce(cmd, timeout_time=timeout_time, | |
56 return_output=return_output, stdin_input=stdin_input) | |
57 except errors.WaitForResponseTimedOutError: | |
58 if retry_count == 0: | |
59 raise | |
60 retry_count -= 1 | |
61 logger.Log("No response for %s, retrying" % cmd) | |
62 else: | |
63 # Success | |
64 return result | |
65 | |
66 def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): | |
67 """Spawns a subprocess to run the given shell command. | |
68 | |
69 Args: | |
70 cmd: shell command to run | |
71 timeout_time: time in seconds to wait for command to run before aborting. | |
72 return_output: if True return output of command as string. Otherwise, | |
73 direct output of command to stdout. | |
74 stdin_input: data to feed to stdin | |
75 Returns: | |
76 output of command | |
77 Raises: | |
78 errors.WaitForResponseTimedOutError if command did not complete within | |
79 timeout_time seconds. | |
80 errors.AbortError is command returned error code and SetAbortOnError is on. | |
81 """ | |
82 start_time = time.time() | |
83 so = [] | |
84 global _abort_on_error, error_occurred | |
85 error_occurred = False | |
86 | |
87 if return_output: | |
88 output_dest = tempfile.TemporaryFile(bufsize=0) | |
89 else: | |
90 # None means direct to stdout | |
91 output_dest = None | |
92 if stdin_input: | |
93 stdin_dest = subprocess.PIPE | |
94 else: | |
95 stdin_dest = None | |
96 pipe = subprocess.Popen( | |
97 cmd, | |
98 executable='/bin/bash', | |
99 stdin=stdin_dest, | |
100 stdout=output_dest, | |
101 stderr=subprocess.STDOUT, | |
102 shell=True, close_fds=True, | |
103 preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL)) | |
104 | |
105 def Run(): | |
106 global error_occurred | |
107 try: | |
108 pipe.communicate(input=stdin_input) | |
109 output = None | |
110 if return_output: | |
111 output_dest.seek(0) | |
112 output = output_dest.read() | |
113 output_dest.close() | |
114 if output is not None and len(output) > 0: | |
115 so.append(output) | |
116 except OSError, e: | |
117 logger.SilentLog("failed to retrieve stdout from: %s" % cmd) | |
118 logger.Log(e) | |
119 so.append("ERROR") | |
120 error_occurred = True | |
121 if pipe.returncode: | |
122 logger.SilentLog("Error: %s returned %d error code" %(cmd, | |
123 pipe.returncode)) | |
124 error_occurred = True | |
125 | |
126 t = threading.Thread(target=Run) | |
127 t.start() | |
128 t.join(timeout_time) | |
129 if t.isAlive(): | |
130 try: | |
131 pipe.kill() | |
132 except OSError: | |
133 # Can't kill a dead process. | |
134 pass | |
135 finally: | |
136 logger.SilentLog("about to raise a timeout for: %s" % cmd) | |
137 raise errors.WaitForResponseTimedOutError | |
138 | |
139 output = "".join(so) | |
140 if _abort_on_error and error_occurred: | |
141 raise errors.AbortError(msg=output) | |
142 | |
143 return "".join(so) | |
144 | |
145 | |
146 def RunHostCommand(binary, valgrind=False): | |
147 """Run a command on the host (opt using valgrind). | |
148 | |
149 Runs the host binary and returns the exit code. | |
150 If successfull, the output (stdout and stderr) are discarded, | |
151 but printed in case of error. | |
152 The command can be run under valgrind in which case all the | |
153 output are always discarded. | |
154 | |
155 Args: | |
156 binary: full path of the file to be run. | |
157 valgrind: If True the command will be run under valgrind. | |
158 | |
159 Returns: | |
160 The command exit code (int) | |
161 """ | |
162 if not valgrind: | |
163 subproc = subprocess.Popen(binary, stdout=subprocess.PIPE, | |
164 stderr=subprocess.STDOUT) | |
165 subproc.wait() | |
166 if subproc.returncode != 0: # In case of error print the output | |
167 print subproc.communicate()[0] | |
168 return subproc.returncode | |
169 else: | |
170 # Need the full path to valgrind to avoid other versions on the system. | |
171 subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck", | |
172 "--leak-check=yes", "-q", binary], | |
173 stdout=subprocess.PIPE, stderr=subprocess.STDOUT) | |
174 # Cannot rely on the retcode of valgrind. Instead look for an empty output. | |
175 valgrind_out = subproc.communicate()[0].strip() | |
176 if valgrind_out: | |
177 print valgrind_out | |
178 return 1 | |
179 else: | |
180 return 0 | |
181 | |
182 | |
183 def HasValgrind(): | |
184 """Check that /usr/bin/valgrind exists. | |
185 | |
186 We look for the fullpath to avoid picking up 'alternative' valgrind | |
187 on the system. | |
188 | |
189 Returns: | |
190 True if a system valgrind was found. | |
191 """ | |
192 return os.path.exists("/usr/bin/valgrind") | |
OLD | NEW |