unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* How can I rethrow an error after recording a backtrace?
@ 2016-08-05  2:12 Clément Pit--Claudel
  2016-08-05  2:23 ` Stefan Monnier
  2016-08-05  2:34 ` Clément Pit--Claudel
  0 siblings, 2 replies; 15+ messages in thread
From: Clément Pit--Claudel @ 2016-08-05  2:12 UTC (permalink / raw)
  To: Emacs developers


[-- Attachment #1.1: Type: text/plain, Size: 4249 bytes --]

Hey emacs-devel,

The Emacs server doesn't print backtraces, so I'm trying to get these backtraces myself and return them to the server.  After some exploration, I found about the debugger variable, and realized that I could actually force code to break into the debugger regardless of handlers using debug-on-signal. So far so good. Then I wrote the following "debugger"

    (defun my-handle-error (&rest args)
      (let (;; Prevent recursive error catching
            (debugger #'debug)
            (debug-on-quit nil)
            (debug-on-error nil)
            (debug-on-signal nil))
        (pcase args
          (`(exit ,retv) retv)
          (`(error ,error-args)
           (setq saved-backtrace (with-output-to-string (backtrace)))
           (signal (car error-args) (cdr error-args))))))

And then I called my code as follows:

    (let* (;; Make sure that we'll intercept all errors
           (debug-on-quit t)
           (debug-on-error t)
           (debug-on-signal t)
           (debug-ignored-errors nil)
           ;; Make sure debugger has room to execute
           (max-specpdl-size (+ 50 max-specpdl-size))
           (max-lisp-eval-depth (+ 50 max-lisp-eval-depth))
           ;; Register ourselves as the debugger
           (debugger #'my-handle-error))
      (my code here))

This was in part inspired by similar code in allout-widgets.el (although I think that code never worked, since it calls signal with a non-list second argument).

When running on an emacs server, though, this code doesn't work. That is, it works fine the first time, but not on further invocations; so I stepped through the code in GDB, and I understood that the issue was here:

    if (
        /* Don't try to run the debugger with interrupts blocked.
       The editing loop would return anyway.  */
        ! input_blocked_p ()
        && NILP (Vinhibit_debugger)
        /* Does user want to enter debugger for this kind of error?  */
        && (EQ (sig, Qquit)
        ? debug_on_quit
        : wants_debugger (Vdebug_on_error, conditions))
        && ! skip_debugger (conditions, combined_data)
        /* RMS: What's this for?  */
        && when_entered_debugger < num_nonmacro_input_events)
      {
        call_debugger (list2 (Qerror, combined_data));
        return 1;
      }

(in maybe_call_debugger). The first time around the debugger is called, but not the second time, because the `when_entered_debugger < num_nonmacro_input_events` is false (on the first round it evaluates to -1 < 0, and on the second one to 0 < 0). This reason for this clause was explained when this code was written back in 1991:

    /* The value of num_nonmacro_input_events as of the last time we
       started to enter the debugger.  If we decide to enter the debugger
       again when this is still equal to num_nonmacro_input_events, then we
       know that the debugger itself has an error, and we should just
       signal the error instead of entering an infinite loop of debugger
       invocations.  */

    static EMACS_INT when_entered_debugger;

The problem is that I'm running this code using emacsclient --eval '(error "abc")', and so the num_nonmacro_input_events value never increases. I "fixed" it this way:

    (defun my-handle-error (&rest args)
      (let (;; Prevent recursive error catching
            (debugger #'debug)
            (debug-on-quit nil)
            (debug-on-error nil)
            (debug-on-signal nil))
        ;; HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ;;
        (setq num-nonmacro-input-events (1+ num-nonmacro-input-events))
        ;; HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK HACK ;;
        (pcase args
          (`(exit ,retv) retv)
          (`(error ,error-args)
           (setq saved-backtrace (with-output-to-string (backtrace)))
           (signal (car error-args) (cdr error-args))))))

Is this going to have horrible consequences? If it is, could someone more experienced point me to how I am supposed to capture a backtrace and then rethrow an error? I can provide a full example if anyone wants to experiment and finds this description incomplete.

Cheers,
Clément.


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2016-08-06 17:38 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-08-05  2:12 How can I rethrow an error after recording a backtrace? Clément Pit--Claudel
2016-08-05  2:23 ` Stefan Monnier
2016-08-05  9:40   ` Clément Pit--Claudel
2016-08-06  2:11     ` Stefan Monnier
2016-08-06 10:03       ` Eli Zaretskii
2016-08-06 13:29         ` Stefan Monnier
2016-08-05  2:34 ` Clément Pit--Claudel
2016-08-05  6:12   ` Eli Zaretskii
     [not found]     ` <73dc7988-7b41-385d-1083-6a6fa4bfbd91@gmail.com>
2016-08-05 13:40       ` Eli Zaretskii
2016-08-05 15:38         ` Clément Pit--Claudel
2016-08-06 10:02           ` Eli Zaretskii
2016-08-06 15:24             ` Clément Pit--Claudel
2016-08-06 15:31               ` Eli Zaretskii
2016-08-06 15:49                 ` Clément Pit--Claudel
2016-08-06 17:38                   ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).