]> git.eshelyaron.com Git - emacs.git/commitdiff
Print a backtrace on unhandled errors in batch mode (Bug#44942).
authorPhilipp Stephani <phst@google.com>
Sun, 29 Nov 2020 13:24:57 +0000 (14:24 +0100)
committerPhilipp Stephani <phst@google.com>
Sun, 6 Dec 2020 16:47:52 +0000 (17:47 +0100)
* 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
src/eval.c
test/src/eval-tests.el

index 525ed8b36ee2b9c070fb98d07042794f223368cd..8390c82c98b2dc0125229005222ace20431c9bca 100644 (file)
--- 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.
+
 \f
 * Editing Changes in Emacs 28.1
 
index d9a424b57a92340680f4058773d5fcc558bbb3b6..18df484aac07fa07490898dad2565221433bf575 100644 (file)
@@ -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
index 074f5be1ef9affd881329c2713bba508793408bd..4125573dc6adac876ce96eb81ab04e7a1e18824f 100644 (file)
@@ -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 @@ in Common Lisp).  Instead, make sure substitution in backquote
 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