From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Neil Jerram Newsgroups: gmane.lisp.guile.user Subject: Re: saving and restoring the error stack trace Date: Mon, 28 Aug 2006 23:21:04 +0100 Message-ID: <87r6z0fqnj.fsf@ossau.uklinux.net> References: <87ejv2gx0x.fsf@ossau.uklinux.net> NNTP-Posting-Host: main.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1156803749 5392 80.91.229.2 (28 Aug 2006 22:22:29 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Mon, 28 Aug 2006 22:22:29 +0000 (UTC) Cc: guile-user Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Tue Aug 29 00:22:27 2006 Return-path: Envelope-to: guile-user@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by ciao.gmane.org with esmtp (Exim 4.43) id 1GHpUv-0005OC-Im for guile-user@m.gmane.org; Tue, 29 Aug 2006 00:22:26 +0200 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1GHpUu-0005oI-SS for guile-user@m.gmane.org; Mon, 28 Aug 2006 18:22:24 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1GHpUq-0005np-EO for guile-user@gnu.org; Mon, 28 Aug 2006 18:22:20 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1GHpUp-0005nT-Mk for guile-user@gnu.org; Mon, 28 Aug 2006 18:22:20 -0400 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1GHpUp-0005nO-Gh for guile-user@gnu.org; Mon, 28 Aug 2006 18:22:19 -0400 Original-Received: from [80.84.72.33] (helo=mail3.uklinux.net) by monty-python.gnu.org with esmtp (Exim 4.52) id 1GHpdr-0005ii-Qn for guile-user@gnu.org; Mon, 28 Aug 2006 18:31:40 -0400 Original-Received: from laruns (host86-129-125-104.range86-129.btcentralplus.com [86.129.125.104]) by mail3.uklinux.net (Postfix) with ESMTP id 8754A40A4D3; Mon, 28 Aug 2006 22:22:17 +0000 (UTC) Original-Received: from laruns (laruns [127.0.0.1]) by laruns (Postfix) with ESMTP id BC9CC6FF90; Mon, 28 Aug 2006 23:21:04 +0100 (BST) Original-To: "Marco Maggi" In-Reply-To: <87ejv2gx0x.fsf@ossau.uklinux.net> (Neil Jerram's message of "Sun, 27 Aug 2006 13:53:34 +0100") User-Agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux) X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: guile-user-bounces+guile-user=m.gmane.org@gnu.org Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org Xref: news.gmane.org gmane.lisp.guile.user:5480 Archived-At: Neil Jerram 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. _______________________________________________ Guile-user mailing list Guile-user@gnu.org http://lists.gnu.org/mailman/listinfo/guile-user