From 6441ca2ca9ed0233cdf474cc64a860a3cff51222 Mon Sep 17 00:00:00 2001 From: Philipp Stephani Date: Sun, 29 Nov 2020 14:24:57 +0100 Subject: [PATCH] Print a backtrace on unhandled errors in batch mode (Bug#44942). * src/eval.c (signal_or_quit): Print a backtrace in batch mode if no error handler was found. * test/src/eval-tests.el (eval-tests/backtrace-in-batch-mode) (eval-tests/backtrace-in-batch-mode/demoted-errors): New unit tests. * etc/NEWS: Document change. --- etc/NEWS | 3 +++ src/eval.c | 15 ++++++++++++++- test/src/eval-tests.el | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index 2fb33e342e..baae624f27 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -192,6 +192,9 @@ have been replaced with "chat.freenode.net" throughout Emacs. These functions return the connection local value of the respective variables. This can be used for remote hosts. +** Emacs now prints a backtrace when signaling an error in batch mode. This +makes debugging Emacs Lisp scripts run in batch mode easier. + * Editing Changes in Emacs 28.1 diff --git a/src/eval.c b/src/eval.c index d9a424b57a..18df484aac 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1709,6 +1709,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) break; } + bool debugger_called = false; if (/* Don't run the debugger for a memory-full error. (There is no room in memory to do that!) */ !NILP (error_symbol) @@ -1722,7 +1723,7 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) if requested". */ || EQ (h->tag_or_ch, Qerror))) { - bool debugger_called + debugger_called = maybe_call_debugger (conditions, error_symbol, data); /* We can't return values to code which signaled an error, but we can continue code which has signaled a quit. */ @@ -1730,6 +1731,18 @@ signal_or_quit (Lisp_Object error_symbol, Lisp_Object data, bool keyboard_quit) return Qnil; } + /* If we're in batch mode, print a backtrace unconditionally to help with + debugging. Make sure to use `debug' unconditionally to not interfere with + ERT or other packages that install custom debuggers. */ + if (!debugger_called && !NILP (error_symbol) + && (NILP (clause) || EQ (h->tag_or_ch, Qerror)) && noninteractive) + { + ptrdiff_t count = SPECPDL_INDEX (); + specbind (Vdebugger, Qdebug); + call_debugger (list2 (Qerror, Fcons (error_symbol, data))); + unbind_to (count, Qnil); + } + if (!NILP (clause)) { Lisp_Object unwind_data diff --git a/test/src/eval-tests.el b/test/src/eval-tests.el index 074f5be1ef..4125573dc6 100644 --- a/test/src/eval-tests.el +++ b/test/src/eval-tests.el @@ -27,6 +27,7 @@ (require 'ert) (eval-when-compile (require 'cl-lib)) +(require 'subr-x) (ert-deftest eval-tests--bug24673 () "Check that Bug#24673 has been fixed." @@ -176,4 +177,36 @@ eval-tests-19790-backquote-comma-dot-substitution expressions works for identifiers starting with period." (should (equal (let ((.x 'identity)) (eval `(,.x 'ok))) 'ok))) +(ert-deftest eval-tests/backtrace-in-batch-mode () + (let ((emacs (expand-file-name invocation-name invocation-directory))) + (skip-unless (file-executable-p emacs)) + (with-temp-buffer + (let ((status (call-process emacs nil t nil + "--quick" "--batch" + (concat "--eval=" + (prin1-to-string + '(progn + (defun foo () (error "Boo")) + (foo))))))) + (should (natnump status)) + (should-not (eql status 0))) + (goto-char (point-min)) + (ert-info ((concat "Process output:\n" (buffer-string))) + (search-forward " foo()") + (search-forward " normal-top-level()"))))) + +(ert-deftest eval-tests/backtrace-in-batch-mode/demoted-errors () + (let ((emacs (expand-file-name invocation-name invocation-directory))) + (skip-unless (file-executable-p emacs)) + (with-temp-buffer + (should (eql 0 (call-process emacs nil t nil + "--quick" "--batch" + (concat "--eval=" + (prin1-to-string + '(with-demoted-errors "Error: %S" + (error "Boo"))))))) + (goto-char (point-min)) + (should (equal (string-trim (buffer-string)) + "Error: (error \"Boo\")"))))) + ;;; eval-tests.el ends here -- 2.29.2.454.gaff20da3a2-goog