1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
"""
LLDB Helpers for working with the swift compiler.
Load into LLDB with 'command script import /path/to/lldbToolBox.py'
This will also import LLVM data formatters as well, assuming that llvm is next
to the swift checkout.
"""
import argparse
import os
import shlex
import subprocess
import sys
import tempfile
import lldb
REPO_BASE = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir,
os.pardir, os.pardir))
SWIFT_REPO = os.path.join(REPO_BASE, "swift")
LLVM_REPO = os.path.join(REPO_BASE, "llvm-project")
LLVM_DATAFORMATTER_PATH = os.path.join(LLVM_REPO, "llvm", "utils",
"lldbDataFormatters.py")
SWIFT_DATAFORMATTER_PATH = os.path.join(SWIFT_REPO, "utils",
"lldb", "lldbSwiftDataFormatters.py")
def import_llvm_dataformatters(debugger):
if not os.access(LLVM_DATAFORMATTER_PATH, os.F_OK):
print("WARNING! Could not find LLVM data formatters!")
return
cmd = 'command script import {}'.format(LLVM_DATAFORMATTER_PATH)
debugger.HandleCommand(cmd)
print("Loaded LLVM data formatters.")
def import_swift_dataformatters(debugger):
if not os.access(SWIFT_DATAFORMATTER_PATH, os.F_OK):
print("WARNING! Could not find Swift data formatters!")
return
cmd = 'command script import {}'.format(SWIFT_DATAFORMATTER_PATH)
debugger.HandleCommand(cmd)
print("Loaded Swift data formatters.")
VIEWCFG_PATH = os.path.join(SWIFT_REPO, "utils", "viewcfg")
BLOCKIFYASM_PATH = os.path.join(SWIFT_REPO, "utils", "dev-scripts",
"blockifyasm")
def disassemble_asm_cfg(debugger, command, exec_ctx, result, internal_dict):
"""
This function disassembles the current assembly frame into a temporary file
and then uses that temporary file as input to blockifyasm | viewcfg. This
will cause a pdf of the cfg to be opened on Darwin.
"""
d = exec_ctx.frame.Disassemble()
with tempfile.TemporaryFile() as f:
f.write(bytes(d, 'utf-8'))
f.flush()
f.seek(0)
p1 = subprocess.Popen([BLOCKIFYASM_PATH], stdin=f,
stdout=subprocess.PIPE)
subprocess.Popen([VIEWCFG_PATH], stdin=p1.stdout)
p1.stdout.close() # Allow p1 to receive a SIGPIPE if p2 exits.
def disassemble_to_file(debugger, command, exec_ctx, result, internal_dict):
"""This function disassembles the current assembly frame into a file specified
by the user.
"""
parser = argparse.ArgumentParser(prog='disassemble-to-file', description="""
Dump the disassembly of the current frame or specified function to the
specified file.
""")
parser.add_argument('file', type=argparse.FileType('w'),
default=sys.stdout)
parser.add_argument('-n', dest='func_name', help="""
Function name to disassembly. Frame used if unset.""")
args = parser.parse_args(shlex.split(command))
if args.func_name is None:
args.file.write(exec_ctx.frame.disassembly)
else:
name = args.func_name
result = exec_ctx.target.FindFunctions(name)
if result is None:
raise RuntimeError('No function with name: {}'.format(name))
if len(result) > 1:
errorStr = 'Matched multiple functions to name: {}'
raise RuntimeError(errorStr.format(name))
f = result[0].GetFunction()
args.file.write(str(f.GetInstructions(exec_ctx.target)) + "\n")
def sequence(debugger, command, exec_ctx, result, internal_dict):
"""
Combine multiple semicolon separated lldb commands into one command.
This command is particularly useful for defining aliases and breakpoint
commands. Some examples:
# Define an alias that prints rax and also steps one instruction.
command alias xs sequence p/x $rax; stepi
# Breakpoint command to show the frame's info and arguments.
breakpoint command add -o 'seq frame info; reg read arg1 arg2 arg3'
# Override `b` to allow a condition to be specified. For example:
# b someMethod if someVar > 2
command regex b
s/(.+) if (.+)/seq _regexp-break %1; break mod -c "%2"/
s/(.*)/_regexp-break %1/
"""
interpreter = debugger.GetCommandInterpreter()
for subcommand in command.split(';'):
subcommand = subcommand.strip()
if not subcommand:
continue # skip empty commands
ret = lldb.SBCommandReturnObject()
interpreter.HandleCommand(subcommand, exec_ctx, ret)
if ret.GetOutput():
print(ret.GetOutput().strip(), file=result)
if not ret.Succeeded():
result.SetError(ret.GetError())
result.SetStatus(ret.GetStatus())
return
def __lldb_init_module(debugger, internal_dict):
import_llvm_dataformatters(debugger)
import_swift_dataformatters(debugger)
debugger.HandleCommand('command script add disassemble-asm-cfg '
'-f lldbToolBox.disassemble_asm_cfg')
debugger.HandleCommand('command script add disassemble-to-file '
'-f lldbToolBox.disassemble_to_file')
debugger.HandleCommand('command script add sequence '
'-h "Run multiple semicolon separated commands" '
'-f lldbToolBox.sequence')
|