diff options
author | Gerd Möllmann <gerd@gnu.org> | 2022-07-12 12:29:14 +0200 |
---|---|---|
committer | Gerd Möllmann <gerd@gnu.org> | 2022-07-14 11:23:25 +0200 |
commit | e1d93302c29916e738552484b9363c5b9e77c5c1 (patch) | |
tree | 7cdc166494e947bf32e411d510d73bbc144c3cdb | |
parent | 63e1b42f5b69d9a7346921b4c1998d7a3a8ae223 (diff) | |
download | emacs-e1d93302c29916e738552484b9363c5b9e77c5c1.tar.gz |
Improve support for debugging Emacs with LLDB
* etc/emacs_lldb.py: Refactor and support more Lisp types.
-rw-r--r-- | etc/emacs_lldb.py | 217 |
1 files changed, 141 insertions, 76 deletions
diff --git a/etc/emacs_lldb.py b/etc/emacs_lldb.py index 4e0b20c8a55..740cbc0956b 100644 --- a/etc/emacs_lldb.py +++ b/etc/emacs_lldb.py @@ -30,63 +30,136 @@ import lldb # Utilties ######################################################################## -# Return the Lisp_Type of Lisp_Object OBJ. -def get_lisp_type(obj): - int_value = obj.GetValueAsUnsigned() - return obj.GetFrame().EvaluateExpression( - f"(enum Lisp_Type) ((EMACS_INT) {int_value} " - "& (1 << GCTYPEBITS) - 1)") - -# Return the Lisp_Type or pseudo-vector type of OBJ. -def get_lisp_type_or_vectorlike(obj): - lisp_type = get_lisp_type(obj) - if enumerator_name(lisp_type) == "Lisp_Vectorlike": - vector = get_lisp_pointer(obj, "struct Lisp_Vector") - header_size = vector.GetValueForExpressionPath( - "->header.size").GetValueAsUnsigned() - frame = obj.GetFrame() - pseudo = frame.EvaluateExpression( - f"{header_size} & PSEUDOVECTOR_FLAG") - if pseudo.GetValueAsUnsigned() != 0: - return frame.EvaluateExpression( - f"(enum pvec_type) (({header_size} " - "& More_Lisp_Bits::PVEC_TYPE_MASK) " - ">> More_Lisp_Bits::PSEUDOVECTOR_AREA_BITS)") - return frame.EvaluateExpression("pvec_type::PVEC_NORMAL_VECTOR") - return lisp_type - -# Return Lisp_Object OBJ as pointer to TYP *. -def get_lisp_pointer(obj, typ): - return obj.GetFrame().EvaluateExpression( - f"({typ}*) (((EMACS_INT) {obj.GetValueAsUnsigned()}) & VALMASK)") - -# Return Lisp_Object OBJ as pointer to Lisp_Symbol. -def get_lisp_symbol(obj): - ptr = get_lisp_pointer(obj, "char") - offset = ptr.GetValueAsUnsigned() - return obj.GetFrame().EvaluateExpression( - f"(struct Lisp_Symbol *) ((char *) &lispsym + {offset})") - -# Return Lisp_Object OBJ as pointer to Lisp_String -def get_lisp_string(obj): - return get_lisp_pointer(obj, "struct Lisp_String") - -# Return the string data of Lisp_Object OBJ which denotes a Lisp_String. -def get_lisp_string_data(obj): - string = get_lisp_string(obj) - return string.GetValueForExpressionPath("->u.s.data") - -# Assuming OBJ denotes a Lisp_Symbol, return the name of the symbol. -def get_lisp_symbol_name(obj): - sym = get_lisp_symbol(obj) - name = sym.GetValueForExpressionPath("->u.s.name") - return get_lisp_string_data(name) - -# Return a string for the enuerator ENUM. +# 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 @@ -101,16 +174,14 @@ def xbacktrace(debugger, command, ctx, result, internal_dict): s = frame.EvaluateExpression(f"current_thread->m_specpdl[{i}]") kind = enumerator_name(s.GetChildMemberWithName("kind")) if kind == "SPECPDL_BACKTRACE": - function = s.GetValueForExpressionPath(".bt.function") - function_type = enumerator_name(get_lisp_type(function)) - if function_type == "Lisp_Symbol": - sym_name = get_lisp_symbol_name(function) + 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_type == "Lisp_Vectorlike": - subtype = get_lisp_type_or_vectorlike(function) - result.AppendMessage(str(subtype)) + elif function.lisp_type == "Lisp_Vectorlike": + result.AppendMessage(function.pvec_type) else: - result.AppendMessage(function_type) + result.AppendMessage(function.lisp_type) def xdebug_print(debugger, command, result, internal_dict): """Print Lisp_Objects using safe_debug_print()""" @@ -121,18 +192,8 @@ def xdebug_print(debugger, command, result, internal_dict): # Formatters ######################################################################## -# Return a type summary for Lisp_Objects. -def format_Lisp_Object(obj, internal_dict): - lisp_type = get_lisp_type_or_vectorlike(obj) - kind = enumerator_name(lisp_type) - summary = "-> " - if kind == "PVEC_FRAME": - ptr = get_lisp_pointer(obj, "struct frame") - summary += str(ptr) - elif kind == "PVEC_WINDOW": - ptr = get_lisp_pointer(obj, "struct window") - summary += str(ptr) - return summary +def type_summary_Lisp_Object(obj, internal_dict): + return "-> " + Lisp_Object(obj).summary() ######################################################################## @@ -155,11 +216,15 @@ def define_command (debugger, function): if not define("--overwrite"): define("") -# Define Python FUNCTION as an LLDB type formatter. -def define_formatter(debugger, regex, function): +# 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}") @@ -167,7 +232,7 @@ def define_formatter(debugger, regex, function): def __lldb_init_module(debugger, internal_dict): define_command(debugger, xbacktrace) define_command(debugger, xdebug_print) - define_formatter(debugger, "Lisp_Object", format_Lisp_Object) + define_type_summary(debugger, "Lisp_Object", type_summary_Lisp_Object) print('Emacs debugging support has been installed.') # end. |