emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 7af425f87b: Support for debugging Emacs with LLDB


From: Gerd Moellmann
Subject: master 7af425f87b: Support for debugging Emacs with LLDB
Date: Mon, 11 Jul 2022 08:08:51 -0400 (EDT)

branch: master
commit 7af425f87bf9866c60ac134cbb6aa9eb0c61f8af
Author: Gerd Möllmann <gerd@gnu.org>
Commit: Gerd Möllmann <gerd@gnu.org>

    Support for debugging Emacs with LLDB
    
    * (src/.lldbinit): New file.
    * (etc/emacs_lldb.py): Module loaded from .lldbinit.
---
 etc/DEBUG         |  36 ++++++++++++
 etc/emacs_lldb.py | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/.lldbinit     |  33 +++++++++++
 3 files changed, 242 insertions(+)

diff --git a/etc/DEBUG b/etc/DEBUG
index 7d2f810d07..df289310f9 100644
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -1036,6 +1036,42 @@ recovering the contents of Emacs buffers from a core 
dump file.  You
 might also find those commands useful for displaying the list of
 buffers in human-readable format from within the debugger.
 
+*** Debugging Emacs with LLDB
+
+On systems where GDB is not available, like macOS with M1 chip, you
+can also use LLDB for Emacs debugging.
+
+To start LLDB to debug Emacs, you can simply type "lldb ./emacs RET"
+at the shell prompt in directory of the Emacs executable, usually the
+'src' sub-directory of the Emacs tree).
+
+When you debug Emacs with LLDB, you should start LLDB in the directory
+where the Emacs executable was built.  That directory has an .lldbinit
+file that loads a Python module emacs_lldb.py from the 'etc' directory
+of the Emacs source tree.  The Python module defines "user-defined"
+commands for debugging Emacs.
+
+LLDB by default does not automatically load .lldbinit files in the
+current directory.  The simplest way to fix this is to add the
+following line to your ~/.lldbinit file (creating such a file if it
+doesn't already exist):
+
+  settings set target.load-cwd-lldbinit true
+
+Alternatively, you can type "lldb --local-lldbinit ./emacs RET".
+
+If everything worked, you should see something like "Emacs debugging
+support has been installed" after starting LLDB.  You can see which
+Emacs-specific commands are defined with
+
+  (lldb) help
+
+User-defined commands for Emacs debugging start with an "x".
+
+Please refer to the LLDB reference on the web for more information
+about LLDB.  If you already know GDB, you will also find a mapping
+from GDB commands to corresponding LLDB commands there.
+
 
 This file is part of GNU Emacs.
 
diff --git a/etc/emacs_lldb.py b/etc/emacs_lldb.py
new file mode 100644
index 0000000000..4e0b20c8a5
--- /dev/null
+++ b/etc/emacs_lldb.py
@@ -0,0 +1,173 @@
+# 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
+
+
+########################################################################
+#                              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.
+def enumerator_name(enum):
+    enumerators = enum.GetType().GetEnumMembers()
+    return enumerators[enum.GetValueAsUnsigned()].GetName()
+
+
+########################################################################
+#                           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 = s.GetValueForExpressionPath(".bt.function")
+            function_type = enumerator_name(get_lisp_type(function))
+            if function_type == "Lisp_Symbol":
+                sym_name = get_lisp_symbol_name(function)
+                result.AppendMessage(str(sym_name))
+            elif function_type == "Lisp_Vectorlike":
+                subtype = get_lisp_type_or_vectorlike(function)
+                result.AppendMessage(str(subtype))
+            else:
+                result.AppendMessage(function_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
+########################################################################
+
+# 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
+
+
+########################################################################
+#                           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 formatter.
+def define_formatter(debugger, regex, function):
+    python_function = __name__ + "." + function.__name__
+    debugger.HandleCommand(f"type summary add "
+                           f"--cascade true "
+                           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_formatter(debugger, "Lisp_Object", format_Lisp_Object)
+    print('Emacs debugging support has been installed.')
+
+# end.
diff --git a/src/.lldbinit b/src/.lldbinit
new file mode 100644
index 0000000000..617d63958b
--- /dev/null
+++ b/src/.lldbinit
@@ -0,0 +1,33 @@
+# -*- mode: shell-script -*-
+# Copyright (C) 1992-1998, 2000-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/>.
+#
+# Use 'lldb --local-init' or add to your ~/.lldbinit the line
+#
+# settings set target.load-cwd-lldbinit true
+#
+# Emacs-specific commands start with 'x'.  Type 'help' to see all
+# commands.  Type 'help <command>' to see help for a command
+# <command>.
+
+# Make Python find our files
+script -- sys.path.append('../etc')
+
+# Load our Python files
+command script import emacs_lldb
+
+# end.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]