From b668fa6eb0d46a8f3c675954dfd8ce79e6441129 Mon Sep 17 00:00:00 2001 From: Chong Yidong Date: Thu, 19 Apr 2012 16:09:30 +0800 Subject: [PATCH] Delete the gdb-inferior pty when the gdb process exits. * lisp/progmodes/gdb-mi.el (gdb-inferior-io--maybe-delete-pty): New function to call delete-process on the gdb-inferior buffer's pty. (gdb-reset): Use it, instead of relying on kill-buffer to kill the pty process. (gdb-update): New arg to suppress talking to the gdb process. (gdb-done-or-error): Use it. (gdb-stopped-functions): Rename from gdb-stopped-hooks. (gdb): Call gdb-inferior-io--maybe-delete-pty as a workaround for sentinel not being called. * lisp/comint.el (make-comint-in-buffer, comint-exec): Doc fix. Fixes: debbugs:11273 --- lisp/ChangeLog | 14 ++++++++ lisp/comint.el | 34 +++++++++++++------ lisp/progmodes/gdb-mi.el | 72 ++++++++++++++++++++++++++-------------- 3 files changed, 86 insertions(+), 34 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 7ef7628b6d6..1ce5e7fceea 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,17 @@ +2012-04-19 Chong Yidong + + * progmodes/gdb-mi.el (gdb-inferior-io--maybe-delete-pty): New + function to call delete-process on the gdb-inferior buffer's pty. + (gdb-reset): Use it, instead of relying on kill-buffer to kill the + pty process (Bug#11273). + (gdb-update): New arg to suppress talking to the gdb process. + (gdb-done-or-error): Use it. + (gdb-stopped-functions): Rename from gdb-stopped-hooks. + (gdb): Call gdb-inferior-io--maybe-delete-pty as a workaround for + sentinel not being called. + + * comint.el (make-comint-in-buffer, comint-exec): Doc fix. + 2012-04-18 Chong Yidong * progmodes/grep.el (grep, rgrep): Doc fix (Bug#11268). diff --git a/lisp/comint.el b/lisp/comint.el index 68fedeb88a9..10981675971 100644 --- a/lisp/comint.el +++ b/lisp/comint.el @@ -699,16 +699,21 @@ BUFFER can be either a buffer or the name of one." (defun make-comint-in-buffer (name buffer program &optional startfile &rest switches) "Make a Comint process NAME in BUFFER, running PROGRAM. If BUFFER is nil, it defaults to NAME surrounded by `*'s. -PROGRAM should be either a string denoting an executable program to create -via `start-file-process', or a cons pair of the form (HOST . SERVICE) denoting -a TCP connection to be opened via `open-network-stream'. If there is already -a running process in that buffer, it is not restarted. Optional fourth arg -STARTFILE is the name of a file, whose contents are sent to the -process as its initial input. +If there is a running process in BUFFER, it is not restarted. + +PROGRAM should be one of the following: +- a string, denoting an executable program to create via + `start-file-process' +- a cons pair of the form (HOST . SERVICE), denoting a TCP + connection to be opened via `open-network-stream' +- nil, denoting a newly-allocated pty. + +Optional fourth arg STARTFILE is the name of a file, whose +contents are sent to the process as its initial input. If PROGRAM is a string, any more args are arguments to PROGRAM. -Returns the (possibly newly created) process buffer." +Return the (possibly newly created) process buffer." (or (fboundp 'start-file-process) (error "Multi-processing is not supported for this system")) (setq buffer (get-buffer-create (or buffer (concat "*" name "*")))) @@ -752,9 +757,18 @@ See `make-comint' and `comint-exec'." (defun comint-exec (buffer name command startfile switches) "Start up a process named NAME in buffer BUFFER for Comint modes. Runs the given COMMAND with SWITCHES, and initial input from STARTFILE. -Blasts any old process running in the buffer. Doesn't set the buffer mode. -You can use this to cheaply run a series of processes in the same Comint -buffer. The hook `comint-exec-hook' is run after each exec." + +COMMAND should be one of the following: +- a string, denoting an executable program to create via + `start-file-process' +- a cons pair of the form (HOST . SERVICE), denoting a TCP + connection to be opened via `open-network-stream' +- nil, denoting a newly-allocated pty. + +This function blasts any old process running in the buffer, and +does not set the buffer mode. You can use this to cheaply run a +series of processes in the same Comint buffer. The hook +`comint-exec-hook' is run after each exec." (with-current-buffer buffer (let ((proc (get-buffer-process buffer))) ; Blast any old process. (if proc (delete-process proc))) diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el index 89450cd2276..450075fde42 100644 --- a/lisp/progmodes/gdb-mi.el +++ b/lisp/progmodes/gdb-mi.el @@ -375,9 +375,8 @@ Emacs always switches to the thread which caused the stop." :version "23.2" :link '(info-link "(gdb)GDB/MI Async Records")) -(defcustom gdb-stopped-hooks nil - "This variable holds a list of functions to be called whenever -GDB stops. +(defcustom gdb-stopped-functions nil + "List of functions called whenever GDB stops. Each function takes one argument, a parsed MI response, which contains fields of corresponding MI *stopped async record: @@ -818,6 +817,11 @@ detailed description of this mode. nil 'local) (local-set-key "\C-i" 'completion-at-point) + ;; FIXME: Under some circumstances, `gud-sentinel' apparently does + ;; not get called when the gdb process is killed (Bug#11273). + (add-hook 'post-command-hook 'gdb-inferior-io--maybe-delete-pty + nil t) + (setq gdb-first-prompt t) (setq gud-running nil) @@ -1510,6 +1514,14 @@ DOC is an optional documentation string." (gdb-display-buffer (gdb-get-buffer-create 'gdb-inferior-io) t)) +(defun gdb-inferior-io--maybe-delete-pty () + (let ((proc (get-buffer-process gud-comint-buffer)) + (inf-pty (get-process "gdb-inferior"))) + (and (or (null proc) + (memq (process-status proc) '(exit signal))) + inf-pty + (delete-process inf-pty)))) + (defconst gdb-frame-parameters '((height . 14) (width . 80) (unsplittable . t) @@ -1746,24 +1758,27 @@ If `gdb-thread-number' is nil, just wrap NAME in asterisks." (setq gdb-output-sink 'user) (setq gdb-pending-triggers nil)) -(defun gdb-update () - "Update buffers showing status of debug session." +(defun gdb-update (&optional no-proc) + "Update buffers showing status of debug session. +If NO-PROC is non-nil, do not try to contact the GDB process." (when gdb-first-prompt (gdb-force-mode-line-update (propertize "initializing..." 'face font-lock-variable-name-face)) (gdb-init-1) (setq gdb-first-prompt nil)) - (gdb-get-main-selected-frame) + (unless no-proc + (gdb-get-main-selected-frame)) + ;; We may need to update gdb-threads-list so we can use (gdb-get-buffer-create 'gdb-threads-buffer) ;; gdb-break-list is maintained in breakpoints handler (gdb-get-buffer-create 'gdb-breakpoints-buffer) - (gdb-emit-signal gdb-buf-publisher 'update) + (unless no-proc + (gdb-emit-signal gdb-buf-publisher 'update)) (gdb-get-changed-registers) - (when (and (boundp 'speedbar-frame) (frame-live-p speedbar-frame)) (dolist (var gdb-var-list) (setcar (nthcdr 5 var) nil)) @@ -2045,7 +2060,7 @@ current thread and update GDB buffers." ;; In all-stop this updates gud-running properly as well. (gdb-update) (setq gdb-first-done-or-error nil)) - (run-hook-with-args 'gdb-stopped-hooks result))) + (run-hook-with-args 'gdb-stopped-functions result))) ;; Remove the trimmings from log stream containing debugging messages ;; being produced by GDB's internals, use warning face and send to GUD @@ -2085,23 +2100,28 @@ current thread and update GDB buffers." (setq gdb-output-sink 'emacs)) (gdb-clear-partial-output) - (when gdb-first-done-or-error - (unless (or token-number gud-running) - (setq gdb-filter-output (concat gdb-filter-output gdb-prompt-name))) - (gdb-update) - (setq gdb-first-done-or-error nil)) - (setq gdb-filter-output - (gdb-concat-output gdb-filter-output output-field)) + ;; The process may already be dead (e.g. C-d at the gdb prompt). + (let* ((proc (get-buffer-process gud-comint-buffer)) + (no-proc (or (null proc) + (memq (process-status proc) '(exit signal))))) - (if token-number - (progn - (with-current-buffer - (gdb-get-buffer-create 'gdb-partial-output-buffer) - (funcall - (cdr (assoc (string-to-number token-number) gdb-handler-alist)))) - (setq gdb-handler-alist - (assq-delete-all token-number gdb-handler-alist))))) + (when gdb-first-done-or-error + (unless (or token-number gud-running no-proc) + (setq gdb-filter-output (concat gdb-filter-output gdb-prompt-name))) + (gdb-update no-proc) + (setq gdb-first-done-or-error nil)) + + (setq gdb-filter-output + (gdb-concat-output gdb-filter-output output-field)) + + (when token-number + (with-current-buffer + (gdb-get-buffer-create 'gdb-partial-output-buffer) + (funcall + (cdr (assoc (string-to-number token-number) gdb-handler-alist)))) + (setq gdb-handler-alist + (assq-delete-all token-number gdb-handler-alist))))) (defun gdb-concat-output (so-far new) (cond @@ -4105,9 +4125,13 @@ This arrangement depends on the value of `gdb-many-windows'." (gud-find-file gdb-main-file))) (setq gdb-source-window win))))) +;; Called from `gud-sentinel' in gud.el: (defun gdb-reset () "Exit a debugging session cleanly. Kills the gdb buffers, and resets variables and the source buffers." + ;; The gdb-inferior buffer has a pty hooked up to the main gdb + ;; process. This pty must be deleted explicitly. + (gdb-inferior-io--maybe-delete-pty) (dolist (buffer (buffer-list)) (unless (eq buffer gud-comint-buffer) (with-current-buffer buffer -- 2.39.2