summaryrefslogtreecommitdiff
path: root/etc/emacs_lldb.py
diff options
context:
space:
mode:
Diffstat (limited to 'etc/emacs_lldb.py')
-rw-r--r--etc/emacs_lldb.py238
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.