xref: /llvm-project/cross-project-tests/debuginfo-tests/llgdb-tests/llgdb.py (revision f98ee40f4b5d7474fc67e82824bf6abbaedb7b1c)
1#!/usr/bin/env python3
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 subprocess, platform, os, sys
10
11try:
12    # Just try for LLDB in case PYTHONPATH is already correctly setup.
13    import lldb
14except ImportError:
15    # Ask the command line driver for the path to the lldb module. Copy over
16    # the environment so that SDKROOT is propagated to xcrun.
17    command = (
18        ["xcrun", "lldb", "-P"] if platform.system() == "Darwin" else ["lldb", "-P"]
19    )
20    # Extend the PYTHONPATH if the path exists and isn't already there.
21    lldb_python_path = subprocess.check_output(command).decode("utf-8").strip()
22    if os.path.exists(lldb_python_path) and not sys.path.__contains__(lldb_python_path):
23        sys.path.append(lldb_python_path)
24    # Try importing LLDB again.
25    try:
26        import lldb
27
28        print('imported lldb from: "%s"' % lldb_python_path)
29    except ImportError:
30        print(
31            "error: couldn't locate the 'lldb' module, please set PYTHONPATH correctly"
32        )
33        sys.exit(1)
34# ----------------------------------------------------------------------
35
36# Command line option handling.
37import argparse
38
39parser = argparse.ArgumentParser(description=__doc__)
40parser.add_argument("--quiet", "-q", action="store_true", help="ignored")
41parser.add_argument(
42    "-batch", action="store_true", help="exit after processing comand line"
43)
44parser.add_argument("-n", action="store_true", help="ignore .lldb file")
45parser.add_argument(
46    "-x", dest="script", type=argparse.FileType("r"), help="execute commands from file"
47)
48parser.add_argument("target", help="the program to debug")
49args = parser.parse_args()
50
51
52# Create a new debugger instance.
53debugger = lldb.SBDebugger.Create()
54debugger.SkipLLDBInitFiles(args.n)
55
56# Make sure to clean up the debugger on exit.
57import atexit
58
59
60def on_exit():
61    debugger.Terminate()
62
63
64atexit.register(on_exit)
65
66# Don't return from lldb function calls until the process stops.
67debugger.SetAsync(False)
68
69# Create a target from a file and arch.
70arch = os.popen("file " + args.target).read().split()[-1]
71target = debugger.CreateTargetWithFileAndArch(args.target, arch)
72
73if not target:
74    print("Could not create target %s" % args.target)
75    sys.exit(1)
76
77if not args.script:
78    print("Interactive mode is not implemented.")
79    sys.exit(1)
80
81import re
82
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(
102                    """
103ERROR: Could not launch process.
104NOTE: There are several reasons why this may happen:
105  * Root needs to run "DevToolsSecurity --enable".
106  * Older versions of lldb cannot launch more than one process simultaneously.
107"""
108                )
109                sys.exit(1)
110
111        elif re.match("^b|(break)$", cmd[0]) and len(cmd) == 2:
112            if re.match("[0-9]+", cmd[1]):
113                # b line
114                mainfile = target.FindFunctions("main")[0].compile_unit.file
115                print(target.BreakpointCreateByLocation(mainfile, int(cmd[1])))
116            else:
117                # b file:line
118                file, line = cmd[1].split(":")
119                print(target.BreakpointCreateByLocation(file, int(line)))
120
121        elif re.match("^ptype$", cmd[0]) and len(cmd) == 2:
122            # GDB's ptype has multiple incarnations depending on its
123            # argument (global variable, function, type).  The definition
124            # here is for looking up the signature of a function and only
125            # if that fails it looks for a type with that name.
126            # Type lookup in LLDB would be "image lookup --type".
127            for elem in target.FindFunctions(cmd[1]):
128                print(elem.function.type)
129                continue
130            print(target.FindFirstType(cmd[1]))
131
132        elif re.match("^po$", cmd[0]) and len(cmd) > 1:
133            try:
134                opts = lldb.SBExpressionOptions()
135                opts.SetFetchDynamicValue(True)
136                opts.SetCoerceResultToId(True)
137                print(target.EvaluateExpression(" ".join(cmd[1:]), opts))
138            except:
139                # FIXME: This is a fallback path for the lab.llvm.org
140                # buildbot running OS X 10.7; it should be removed.
141                thread = process.GetThreadAtIndex(0)
142                frame = thread.GetFrameAtIndex(0)
143                print(frame.EvaluateExpression(" ".join(cmd[1:])))
144
145        elif re.match("^p|(print)$", cmd[0]) and len(cmd) > 1:
146            thread = process.GetThreadAtIndex(0)
147            frame = thread.GetFrameAtIndex(0)
148            print(frame.EvaluateExpression(" ".join(cmd[1:])))
149
150        elif re.match("^n|(next)$", cmd[0]):
151            thread = process.GetThreadAtIndex(0)
152            thread.StepOver()
153
154        elif re.match("^q|(quit)$", cmd[0]):
155            sys.exit(0)
156
157        else:
158            print(debugger.HandleCommand(" ".join(cmd)))
159
160    except SystemExit:
161        raise
162    except:
163        print('Could not handle the command "%s"' % " ".join(cmd))
164