diff options
Diffstat (limited to 'etc/emacs_lldb.py')
-rw-r--r-- | etc/emacs_lldb.py | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/etc/emacs_lldb.py b/etc/emacs_lldb.py new file mode 100644 index 00000000000..b8530915f81 --- /dev/null +++ b/etc/emacs_lldb.py @@ -0,0 +1,238 @@ +# Copyright (C) 2022 Free Software Foundation, Inc. +# +# This file is part of GNU Emacs. +# +# GNU Emacs is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Emacs is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. + +# Load this module in LLDB with +# +# (lldb) command script import emacs_lldb +# +# Available commands start with 'x' and can be seen with +# +# (lldb) help + +import lldb + + +######################################################################## +# Utilities +######################################################################## + +# Return the name of enumerator ENUM as a string. +def enumerator_name(enum): + enumerators = enum.GetType().GetEnumMembers() + return enumerators[enum.GetValueAsUnsigned()].GetName() + +# A class wrapping an SBValue for a Lisp_Object, providing convenience +# functions. +class Lisp_Object: + # Map pvec_type enumerators to corresponding C types. + pvec2type = { + "PVEC_FRAME": "struct frame", + "PVEC_WINDOW": "struct window", + "PVEC_BIGNUM": "struct Lisp_Bignum", + "PVEC_MARKER": "struct Lisp_Marker", + "PVEC_OVERLAY": "struct Lisp_Overlay", + "PVEC_FINALIZER": "struct Lisp_Finalizer", + "PVEC_SYMBOL_WITH_POS": "struct Lisp_Symbol_With_Pos", + "PVEC_MISC_PTR": "", + "PVEC_USER_PTR": "struct Lisp_User_Ptr", + "PVEC_PROCESS": "struct Lisp_Process", + "PVEC_BOOL_VECTOR": "struct Lisp_Bool_Vector", + "PVEC_BUFFER": "struct buffer", + "PVEC_HASH_TABLE": "struct Lisp_Hash_Table", + "PVEC_TERMINAL": "struct terminal", + "PVEC_WINDOW_CONFIGURATION": "struct save_window_data", + "PVEC_SUBR": "struct Lisp_Subr", + "PVEC_OTHER": "void", + "PVEC_XWIDGET": "void", + "PVEC_XWIDGET_VIEW": "void", + "PVEC_THREAD": "struct thread_state", + "PVEC_MUTEX": "Lisp_Mutex", + "PVEC_CONDVAR": "Lisp_CondVar", + "PVEC_MODULE_FUNCTION": "struct Lisp_Module_Function", + "PVEC_NATIVE_COMP_UNIT": "struct Lisp_Native_Comp_Unit", + "PVEC_SQLITE": "struct Lisp_Sqlite", + "PVEC_COMPILED": "struct Lisp_Vector", + "PVEC_CHAR_TABLE": "struct Lisp_Vector", + "PVEC_SUB_CHAR_TABLE": "void", + "PVEC_RECORD": "struct Lisp_Vector", + "PVEC_FONT": "struct font", + "PVEC_NORMAL_VECTOR": "struct Lisp_Vector" + } + + # Object construction/initialization. + def __init__(self, lisp_obj): + self.frame = lisp_obj.GetFrame() + self.lisp_obj = lisp_obj + self.unsigned = lisp_obj.GetValueAsUnsigned() + self.lisp_type = None + self.pvec_type = None + self.value = None + self.init_lisp_types() + self.init_values() + + # Initialize self.lisp_type to the C Lisp_Type enumerator of the + # Lisp_Object, as a string. Initialize self.pvec_type likewise to + # the pvec_type enumerator if the object is a vector-like, as a + # string. + def init_lisp_types(self): + t = self.eval(f"(enum Lisp_Type)" + f"((EMACS_INT) {self.unsigned} " + f"& (1 << GCTYPEBITS) - 1)") + self.lisp_type = enumerator_name(t) + if self.lisp_type == "Lisp_Vectorlike": + self.pvec_type = "PVEC_NORMAL_VECTOR" + vector = self.get_lisp_pointer("struct Lisp_Vector") + size = vector.GetValueForExpressionPath("->header.size") + size = size.GetValueAsUnsigned() + pseudo = self.eval(f"{size} & PSEUDOVECTOR_FLAG") + if pseudo.GetValueAsUnsigned() != 0: + typ = self.eval( + f"(enum pvec_type) (({size} " + f"& More_Lisp_Bits::PVEC_TYPE_MASK) " + f">> More_Lisp_Bits::PSEUDOVECTOR_AREA_BITS)") + self.pvec_type = enumerator_name(typ) + + # Initialize self.value according to lisp_type and pvec_type. + def init_values(self): + if self.lisp_type == "Lisp_Symbol": + offset = self.get_lisp_pointer("char").GetValueAsUnsigned() + self.value = self.eval(f"(struct Lisp_Symbol *)" + f" ((char *) &lispsym + {offset})") + elif self.lisp_type == "Lisp_String": + self.value = self.get_lisp_pointer("struct Lisp_String") + elif self.lisp_type == "Lisp_Vectorlike": + c_type = Lisp_Object.pvec2type[self.pvec_type] + self.value = self.get_lisp_pointer(c_type) + elif self.lisp_type == "Lisp_Cons": + self.value = self.get_lisp_pointer("struct Lisp_Cons") + elif self.lisp_type == "Lisp_Float": + self.value = self.get_lisp_pointer("struct Lisp_Float") + elif self.lisp_type in ("Lisp_Int0", "Lisp_Int1"): + self.value = self.eval(f"((EMACS_INT) {self.unsigned}) " + f">> (GCTYPEBITS - 1)") + else: + assert False, "Unknown Lisp type" + + # Create an SBValue for EXPR with name NAME. + def create_value(self, name, expr): + return self.lisp_obj.CreateValueFromExpression(name, expr) + + # Evaluate EXPR in the context of the current frame. + def eval(self, expr): + return self.frame.EvaluateExpression(expr) + + # Return an SBValue for this object denoting a pointer of type + # TYP*. + def get_lisp_pointer(self, typ): + return self.eval(f"({typ}*) (((EMACS_INT) " + f"{self.unsigned}) & VALMASK)") + + # If this is a Lisp_String, return an SBValue for its string data. + # Return None otherwise. + def get_string_data(self): + if self.lisp_type == "Lisp_String": + return self.value.GetValueForExpressionPath("->u.s.data") + return None + + # if this is a Lisp_Symbol, return an SBBalue for its name. + # Return None otherwise. + def get_symbol_name(self): + if self.lisp_type == "Lisp_Symbol": + name = self.value.GetValueForExpressionPath("->u.s.name") + return Lisp_Object(name).get_string_data() + return None + + # Return a summary string for this object. + def summary(self): + return str(self.value) + + +######################################################################## +# LLDB Commands +######################################################################## + +def xbacktrace(debugger, command, ctx, result, internal_dict): + """Print Emacs Lisp backtrace""" + frame = ctx.GetFrame() + n = frame.EvaluateExpression( + "current_thread->m_specpdl_ptr - current_thread->m_specpdl") + for i in reversed(range(0, n.GetValueAsUnsigned())): + s = frame.EvaluateExpression(f"current_thread->m_specpdl[{i}]") + kind = enumerator_name(s.GetChildMemberWithName("kind")) + if kind == "SPECPDL_BACKTRACE": + function = Lisp_Object(s.GetValueForExpressionPath(".bt.function")) + if function.lisp_type == "Lisp_Symbol": + sym_name = function.get_symbol_name() + result.AppendMessage(str(sym_name)) + elif function.lisp_type == "Lisp_Vectorlike": + result.AppendMessage(function.pvec_type) + else: + result.AppendMessage(function.lisp_type) + +def xdebug_print(debugger, command, result, internal_dict): + """Print Lisp_Objects using safe_debug_print()""" + debugger.HandleCommand(f"expr safe_debug_print({command})") + + +######################################################################## +# Formatters +######################################################################## + +def type_summary_Lisp_Object(obj, internal_dict): + return "-> " + Lisp_Object(obj).summary() + + +######################################################################## +# Initialization +######################################################################## + +# Define Python FUNCTION as an LLDB command. +def define_command (debugger, function): + lldb_command = function.__name__ + python_function = __name__ + "." + function.__name__ + interpreter = debugger.GetCommandInterpreter() + def define(overwrite): + res = lldb.SBCommandReturnObject() + interpreter.HandleCommand(f"command script add " + f"{overwrite} " + f"--function {python_function} " + f"{lldb_command}", + res) + return res.Succeeded() + if not define("--overwrite"): + define("") + +# Define Python FUNCTION as an LLDB type summary provider for types +# matching REGEX. Type summaries defined here are defined in the +# category Emacs, and can be seen with 'type summary list -w Emacs', +# and deleted in a similar way. +def define_type_summary(debugger, regex, function): + python_function = __name__ + "." + function.__name__ + debugger.HandleCommand(f"type summary add " + f"--cascade true " + f"--category Emacs " + f'--regex "{regex}" ' + f"--python-function {python_function}") + +# This function is called by LLDB to initialize the module. +def __lldb_init_module(debugger, internal_dict): + define_command(debugger, xbacktrace) + define_command(debugger, xdebug_print) + define_type_summary(debugger, "Lisp_Object", type_summary_Lisp_Object) + print('Emacs debugging support has been installed.') + +# end. |