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.
213 lines
6.9 KiB
213 lines
6.9 KiB
# This is a helper utility that launches Pin with "-appdebug", gets the
|
|
# TCP port number that it is waiting on, and then launches a debugger so
|
|
# that it attaches to that port.
|
|
#
|
|
# We used to do this with shell commands in the makefile, but there was
|
|
# a Cygwin bug that caused this to hang periodically (see Mantis #1839).
|
|
|
|
import optparse
|
|
import os.path
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import time
|
|
import platform
|
|
|
|
|
|
def Main(argv):
|
|
"""
|
|
Main entry point.
|
|
|
|
@param argv: Command line arguments.
|
|
@type argv: List of String.
|
|
"""
|
|
|
|
# There appears to be a bug in the Cygwin version that prevents us from
|
|
# reading the output file of a subprocess while that subprocess is running.
|
|
#
|
|
if sys.platform == 'cygwin':
|
|
print("This script must be run with the Windows native Python")
|
|
return 1
|
|
|
|
loader_library_path = ""
|
|
if (platform.system() == "Linux"):
|
|
loader_library_path ="LD_LIBRARY_PATH"
|
|
if (platform.system() == "Darwin"):
|
|
loader_library_path ="DYLD_LIBRARY_PATH"
|
|
|
|
# Parse options.
|
|
#
|
|
parser = optparse.OptionParser()
|
|
parser.add_option("--pin", dest="pin", type="string", default="",
|
|
help="Pathname to the Pin driver script (or executable). Required.")
|
|
parser.add_option("--pin-exe", dest="pinexe", type="string", default="",
|
|
help="Pathname to the Pin executable. Required.")
|
|
parser.add_option("--pindb", dest="pindb", type="string", default="",
|
|
help="Pathname to the PinDB executable. Required.")
|
|
parser.add_option("--pindb-libpath", dest="pindbLibPath", type="string", default="",
|
|
help="Additional path(s) to append to " + loader_library_path + " when launching PinDB. Separate paths with a ':'.")
|
|
parser.add_option("--tool", dest="tool", type="string", default="",
|
|
help="Pathname to the Pin tool. Required.")
|
|
parser.add_option("--app", dest="app", type="string", default="",
|
|
help="Pathname to the application. Required.")
|
|
parser.add_option("--pin-out", dest="pinout", type="string", default="",
|
|
help="Pathname to the output file which receives the Pin output. Required.")
|
|
parser.add_option("--pindb-in", dest="pindbin", type="string", default="",
|
|
help="Pathname to the input file for PinDB. Required.")
|
|
parser.add_option("--pindb-out", dest="pindbout", type="string", default="",
|
|
help="Pathname to the output file which receives the PinDB output. Required.")
|
|
parser.add_option("--cpu", dest="cpu", type="string", default="",
|
|
help="Name of the CPU to test [ia32 | intel64]. Required.")
|
|
parser.add_option("--timeout", dest="timeout", type="int", default=30,
|
|
help="Time limit (seconds) before deciding that a command is hung.")
|
|
|
|
(options, args) = parser.parse_args(args=argv)
|
|
if not options.pin:
|
|
print("Must specify --pin=PIN option")
|
|
return 1
|
|
if not options.pinexe:
|
|
print("Must specify --pin-exe=PINEXE option")
|
|
return 1
|
|
if not options.pindb:
|
|
print("Must specify --pindb=PINDB option")
|
|
return 1
|
|
if not options.tool:
|
|
print("Must specify --tool=TOOL option")
|
|
return 1
|
|
if not options.app:
|
|
print("Must specify --app=APP option")
|
|
return 1
|
|
if not options.pinout:
|
|
print("Must specify --pin-out=PINOUT option")
|
|
return 1
|
|
if not options.pindbin:
|
|
print("Must specify --pindb-in=PINDBIN option")
|
|
return 1
|
|
if not options.pindbout:
|
|
print("Must specify --pindb-out=PINDBOUT option")
|
|
return 1
|
|
if not options.cpu:
|
|
print("Must specify --cpu=CPU option")
|
|
return 1
|
|
if options.cpu not in ['ia32', 'intel64']:
|
|
print("Invalid --cpu=CPU option")
|
|
return 1
|
|
|
|
# Launch Pin.
|
|
#
|
|
cmd = [os.path.normcase(options.pin)]
|
|
cmd.append('-slow_asserts')
|
|
if (platform.system() == "Windows"):
|
|
cmd.append('-xyzzy')
|
|
cmd.append('-late_injection')
|
|
cmd.append('-appdebug')
|
|
cmd.append('-t')
|
|
cmd.append(os.path.normcase(options.tool))
|
|
cmd.append('--')
|
|
cmd.append(os.path.normcase(options.app))
|
|
|
|
outFile = open(os.path.normcase(options.pinout), 'w')
|
|
|
|
PrintCommand(cmd)
|
|
try:
|
|
subPin = subprocess.Popen(cmd, stdout=outFile)
|
|
except (OSError):
|
|
print("Unable to launch Pin")
|
|
return 1
|
|
|
|
# Pin waits for the debugger to attach. Wait for it to print the
|
|
# port number, and get that port number from the output file.
|
|
#
|
|
port = None
|
|
startTime = time.time()
|
|
rePort = re.compile('\s.*remote :?([0-9]+)$')
|
|
while not port:
|
|
# Check for timeout.
|
|
#
|
|
now = time.time()
|
|
if (now - startTime) > options.timeout:
|
|
print("Timed out waiting for port after " + str(options.timeout) + " seconds")
|
|
KillProcess(subPin)
|
|
return 1
|
|
time.sleep(1)
|
|
|
|
f = open(os.path.normcase(options.pinout), 'r')
|
|
lines = f.readlines()
|
|
f.close()
|
|
for line in lines:
|
|
m = rePort.match(line)
|
|
if m:
|
|
port = m.group(1)
|
|
break
|
|
|
|
# Launch PinDB.
|
|
#
|
|
cmd = [os.path.normcase(options.pindb)]
|
|
cmd.append('--pin=' + os.path.normcase(options.pinexe))
|
|
cmd.append('--timeout=' + str(options.timeout))
|
|
cmd.append('--gdb-protocol=:' + port)
|
|
cmd.append('--cpu=' + options.cpu)
|
|
cmd.append('--noprompt')
|
|
|
|
env = None
|
|
if options.pindbLibPath:
|
|
env = os.environ
|
|
if loader_library_path in env:
|
|
env[loader_library_path] += ':' + options.pindbLibPath
|
|
else:
|
|
env[loader_library_path] = options.pindbLibPath
|
|
print("PinDB runs with this " + loader_library_path + ": " + env[loader_library_path])
|
|
|
|
inFile = open(os.path.normcase(options.pindbin), 'r')
|
|
outFile = open(os.path.normcase(options.pindbout), 'w')
|
|
|
|
PrintCommand(cmd)
|
|
try:
|
|
subPinDB = subprocess.Popen(cmd, env=env, stdin=inFile, stdout=outFile)
|
|
except (OSError):
|
|
print("Unable to launch PinDB")
|
|
KillProcess(subPin)
|
|
return 1
|
|
|
|
# If PinDB exits abnormally, it may have left the Pin process hanging, so try to kill it.
|
|
#
|
|
if subPinDB.wait() != 0:
|
|
KillProcess(subPin)
|
|
return subPinDB.returncode
|
|
return subPin.wait()
|
|
|
|
|
|
def KillProcess(sub):
|
|
"""
|
|
Kill a subprocess.
|
|
|
|
@param sub: The subprocess to kill.
|
|
@type sub: Popen object.
|
|
"""
|
|
|
|
# The functions below are only available on Python 2.6 or later.
|
|
#
|
|
if sys.hexversion < 0x20600f0:
|
|
return
|
|
|
|
# First try to kill it gracefully, then terminate forcefully.
|
|
#
|
|
sub.terminate()
|
|
time.sleep(1)
|
|
sub.kill()
|
|
|
|
|
|
def PrintCommand(cmd):
|
|
"""
|
|
Print an informational message about a command that is executed.
|
|
|
|
@param cmd: The command.
|
|
@type cmd: List of String.
|
|
"""
|
|
|
|
mesg = "Launching: " + " ".join(cmd)
|
|
print(mesg)
|
|
|
|
|
|
if __name__ == "__main__": sys.exit(Main(sys.argv[1:]))
|