guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: saving and restoring the error stack trace


From: Neil Jerram
Subject: Re: saving and restoring the error stack trace
Date: Mon, 28 Aug 2006 23:21:04 +0100
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

Neil Jerram <address@hidden> writes:

> Thanks; I expect to have some draft text for you by end tomorrow.

Draft text is below; please let me know your thoughts on it - most
importantly, of course, whether it answers your question!

Regards,
     Neil


5.21.2 Debugging when an error occurs
-------------------------------------

A common requirement is to be able to show as much useful context as
possible when a Scheme program hits an error.  The most immediate
information about an error is the kind of error that it is - such as
"division by zero" - and any parameters that the code which signalled
the error chose explicitly to provide.  This information originates with
the `error' or `throw' call (or their C code equivalents, if the error
is detected by C code) that signals the error, and is passed
automatically to the handler procedure of the innermost applicable
`catch', `lazy-catch' or `with-throw-handler' expression.

5.21.2.1 Intercepting basic error information
.............................................

Therefore, to catch errors that occur within a chunk of Scheme code, and
to intercept basic information about those errors, you need to execute
that code inside the dynamic context of a `catch', `lazy-catch' or
`with-throw-handler' expression, or the equivalent in C.  In Scheme,
this means you need something like this:

     (catch #t
            (lambda ()
              ;; Execute the code in which
              ;; you want to catch errors here.
              ...)
            (lambda (key . parameters)
              ;; Put the code which you want
              ;; to handle an error here.
              ...))

The `catch' here can also be `lazy-catch' or `with-throw-handler'; see
*Note Throw Handlers:: and *Note Lazy Catch:: for the details of how
these differ from `catch'.  The `#t' means that the catch is applicable
to all kinds of error; if you want to restrict your catch to just one
kind of error, you can put the symbol for that kind of error instead of
`#t'.  The equivalent to this in C would be something like this:

     SCM my_body_proc (void *body_data)
     {
       /* Execute the code in which
          you want to catch errors here. */
       ...
     }

     SCM my_handler_proc (void *handler_data,
                          SCM key,
                          SCM parameters)
     {
       /* Put the code which you want
          to handle an error here. */
       ...
     }

     {
       ...
       scm_c_catch (SCM_BOOL_T,
                    my_body_proc, body_data,
                    my_handler_proc, handler_data,
                    NULL, NULL);
       ...
     }

Again, as with the Scheme version, `scm_c_catch' could be replaced by
`scm_internal_lazy_catch' or `scm_c_with_throw_handler', and
`SCM_BOOL_T' could instead be the symbol for a particular kind of error.

5.21.2.2 Capturing the full error stack
.......................................

The other interesting information about an error is the full Scheme
stack at the point where the error occurred; in other words what
innermost expression was being evaluated, what was the expression that
called that one, and so on.  If you want to write your code so that it
captures and can display this information as well, there are two
important things to understand.

   Firstly, the stack at the point of the error needs to be explicitly
captured by a `make-stack' call (or the C equivalent `scm_make_stack').
The Guile library does not do this "automatically" for you, so you
will need to write code with a `make-stack' or `scm_make_stack' call
yourself.  (We emphasise this point because some people are misled by
the fact that the Guile interactive REPL code _does_ capture and
display the stack automatically.  But the Guile interactive REPL is
itself a Scheme program(1) running on top of the Guile library, and
which uses `catch' and `make-stack' in the way we are about to describe
to capture the stack when an error occurs.)

   Secondly, in order to capture the stack effectively at the point
where the error occurred, the `make-stack' call must be made before
Guile unwinds the stack back to the location of the prevailing catch
expression.  This means that the `make-stack' call must be made within
the handler of a `lazy-catch' or `with-throw-handler' expression, or
the optional "pre-unwind" handler of a `catch'.  (For the full story of
how these alternatives differ from each other, see *Note Exceptions::.
The main difference is that `catch' terminates the error, whereas
`lazy-catch' and `with-throw-handler' only intercept it temporarily and
then allow it to continue propagating up to the next innermost handler.)

   So, here are some examples of how to do all this in Scheme and in C.
For the purpose of these examples we assume that the captured stack
should be stored in a variable, so that it can be displayed or
arbitrarily processed later on.  In Scheme:

     (let ((captured-stack #f))
       (catch #t
              (lambda ()
                ;; Execute the code in which
                ;; you want to catch errors here.
                ...)
              (lambda (key . parameters)
                ;; Put the code which you want
                ;; to handle an error after the
                ;; stack has been unwound here.
                ...)
              (lambda (key . parameters)
                ;; Capture the stack here:
                (set! captured-stack (make-stack #t))))
       ...
       (if captured-stack
           (begin
             ;; Display or process the captured stack.
             ...))
       ...)

And in C:

     SCM my_body_proc (void *body_data)
     {
       /* Execute the code in which
          you want to catch errors here. */
       ...
     }

     SCM my_handler_proc (void *handler_data,
                          SCM key,
                          SCM parameters)
     {
       /* Put the code which you want
          to handle an error after the
          stack has been unwound here. */
       ...
     }

     SCM my_preunwind_proc (void *handler_data,
                            SCM key,
                            SCM parameters)
     {
       /* Capture the stack here: */
       *(SCM *)handler_data = scm_make_stack (SCM_BOOL_T, SCM_EOL);
     }

     {
       SCM captured_stack = SCM_BOOL_F;
       ...
       scm_c_catch (SCM_BOOL_T,
                    my_body_proc, body_data,
                    my_handler_proc, handler_data,
                    my_preunwind_proc, &captured_stack);
       ...
       if (captured_stack != SCM_BOOL_F)
       {
         /* Display or process the captured stack. */
         ...
       }
       ...
     }

Note that you don't have to wait until after the `catch' or
`scm_c_catch' has returned.  You can also do whatever you like with the
stack immediately after it has been captured in the pre-unwind handler,
or in the normal (post-unwind) handler.  (Except that for the latter
case in C you will need to change `handler_data' in the
`scm_c_catch(...)' call to `&captured_stack', so that `my_handler_proc'
has access to the captured stack.)

5.21.2.3 Displaying or interrogating the captured stack
.......................................................

Once you have a captured stack, you can interrogate and display its
details in any way that you want, using the `stack-...' and `frame-...'
API described in *Note Examining the Stack:: and *Note Examining Stack
Frames::.

   If you want to print out a backtrace in the same format that the
Guile REPL does, you can use the `display-backtrace' procedure to do so.
You can also use `display-application' to display an individual
application frame - that is, a frame that satisfies the
`frame-procedure?' predicate - in the Guile REPL format.





reply via email to

[Prev in Thread] Current Thread [Next in Thread]