blob: 79c7ea5cd2fe28c3bdb421074221f48d9274d9bc [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
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080037def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True):
38 """Spawns a subprocess to run the given shell command, and checks for
39 timeout_time. If return_output is True, the output of the command is returned
40 as a string. Otherwise, output of command directed to stdout """
41
42 result = None
43 while True:
44 try:
Nicolas Catania97b24c42009-04-22 11:08:32 -070045 result = RunOnce(cmd, timeout_time=timeout_time,
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080046 return_output=return_output)
47 except errors.WaitForResponseTimedOutError:
48 if retry_count == 0:
49 raise
50 retry_count -= 1
51 logger.Log("No response for %s, retrying" % cmd)
52 else:
53 # Success
54 return result
55
56def RunOnce(cmd, timeout_time=None, return_output=True):
57 start_time = time.time()
58 so = []
59 pid = []
Brett Chabot8a101cb2009-05-05 12:56:39 -070060 global _abort_on_error, error_occurred
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080061 error_occurred = False
Nicolas Catania97b24c42009-04-22 11:08:32 -070062
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080063 def Run():
Brett Chabot8a101cb2009-05-05 12:56:39 -070064 global error_occurred
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080065 if return_output:
66 output_dest = subprocess.PIPE
67 else:
68 # None means direct to stdout
Nicolas Catania97b24c42009-04-22 11:08:32 -070069 output_dest = None
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080070 pipe = subprocess.Popen(
71 cmd,
72 executable='/bin/bash',
73 stdout=output_dest,
74 stderr=subprocess.STDOUT,
75 shell=True)
76 pid.append(pipe.pid)
77 try:
78 output = pipe.communicate()[0]
79 if output is not None and len(output) > 0:
80 so.append(output)
81 except OSError, e:
82 logger.SilentLog("failed to retrieve stdout from: %s" % cmd)
83 logger.Log(e)
84 so.append("ERROR")
85 error_occurred = True
Brett Chabot8a101cb2009-05-05 12:56:39 -070086 if pipe.returncode != 0:
87 logger.SilentLog("Error: %s returned %d error code" %(cmd,
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080088 pipe.returncode))
Nicolas Catania97b24c42009-04-22 11:08:32 -070089 error_occurred = True
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -080090
91 t = threading.Thread(target=Run)
92 t.start()
93
94 break_loop = False
95 while not break_loop:
96 if not t.isAlive():
97 break_loop = True
98
99 # Check the timeout
100 if (not break_loop and timeout_time is not None
101 and time.time() > start_time + timeout_time):
102 try:
103 os.kill(pid[0], signal.SIGKILL)
104 except OSError:
105 # process already dead. No action required.
106 pass
107
108 logger.SilentLog("about to raise a timeout for: %s" % cmd)
109 raise errors.WaitForResponseTimedOutError
110 if not break_loop:
111 time.sleep(0.1)
112
113 t.join()
Brett Chabot8a101cb2009-05-05 12:56:39 -0700114 output = "".join(so)
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800115 if _abort_on_error and error_occurred:
Brett Chabot8a101cb2009-05-05 12:56:39 -0700116 raise errors.AbortError(msg=output)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700117
The Android Open Source Project2b83cbd2009-03-05 17:04:45 -0800118 return "".join(so)
Nicolas Catania97b24c42009-04-22 11:08:32 -0700119
120
121def RunHostCommand(binary, valgrind=False):
122 """Run a command on the host (opt using valgrind).
123
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700124 Runs the host binary and returns the exit code.
125 If successfull, the output (stdout and stderr) are discarded,
126 but printed in case of error.
127 The command can be run under valgrind in which case all the
128 output are always discarded.
Nicolas Catania97b24c42009-04-22 11:08:32 -0700129
130 Args:
Brett Chabotbf7863e2009-06-29 17:40:45 -0700131 binary: full path of the file to be run.
Nicolas Catania97b24c42009-04-22 11:08:32 -0700132 valgrind: If True the command will be run under valgrind.
133
134 Returns:
135 The command exit code (int)
136 """
Nicolas Catania97b24c42009-04-22 11:08:32 -0700137 if not valgrind:
Brett Chabotbf7863e2009-06-29 17:40:45 -0700138 subproc = subprocess.Popen(binary, stdout=subprocess.PIPE,
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700139 stderr=subprocess.STDOUT)
140 subproc.wait()
141 if subproc.returncode != 0: # In case of error print the output
142 print subproc.communicate()[0]
143 return subproc.returncode
Nicolas Catania97b24c42009-04-22 11:08:32 -0700144 else:
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700145 # Need the full path to valgrind to avoid other versions on the system.
Nicolas Cataniaab80b392009-06-04 09:42:03 -0700146 subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck",
niko2dafd062009-07-07 13:22:33 -0700147 "--leak-check=yes", "-q", binary],
Nicolas Catania1ecf93b2009-04-30 19:27:52 -0700148 stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
Nicolas Cataniaab80b392009-06-04 09:42:03 -0700149 # Cannot rely on the retcode of valgrind. Instead look for an empty output.
150 valgrind_out = subproc.communicate()[0].strip()
151 if valgrind_out:
152 print valgrind_out
153 return 1
154 else:
155 return 0
Nicolas Cataniabcd93dc2009-05-14 12:25:23 -0700156
157
158def HasValgrind():
159 """Check that /usr/bin/valgrind exists.
160
161 We look for the fullpath to avoid picking up 'alternative' valgrind
162 on the system.
163
164 Returns:
165 True if a system valgrind was found.
166 """
167 return os.path.exists("/usr/bin/valgrind")