blob: 8cf385b876e8ec9fab8d15d6f6a43a0917f8227f [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
Nicolas Cataniaff096c12009-05-01 11:55:36 -070026import android_build
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080027import errors
Nicolas Catania97b24c42009-04-22 11:08:32 -070028import logger
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080029
30_abort_on_error = False
31
32def SetAbortOnError(abort=True):
Nicolas Catania97b24c42009-04-22 11:08:32 -070033 """Sets behavior of RunCommand to throw AbortError if command process returns
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080034 a negative error code"""
35 global _abort_on_error
36 _abort_on_error = abort
Nicolas Catania97b24c42009-04-22 11:08:32 -070037
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080038def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True):
39 """Spawns a subprocess to run the given shell command, and checks for
40 timeout_time. If return_output is True, the output of the command is returned
41 as a string. Otherwise, output of command directed to stdout """
42
43 result = None
44 while True:
45 try:
Nicolas Catania97b24c42009-04-22 11:08:32 -070046 result = RunOnce(cmd, timeout_time=timeout_time,
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080047 return_output=return_output)
48 except errors.WaitForResponseTimedOutError:
49 if retry_count == 0:
50 raise
51 retry_count -= 1
52 logger.Log("No response for %s, retrying" % cmd)
53 else:
54 # Success
55 return result
56
57def RunOnce(cmd, timeout_time=None, return_output=True):
58 start_time = time.time()
59 so = []
60 pid = []
Brett Chabot8a101cb2009-05-05 12:56:39 -070061 global _abort_on_error, error_occurred
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080062 error_occurred = False
Nicolas Catania97b24c42009-04-22 11:08:32 -070063
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080064 def Run():
Brett Chabot8a101cb2009-05-05 12:56:39 -070065 global error_occurred
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080066 if return_output:
67 output_dest = subprocess.PIPE
68 else:
69 # None means direct to stdout
Nicolas Catania97b24c42009-04-22 11:08:32 -070070 output_dest = None
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080071 pipe = subprocess.Popen(
72 cmd,
73 executable='/bin/bash',
74 stdout=output_dest,
75 stderr=subprocess.STDOUT,
76 shell=True)
77 pid.append(pipe.pid)
78 try:
79 output = pipe.communicate()[0]
80 if output is not None and len(output) > 0:
81 so.append(output)
82 except OSError, e:
83 logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
84 logger.Log(e)
85 so.append("ERROR")
86 error_occurred = True
Brett Chabot8a101cb2009-05-05 12:56:39 -070087 if pipe.returncode != 0:
88 logger.SilentLog("Error: %s returned %d error code" %(cmd,
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080089 pipe.returncode))
Nicolas Catania97b24c42009-04-22 11:08:32 -070090 error_occurred = True
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080091
92 t = threading.Thread(target=Run)
93 t.start()
94
95 break_loop = False
96 while not break_loop:
97 if not t.isAlive():
98 break_loop = True
99
100 # Check the timeout
101 if (not break_loop and timeout_time is not None
102 and time.time() > start_time + timeout_time):
103 try:
104 os.kill(pid[0], signal.SIGKILL)
105 except OSError:
106 # process already dead. No action required.
107 pass
108
109 logger.SilentLog("about to raise a timeout for: %s" % cmd)
110 raise errors.WaitForResponseTimedOutError
111 if not break_loop:
112 time.sleep(0.1)
113
114 t.join()
Brett Chabot8a101cb2009-05-05 12:56:39 -0700115 output = "".join(so)
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800116 if _abort_on_error and error_occurred:
Brett Chabot8a101cb2009-05-05 12:56:39 -0700117 raise errors.AbortError(msg=output)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700118
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800119 return "".join(so)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700120
121
122def RunHostCommand(binary, valgrind=False):
123 """Run a command on the host (opt using valgrind).
124
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700125 Runs the host binary and returns the exit code.
126 If successfull, the output (stdout and stderr) are discarded,
127 but printed in case of error.
128 The command can be run under valgrind in which case all the
129 output are always discarded.
Nicolas Catania97b24c42009-04-22 11:08:32 -0700130
131 Args:
132 binary: basename of the file to be run. It is expected to be under
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700133 out/host/<os>-<arch>/bin.
Nicolas Catania97b24c42009-04-22 11:08:32 -0700134 valgrind: If True the command will be run under valgrind.
135
136 Returns:
137 The command exit code (int)
138 """
Nicolas Cataniaff096c12009-05-01 11:55:36 -0700139 full_path = os.path.join(android_build.GetHostBin(), binary)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700140 if not valgrind:
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700141 subproc = subprocess.Popen(full_path, stdout=subprocess.PIPE,
142 stderr=subprocess.STDOUT)
143 subproc.wait()
144 if subproc.returncode != 0: # In case of error print the output
145 print subproc.communicate()[0]
146 return subproc.returncode
Nicolas Catania97b24c42009-04-22 11:08:32 -0700147 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700148 # Need the full path to valgrind to avoid other versions on the system.
Nicolas Cataniaab80b392009-06-04 09:42:03 -0700149 subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
150 "--leak-check=yes", "-q", full_path],
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700151 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
Nicolas Cataniaab80b392009-06-04 09:42:03 -0700152 # Cannot rely on the retcode of valgrind. Instead look for an empty output.
153 valgrind_out = subproc.communicate()[0].strip()
154 if valgrind_out:
155 print valgrind_out
156 return 1
157 else:
158 return 0
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700159
160
161def HasValgrind():
162 """Check that /usr/bin/valgrind exists.
163
164 We look for the fullpath to avoid picking up 'alternative' valgrind
165 on the system.
166
167 Returns:
168 True if a system valgrind was found.
169 """
170 return os.path.exists("/usr/bin/valgrind")