You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
196 lines
5.7 KiB
196 lines
5.7 KiB
#!/usr/bin/python2.4 |
|
# |
|
# |
|
# Copyright 2007, The Android Open Source Project |
|
# |
|
# Licensed under the Apache License, Version 2.0 (the "License"); |
|
# you may not use this file except in compliance with the License. |
|
# You may obtain a copy of the License at |
|
# |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
|
# |
|
# Unless required by applicable law or agreed to in writing, software |
|
# distributed under the License is distributed on an "AS IS" BASIS, |
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
# See the License for the specific language governing permissions and |
|
# limitations under the License. |
|
|
|
# System imports |
|
import os |
|
import signal |
|
import subprocess |
|
import threading |
|
import time |
|
|
|
# local imports |
|
import errors |
|
import logger |
|
|
|
_abort_on_error = False |
|
|
|
def SetAbortOnError(abort=True): |
|
"""Sets behavior of RunCommand to throw AbortError if command process returns |
|
a negative error code""" |
|
global _abort_on_error |
|
_abort_on_error = abort |
|
|
|
def RunCommand(cmd, timeout_time=None, retry_count=3, return_output=True, |
|
stdin_input=None): |
|
"""Spawn and retry a subprocess to run the given shell command. |
|
|
|
Args: |
|
cmd: shell command to run |
|
timeout_time: time in seconds to wait for command to run before aborting. |
|
retry_count: number of times to retry command |
|
return_output: if True return output of command as string. Otherwise, |
|
direct output of command to stdout. |
|
stdin_input: data to feed to stdin |
|
Returns: |
|
output of command |
|
""" |
|
result = None |
|
while True: |
|
try: |
|
result = RunOnce(cmd, timeout_time=timeout_time, |
|
return_output=return_output, stdin_input=stdin_input) |
|
except errors.WaitForResponseTimedOutError: |
|
if retry_count == 0: |
|
raise |
|
retry_count -= 1 |
|
logger.Log("No response for %s, retrying" % cmd) |
|
else: |
|
# Success |
|
return result |
|
|
|
def RunOnce(cmd, timeout_time=None, return_output=True, stdin_input=None): |
|
"""Spawns a subprocess to run the given shell command. |
|
|
|
Args: |
|
cmd: shell command to run |
|
timeout_time: time in seconds to wait for command to run before aborting. |
|
return_output: if True return output of command as string. Otherwise, |
|
direct output of command to stdout. |
|
stdin_input: data to feed to stdin |
|
Returns: |
|
output of command |
|
Raises: |
|
errors.WaitForResponseTimedOutError if command did not complete within |
|
timeout_time seconds. |
|
errors.AbortError is command returned error code and SetAbortOnError is on. |
|
""" |
|
start_time = time.time() |
|
so = [] |
|
pid = [] |
|
global _abort_on_error, error_occurred |
|
error_occurred = False |
|
|
|
def Run(): |
|
global error_occurred |
|
if return_output: |
|
output_dest = subprocess.PIPE |
|
else: |
|
# None means direct to stdout |
|
output_dest = None |
|
if stdin_input: |
|
stdin_dest = subprocess.PIPE |
|
else: |
|
stdin_dest = None |
|
pipe = subprocess.Popen( |
|
cmd, |
|
executable='/bin/bash', |
|
stdin=stdin_dest, |
|
stdout=output_dest, |
|
stderr=subprocess.STDOUT, |
|
shell=True) |
|
pid.append(pipe.pid) |
|
try: |
|
output = pipe.communicate(input=stdin_input)[0] |
|
if output is not None and len(output) > 0: |
|
so.append(output) |
|
except OSError, e: |
|
logger.SilentLog("failed to retrieve stdout from: %s" % cmd) |
|
logger.Log(e) |
|
so.append("ERROR") |
|
error_occurred = True |
|
if pipe.returncode: |
|
logger.SilentLog("Error: %s returned %d error code" %(cmd, |
|
pipe.returncode)) |
|
error_occurred = True |
|
|
|
t = threading.Thread(target=Run) |
|
t.start() |
|
|
|
break_loop = False |
|
while not break_loop: |
|
if not t.isAlive(): |
|
break_loop = True |
|
|
|
# Check the timeout |
|
if (not break_loop and timeout_time is not None |
|
and time.time() > start_time + timeout_time): |
|
try: |
|
os.kill(pid[0], signal.SIGKILL) |
|
except OSError: |
|
# process already dead. No action required. |
|
pass |
|
|
|
logger.SilentLog("about to raise a timeout for: %s" % cmd) |
|
raise errors.WaitForResponseTimedOutError |
|
if not break_loop: |
|
time.sleep(0.1) |
|
|
|
t.join() |
|
output = "".join(so) |
|
if _abort_on_error and error_occurred: |
|
raise errors.AbortError(msg=output) |
|
|
|
return "".join(so) |
|
|
|
|
|
def RunHostCommand(binary, valgrind=False): |
|
"""Run a command on the host (opt using valgrind). |
|
|
|
Runs the host binary and returns the exit code. |
|
If successfull, the output (stdout and stderr) are discarded, |
|
but printed in case of error. |
|
The command can be run under valgrind in which case all the |
|
output are always discarded. |
|
|
|
Args: |
|
binary: full path of the file to be run. |
|
valgrind: If True the command will be run under valgrind. |
|
|
|
Returns: |
|
The command exit code (int) |
|
""" |
|
if not valgrind: |
|
subproc = subprocess.Popen(binary, stdout=subprocess.PIPE, |
|
stderr=subprocess.STDOUT) |
|
subproc.wait() |
|
if subproc.returncode != 0: # In case of error print the output |
|
print subproc.communicate()[0] |
|
return subproc.returncode |
|
else: |
|
# Need the full path to valgrind to avoid other versions on the system. |
|
subproc = subprocess.Popen(["/usr/bin/valgrind", "--tool=memcheck", |
|
"--leak-check=yes", "-q", binary], |
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT) |
|
# Cannot rely on the retcode of valgrind. Instead look for an empty output. |
|
valgrind_out = subproc.communicate()[0].strip() |
|
if valgrind_out: |
|
print valgrind_out |
|
return 1 |
|
else: |
|
return 0 |
|
|
|
|
|
def HasValgrind(): |
|
"""Check that /usr/bin/valgrind exists. |
|
|
|
We look for the fullpath to avoid picking up 'alternative' valgrind |
|
on the system. |
|
|
|
Returns: |
|
True if a system valgrind was found. |
|
""" |
|
return os.path.exists("/usr/bin/valgrind")
|
|
|