1 ;;;; (ice-9 debugger trap-hooks) -- abstraction of libguile's traps interface
3 ;;; Copyright (C) 2002 Free Software Foundation, Inc.
5 ;; This library is free software; you can redistribute it and/or
6 ;; modify it under the terms of the GNU Lesser General Public
7 ;; License as published by the Free Software Foundation; either
8 ;; version 2.1 of the License, or (at your option) any later version.
10 ;; This library is distributed in the hope that it will be useful,
11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;; Lesser General Public License for more details.
15 ;; You should have received a copy of the GNU Lesser General Public
16 ;; License along with this library; if not, write to the Free Software
17 ;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 ;;; This module provides an abstraction around Guile's low level trap
20 ;;; handler interface; its aim is to make the low level trap mechanism
21 ;;; shareable between the debugger and other applications, and to
22 ;;; insulate the rest of the debugger code a bit from changes that may
23 ;;; occur in the low level trap interface in future.
25 (define-module (ice-9 debugger trap-hooks)
26 #:use-module (ice-9 debugger trc)
27 #:export (add-trapped-stack-id!
28 remove-trapped-stack-id!
29 before-apply-frame-hook
30 before-enter-frame-hook
31 before-exit-frame-hook
32 after-apply-frame-hook
33 after-enter-frame-hook
40 remove-apply-frame-hook!
41 remove-breakpoint-hook!
42 remove-enter-frame-hook!
43 remove-exit-frame-hook!
46 ;;; The current low level traps interface is as follows.
48 ;;; All trap handlers are subject to SCM_TRAPS_P, which is controlled
49 ;;; by the `traps' setting of `(evaluator-traps-interface)' but also
50 ;;; (and more relevant in most cases) by the `with-traps' procedure.
51 ;;; Basically, `with-traps' sets SCM_TRAPS_P to 1 during execution of
52 ;;; its thunk parameter.
54 ;;; Note that all trap handlers are called with SCM_TRAPS_P set to 0
55 ;;; for the duration of the call, to avoid nasty recursive trapping
56 ;;; loops. If a trap handler knows what it is doing, it can override
57 ;;; this by `(trap-enable traps)'.
59 ;;; The apply-frame handler is called when Guile is about to perform
60 ;;; an application if EITHER the `apply-frame' evaluator trap option
61 ;;; is set, OR the `trace' debug option is set and the procedure to
62 ;;; apply has its `trace' procedure property set. The arguments
65 ;;; - the symbol 'apply-frame
67 ;;; - a continuation or debug object describing the current stack
69 ;;; - a boolean indicating whether the application is tail-recursive.
71 ;;; The enter-frame handler is called when the evaluator begins a new
72 ;;; evaluation frame if EITHER the `enter-frame' evaluator trap option
73 ;;; is set, OR the `breakpoints' debug option is set and the code to
74 ;;; be evaluated has its `breakpoint' source property set. The
75 ;;; arguments passed are:
77 ;;; - the symbol 'enter-frame
79 ;;; - a continuation or debug object describing the current stack
81 ;;; - a boolean indicating whether the application is tail-recursive.
83 ;;; - an unmemoized copy of the expression to be evaluated.
85 ;;; If the `enter-frame' evaluator trap option is set, the enter-frame
86 ;;; handler is also called when about to perform an application in
87 ;;; SCM_APPLY, immediately before possible calling the apply-frame
88 ;;; handler. (I don't totally understand this.) In this case, the
89 ;;; arguments passed are:
91 ;;; - the symbol 'enter-frame
93 ;;; - a continuation or debug object describing the current stack.
95 ;;; The exit-frame handler is called when Guile exits an evaluation
96 ;;; frame (in SCM_CEVAL) or an application frame (in SCM_APPLY), if
97 ;;; EITHER the `exit-frame' evaluator trap option is set, OR the
98 ;;; `trace' debug option is set and the frame is marked as having been
99 ;;; traced. The frame will be marked as having been traced if the
100 ;;; apply-frame handler was called for this frame. (This is trickier
101 ;;; than it sounds because of tail recursion: the same debug frame
102 ;;; could have been used for multiple applications, only some of which
103 ;;; were traced - I think.) The arguments passed are:
105 ;;; - the symbol 'exit-frame
107 ;;; - a continuation or debug object describing the current stack
109 ;;; - the result of the evaluation or application.
113 ;;; Mechanism for limiting trapping to contexts whose stack ID matches
114 ;;; one of a registered set. The default set up is to limit trapping
115 ;;; to events in the contexts of the Guile REPL and of file loading.
117 (define trapped-stack-ids (list 'repl-stack 'load-stack))
118 (define all-stack-ids-trapped? #f)
120 (define (add-trapped-stack-id! id)
121 "Add ID to the set of stack ids for which traps are active.
122 If `#t' is in this set, traps are active regardless of stack context.
123 To remove ID again, use `remove-trapped-stack-id!'. If you add the
124 same ID twice using `add-trapped-stack-id!', you will need to remove
126 (set! trapped-stack-ids (cons id trapped-stack-ids))
127 (set! all-stack-ids-trapped? (memq #t trapped-stack-ids)))
129 (define (remove-trapped-stack-id! id)
130 "Remove ID from the set of stack ids for which traps are active."
131 (set! trapped-stack-ids (delq1! id trapped-stack-ids))
132 (set! all-stack-ids-trapped? (memq #t trapped-stack-ids)))
134 (define (trap-here? cont)
135 ;; Return true if the stack id of the specified continuation (or
136 ;; debug object) is in the set that we should trap for; otherwise
138 (or all-stack-ids-trapped?
139 (memq (stack-id cont) trapped-stack-ids)))
143 ;;; Variables tracking registered handlers, relevant procedures, and
144 ;;; what's turned on as regards the evaluator's debugging options.
146 (define before-enter-frame-hook (make-hook 3))
147 (define enter-frame-hook (make-hook))
148 (define breakpoint-hook (make-hook))
149 (define after-enter-frame-hook (make-hook))
151 (define before-exit-frame-hook (make-hook 2))
152 (define exit-frame-hook (make-hook))
153 (define after-exit-frame-hook (make-hook))
155 (define before-apply-frame-hook (make-hook 2))
156 (define apply-frame-hook (make-hook))
157 (define trace-hook (make-hook))
158 (define after-apply-frame-hook (make-hook))
160 (define (hook-not-empty? hook)
161 (not (hook-empty? hook)))
163 (define set-debug-and-trap-options
164 (let ((dopts (debug-options))
165 (topts (evaluator-traps-interface))
166 (setting (lambda (key opts)
167 (let ((l (memq key opts)))
169 (not (null? (cdr l)))
171 (debug-set-boolean! (lambda (key value)
172 ((if value debug-enable debug-disable) key)))
173 (trap-set-boolean! (lambda (key value)
174 ((if value trap-enable trap-disable) key))))
175 (let ((save-debug (memq 'debug dopts))
176 (save-trace (memq 'trace dopts))
177 (save-breakpoints (memq 'breakpoints dopts))
178 (save-enter-frame (memq 'enter-frame topts))
179 (save-apply-frame (memq 'apply-frame topts))
180 (save-exit-frame (memq 'exit-frame topts))
181 (save-enter-frame-handler (setting 'enter-frame-handler topts))
182 (save-apply-frame-handler (setting 'apply-frame-handler topts))
183 (save-exit-frame-handler (setting 'exit-frame-handler topts)))
185 (let ((need-trace (hook-not-empty? trace-hook))
186 (need-breakpoints (hook-not-empty? breakpoint-hook))
187 (need-enter-frame (hook-not-empty? enter-frame-hook))
188 (need-apply-frame (hook-not-empty? apply-frame-hook))
189 (need-exit-frame (hook-not-empty? exit-frame-hook)))
190 (debug-set-boolean! 'debug
197 (debug-set-boolean! 'trace
200 (debug-set-boolean! 'breakpoints
203 (trap-set-boolean! 'enter-frame
206 (trap-set-boolean! 'apply-frame
209 (trap-set-boolean! 'exit-frame
212 (trap-set! enter-frame-handler
213 (cond ((or need-breakpoints
216 (else save-enter-frame-handler)))
217 (trap-set! apply-frame-handler
218 (cond ((or need-trace
221 (else save-apply-frame-handler)))
222 (trap-set! exit-frame-handler
223 (cond ((or need-exit-frame)
225 (else save-exit-frame-handler))))
226 ;;(write (evaluator-traps-interface))
229 (define (enter-frame-handler key cont . args)
230 ;; For a non-application entry, ARGS is (TAIL? EXP), where EXP is an
231 ;; unmemoized copy of the source expression. For an application
232 ;; entry, ARGS is empty.
233 (if (trap-here? cont)
234 (let ((application-entry? (null? args)))
235 (trc 'enter-frame-handler)
236 (if application-entry?
237 (run-hook before-enter-frame-hook cont #f #f)
238 (run-hook before-enter-frame-hook cont (car args) (cadr args)))
239 (run-hook enter-frame-hook)
240 (or application-entry?
241 (run-hook breakpoint-hook))
242 (run-hook after-enter-frame-hook))))
244 (define (exit-frame-handler key cont retval)
245 (if (trap-here? cont)
247 (trc 'exit-frame-handler retval (stack-length (make-stack cont)))
248 (run-hook before-exit-frame-hook cont retval)
249 (run-hook exit-frame-hook)
250 (run-hook after-exit-frame-hook))))
252 (define (apply-frame-handler key cont tail?)
253 (if (trap-here? cont)
255 (trc 'apply-frame-handler tail?)
256 (run-hook before-apply-frame-hook cont tail?)
257 (run-hook apply-frame-hook)
258 (run-hook trace-hook)
259 (run-hook after-apply-frame-hook))))
261 (define-public (add-enter-frame-hook! proc)
262 (add-hook! enter-frame-hook proc)
263 (set-debug-and-trap-options))
265 (define-public (add-breakpoint-hook! proc)
266 (add-hook! breakpoint-hook proc)
267 (set-debug-and-trap-options))
269 (define-public (add-exit-frame-hook! proc)
270 (add-hook! exit-frame-hook proc)
271 (set-debug-and-trap-options))
273 (define-public (add-apply-frame-hook! proc)
274 (add-hook! apply-frame-hook proc)
275 (set-debug-and-trap-options))
277 (define-public (add-trace-hook! proc)
278 (add-hook! trace-hook proc)
279 (set-debug-and-trap-options))
281 (define-public (remove-enter-frame-hook! proc)
282 (remove-hook! enter-frame-hook proc)
283 (set-debug-and-trap-options))
285 (define-public (remove-breakpoint-hook! proc)
286 (remove-hook! breakpoint-hook proc)
287 (set-debug-and-trap-options))
289 (define-public (remove-exit-frame-hook! proc)
290 (remove-hook! exit-frame-hook proc)
291 (set-debug-and-trap-options))
293 (define-public (remove-apply-frame-hook! proc)
294 (remove-hook! apply-frame-hook proc)
295 (set-debug-and-trap-options))
297 (define-public (remove-trace-hook! proc)
298 (remove-hook! trace-hook proc)
299 (set-debug-and-trap-options))
301 ;;; (ice-9 debugger trap-hooks) ends here.