summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2022-10-21 09:47:16 -0700
committerSean Whitton <spwhitton@spwhitton.name>2022-10-29 21:47:37 -0700
commit68526933a835f4a4984769083aebb47405af6086 (patch)
treea6c87155d5eee22e6bffc6c4b22b6ea6b39294e0
parent7bae01d026509830800f2cacdb756462823a011d (diff)
downloaddotfiles-68526933a835f4a4984769083aebb47405af6086.tar.gz
have gdbmacs start up Emacs rather than attach to it
Additionally, either re-use or kill the GUD interaction buffer rather than calling `gdb-reset' ourselves: killing it is the documented way to reset.
-rw-r--r--.emacs.d/init.el94
-rwxr-xr-xbin/emacsclient82
2 files changed, 141 insertions, 35 deletions
diff --git a/.emacs.d/init.el b/.emacs.d/init.el
index e66dd3ad..2b955f63 100644
--- a/.emacs.d/init.el
+++ b/.emacs.d/init.el
@@ -2274,40 +2274,72 @@ Called by '~/src/dotfiles/bin/emacsclient --spw/update-environment'."
(let ((slime-dispatching-connection connection))
(slime-eval cl-form))))))
-(defvar-local spw/gdbmacs-target-emacs nil)
-
-(defun spw/gdbmacs-attach (pid)
- (interactive "nEmacs PID: ")
- (when-let ((proc (and (boundp 'gud-comint-buffer)
- (get-buffer-process gud-comint-buffer))))
- (if (string= gdb-inferior-status "signal-received")
- ;; Avoid wiping out useful info.
- (error "Possibly Emacs just crashed; not attaching for now")
- (set-process-query-on-exit-flag proc nil)
- (kill-buffer gud-comint-buffer)))
- (require 'gdb-mi)
- (let ((default-directory (expand-file-name "~/src/emacs/")))
- (gdb-reset)
- (gdb (format "gdb -i=mi --pid=%d src/emacs" pid))
- (setq spw/gdbmacs-target-emacs pid)
- (gud-basic-call "continue")))
-
-(defun spw/remote-gdbmacs-attach ()
- (interactive)
- (call-process "emacsclient" nil "*gdbmacs-emacsclient*" nil
- "--socket-name=gdbmacs" "--spw/installed"
- "-e" (prin1-to-string `(spw/gdbmacs-attach ,(emacs-pid)))))
-
-(defun spw/maybe-remote-gdbmacs-attach ()
- (when (and (eq (daemonp) t)
- (file-in-directory-p invocation-directory "~/src/emacs/"))
- (spw/remote-gdbmacs-attach)))
-(add-hook 'after-init-hook #'spw/maybe-remote-gdbmacs-attach)
+(defun spw/daemon-pid (&optional name)
+ ;; We don't use `server-eval-at' because perhaps we are trying to attach gdb
+ ;; to a wedged Emacs.
+ (let ((socket (file-name-concat server-socket-dir (or name "server"))))
+ (and (file-exists-p socket)
+ (and-let* ((output (car (process-lines "ss" "-Hplx" "src" socket))))
+ (and (string-match "pid=\\([[:digit:]]+\\)" output)
+ (string-to-number (match-string 1 output)))))))
+
+(defvar-local spw/gdbmacs-target-pid nil)
+(defvar-local spw/gdbmacs-target-name nil)
+
+(defun spw/gdbmacs-attach (&optional name)
+ (let (pid
+ (arg (if name (concat "--fg-daemon=" name) "--fg-daemon"))
+ (proc (and (boundp 'gud-comint-buffer)
+ (get-buffer-process gud-comint-buffer))))
+ (when (and proc (string= gdb-inferior-status "signal-received"))
+ ;; Avoid wiping out useful info.
+ (error "Possibly Emacs just crashed; not attaching for now"))
+ (require 'gdb-mi)
+ (cl-flet ((run-or-continue ()
+ (gdb-wait-for-pending
+ (lambda ()
+ (with-current-buffer gud-comint-buffer
+ (setq spw/gdbmacs-target-pid pid
+ spw/gdbmacs-target-name name))
+ (if pid
+ (gud-basic-call "continue")
+ (gud-basic-call "set cwd ~")
+ (gdb-wait-for-pending
+ (lambda () (gud-basic-call "run"))))))))
+ (gdb-wait-for-pending
+ (if (and proc
+ ;; Check it looks safe to re-use existing gdb process.
+ (string-prefix-p "exited" gdb-inferior-status)
+ (file-in-directory-p
+ (buffer-local-value 'default-directory gud-comint-buffer)
+ (expand-file-name "~/src/emacs/")))
+ (lambda ()
+ (gud-basic-call (if (setq pid (spw/daemon-pid name))
+ (format "attach %d" pid)
+ (format "set args %s" arg)))
+ (run-or-continue))
+ ;; Start up a new process.
+ (lambda ()
+ (when (buffer-live-p gud-comint-buffer)
+ (when proc (set-process-query-on-exit-flag proc nil))
+ (kill-buffer gud-comint-buffer))
+ (gdb-wait-for-pending
+ (lambda ()
+ (let ((default-directory (expand-file-name "~/src/emacs/")))
+ (gdb (if (setq pid (spw/daemon-pid name))
+ (format "gdb -i=mi --pid=%d src/emacs" pid)
+ (format "gdb -i=mi --args src/emacs %s" arg))))
+ (run-or-continue)))))))))
;; C-c C-z to attempt to return control to the debugger.
+;;
+;; In the --fg-daemon case, AIUI we are here working around this:
+;; <https://lwn.net/Articles/909496/>.
(defun spw/comint-stop-subjob (orig-fun)
- (if spw/gdbmacs-target-emacs
- (signal-process spw/gdbmacs-target-emacs 'SIGTSTP)
+ (if-let ((pid (or spw/gdbmacs-target-pid
+ (setq spw/gdbmacs-target-pid
+ (spw/daemon-pid spw/gdbmacs-target-name)))))
+ (signal-process pid 'SIGTSTP)
(funcall orig-fun)))
(advice-add 'comint-stop-subjob :around #'spw/comint-stop-subjob)
diff --git a/bin/emacsclient b/bin/emacsclient
index fe08427d..920c0102 100755
--- a/bin/emacsclient
+++ b/bin/emacsclient
@@ -24,8 +24,11 @@ min_arg=0
may_start=true
want_update=false
want_installed=false
+want_eval=false
+want_version=false
devel_running=false
installed_running=false
+pass_to_gdbmacs=false
socket_dir="/run/user/$(id -u)/emacs/"
get_listener () {
@@ -40,12 +43,34 @@ get_listener () {
fi
}
+maybe_notify () {
+ echo >&2 "$1"
+ [ -n "$XDG_RUNTIME_DIR" ] \
+ && notify-send --urgency=low --expire-time=10000 "$1"
+}
+
+die_wedged () {
+ maybe_notify "in-tree Emacs appears to be wedged"
+ # Only exit non-zero if we were expected to start the daemon.
+ ! $may_start; exit
+}
+
+egrep_negate () {
+ echo -n "^([^${1:0:1}]"
+ for (( i=1; i < ${#1}; i++ )); do
+ echo -n "|(${1:0:$i}([^${1:$i:1}]|$))"
+ done
+ echo -n "|($1.+))"
+}
+
for arg do
shift
case "$arg" in
'--spw/installed') want_installed=true ;;
'--spw/no-start') may_start=false ;;
'--spw/update-environment') want_update=true ;;
+ '-V'|'--version') want_version=true ;;&
+ '-e'|'--eval') want_eval=true ;;&
'-s'|'--socket-name') min_arg=2; daemon_name="$1" ;;&
'-s'?*) min_arg=1; daemon_name="${arg:2}" ;;&
'--socket-name='?*) min_arg=1; daemon_name="${arg:14}" ;;&
@@ -81,10 +106,11 @@ if [ -z "$daemon_name" ] && $devel_running && $want_installed; then
# Detach gdb so that Emacs can handle the SIGTERM.
# We also have to quit gdb so that `gdb-inferior-status' is reset from
- # "signal-received"; see `spw/gdbmacs-attach' command.
- [ -n "$(get_listener ${socket_dir}gdbmacs)" ] \
- && emacsclient -sgdbmacs --eval '(gud-basic-call "detach")' \
- --eval '(gud-basic-call "quit")'
+ # "signal-received"; see `spw/gdbmacs-attach' function.
+ [ -n "${gdbmacs:=$(get_listener ${socket_dir}gdbmacs)}" ] \
+ && "$installed_emacsclient" -sgdbmacs \
+ --eval '(gud-basic-call "detach")' \
+ --eval '(gud-basic-call "quit")'
wait $inotifywait
devel_running=false
@@ -106,6 +132,54 @@ if ! $want_installed && ! $installed_running \
| xargs pwdx | grep -q "$HOME/src/emacs" ); then
emacs="$devel_emacs"
emacsclient="$devel_emacsclient"
+
+ # ---- Two special cases for primary session that's always under gdb
+ if [ -z "$daemon_name" ] && ! $want_version; then
+ # If devel Emacs is stopped or gdbmacs can't start it, ask gdbmacs to
+ # handle the request: it's probs. C-i e or editing a file via EDITOR.
+
+ [ -n "${gdbmacs:=$(get_listener ${socket_dir}gdbmacs)}" ] \
+ && status=$("$installed_emacsclient" \
+ -sgdbmacs \
+ --eval \
+ '(and (boundp '"'"'gud-comint-buffer)
+ (get-buffer-process gud-comint-buffer)
+ (string= "signal-received"
+ gdb-inferior-status))')
+
+ if [ "$status" = t ]; then
+ if $want_eval || $want_update; then
+ die_wedged
+ else
+ pass_to_gdbmacs=true
+ fi
+ elif [ -z "$listener" ]; then
+ # inotifywait(1) in Debian "bullseye" doesn't have --include.
+ inotifywait -qq -e create "${socket_dir}" \
+ --exclude "$(egrep_negate ${socket_dir}server)" &
+ inotifywait=$!
+
+ if "$installed_emacsclient" -a '' -sgdbmacs \
+ --eval '(spw/gdbmacs-attach)'; then
+ wait $inotifywait
+ listener=true
+ elif $want_eval || $want_update; then
+ kill $inotifywait
+ die_wedged
+ else
+ kill $inotifywait
+ pass_to_gdbmacs=true
+ fi
+ fi
+
+ if $pass_to_gdbmacs; then
+ emacs="$installed_emacs"
+ emacsclient="$installed_emacsclient"
+ listener="$gdbmacs"
+ set -- "$@" -sgdbmacs
+ fi
+ fi
+ # ---- End two special cases for primary session that's always under gdb
else
emacs="$installed_emacs"
emacsclient="$installed_emacsclient"