1#!/usr/bin/env python
2"""
3A gdb-compatible frontend for lldb that implements just enough
4commands to run the tests in the debuginfo-tests repository with lldb.
5"""
6
7# ----------------------------------------------------------------------
8# Auto-detect lldb python module.
9import commands, platform, os,  sys
10try:
11    # Just try for LLDB in case PYTHONPATH is already correctly setup.
12    import lldb
13except ImportError:
14    lldb_python_dirs = list()
15    # lldb is not in the PYTHONPATH, try some defaults for the current platform.
16    platform_system = platform.system()
17    if platform_system == 'Darwin':
18        # On Darwin, try the currently selected Xcode directory
19        xcode_dir = commands.getoutput("xcode-select --print-path")
20        if xcode_dir:
21            lldb_python_dirs.append(os.path.realpath(xcode_dir +
22'/../SharedFrameworks/LLDB.framework/Resources/Python'))
23            lldb_python_dirs.append(xcode_dir +
24'/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
25        lldb_python_dirs.append(
26'/System/Library/PrivateFrameworks/LLDB.framework/Resources/Python')
27    success = False
28    for lldb_python_dir in lldb_python_dirs:
29        if os.path.exists(lldb_python_dir):
30            if not (sys.path.__contains__(lldb_python_dir)):
31                sys.path.append(lldb_python_dir)
32                try:
33                    import lldb
34                except ImportError:
35                    pass
36                else:
37                    print 'imported lldb from: "%s"' % (lldb_python_dir)
38                    success = True
39                    break
40    if not success:
41        print "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
42        sys.exit(1)
43# ----------------------------------------------------------------------
44
45# Command line option handling.
46import argparse
47parser = argparse.ArgumentParser(description=__doc__)
48parser.add_argument('--quiet', '-q', action="store_true", help='ignored')
49parser.add_argument('-batch', action="store_true",
50                    help='exit after processing comand line')
51parser.add_argument('-n', action="store_true", help='ignore .lldb file')
52parser.add_argument('-x', dest='script', type=file, help='execute commands from file')
53parser.add_argument("target", help="the program to debug")
54args = parser.parse_args()
55
56
57# Create a new debugger instance.
58debugger = lldb.SBDebugger.Create()
59debugger.SkipLLDBInitFiles(args.n)
60
61# Make sure to clean up the debugger on exit.
62import atexit
63def on_exit():
64    debugger.Terminate()
65atexit.register(on_exit)
66
67# Don't return from lldb function calls until the process stops.
68debugger.SetAsync(False)
69
70# Create a target from a file and arch.
71arch = os.popen("file "+args.target).read().split()[-1]
72target = debugger.CreateTargetWithFileAndArch(args.target, arch)
73
74if not target:
75    print "Could not create target", args.target
76    sys.exit(1)
77
78if not args.script:
79    print "Interactive mode is not implemented."
80    sys.exit(1)
81
82import re
83for command in args.script:
84    # Strip newline and whitespaces and split into words.
85    cmd = command[:-1].strip().split()
86    if not cmd:
87        continue
88
89    print '> %s'% command[:-1]
90
91    try:
92        if re.match('^r|(run)$', cmd[0]):
93            error = lldb.SBError()
94            launchinfo = lldb.SBLaunchInfo([])
95            launchinfo.SetWorkingDirectory(os.getcwd())
96            process = target.Launch(launchinfo, error)
97            print error
98            if not process or error.fail:
99                state = process.GetState()
100                print "State = %d" % state
101                print """
102ERROR: Could not launch process.
103NOTE: There are several reasons why this may happen:
104  * Root needs to run "DevToolsSecurity --enable".
105  * Older versions of lldb cannot launch more than one process simultaneously.
106"""
107                sys.exit(1)
108
109        elif re.match('^b|(break)$', cmd[0]) and len(cmd) == 2:
110            if re.match('[0-9]+', cmd[1]):
111                # b line
112                mainfile = target.FindFunctions('main')[0].compile_unit.file
113                print target.BreakpointCreateByLocation(mainfile, int(cmd[1]))
114            else:
115                # b file:line
116                file, line = cmd[1].split(':')
117                print target.BreakpointCreateByLocation(file, int(line))
118
119        elif re.match('^ptype$', cmd[0]) and len(cmd) == 2:
120            # GDB's ptype has multiple incarnations depending on its
121            # argument (global variable, function, type).  The definition
122            # here is for looking up the signature of a function and only
123            # if that fails it looks for a type with that name.
124            # Type lookup in LLDB would be "image lookup --type".
125            for elem in target.FindFunctions(cmd[1]):
126                print elem.function.type
127                continue
128            print target.FindFirstType(cmd[1])
129
130        elif re.match('^po$', cmd[0]) and len(cmd) > 1:
131            try:
132                opts = lldb.SBExpressionOptions()
133                opts.SetFetchDynamicValue(True)
134                opts.SetCoerceResultToId(True)
135                print target.EvaluateExpression(' '.join(cmd[1:]), opts)
136            except:
137                # FIXME: This is a fallback path for the lab.llvm.org
138                # buildbot running OS X 10.7; it should be removed.
139                thread = process.GetThreadAtIndex(0)
140                frame = thread.GetFrameAtIndex(0)
141                print frame.EvaluateExpression(' '.join(cmd[1:]))
142
143        elif re.match('^p|(print)$', cmd[0]) and len(cmd) > 1:
144            thread = process.GetThreadAtIndex(0)
145            frame = thread.GetFrameAtIndex(0)
146            print frame.EvaluateExpression(' '.join(cmd[1:]))
147
148        elif re.match('^n|(next)$', cmd[0]):
149            thread = process.GetThreadAtIndex(0)
150            thread.StepOver()
151
152        elif re.match('^q|(quit)$', cmd[0]):
153            sys.exit(0)
154
155        else:
156            print debugger.HandleCommand(' '.join(cmd))
157
158    except SystemExit:
159        raise
160    except:
161        print 'Could not handle the command "%s"' % ' '.join(cmd)
162
163