unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
From: "Gerd Möllmann" <gerd.moellmann@gmail.com>
To: 56495@debbugs.gnu.org
Subject: bug#56495: 29.0.50; Support for debugging Emacs with LLDB
Date: Mon, 11 Jul 2022 10:13:35 +0200	[thread overview]
Message-ID: <AAB63EE4-18DD-40D7-A64E-DEB6729B60B3@gmail.com> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 629 bytes --]

My system, macOS with Apple M1 chip, is currently not supported by GDB.
To quote gdb-devel, "lldb is the way to go" to debug Emacs for me.

Attached patch adds rather limited support for that.  Limited by

- the fact that I don't know LLDB,
- that I don't know LLDB's Python API,
- that I'm not a Python programmer,
- that the Python API documentation is pretty lacking in itself,
- that I didn't implement support for ENABLE_CHECKING and what else
  might change Lisp_Object layout

So please bear with me.

Anyway, at least displaying some Lisp_Objects with 'p obj' seems to
work, and 'xbacktrace' seems to be working.

YMMV.


[-- Attachment #1.2: 0001-Support-for-debugging-Emacs-with-LLDB.patch --]
[-- Type: application/octet-stream, Size: 8978 bytes --]

From c5cfd261a549740a0619eb1854032778204dc51e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gerd=20M=C3=B6llmann?= <gerd@gnu.org>
Date: Sun, 10 Jul 2022 13:35:32 +0200
Subject: [PATCH] Support for debugging Emacs with LLDB

* (src/.lldbinit): New file.
* (etc/emacs_lldb.py): Module loaded from .lldbinit.
---
 etc/emacs_lldb.py | 166 ++++++++++++++++++++++++++++++++++++++++++++++
 src/.lldbinit     |  33 +++++++++
 2 files changed, 199 insertions(+)
 create mode 100644 etc/emacs_lldb.py
 create mode 100644 src/.lldbinit

diff --git a/etc/emacs_lldb.py b/etc/emacs_lldb.py
new file mode 100644
index 0000000000..3a9f17e020
--- /dev/null
+++ b/etc/emacs_lldb.py
@@ -0,0 +1,166 @@
+# 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
+
+\f
+########################################################################
+#                              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()
+
+\f
+########################################################################
+#                           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})")
+
+\f
+########################################################################
+#                             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
+
+\f
+########################################################################
+#                           Initialization
+########################################################################
+
+# Define Python FUNCTION as an LLDB command.
+def define_command (debugger, function):
+    lldb_command = function.__name__
+    python_function = __name__ + "." + function.__name__
+    debugger.HandleCommand(f"command script add "
+                           f"--overwrite "
+                           f"--function {python_function} "
+                           f"{lldb_command}")
+
+# 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.
-- 
2.37.0


[-- Attachment #2: Message signed with OpenPGP --]
[-- Type: application/pgp-signature, Size: 874 bytes --]

             reply	other threads:[~2022-07-11  8:13 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-11  8:13 Gerd Möllmann [this message]
2022-07-11  9:18 ` bug#56495: 29.0.50; Support for debugging Emacs with LLDB Robert Pluim
2022-07-11 10:42   ` Gerd Möllmann
2022-07-11 10:56     ` Robert Pluim
2022-07-11 11:27       ` Gerd Möllmann
2022-07-11 12:28         ` Robert Pluim
2022-09-05 19:16           ` Lars Ingebrigtsen
2022-07-11 11:19 ` Eli Zaretskii
2022-07-11 11:31   ` Gerd Möllmann
2022-07-12  3:04 ` Richard Stallman
2022-07-12  3:15   ` Stefan Kangas
2022-07-14  3:10     ` Richard Stallman
2022-07-12  8:04   ` Gerd Möllmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=AAB63EE4-18DD-40D7-A64E-DEB6729B60B3@gmail.com \
    --to=gerd.moellmann@gmail.com \
    --cc=56495@debbugs.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).