aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGemini Lasswell2018-08-09 14:21:57 -0700
committerGemini Lasswell2018-09-09 07:41:49 -0700
commit3fb8f306475a87a30a7dd68387d8da859cffc90a (patch)
tree8d8f600a2bae48e351a7424648ef100d402ae261
parentdc5c76c37488d6fd546eefb33cea1edf4d13859e (diff)
downloademacs-3fb8f306475a87a30a7dd68387d8da859cffc90a.tar.gz
emacs-3fb8f306475a87a30a7dd68387d8da859cffc90a.zip
Show backtraces of threads from thread list buffer
* src/eval.c (backtrace_thread_p, backtrace_thread_top) (backtrace_thread_next, Fbacktrace_frames_from_thread): New functions. * lisp/thread.el (thread-list-mode-map): Add keybinding and menu item for 'thread-list-pop-to-backtrace'. (thread-list-mode): Make "Thread Name" column wide enough for the result of printing a thread with no name with 'prin1'. (thread-list--get-entries): Use 'thread-list--name'. (thread-list--send-signal): Remove unnecessary calls to 'threadp'. (thread-list-backtrace--thread): New variable. (thread-list-pop-to-backtrace): New command. (thread-list-backtrace--revert-hook-function) (thread-list--make-backtrace-frame) (thread-list-backtrace--insert-header, thread-list--name): New functions.
-rw-r--r--lisp/thread.el61
-rw-r--r--src/eval.c59
2 files changed, 114 insertions, 6 deletions
diff --git a/lisp/thread.el b/lisp/thread.el
index 4cd253e2cf5..c9f50ff5dba 100644
--- a/lisp/thread.el
+++ b/lisp/thread.el
@@ -26,6 +26,7 @@
26;;; Code: 26;;; Code:
27 27
28(require 'cl-lib) 28(require 'cl-lib)
29(require 'backtrace)
29(require 'pcase) 30(require 'pcase)
30(require 'subr-x) 31(require 'subr-x)
31 32
@@ -55,11 +56,13 @@ An EVENT has the format
55(defvar thread-list-mode-map 56(defvar thread-list-mode-map
56 (let ((map (make-sparse-keymap))) 57 (let ((map (make-sparse-keymap)))
57 (set-keymap-parent map tabulated-list-mode-map) 58 (set-keymap-parent map tabulated-list-mode-map)
59 (define-key map "b" #'thread-list-pop-to-backtrace)
58 (define-key map "s" nil) 60 (define-key map "s" nil)
59 (define-key map "sq" #'thread-list-send-quit-signal) 61 (define-key map "sq" #'thread-list-send-quit-signal)
60 (define-key map "se" #'thread-list-send-error-signal) 62 (define-key map "se" #'thread-list-send-error-signal)
61 (easy-menu-define nil map "" 63 (easy-menu-define nil map ""
62 '("Threads" 64 '("Threads"
65 ["Show backtrace" thread-list-pop-to-backtrace t]
63 ["Send Quit Signal" thread-list-send-quit-signal t] 66 ["Send Quit Signal" thread-list-send-quit-signal t]
64 ["Send Error Signal" thread-list-send-error-signal t])) 67 ["Send Error Signal" thread-list-send-error-signal t]))
65 map) 68 map)
@@ -68,7 +71,7 @@ An EVENT has the format
68(define-derived-mode thread-list-mode tabulated-list-mode "Thread-List" 71(define-derived-mode thread-list-mode tabulated-list-mode "Thread-List"
69 "Major mode for monitoring Lisp threads." 72 "Major mode for monitoring Lisp threads."
70 (setq tabulated-list-format 73 (setq tabulated-list-format
71 [("Thread Name" 15 t) 74 [("Thread Name" 20 t)
72 ("Status" 10 t) 75 ("Status" 10 t)
73 ("Blocked On" 30 t)]) 76 ("Blocked On" 30 t)])
74 (setq tabulated-list-sort-key (cons (car (aref tabulated-list-format 0)) nil)) 77 (setq tabulated-list-sort-key (cons (car (aref tabulated-list-format 0)) nil))
@@ -105,9 +108,7 @@ An EVENT has the format
105 (let (entries) 108 (let (entries)
106 (dolist (thread (all-threads)) 109 (dolist (thread (all-threads))
107 (pcase-let ((`(,status ,blocker) (thread-list--get-status thread))) 110 (pcase-let ((`(,status ,blocker) (thread-list--get-status thread)))
108 (push `(,thread [,(or (thread-name thread) 111 (push `(,thread [,(thread-list--name thread)
109 (and (eq thread main-thread) "Main")
110 (prin1-to-string thread))
111 ,status ,blocker]) 112 ,status ,blocker])
112 entries))) 113 entries)))
113 entries)) 114 entries))
@@ -137,12 +138,60 @@ other describing THREAD's blocker, if any."
137 "Send the specified SIGNAL to the thread at point. 138 "Send the specified SIGNAL to the thread at point.
138Ask for user confirmation before signaling the thread." 139Ask for user confirmation before signaling the thread."
139 (let ((thread (tabulated-list-get-id))) 140 (let ((thread (tabulated-list-get-id)))
140 (if (and (threadp thread) (thread-alive-p thread)) 141 (if (thread-alive-p thread)
141 (when (y-or-n-p (format "Send %s signal to %s? " signal thread)) 142 (when (y-or-n-p (format "Send %s signal to %s? " signal thread))
142 (if (and (threadp thread) (thread-alive-p thread)) 143 (if (thread-alive-p thread)
143 (thread-signal thread signal nil) 144 (thread-signal thread signal nil)
144 (message "This thread is no longer alive"))) 145 (message "This thread is no longer alive")))
145 (message "This thread is no longer alive")))) 146 (message "This thread is no longer alive"))))
146 147
148(defvar-local thread-list-backtrace--thread nil
149 "Thread whose backtrace is displayed in the current buffer.")
150
151(defun thread-list-pop-to-backtrace ()
152 "Display the backtrace for the thread at point."
153 (interactive)
154 (let ((thread (tabulated-list-get-id)))
155 (if (thread-alive-p thread)
156 (let ((buffer (get-buffer-create "*Thread Backtrace*")))
157 (pop-to-buffer buffer)
158 (unless (derived-mode-p 'backtrace-mode)
159 (backtrace-mode)
160 (add-hook 'backtrace-revert-hook
161 #'thread-list-backtrace--revert-hook-function)
162 (setq backtrace-insert-header-function
163 #'thread-list-backtrace--insert-header))
164 (setq thread-list-backtrace--thread thread)
165 (thread-list-backtrace--revert-hook-function)
166 (backtrace-print)
167 (goto-char (point-min)))
168 (message "This thread is no longer alive"))))
169
170(defun thread-list-backtrace--revert-hook-function ()
171 (setq backtrace-frames
172 (when (thread-alive-p thread-list-backtrace--thread)
173 (mapcar #'thread-list--make-backtrace-frame
174 (backtrace--frames-from-thread
175 thread-list-backtrace--thread)))))
176
177(cl-defun thread-list--make-backtrace-frame ((evald fun &rest args))
178 (backtrace-make-frame :evald evald :fun fun :args args))
179
180(defun thread-list-backtrace--insert-header ()
181 (let ((name (thread-list--name thread-list-backtrace--thread)))
182 (if (thread-alive-p thread-list-backtrace--thread)
183 (progn
184 (insert (substitute-command-keys "Backtrace for thread `"))
185 (insert name)
186 (insert (substitute-command-keys "':\n")))
187 (insert (substitute-command-keys "Thread `"))
188 (insert name)
189 (insert (substitute-command-keys "' is no longer running\n")))))
190
191(defun thread-list--name (thread)
192 (or (thread-name thread)
193 (and (eq thread main-thread) "Main")
194 (prin1-to-string thread)))
195
147(provide 'thread) 196(provide 'thread)
148;;; thread.el ends here 197;;; thread.el ends here
diff --git a/src/eval.c b/src/eval.c
index 1011fc888b5..60dd6f1e8d2 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -204,6 +204,10 @@ bool
204backtrace_p (union specbinding *pdl) 204backtrace_p (union specbinding *pdl)
205{ return pdl >= specpdl; } 205{ return pdl >= specpdl; }
206 206
207static bool
208backtrace_thread_p (struct thread_state *tstate, union specbinding *pdl)
209{ return pdl >= tstate->m_specpdl; }
210
207union specbinding * 211union specbinding *
208backtrace_top (void) 212backtrace_top (void)
209{ 213{
@@ -213,6 +217,15 @@ backtrace_top (void)
213 return pdl; 217 return pdl;
214} 218}
215 219
220static union specbinding *
221backtrace_thread_top (struct thread_state *tstate)
222{
223 union specbinding *pdl = tstate->m_specpdl_ptr - 1;
224 while (backtrace_thread_p (tstate, pdl) && pdl->kind != SPECPDL_BACKTRACE)
225 pdl--;
226 return pdl;
227}
228
216union specbinding * 229union specbinding *
217backtrace_next (union specbinding *pdl) 230backtrace_next (union specbinding *pdl)
218{ 231{
@@ -222,6 +235,15 @@ backtrace_next (union specbinding *pdl)
222 return pdl; 235 return pdl;
223} 236}
224 237
238static union specbinding *
239backtrace_thread_next (struct thread_state *tstate, union specbinding *pdl)
240{
241 pdl--;
242 while (backtrace_thread_p (tstate, pdl) && pdl->kind != SPECPDL_BACKTRACE)
243 pdl--;
244 return pdl;
245}
246
225void 247void
226init_eval_once (void) 248init_eval_once (void)
227{ 249{
@@ -3730,6 +3752,42 @@ Return the result of FUNCTION, or nil if no matching frame could be found. */)
3730 return backtrace_frame_apply (function, get_backtrace_frame (nframes, base)); 3752 return backtrace_frame_apply (function, get_backtrace_frame (nframes, base));
3731} 3753}
3732 3754
3755DEFUN ("backtrace--frames-from-thread", Fbacktrace_frames_from_thread,
3756 Sbacktrace_frames_from_thread, 1, 1, NULL,
3757 doc: /* Return the list of backtrace frames from current execution point in THREAD.
3758If a frame has not evaluated the arguments yet (or is a special form),
3759the value of the list element is (nil FUNCTION ARG-FORMS...).
3760If a frame has evaluated its arguments and called its function already,
3761the value of the list element is (t FUNCTION ARG-VALUES...).
3762A &rest arg is represented as the tail of the list ARG-VALUES.
3763FUNCTION is whatever was supplied as car of evaluated list,
3764or a lambda expression for macro calls. */)
3765 (Lisp_Object thread)
3766{
3767 struct thread_state *tstate;
3768 CHECK_THREAD (thread);
3769 tstate = XTHREAD (thread);
3770
3771 union specbinding *pdl = backtrace_thread_top (tstate);
3772 Lisp_Object list = Qnil;
3773
3774 while (backtrace_thread_p (tstate, pdl))
3775 {
3776 Lisp_Object frame;
3777 if (backtrace_nargs (pdl) == UNEVALLED)
3778 frame = Fcons (Qnil,
3779 Fcons (backtrace_function (pdl), *backtrace_args (pdl)));
3780 else
3781 {
3782 Lisp_Object tem = Flist (backtrace_nargs (pdl), backtrace_args (pdl));
3783 frame = Fcons (Qt, Fcons (backtrace_function (pdl), tem));
3784 }
3785 list = Fcons (frame, list);
3786 pdl = backtrace_thread_next (tstate, pdl);
3787 }
3788 return Fnreverse (list);
3789}
3790
3733/* For backtrace-eval, we want to temporarily unwind the last few elements of 3791/* For backtrace-eval, we want to temporarily unwind the last few elements of
3734 the specpdl stack, and then rewind them. We store the pre-unwind values 3792 the specpdl stack, and then rewind them. We store the pre-unwind values
3735 directly in the pre-existing specpdl elements (i.e. we swap the current 3793 directly in the pre-existing specpdl elements (i.e. we swap the current
@@ -4205,6 +4263,7 @@ alist of active lexical bindings. */);
4205 DEFSYM (QCdebug_on_exit, ":debug-on-exit"); 4263 DEFSYM (QCdebug_on_exit, ":debug-on-exit");
4206 defsubr (&Smapbacktrace); 4264 defsubr (&Smapbacktrace);
4207 defsubr (&Sbacktrace_frame_internal); 4265 defsubr (&Sbacktrace_frame_internal);
4266 defsubr (&Sbacktrace_frames_from_thread);
4208 defsubr (&Sbacktrace_eval); 4267 defsubr (&Sbacktrace_eval);
4209 defsubr (&Sbacktrace__locals); 4268 defsubr (&Sbacktrace__locals);
4210 defsubr (&Sspecial_variable_p); 4269 defsubr (&Sspecial_variable_p);