Subject: [PATCH] New debugger-trap function to break to GDB
From: Jeremy Bryant <[email-masked]>
To: [email-masked]
Cc: , Eli Zaretskii <[email-masked]>, Paul Eggert <[email-masked]>
Date: Sat, 02 Aug 2025 00:19:26 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

Here is a patch to add a default command debugger-trap to break into
GDB, together with a default breakpoint set in .gdbinit
The immediate benefit for developers is a standard command to use where
there isn't one at the moment, as explained in DEBUG, only general guidance.

The long-term benefit is it can be the foundation for further work to
enhance debugging in Emacs with GDB, with the ability to
programmatically break into GDB, something I am working on as part of
C/Lisp hybrid debugging aids.

This patch has received substantial advice and reviewed by Eli.  It is
posted here for the benefit of the community for further discussion.

To recap, we need a do-nothing primitive, whose sole purpose is to
allow setting a breakpoint there to get control to GDB.
redraw-display and similar functions are usually okay, but they could
 get in the way if what they do modifies Emacs in a way that interferes
with debugging, for example if they remove some display artifact one
is debugging, or run functions as side effect that modify the global
state.

Adding Paul Eggert as the main recent contributor to etc/DEBUG for thoughts.

From 8f790c3fb884668a829b11ea00c1ffc6e4f37488 Mon Sep 17 00:00:00 2001
From: Jeremy Bryant <[email-masked]>
Date: Wed, 23 Jul 2025 23:35:01 +0100
Subject: [PATCH] New debugger-trap function to break to GDB

* src/eval.c: new primitive debugger-trap
* src/.gdbinit: set breakpoint to Fdebugger_trap
* etc/DEBUG: document it.
Remove suggestion to use Fredraw_display.

This do-nothing primitive gives control to GDB, and for debugging
convenience a breakpoint is set by default in .gdbinit.
---
 etc/DEBUG    | 11 ++++++-----
 src/.gdbinit |  5 +++++
 src/eval.c   | 10 ++++++++++
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/etc/DEBUG b/etc/DEBUG
index 25049ad42b4..44f19900c64 100644
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -211,16 +211,17 @@ the debugger, but before running it, is the most efficient way of
 making sure control will be returned to the debugger when you need
 that.
 
+There is a default function to give control to the debugger.  It is
+called debugger-trap.  This is a do-nothing primitive, as a convenient
+point to return control to the debugger.  You can invoke interactively
+with "M-x debugger-trap RET".  The src/.gdbinit file in the Emacs source
+distribution sets a breakpoint on this function.
+
 'Fsignal' is a very useful place to put a breakpoint in.  All Lisp
 errors go through there.  If you are only interested in errors that
 would fire the Lisp debugger, breaking at 'maybe_call_debugger' is
 useful.
 
-Another technique for getting control to the debugger is to put a
-breakpoint in some rarely used function.  One such convenient function
-is Fredraw_display, which you can invoke at will interactively with
-"M-x redraw-display RET".
-
 It is also useful to have a guaranteed way to return to the debugger
 at any arbitrary time.  When using X, this is easy: type C-z at the
 window where you are interacting with GDB, and it will stop Emacs just
diff --git a/src/.gdbinit b/src/.gdbinit
index d3bfad59486..0eab7ac9afa 100644
--- a/src/.gdbinit
+++ b/src/.gdbinit
@@ -1314,6 +1314,11 @@ if defined_WINDOWSNT
   end
 end
 
+# Break at default trap function to give control to GDB.
+# Call from Emacs with M-x debugger-trap
+break Fdebugger_trap
+
+
 # Put the Python code at the end of .gdbinit so that if GDB does not
 # support Python, GDB will do all the above initializations before
 # reporting an error.
diff --git a/src/eval.c b/src/eval.c
index 5e2b5bff796..baea44b8ce1 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -3089,6 +3089,15 @@ FUNCTIONP (Lisp_Object object)
     return false;
 }
 
+DEFUN ("debugger-trap", Fdebugger_trap, Sdebugger_trap, 0, 0, "",
+       doc: /* Trap execution flow and hand over control to GDB.
+To use, set up a breakpoint to this function from GDB.
+The function itself is a do-nothing primitive.  */)
+  (void)
+{
+  return Qnil;
+}
+
 Lisp_Object
 funcall_general (Lisp_Object fun, ptrdiff_t numargs, Lisp_Object *args)
 {
@@ -4615,4 +4624,5 @@ syms_of_eval (void)
   defsubr (&Sspecial_variable_p);
   DEFSYM (Qfunctionp, "functionp");
   defsubr (&Sfunctionp);
+  defsubr (&Sdebugger_trap);
 }
-- 
2.47.2