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:
parent
63e1b42f5b
commit
e1d93302c2
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user