* 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
* Re: How can I rethrow an error after recording a backtrace?
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-05 2:34 ` Clément Pit--Claudel
1 sibling, 1 reply; 15+ messages in thread
From: Stefan Monnier @ 2016-08-05 2:23 UTC (permalink / raw)
To: emacs-devel
> (setq num-nonmacro-input-events (1+ num-nonmacro-input-events))
This should be in the server.el's process sentinel, so as to consider
emacsclient connections as "nonmacro input event".
Stefan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: How can I rethrow an error after recording a backtrace?
2016-08-05 2:23 ` Stefan Monnier
@ 2016-08-05 9:40 ` Clément Pit--Claudel
2016-08-06 2:11 ` Stefan Monnier
0 siblings, 1 reply; 15+ messages in thread
From: Clément Pit--Claudel @ 2016-08-05 9:40 UTC (permalink / raw)
To: emacs-devel; +Cc: Stefan Monnier
[-- Attachment #1.1: Type: text/plain, Size: 734 bytes --]
On 2016-08-04 22:23, Stefan Monnier wrote:
>> (setq num-nonmacro-input-events (1+ num-nonmacro-input-events))
>
> This should be in the server.el's process sentinel, so as to consider
> emacsclient connections as "nonmacro input event".
Hmm. Even with that, I'm not sure. Take the following (untested) program:
(condition-case err1
(condition-case err2
(error "A")
(error (error "B)))
(error (message "Oups")))
Suppose I want to capture one backtrace for each of the signals in this program. Assuming I register a debugger that re-throws all errors that it receives, won't the second error slip through, due to the nonmacro input event counter not having been increased?
Clément.
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: How can I rethrow an error after recording a backtrace?
2016-08-05 9:40 ` Clément Pit--Claudel
@ 2016-08-06 2:11 ` Stefan Monnier
2016-08-06 10:03 ` Eli Zaretskii
0 siblings, 1 reply; 15+ messages in thread
From: Stefan Monnier @ 2016-08-06 2:11 UTC (permalink / raw)
To: Clément Pit--Claudel; +Cc: emacs-devel
> Hmm. Even with that, I'm not sure. Take the following (untested) program:
>
> (condition-case err1
> (condition-case err2
> (error "A")
> (error (error "B)))
> (error (message "Oups")))
This problem also affects the normal case (i.e. without server.el).
As I said elsewhere, I think this num-* side-condition can be dropped
because the inhibit-debugger variable should catch the same problems
but better.
Stefan
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: How can I rethrow an error after recording a backtrace?
2016-08-06 2:11 ` Stefan Monnier
@ 2016-08-06 10:03 ` Eli Zaretskii
2016-08-06 13:29 ` Stefan Monnier
0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2016-08-06 10:03 UTC (permalink / raw)
To: Stefan Monnier; +Cc: clement.pit, emacs-devel
> From: Stefan Monnier <monnier@IRO.UMontreal.CA>
> Date: Fri, 05 Aug 2016 22:11:55 -0400
> Cc: emacs-devel@gnu.org
>
> As I said elsewhere, I think this num-* side-condition can be dropped
> because the inhibit-debugger variable should catch the same problems
> but better.
So maybe we should avoid entering the debugger only if _both_
variables say we shouldn't. That would be safer than just tossing
num-nonmacro-input-events.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: How can I rethrow an error after recording a backtrace?
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 2:34 ` Clément Pit--Claudel
2016-08-05 6:12 ` Eli Zaretskii
1 sibling, 1 reply; 15+ messages in thread
From: Clément Pit--Claudel @ 2016-08-05 2:34 UTC (permalink / raw)
To: emacs-devel
[-- Attachment #1.1: Type: text/plain, Size: 5479 bytes --]
Some notes:
* I should really have asked "how can I capture multiple errors on Emacs server?". It turns out that the rethrowing is pretty irrelevant, as it just seems that the number of non-macro input events stays 0 on a --daemon server.
* In light of this, is this comment actually true?
/* 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. */
What if num_nonmacro_input_events just didn't increase because there were no such events? And thus, is the behavior that I'm seeing a bug? Or is it just a weird aspect of running code on a --daemon server, and I should submit a documentation patch suggesting to `(cl-incf num-nonmacro-input-events)'?
Cheers,
Clément.
On 2016-08-04 22:12, Clément Pit--Claudel wrote:
> 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
* Re: How can I rethrow an error after recording a backtrace?
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>
0 siblings, 1 reply; 15+ messages in thread
From: Eli Zaretskii @ 2016-08-05 6:12 UTC (permalink / raw)
To: Clément Pit--Claudel; +Cc: emacs-devel
> From: Clément Pit--Claudel <clement.pit@gmail.com>
> Date: Thu, 4 Aug 2016 22:34:23 -0400
>
> * I should really have asked "how can I capture multiple errors on Emacs server?". It turns out that the rethrowing is pretty irrelevant, as it just seems that the number of non-macro input events stays 0 on a --daemon server.
Only when invoking emacsclient to perform commands non-interactively,
right? If you start an interactive session using emacsclient, the
debugger behaves as expected, right?
If so, the bug is that the daemon thinks it is in a situation
described by the comment you quote, while "emacs -batch" somehow
avoids that pitfall. You should debug Emacs to understand why the
difference happens.
> * In light of this, is this comment actually true?
>
> /* 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. */
>
> What if num_nonmacro_input_events just didn't increase because there were no such events? And thus, is the behavior that I'm seeing a bug?
Then how come "emacs -batch" doesn't exhibit the same behavior?
> Or is it just a weird aspect of running code on a --daemon server, and I should submit a documentation patch suggesting to `(cl-incf num-nonmacro-input-events)'?
No, I don't think so.
^ 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).