1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-27 10:54:40 +00:00

Improve support for debugging Emacs with LLDB

* etc/emacs_lldb.py: Refactor and support more Lisp types.
This commit is contained in:
Gerd Möllmann 2022-07-12 12:29:14 +02:00
parent 63e1b42f5b
commit e1d93302c2

View File

@ -30,63 +30,136 @@
# 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(overwrite):
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.