""" 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. """ from __future__ import print_function 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')