| #!/usr/bin/env python |
| """ |
| A gdb-compatible frontend for lldb that implements just enough |
| commands to run the tests in the debuginfo-tests repository with lldb. |
| """ |
| |
| # ---------------------------------------------------------------------- |
| # Auto-detect lldb python module. |
| import commands, platform, os, sys |
| try: |
| # Just try for LLDB in case PYTHONPATH is already correctly setup. |
| import lldb |
| except ImportError: |
| lldb_python_dirs = list() |
| # lldb is not in the PYTHONPATH, try some defaults for the current platform. |
| platform_system = platform.system() |
| if platform_system == 'Darwin': |
| # On Darwin, try the currently selected Xcode directory |
| xcode_dir = commands.getoutput("xcode-select --print-path") |
| if xcode_dir: |
| lldb_python_dirs.append(os.path.realpath(xcode_dir + |
| '/../SharedFrameworks/LLDB.framework/Resources/Python')) |
| lldb_python_dirs.append(xcode_dir + |
| '/Library/PrivateFrameworks/LLDB.framework/Resources/Python') |
| lldb_python_dirs.append( |
| '/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python') |
| success = False |
| for lldb_python_dir in lldb_python_dirs: |
| if os.path.exists(lldb_python_dir): |
| if not (sys.path.__contains__(lldb_python_dir)): |
| sys.path.append(lldb_python_dir) |
| try: |
| import lldb |
| except ImportError: |
| pass |
| else: |
| print 'imported lldb from: "%s"' % (lldb_python_dir) |
| success = True |
| break |
| if not success: |
| print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly" |
| sys.exit(1) |
| # ---------------------------------------------------------------------- |
| |
| # Command line option handling. |
| import argparse |
| parser = argparse.ArgumentParser(description=__doc__) |
| parser.add_argument('--quiet', '-q', action="store_true", help='ignored') |
| parser.add_argument('-batch', action="store_true", |
| help='exit after processing comand line') |
| parser.add_argument('-n', action="store_true", help='ignore .lldb file') |
| parser.add_argument('-x', dest='script', type=file, help='execute commands from file') |
| parser.add_argument("target", help="the program to debug") |
| args = parser.parse_args() |
| |
| |
| # Create a new debugger instance. |
| debugger = lldb.SBDebugger.Create() |
| debugger.SkipLLDBInitFiles(args.n) |
| |
| # Make sure to clean up the debugger on exit. |
| import atexit |
| def on_exit(): |
| debugger.Terminate() |
| atexit.register(on_exit) |
| |
| # Don't return from lldb function calls until the process stops. |
| debugger.SetAsync(False) |
| |
| # Create a target from a file and arch. |
| arch = os.popen("file "+args.target).read().split()[-1] |
| target = debugger.CreateTargetWithFileAndArch(args.target, arch) |
| |
| if not target: |
| print "Could not create target", args.target |
| sys.exit(1) |
| |
| if not args.script: |
| print "Interactive mode is not implemented." |
| sys.exit(1) |
| |
| import re |
| for command in args.script: |
| # Strip newline and whitespaces and split into words. |
| cmd = command[:-1].strip().split() |
| if not cmd: |
| continue |
| |
| print '> %s'% command[:-1] |
| |
| try: |
| if re.match('^r|(run)$', cmd[0]): |
| error = lldb.SBError() |
| launchinfo = lldb.SBLaunchInfo([]) |
| launchinfo.SetWorkingDirectory(os.getcwd()) |
| process = target.Launch(launchinfo, error) |
| print error |
| if not process or error.fail: |
| state = process.GetState() |
| print "State = %d" % state |
| print """ |
| ERROR: Could not launch process. |
| NOTE: There are several reasons why this may happen: |
| * Root needs to run "DevToolsSecurity --enable". |
| * Older versions of lldb cannot launch more than one process simultaneously. |
| """ |
| sys.exit(1) |
| |
| elif re.match('^b|(break)$', cmd[0]) and len(cmd) == 2: |
| if re.match('[0-9]+', cmd[1]): |
| # b line |
| mainfile = target.FindFunctions('main')[0].compile_unit.file |
| print target.BreakpointCreateByLocation(mainfile, int(cmd[1])) |
| else: |
| # b file:line |
| file, line = cmd[1].split(':') |
| print target.BreakpointCreateByLocation(file, int(line)) |
| |
| elif re.match('^ptype$', cmd[0]) and len(cmd) == 2: |
| # GDB's ptype has multiple incarnations depending on its |
| # argument (global variable, function, type). The definition |
| # here is for looking up the signature of a function and only |
| # if that fails it looks for a type with that name. |
| # Type lookup in LLDB would be "image lookup --type". |
| for elem in target.FindFunctions(cmd[1]): |
| print elem.function.type |
| continue |
| print target.FindFirstType(cmd[1]) |
| |
| elif re.match('^po$', cmd[0]) and len(cmd) > 1: |
| try: |
| opts = lldb.SBExpressionOptions() |
| opts.SetFetchDynamicValue(True) |
| opts.SetCoerceResultToId(True) |
| print target.EvaluateExpression(' '.join(cmd[1:]), opts) |
| except: |
| # FIXME: This is a fallback path for the lab.llvm.org |
| # buildbot running OS X 10.7; it should be removed. |
| thread = process.GetThreadAtIndex(0) |
| frame = thread.GetFrameAtIndex(0) |
| print frame.EvaluateExpression(' '.join(cmd[1:])) |
| |
| elif re.match('^p|(print)$', cmd[0]) and len(cmd) > 1: |
| thread = process.GetThreadAtIndex(0) |
| frame = thread.GetFrameAtIndex(0) |
| print frame.EvaluateExpression(' '.join(cmd[1:])) |
| |
| elif re.match('^n|(next)$', cmd[0]): |
| thread = process.GetThreadAtIndex(0) |
| thread.StepOver() |
| |
| elif re.match('^q|(quit)$', cmd[0]): |
| sys.exit(0) |
| |
| else: |
| print debugger.HandleCommand(' '.join(cmd)) |
| |
| except SystemExit: |
| raise |
| except: |
| print 'Could not handle the command "%s"' % ' '.join(cmd) |
| |