aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlan Mackenzie2022-08-11 19:31:09 +0000
committerAlan Mackenzie2022-08-11 19:31:09 +0000
commit48215c41d16fadb69e85121b3baca0dfca82cc44 (patch)
tree1c410ce8b1b63d20cc02810078acb337af29a2b1 /src
parente7f1d4f6e106576f3d8de4074290dc4e8c7c544f (diff)
downloademacs-48215c41d16fadb69e85121b3baca0dfca82cc44.tar.gz
emacs-48215c41d16fadb69e85121b3baca0dfca82cc44.zip
New debugging facility: backtraces from errors in Lisp called from redisplay
Setting backtrace-on-redisplay-error to non-nil enables the generation of a Lisp backtrace in buffer *Redisplay-trace* following an error in Lisp called from redisplay. * doc/lispref/debugging.texi (Debugging Redisplay): New subsection. (Error Debugging): Reference to the new subsection. * etc/NEWS: New entry for the new facility. * src/eval.c (redisplay_deep_handler): New variable. (init_eval): Initialize redisplay_deep_handler. (call_debugger): Don't throw to top-level after calling debug-early (internal_condition_case_n): "Bind" redisplay_deep_handler to the current handler. (backtrace_yet): New boolean variable. (signal_or_quit): New code section to handle Lisp errors occurring in redisplay. (syms_of_eval): New DEFVAR_BOOL backtrace-on-redisplay-error. * src/keyboard.c (command_loop_1): Set backtrace_yet to false each time around the loop. (safe_run_hooks_error): Allow args to be up to four Lisp_Objects long. (safe_run_hooks_2): New function. * src/lisp.h (top level): declare as externs backtrace_yet and safe_run_hooks_2. * src/xdisp.c (run_window_scroll_functions): Replace a call to run_hook_with_args_2 with one to safe_run_hooks_2.
Diffstat (limited to 'src')
-rw-r--r--src/eval.c59
-rw-r--r--src/keyboard.c14
-rw-r--r--src/lisp.h2
-rw-r--r--src/xdisp.c4
4 files changed, 75 insertions, 4 deletions
diff --git a/src/eval.c b/src/eval.c
index d82d05797b2..56b42966623 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -57,6 +57,12 @@ 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
60/* These would ordinarily be static, but they need to be visible to GDB. */ 66/* These would ordinarily be static, but they need to be visible to GDB. */
61bool backtrace_p (union specbinding *) EXTERNALLY_VISIBLE; 67bool backtrace_p (union specbinding *) EXTERNALLY_VISIBLE;
62Lisp_Object *backtrace_args (union specbinding *) EXTERNALLY_VISIBLE; 68Lisp_Object *backtrace_args (union specbinding *) EXTERNALLY_VISIBLE;
@@ -246,6 +252,7 @@ init_eval (void)
246 lisp_eval_depth = 0; 252 lisp_eval_depth = 0;
247 /* This is less than the initial value of num_nonmacro_input_events. */ 253 /* This is less than the initial value of num_nonmacro_input_events. */
248 when_entered_debugger = -1; 254 when_entered_debugger = -1;
255 redisplay_deep_handler = NULL;
249} 256}
250 257
251/* Ensure that *M is at least A + B if possible, or is its maximum 258/* Ensure that *M is at least A + B if possible, or is its maximum
@@ -333,7 +340,8 @@ call_debugger (Lisp_Object arg)
333 /* Interrupting redisplay and resuming it later is not safe under 340 /* Interrupting redisplay and resuming it later is not safe under
334 all circumstances. So, when the debugger returns, abort the 341 all circumstances. So, when the debugger returns, abort the
335 interrupted redisplay by going back to the top-level. */ 342 interrupted redisplay by going back to the top-level. */
336 if (debug_while_redisplaying) 343 if (debug_while_redisplaying
344 && !EQ (Vdebugger, Qdebug_early))
337 Ftop_level (); 345 Ftop_level ();
338 346
339 return unbind_to (count, val); 347 return unbind_to (count, val);
@@ -1556,12 +1564,16 @@ internal_condition_case_n (Lisp_Object (*bfun) (ptrdiff_t, Lisp_Object *),
1556 ptrdiff_t nargs, 1564 ptrdiff_t nargs,
1557 Lisp_Object *args)) 1565 Lisp_Object *args))
1558{ 1566{
1567 struct handler *old_deep = redisplay_deep_handler;
1559 struct handler *c = push_handler (handlers, CONDITION_CASE); 1568 struct handler *c = push_handler (handlers, CONDITION_CASE);
1569 if (redisplaying_p)
1570 redisplay_deep_handler = c;
1560 if (sys_setjmp (c->jmp)) 1571 if (sys_setjmp (c->jmp))
1561 { 1572 {
1562 Lisp_Object val = handlerlist->val; 1573 Lisp_Object val = handlerlist->val;
1563 clobbered_eassert (handlerlist == c); 1574 clobbered_eassert (handlerlist == c);
1564 handlerlist = handlerlist->next; 1575 handlerlist = handlerlist->next;
1576 redisplay_deep_handler = old_deep;
1565 return hfun (val, nargs, args); 1577 return hfun (val, nargs, args);
1566 } 1578 }
1567 else 1579 else
@@ -1569,6 +1581,7 @@ internal_condition_case_n (Lisp_Object (*bfun) (ptrdiff_t, Lisp_Object *),
1569 Lisp_Object val = bfun (nargs, args); 1581 Lisp_Object val = bfun (nargs, args);
1570 eassert (handlerlist == c); 1582 eassert (handlerlist == c);
1571 handlerlist = c->next; 1583 handlerlist = c->next;
1584 redisplay_deep_handler = old_deep;
1572 return val; 1585 return val;
1573 } 1586 }
1574} 1587}
@@ -1701,6 +1714,11 @@ quit (void)
1701 return signal_or_quit (Qquit, Qnil, true); 1714 return signal_or_quit (Qquit, Qnil, true);
1702} 1715}
1703 1716
1717/* Has an error in redisplay giving rise to a backtrace occurred as
1718 yet in the current command? This gets reset in the command
1719 loop. */
1720bool backtrace_yet = false;
1721
1704/* Signal an error, or quit. ERROR_SYMBOL and DATA are as with Fsignal. 1722/* Signal an error, or quit. ERROR_SYMBOL and DATA are as with Fsignal.
1705 If KEYBOARD_QUIT, this is a quit; ERROR_SYMBOL should be 1723 If KEYBOARD_QUIT, this is a quit; ERROR_SYMBOL should be
1706 Qquit and DATA should be Qnil, and this function may return. 1724 Qquit and DATA should be Qnil, and this function may return.
@@ -1816,6 +1834,40 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit)
1816 unbind_to (count, Qnil); 1834 unbind_to (count, Qnil);
1817 } 1835 }
1818 1836
1837 /* If an error is signalled during a Lisp hook in redisplay, write a
1838 backtrace into the buffer *Redisplay-trace*. */
1839 if (!debugger_called && !NILP (error_symbol)
1840 && backtrace_on_redisplay_error
1841 && (NILP (clause) || h == redisplay_deep_handler)
1842 && NILP (Vinhibit_debugger)
1843 && !NILP (Ffboundp (Qdebug_early)))
1844 {
1845 max_ensure_room (&max_lisp_eval_depth, lisp_eval_depth, 100);
1846 specpdl_ref count = SPECPDL_INDEX ();
1847 ptrdiff_t counti = specpdl_ref_to_count (count);
1848 AUTO_STRING (redisplay_trace, "*Redisplay_trace*");
1849 Lisp_Object redisplay_trace_buffer;
1850 AUTO_STRING (gap, "\n\n\n\n"); /* Separates things in *Redisplay-trace* */
1851 Lisp_Object delayed_warning;
1852 max_ensure_room (&max_specpdl_size, counti, 200);
1853 redisplay_trace_buffer = Fget_buffer_create (redisplay_trace, Qnil);
1854 current_buffer = XBUFFER (redisplay_trace_buffer);
1855 if (!backtrace_yet) /* Are we on the first backtrace of the command? */
1856 Ferase_buffer ();
1857 else
1858 Finsert (1, &gap);
1859 backtrace_yet = true;
1860 specbind (Qstandard_output, redisplay_trace_buffer);
1861 specbind (Qdebugger, Qdebug_early);
1862 call_debugger (list2 (Qerror, Fcons (error_symbol, data)));
1863 unbind_to (count, Qnil);
1864 delayed_warning = make_string
1865 ("Error in a redisplay Lisp hook. See buffer *Redisplay_trace*", 61);
1866
1867 Vdelayed_warnings_list = Fcons (list2 (Qerror, delayed_warning),
1868 Vdelayed_warnings_list);
1869 }
1870
1819 if (!NILP (clause)) 1871 if (!NILP (clause))
1820 { 1872 {
1821 Lisp_Object unwind_data 1873 Lisp_Object unwind_data
@@ -4278,6 +4330,11 @@ Does not apply if quit is handled by a `condition-case'. */);
4278 DEFVAR_BOOL ("debug-on-next-call", debug_on_next_call, 4330 DEFVAR_BOOL ("debug-on-next-call", debug_on_next_call,
4279 doc: /* Non-nil means enter debugger before next `eval', `apply' or `funcall'. */); 4331 doc: /* Non-nil means enter debugger before next `eval', `apply' or `funcall'. */);
4280 4332
4333 DEFVAR_BOOL ("backtrace-on-redisplay-error", backtrace_on_redisplay_error,
4334 doc: /* Non-nil means create a backtrace if a lisp error occurs in redisplay.
4335The backtrace is written to buffer *Redisplay-trace*. */);
4336 backtrace_on_redisplay_error = false;
4337
4281 DEFVAR_BOOL ("debugger-may-continue", debugger_may_continue, 4338 DEFVAR_BOOL ("debugger-may-continue", debugger_may_continue,
4282 doc: /* Non-nil means debugger may continue execution. 4339 doc: /* Non-nil means debugger may continue execution.
4283This is nil when the debugger is called under circumstances where it 4340This is nil when the debugger is called under circumstances where it
diff --git a/src/keyboard.c b/src/keyboard.c
index 4ad6e4e6bd1..719226caedc 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -1331,6 +1331,7 @@ command_loop_1 (void)
1331 display_malloc_warning (); 1331 display_malloc_warning ();
1332 1332
1333 Vdeactivate_mark = Qnil; 1333 Vdeactivate_mark = Qnil;
1334 backtrace_yet = false;
1334 1335
1335 /* Don't ignore mouse movements for more than a single command 1336 /* Don't ignore mouse movements for more than a single command
1336 loop. (This flag is set in xdisp.c whenever the tool bar is 1337 loop. (This flag is set in xdisp.c whenever the tool bar is
@@ -1841,7 +1842,7 @@ safe_run_hooks_1 (ptrdiff_t nargs, Lisp_Object *args)
1841static Lisp_Object 1842static Lisp_Object
1842safe_run_hooks_error (Lisp_Object error, ptrdiff_t nargs, Lisp_Object *args) 1843safe_run_hooks_error (Lisp_Object error, ptrdiff_t nargs, Lisp_Object *args)
1843{ 1844{
1844 eassert (nargs == 2); 1845 eassert (nargs >= 2 && nargs <= 4);
1845 AUTO_STRING (format, "Error in %s (%S): %S"); 1846 AUTO_STRING (format, "Error in %s (%S): %S");
1846 Lisp_Object hook = args[0]; 1847 Lisp_Object hook = args[0];
1847 Lisp_Object fun = args[1]; 1848 Lisp_Object fun = args[1];
@@ -1915,6 +1916,17 @@ safe_run_hooks_maybe_narrowed (Lisp_Object hook, struct window *w)
1915 unbind_to (count, Qnil); 1916 unbind_to (count, Qnil);
1916} 1917}
1917 1918
1919void
1920safe_run_hooks_2 (Lisp_Object hook, Lisp_Object arg1, Lisp_Object arg2)
1921{
1922 specpdl_ref count = SPECPDL_INDEX ();
1923
1924 specbind (Qinhibit_quit, Qt);
1925 run_hook_with_args (4, ((Lisp_Object []) {hook, hook, arg1, arg2}),
1926 safe_run_hook_funcall);
1927 unbind_to (count, Qnil);
1928}
1929
1918 1930
1919/* Nonzero means polling for input is temporarily suppressed. */ 1931/* Nonzero means polling for input is temporarily suppressed. */
1920 1932
diff --git a/src/lisp.h b/src/lisp.h
index fe6e98843d1..2f73ba4c617 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4530,6 +4530,7 @@ extern Lisp_Object Vrun_hooks;
4530extern Lisp_Object Vsignaling_function; 4530extern Lisp_Object Vsignaling_function;
4531extern Lisp_Object inhibit_lisp_code; 4531extern Lisp_Object inhibit_lisp_code;
4532extern bool signal_quit_p (Lisp_Object); 4532extern bool signal_quit_p (Lisp_Object);
4533extern bool backtrace_yet;
4533 4534
4534/* To run a normal hook, use the appropriate function from the list below. 4535/* To run a normal hook, use the appropriate function from the list below.
4535 The calling convention: 4536 The calling convention:
@@ -4831,6 +4832,7 @@ extern bool detect_input_pending_ignore_squeezables (void);
4831extern bool detect_input_pending_run_timers (bool); 4832extern bool detect_input_pending_run_timers (bool);
4832extern void safe_run_hooks (Lisp_Object); 4833extern void safe_run_hooks (Lisp_Object);
4833extern void safe_run_hooks_maybe_narrowed (Lisp_Object, struct window *); 4834extern void safe_run_hooks_maybe_narrowed (Lisp_Object, struct window *);
4835extern void safe_run_hooks_2 (Lisp_Object, Lisp_Object, Lisp_Object);
4834extern void cmd_error_internal (Lisp_Object, const char *); 4836extern void cmd_error_internal (Lisp_Object, const char *);
4835extern Lisp_Object command_loop_2 (Lisp_Object); 4837extern Lisp_Object command_loop_2 (Lisp_Object);
4836extern Lisp_Object read_menu_command (void); 4838extern Lisp_Object read_menu_command (void);
diff --git a/src/xdisp.c b/src/xdisp.c
index 855f48f2bde..5268c359ecd 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -18133,8 +18133,8 @@ run_window_scroll_functions (Lisp_Object window, struct text_pos startp)
18133 { 18133 {
18134 specpdl_ref count = SPECPDL_INDEX (); 18134 specpdl_ref count = SPECPDL_INDEX ();
18135 specbind (Qinhibit_quit, Qt); 18135 specbind (Qinhibit_quit, Qt);
18136 run_hook_with_args_2 (Qwindow_scroll_functions, window, 18136 safe_run_hooks_2
18137 make_fixnum (CHARPOS (startp))); 18137 (Qwindow_scroll_functions, window, make_fixnum (CHARPOS (startp)));
18138 unbind_to (count, Qnil); 18138 unbind_to (count, Qnil);
18139 SET_TEXT_POS_FROM_MARKER (startp, w->start); 18139 SET_TEXT_POS_FROM_MARKER (startp, w->start);
18140 /* In case the hook functions switch buffers. */ 18140 /* In case the hook functions switch buffers. */