summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerd Möllmann <gerd@gnu.org>2022-07-12 12:29:14 +0200
committerGerd Möllmann <gerd@gnu.org>2022-07-14 11:23:25 +0200
commite1d93302c29916e738552484b9363c5b9e77c5c1 (patch)
tree7cdc166494e947bf32e411d510d73bbc144c3cdb
parent63e1b42f5b69d9a7346921b4c1998d7a3a8ae223 (diff)
downloademacs-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.py217
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.