diff options
| -rw-r--r-- | lisp/emacs-lisp/debug-early.el | 83 | ||||
| -rw-r--r-- | src/eval.c | 67 | ||||
| -rw-r--r-- | src/keyboard.c | 4 | ||||
| -rw-r--r-- | src/lisp.h | 1 | ||||
| -rw-r--r-- | src/xdisp.c | 20 |
5 files changed, 84 insertions, 91 deletions
diff --git a/lisp/emacs-lisp/debug-early.el b/lisp/emacs-lisp/debug-early.el index 2e56d5ab321..bb41d55f02d 100644 --- a/lisp/emacs-lisp/debug-early.el +++ b/lisp/emacs-lisp/debug-early.el | |||
| @@ -27,14 +27,17 @@ | |||
| 27 | ;; This file dumps a backtrace on stderr when an error is thrown. It | 27 | ;; This file dumps a backtrace on stderr when an error is thrown. It |
| 28 | ;; has no dependencies on any Lisp libraries and is thus used for | 28 | ;; has no dependencies on any Lisp libraries and is thus used for |
| 29 | ;; generating backtraces for bugs in the early parts of bootstrapping. | 29 | ;; generating backtraces for bugs in the early parts of bootstrapping. |
| 30 | ;; It is also always used in batch model. It was introduced in Emacs | 30 | ;; It is also always used in batch mode. It was introduced in Emacs |
| 31 | ;; 29, before which there was no backtrace available during early | 31 | ;; 29, before which there was no backtrace available during early |
| 32 | ;; bootstrap. | 32 | ;; bootstrap. |
| 33 | 33 | ||
| 34 | ;;; Code: | 34 | ;;; Code: |
| 35 | 35 | ||
| 36 | ;; For bootstrap reasons, we cannot use any macros here since they're | ||
| 37 | ;; not defined yet. | ||
| 38 | |||
| 36 | (defalias 'debug-early-backtrace | 39 | (defalias 'debug-early-backtrace |
| 37 | #'(lambda () | 40 | #'(lambda (&optional base) |
| 38 | "Print a trace of Lisp function calls currently active. | 41 | "Print a trace of Lisp function calls currently active. |
| 39 | The output stream used is the value of `standard-output'. | 42 | The output stream used is the value of `standard-output'. |
| 40 | 43 | ||
| @@ -51,26 +54,39 @@ of the build process." | |||
| 51 | (require 'cl-print) | 54 | (require 'cl-print) |
| 52 | (error nil))) | 55 | (error nil))) |
| 53 | #'cl-prin1 | 56 | #'cl-prin1 |
| 54 | #'prin1))) | 57 | #'prin1)) |
| 58 | (first t)) | ||
| 55 | (mapbacktrace | 59 | (mapbacktrace |
| 56 | #'(lambda (evald func args _flags) | 60 | #'(lambda (evald func args _flags) |
| 57 | (let ((args args)) | 61 | (if first |
| 58 | (if evald | 62 | ;; The first is the debug-early entry point itself. |
| 63 | (setq first nil) | ||
| 64 | (let ((args args)) | ||
| 65 | (if evald | ||
| 66 | (progn | ||
| 67 | (princ " ") | ||
| 68 | (funcall prin1 func) | ||
| 69 | (princ "(")) | ||
| 59 | (progn | 70 | (progn |
| 60 | (princ " ") | 71 | (princ " (") |
| 61 | (funcall prin1 func) | 72 | (setq args (cons func args)))) |
| 62 | (princ "(")) | 73 | (if args |
| 63 | (progn | 74 | (while (progn |
| 64 | (princ " (") | 75 | (funcall prin1 (car args)) |
| 65 | (setq args (cons func args)))) | 76 | (setq args (cdr args))) |
| 66 | (if args | 77 | (princ " "))) |
| 67 | (while (progn | 78 | (princ ")\n")))) |
| 68 | (funcall prin1 (car args)) | 79 | base)))) |
| 69 | (setq args (cdr args))) | 80 | |
| 70 | (princ " "))) | 81 | (defalias 'debug--early |
| 71 | (princ ")\n"))))))) | 82 | #'(lambda (error base) |
| 72 | 83 | (princ "\nError: ") | |
| 73 | (defalias 'debug-early | 84 | (prin1 (car error)) ; The error symbol. |
| 85 | (princ " ") | ||
| 86 | (prin1 (cdr error)) ; The error data. | ||
| 87 | (debug-early-backtrace base))) | ||
| 88 | |||
| 89 | (defalias 'debug-early ;Called from C. | ||
| 74 | #'(lambda (&rest args) | 90 | #'(lambda (&rest args) |
| 75 | "Print an error message with a backtrace of active Lisp function calls. | 91 | "Print an error message with a backtrace of active Lisp function calls. |
| 76 | The output stream used is the value of `standard-output'. | 92 | The output stream used is the value of `standard-output'. |
| @@ -88,14 +104,31 @@ support the latter, except in batch mode which always uses | |||
| 88 | 104 | ||
| 89 | \(In versions of Emacs prior to Emacs 29, no backtrace was | 105 | \(In versions of Emacs prior to Emacs 29, no backtrace was |
| 90 | available before `debug' was usable.)" | 106 | available before `debug' was usable.)" |
| 91 | (princ "\nError: ") | 107 | (debug--early (car (cdr args)) #'debug-early))) ; The error object. |
| 92 | (prin1 (car (car (cdr args)))) ; The error symbol. | ||
| 93 | (princ " ") | ||
| 94 | (prin1 (cdr (car (cdr args)))) ; The error data. | ||
| 95 | (debug-early-backtrace))) | ||
| 96 | 108 | ||
| 97 | (defalias 'debug-early--handler ;Called from C. | 109 | (defalias 'debug-early--handler ;Called from C. |
| 98 | #'(lambda (err) | 110 | #'(lambda (err) |
| 99 | (if backtrace-on-error-noninteractive (debug-early 'error err)))) | 111 | (if backtrace-on-error-noninteractive |
| 112 | (debug--early err #'debug-early--handler)))) | ||
| 113 | |||
| 114 | (defalias 'debug-early--muted ;Called from C. | ||
| 115 | #'(lambda (err) | ||
| 116 | (save-current-buffer | ||
| 117 | (set-buffer (get-buffer-create "*Redisplay-trace*")) | ||
| 118 | (goto-char (point-max)) | ||
| 119 | (if (bobp) nil | ||
| 120 | (let ((separator "\n\n\n\n")) | ||
| 121 | (save-excursion | ||
| 122 | ;; The C code tested `backtrace_yet', instead we | ||
| 123 | ;; keep a max of 10 backtraces. | ||
| 124 | (if (search-backward separator nil t 10) | ||
| 125 | (delete-region (point-min) (match-end 0)))) | ||
| 126 | (insert separator))) | ||
| 127 | (insert "-- Caught at " (current-time-string) "\n") | ||
| 128 | (let ((standard-output (current-buffer))) | ||
| 129 | (debug--early err #'debug-early--muted)) | ||
| 130 | (setq delayed-warnings-list | ||
| 131 | (cons '(error "Error in a redisplay Lisp hook. See buffer *Redisplay-trace*") | ||
| 132 | delayed-warnings-list))))) | ||
| 100 | 133 | ||
| 101 | ;;; debug-early.el ends here. | 134 | ;;; debug-early.el ends here. |
diff --git a/src/eval.c b/src/eval.c index 0cff38ce7a8..3e352911479 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -57,12 +57,6 @@ Lisp_Object Vrun_hooks; | |||
| 57 | /* FIXME: We should probably get rid of this! */ | 57 | /* FIXME: We should probably get rid of this! */ |
| 58 | Lisp_Object Vsignaling_function; | 58 | Lisp_Object Vsignaling_function; |
| 59 | 59 | ||
| 60 | /* The handler structure which will catch errors in Lisp hooks called | ||
| 61 | from redisplay. We do not use it for this; we compare it with the | ||
| 62 | handler which is about to be used in signal_or_quit, and if it | ||
| 63 | matches, cause a backtrace to be generated. */ | ||
| 64 | static struct handler *redisplay_deep_handler; | ||
| 65 | |||
| 66 | /* These would ordinarily be static, but they need to be visible to GDB. */ | 60 | /* These would ordinarily be static, but they need to be visible to GDB. */ |
| 67 | bool backtrace_p (union specbinding *) EXTERNALLY_VISIBLE; | 61 | bool backtrace_p (union specbinding *) EXTERNALLY_VISIBLE; |
| 68 | Lisp_Object *backtrace_args (union specbinding *) EXTERNALLY_VISIBLE; | 62 | Lisp_Object *backtrace_args (union specbinding *) EXTERNALLY_VISIBLE; |
| @@ -244,7 +238,6 @@ init_eval (void) | |||
| 244 | lisp_eval_depth = 0; | 238 | lisp_eval_depth = 0; |
| 245 | /* This is less than the initial value of num_nonmacro_input_events. */ | 239 | /* This is less than the initial value of num_nonmacro_input_events. */ |
| 246 | when_entered_debugger = -1; | 240 | when_entered_debugger = -1; |
| 247 | redisplay_deep_handler = NULL; | ||
| 248 | } | 241 | } |
| 249 | 242 | ||
| 250 | static void | 243 | static void |
| @@ -1611,16 +1604,12 @@ internal_condition_case_n (Lisp_Object (*bfun) (ptrdiff_t, Lisp_Object *), | |||
| 1611 | ptrdiff_t nargs, | 1604 | ptrdiff_t nargs, |
| 1612 | Lisp_Object *args)) | 1605 | Lisp_Object *args)) |
| 1613 | { | 1606 | { |
| 1614 | struct handler *old_deep = redisplay_deep_handler; | ||
| 1615 | struct handler *c = push_handler (handlers, CONDITION_CASE); | 1607 | struct handler *c = push_handler (handlers, CONDITION_CASE); |
| 1616 | if (redisplaying_p) | ||
| 1617 | redisplay_deep_handler = c; | ||
| 1618 | if (sys_setjmp (c->jmp)) | 1608 | if (sys_setjmp (c->jmp)) |
| 1619 | { | 1609 | { |
| 1620 | Lisp_Object val = handlerlist->val; | 1610 | Lisp_Object val = handlerlist->val; |
| 1621 | clobbered_eassert (handlerlist == c); | 1611 | clobbered_eassert (handlerlist == c); |
| 1622 | handlerlist = handlerlist->next; | 1612 | handlerlist = handlerlist->next; |
| 1623 | redisplay_deep_handler = old_deep; | ||
| 1624 | return hfun (val, nargs, args); | 1613 | return hfun (val, nargs, args); |
| 1625 | } | 1614 | } |
| 1626 | else | 1615 | else |
| @@ -1628,7 +1617,6 @@ internal_condition_case_n (Lisp_Object (*bfun) (ptrdiff_t, Lisp_Object *), | |||
| 1628 | Lisp_Object val = bfun (nargs, args); | 1617 | Lisp_Object val = bfun (nargs, args); |
| 1629 | eassert (handlerlist == c); | 1618 | eassert (handlerlist == c); |
| 1630 | handlerlist = c->next; | 1619 | handlerlist = c->next; |
| 1631 | redisplay_deep_handler = old_deep; | ||
| 1632 | return val; | 1620 | return val; |
| 1633 | } | 1621 | } |
| 1634 | } | 1622 | } |
| @@ -1766,11 +1754,6 @@ quit (void) | |||
| 1766 | return signal_or_quit (Qquit, Qnil, true); | 1754 | return signal_or_quit (Qquit, Qnil, true); |
| 1767 | } | 1755 | } |
| 1768 | 1756 | ||
| 1769 | /* Has an error in redisplay giving rise to a backtrace occurred as | ||
| 1770 | yet in the current command? This gets reset in the command | ||
| 1771 | loop. */ | ||
| 1772 | bool backtrace_yet = false; | ||
| 1773 | |||
| 1774 | /* Signal an error, or quit. ERROR_SYMBOL and DATA are as with Fsignal. | 1757 | /* Signal an error, or quit. ERROR_SYMBOL and DATA are as with Fsignal. |
| 1775 | If CONTINUABLE, the caller allows this function to return | 1758 | If CONTINUABLE, the caller allows this function to return |
| 1776 | (presumably after calling the debugger); | 1759 | (presumably after calling the debugger); |
| @@ -1897,51 +1880,13 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool continuable) | |||
| 1897 | return Qnil; | 1880 | return Qnil; |
| 1898 | } | 1881 | } |
| 1899 | 1882 | ||
| 1900 | /* If an error is signaled during a Lisp hook in redisplay, write a | ||
| 1901 | backtrace into the buffer *Redisplay-trace*. */ | ||
| 1902 | /* FIXME: Turn this into a `handler-bind` installed during redisplay? */ | ||
| 1903 | if (!debugger_called && !oom | ||
| 1904 | && backtrace_on_redisplay_error | ||
| 1905 | && (NILP (clause) || h == redisplay_deep_handler) | ||
| 1906 | && NILP (Vinhibit_debugger) | ||
| 1907 | && !NILP (Ffboundp (Qdebug_early))) | ||
| 1908 | { | ||
| 1909 | specpdl_ref count = SPECPDL_INDEX (); | ||
| 1910 | max_ensure_room (100); | ||
| 1911 | AUTO_STRING (redisplay_trace, "*Redisplay-trace*"); | ||
| 1912 | Lisp_Object redisplay_trace_buffer; | ||
| 1913 | AUTO_STRING (gap, "\n\n\n\n"); /* Separates things in *Redisplay-trace* */ | ||
| 1914 | Lisp_Object delayed_warning; | ||
| 1915 | redisplay_trace_buffer = Fget_buffer_create (redisplay_trace, Qnil); | ||
| 1916 | current_buffer = XBUFFER (redisplay_trace_buffer); | ||
| 1917 | if (!backtrace_yet) /* Are we on the first backtrace of the command? */ | ||
| 1918 | Ferase_buffer (); | ||
| 1919 | else | ||
| 1920 | Finsert (1, &gap); | ||
| 1921 | backtrace_yet = true; | ||
| 1922 | specbind (Qstandard_output, redisplay_trace_buffer); | ||
| 1923 | specbind (Qdebugger, Qdebug_early); | ||
| 1924 | call_debugger (list2 (Qerror, error)); | ||
| 1925 | unbind_to (count, Qnil); | ||
| 1926 | delayed_warning = make_string | ||
| 1927 | ("Error in a redisplay Lisp hook. See buffer *Redisplay-trace*", 61); | ||
| 1928 | |||
| 1929 | Vdelayed_warnings_list = Fcons (list2 (Qerror, delayed_warning), | ||
| 1930 | Vdelayed_warnings_list); | ||
| 1931 | } | ||
| 1932 | |||
| 1933 | if (!NILP (clause)) | 1883 | if (!NILP (clause)) |
| 1934 | { | 1884 | unwind_to_catch (h, NONLOCAL_EXIT_SIGNAL, error); |
| 1935 | unwind_to_catch (h, NONLOCAL_EXIT_SIGNAL, error); | 1885 | else if (handlerlist != handlerlist_sentinel) |
| 1936 | } | 1886 | /* FIXME: This will come right back here if there's no `top-level' |
| 1937 | else | 1887 | catcher. A better solution would be to abort here, and instead |
| 1938 | { | 1888 | add a catch-all condition handler so we never come here. */ |
| 1939 | if (handlerlist != handlerlist_sentinel) | 1889 | Fthrow (Qtop_level, Qt); |
| 1940 | /* FIXME: This will come right back here if there's no `top-level' | ||
| 1941 | catcher. A better solution would be to abort here, and instead | ||
| 1942 | add a catch-all condition handler so we never come here. */ | ||
| 1943 | Fthrow (Qtop_level, Qt); | ||
| 1944 | } | ||
| 1945 | 1890 | ||
| 1946 | string = Ferror_message_string (error); | 1891 | string = Ferror_message_string (error); |
| 1947 | fatal ("%s", SDATA (string)); | 1892 | fatal ("%s", SDATA (string)); |
diff --git a/src/keyboard.c b/src/keyboard.c index f10e9fd79b7..447f8d5d4ff 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -1167,9 +1167,10 @@ top_level_2 (void) | |||
| 1167 | encountering an error, to help with debugging. */ | 1167 | encountering an error, to help with debugging. */ |
| 1168 | bool setup_handler = noninteractive; | 1168 | bool setup_handler = noninteractive; |
| 1169 | if (setup_handler) | 1169 | if (setup_handler) |
| 1170 | /* FIXME: Should we (re)use `list_of_error` from `xdisp.c`? */ | ||
| 1170 | push_handler_bind (list1 (Qerror), Qdebug_early__handler, 0); | 1171 | push_handler_bind (list1 (Qerror), Qdebug_early__handler, 0); |
| 1171 | 1172 | ||
| 1172 | Lisp_Object res = Feval (Vtop_level, Qnil); | 1173 | Lisp_Object res = Feval (Vtop_level, Qt); |
| 1173 | 1174 | ||
| 1174 | if (setup_handler) | 1175 | if (setup_handler) |
| 1175 | pop_handler (); | 1176 | pop_handler (); |
| @@ -1365,7 +1366,6 @@ command_loop_1 (void) | |||
| 1365 | display_malloc_warning (); | 1366 | display_malloc_warning (); |
| 1366 | 1367 | ||
| 1367 | Vdeactivate_mark = Qnil; | 1368 | Vdeactivate_mark = Qnil; |
| 1368 | backtrace_yet = false; | ||
| 1369 | 1369 | ||
| 1370 | /* Don't ignore mouse movements for more than a single command | 1370 | /* Don't ignore mouse movements for more than a single command |
| 1371 | loop. (This flag is set in xdisp.c whenever the tool bar is | 1371 | loop. (This flag is set in xdisp.c whenever the tool bar is |
diff --git a/src/lisp.h b/src/lisp.h index db6c3e32be7..c051c35e169 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4529,7 +4529,6 @@ extern Lisp_Object Vrun_hooks; | |||
| 4529 | extern Lisp_Object Vsignaling_function; | 4529 | extern Lisp_Object Vsignaling_function; |
| 4530 | extern Lisp_Object inhibit_lisp_code; | 4530 | extern Lisp_Object inhibit_lisp_code; |
| 4531 | extern bool signal_quit_p (Lisp_Object); | 4531 | extern bool signal_quit_p (Lisp_Object); |
| 4532 | extern bool backtrace_yet; | ||
| 4533 | 4532 | ||
| 4534 | /* To run a normal hook, use the appropriate function from the list below. | 4533 | /* To run a normal hook, use the appropriate function from the list below. |
| 4535 | The calling convention: | 4534 | The calling convention: |
diff --git a/src/xdisp.c b/src/xdisp.c index 2a979c5cb9e..aa1d4433914 100644 --- a/src/xdisp.c +++ b/src/xdisp.c | |||
| @@ -3072,10 +3072,24 @@ dsafe__call (bool inhibit_quit, Lisp_Object (f) (ptrdiff_t, Lisp_Object *), | |||
| 3072 | return val; | 3072 | return val; |
| 3073 | } | 3073 | } |
| 3074 | 3074 | ||
| 3075 | static Lisp_Object | ||
| 3076 | funcall_with_backtraces (ptrdiff_t nargs, Lisp_Object *args) | ||
| 3077 | { | ||
| 3078 | /* If an error is signaled during a Lisp hook in redisplay, write a | ||
| 3079 | backtrace into the buffer *Redisplay-trace*. */ | ||
| 3080 | push_handler_bind (list_of_error, Qdebug_early__muted, 0); | ||
| 3081 | Lisp_Object res = Ffuncall (nargs, args); | ||
| 3082 | pop_handler (); | ||
| 3083 | return res; | ||
| 3084 | } | ||
| 3085 | |||
| 3075 | #define SAFE_CALLMANY(inhibit_quit, f, array) \ | 3086 | #define SAFE_CALLMANY(inhibit_quit, f, array) \ |
| 3076 | dsafe__call ((inhibit_quit), f, ARRAYELTS (array), array) | 3087 | dsafe__call ((inhibit_quit), f, ARRAYELTS (array), array) |
| 3077 | #define dsafe_calln(inhibit_quit, ...) \ | 3088 | #define dsafe_calln(inhibit_quit, ...) \ |
| 3078 | SAFE_CALLMANY ((inhibit_quit), Ffuncall, ((Lisp_Object []) {__VA_ARGS__})) | 3089 | SAFE_CALLMANY ((inhibit_quit), \ |
| 3090 | backtrace_on_redisplay_error \ | ||
| 3091 | ? funcall_with_backtraces : Ffuncall, \ | ||
| 3092 | ((Lisp_Object []) {__VA_ARGS__})) | ||
| 3079 | 3093 | ||
| 3080 | static Lisp_Object | 3094 | static Lisp_Object |
| 3081 | dsafe_call1 (Lisp_Object f, Lisp_Object arg) | 3095 | dsafe_call1 (Lisp_Object f, Lisp_Object arg) |
| @@ -37748,6 +37762,8 @@ cursor shapes. */); | |||
| 37748 | DEFSYM (Qthin_space, "thin-space"); | 37762 | DEFSYM (Qthin_space, "thin-space"); |
| 37749 | DEFSYM (Qzero_width, "zero-width"); | 37763 | DEFSYM (Qzero_width, "zero-width"); |
| 37750 | 37764 | ||
| 37765 | DEFSYM (Qdebug_early__muted, "debug-early--muted"); | ||
| 37766 | |||
| 37751 | DEFVAR_LISP ("pre-redisplay-function", Vpre_redisplay_function, | 37767 | DEFVAR_LISP ("pre-redisplay-function", Vpre_redisplay_function, |
| 37752 | doc: /* Function run just before redisplay. | 37768 | doc: /* Function run just before redisplay. |
| 37753 | It is called with one argument, which is the set of windows that are to | 37769 | It is called with one argument, which is the set of windows that are to |