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