aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2023-12-25 21:41:08 -0500
committerStefan Monnier2023-12-28 01:17:21 -0500
commit26b7078705ae5b9226c99e370740ab9a4063f20f (patch)
tree45f130466b1c6cddf5ae705a49e410979ac183a7
parentb925152bffce30abbd48361af6858cd45b785d84 (diff)
downloademacs-scratch/handler-bind.tar.gz
emacs-scratch/handler-bind.zip
(backtrace-on-redisplay-error): Use `handler-bind`scratch/handler-bind
Reimplement `backtrace-on-redisplay-error` using `push_handler_bind`. This moves the code from `signal_or_quit` to `xdisp.c` and `debug-early.el`. * lisp/emacs-lisp/debug-early.el (debug-early-backtrace): Add `base` arg to strip "internal" frames. (debug--early): New function, extracted from `debug-early`. (debug-early, debug-early--handler): Use it. (debug-early--muted): New function, extracted (translated) from `signal_or_quit`; trim the buffer to a max of 10 backtraces. * src/xdisp.c (funcall_with_backtraces): New function. (dsafe_calln): Use it. (syms_of_xdisp): Defsym `Qdebug_early__muted`. * src/eval.c (redisplay_deep_handler): Delete var. (init_eval, internal_condition_case_n): Don't set it any more. (backtrace_yet): Delete var. (signal_or_quit): Remove special case for `backtrace_on_redisplay_error`. * src/keyboard.c (command_loop_1): Don't set `backtrace_yet` any more. * src/lisp.h (backtrace_yet): Don't declare.
-rw-r--r--lisp/emacs-lisp/debug-early.el83
-rw-r--r--src/eval.c67
-rw-r--r--src/keyboard.c4
-rw-r--r--src/lisp.h1
-rw-r--r--src/xdisp.c20
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.
39The output stream used is the value of `standard-output'. 42The 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.
76The output stream used is the value of `standard-output'. 92The 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
90available before `debug' was usable.)" 106available 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! */
58Lisp_Object Vsignaling_function; 58Lisp_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. */
64static 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. */
67bool backtrace_p (union specbinding *) EXTERNALLY_VISIBLE; 61bool backtrace_p (union specbinding *) EXTERNALLY_VISIBLE;
68Lisp_Object *backtrace_args (union specbinding *) EXTERNALLY_VISIBLE; 62Lisp_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
250static void 243static 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. */
1772bool 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;
4529extern Lisp_Object Vsignaling_function; 4529extern Lisp_Object Vsignaling_function;
4530extern Lisp_Object inhibit_lisp_code; 4530extern Lisp_Object inhibit_lisp_code;
4531extern bool signal_quit_p (Lisp_Object); 4531extern bool signal_quit_p (Lisp_Object);
4532extern 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
3075static Lisp_Object
3076funcall_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
3080static Lisp_Object 3094static Lisp_Object
3081dsafe_call1 (Lisp_Object f, Lisp_Object arg) 3095dsafe_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.
37753It is called with one argument, which is the set of windows that are to 37769It is called with one argument, which is the set of windows that are to