Commit | Line | Data |
---|---|---|
ea19f0b3 NJ |
1 | (define-module (ice-9 gds-client) |
2 | #:use-module (oop goops) | |
3 | #:use-module (oop goops describe) | |
4 | #:use-module (ice-9 debugging breakpoints) | |
5 | #:use-module (ice-9 debugging trace) | |
6 | #:use-module (ice-9 debugging traps) | |
7 | #:use-module (ice-9 debugging trc) | |
8 | #:use-module (ice-9 debugging steps) | |
9 | #:use-module (ice-9 pretty-print) | |
10 | #:use-module (ice-9 regex) | |
11 | #:use-module (ice-9 session) | |
12 | #:use-module (ice-9 string-fun) | |
13 | #:export (gds-debug-trap | |
14 | run-utility | |
15 | set-gds-breakpoints | |
16 | gds-accept-input)) | |
17 | ||
18 | (cond ((string>=? (version) "1.7") | |
19 | (use-modules (ice-9 debugger utils))) | |
20 | (else | |
21 | (define the-ice-9-debugger-module (resolve-module '(ice-9 debugger))) | |
22 | (module-export! the-ice-9-debugger-module | |
23 | '(source-position | |
24 | write-frame-short/application | |
25 | write-frame-short/expression | |
26 | write-frame-args-long | |
27 | write-frame-long)))) | |
28 | ||
29 | (use-modules (ice-9 debugger)) | |
30 | ||
31 | (define gds-port #f) | |
32 | ||
33 | ;; Return an integer that somehow identifies the current thread. | |
34 | (define (get-thread-id) | |
35 | (let ((root (dynamic-root))) | |
36 | (cond ((integer? root) | |
37 | root) | |
38 | ((pair? root) | |
39 | (object-address root)) | |
40 | (else | |
41 | (error "Unexpected dynamic root:" root))))) | |
42 | ||
43 | ;; gds-debug-read is a high-priority read. The (debug-thread-id ID) | |
44 | ;; form causes the frontend to dismiss any reads from threads whose id | |
45 | ;; is not ID, until it receives the (thread-id ...) form with the same | |
46 | ;; id as ID. Dismissing the reads of any other threads (by sending a | |
47 | ;; form that is otherwise ignored) causes those threads to release the | |
48 | ;; read mutex, which allows the (gds-read) here to proceed. | |
49 | (define (gds-debug-read) | |
50 | (write-form `(debug-thread-id ,(get-thread-id))) | |
51 | (gds-read)) | |
52 | ||
53 | (define (gds-debug-trap trap-context) | |
54 | "Invoke the GDS debugger to explore the stack at the specified trap." | |
55 | (connect-to-gds) | |
56 | (start-stack 'debugger | |
57 | (let* ((stack (tc:stack trap-context)) | |
58 | (flags1 (let ((trap-type (tc:type trap-context))) | |
59 | (case trap-type | |
60 | ((#:return #:error) | |
61 | (list trap-type | |
62 | (tc:return-value trap-context))) | |
63 | (else | |
64 | (list trap-type))))) | |
65 | (flags (if (tc:continuation trap-context) | |
66 | (cons #:continuable flags1) | |
67 | flags1)) | |
68 | (fired-traps (tc:fired-traps trap-context)) | |
69 | (special-index (and (= (length fired-traps) 1) | |
70 | (is-a? (car fired-traps) <exit-trap>) | |
71 | (eq? (tc:type trap-context) #:return) | |
72 | (- (tc:depth trap-context) | |
73 | (slot-ref (car fired-traps) 'depth))))) | |
74 | ;; Write current stack to the frontend. | |
75 | (write-form (list 'stack | |
b1860cb3 NJ |
76 | (if (and special-index (> special-index 0)) |
77 | special-index | |
78 | 0) | |
ea19f0b3 NJ |
79 | (stack->emacs-readable stack) |
80 | (append (flags->emacs-readable flags) | |
81 | (slot-ref trap-context | |
82 | 'handler-return-syms)))) | |
83 | ;; Now wait for instruction. | |
84 | (let loop ((protocol (gds-debug-read))) | |
85 | ;; Act on it. | |
86 | (case (car protocol) | |
87 | ((tweak) | |
88 | ;; Request to tweak the handler return value. | |
89 | (let ((tweaking (catch #t | |
90 | (lambda () | |
91 | (list (with-input-from-string | |
92 | (cadr protocol) | |
93 | read))) | |
94 | (lambda ignored #f)))) | |
95 | (if tweaking | |
96 | (slot-set! trap-context | |
97 | 'handler-return-value | |
98 | (cons 'instead (car tweaking))))) | |
99 | (loop (gds-debug-read))) | |
100 | ((continue) | |
101 | ;; Continue (by exiting the debugger). | |
102 | *unspecified*) | |
103 | ((evaluate) | |
104 | ;; Evaluate expression in specified frame. | |
105 | (eval-in-frame stack (cadr protocol) (caddr protocol)) | |
106 | (loop (gds-debug-read))) | |
107 | ((info-frame) | |
108 | ;; Return frame info. | |
109 | (let ((frame (stack-ref stack (cadr protocol)))) | |
110 | (write-form (list 'info-result | |
111 | (with-output-to-string | |
112 | (lambda () | |
113 | (write-frame-long frame)))))) | |
114 | (loop (gds-debug-read))) | |
115 | ((info-args) | |
116 | ;; Return frame args. | |
117 | (let ((frame (stack-ref stack (cadr protocol)))) | |
118 | (write-form (list 'info-result | |
119 | (with-output-to-string | |
120 | (lambda () | |
121 | (write-frame-args-long frame)))))) | |
122 | (loop (gds-debug-read))) | |
123 | ((proc-source) | |
124 | ;; Show source of application procedure. | |
125 | (let* ((frame (stack-ref stack (cadr protocol))) | |
126 | (proc (frame-procedure frame)) | |
127 | (source (and proc (procedure-source proc)))) | |
128 | (write-form (list 'info-result | |
129 | (if source | |
130 | (sans-surrounding-whitespace | |
131 | (with-output-to-string | |
132 | (lambda () | |
133 | (pretty-print source)))) | |
134 | (if proc | |
135 | "This procedure is coded in C" | |
136 | "This frame has no procedure"))))) | |
137 | (loop (gds-debug-read))) | |
138 | ((traps-here) | |
139 | ;; Show the traps that fired here. | |
140 | (write-form (list 'info-result | |
141 | (with-output-to-string | |
142 | (lambda () | |
143 | (for-each describe | |
144 | (tc:fired-traps trap-context)))))) | |
145 | (loop (gds-debug-read))) | |
146 | ((step-into) | |
147 | ;; Set temporary breakpoint on next trap. | |
148 | (at-step gds-debug-trap | |
149 | 1 | |
150 | #f | |
151 | (if (memq #:return flags) | |
152 | #f | |
153 | (- (stack-length stack) | |
154 | (cadr protocol))))) | |
155 | ((step-over) | |
156 | ;; Set temporary breakpoint on exit from | |
157 | ;; specified frame. | |
158 | (at-exit (- (stack-length stack) (cadr protocol)) | |
159 | gds-debug-trap)) | |
160 | ((step-file) | |
161 | ;; Set temporary breakpoint on next trap in same | |
162 | ;; source file. | |
163 | (at-step gds-debug-trap | |
164 | 1 | |
165 | (frame-file-name (stack-ref stack | |
166 | (cadr protocol))) | |
167 | (if (memq #:return flags) | |
168 | #f | |
169 | (- (stack-length stack) | |
170 | (cadr protocol))))) | |
171 | (else | |
172 | (safely-handle-nondebug-protocol protocol) | |
173 | (loop (gds-debug-read)))))))) | |
174 | ||
169ccff5 | 175 | (define (connect-to-gds . application-name) |
ea19f0b3 NJ |
176 | (or gds-port |
177 | (begin | |
178 | (set! gds-port | |
e2d23cc0 NJ |
179 | (or (let ((s (socket PF_INET SOCK_STREAM 0)) |
180 | (SOL_TCP 6) | |
181 | (TCP_NODELAY 1)) | |
182 | (setsockopt s SOL_TCP TCP_NODELAY 1) | |
183 | (catch #t | |
184 | (lambda () | |
185 | (connect s AF_INET (inet-aton "127.0.0.1") 8333) | |
186 | s) | |
187 | (lambda _ #f))) | |
188 | (let ((s (socket PF_UNIX SOCK_STREAM 0))) | |
189 | (catch #t | |
190 | (lambda () | |
191 | (connect s AF_UNIX "/tmp/.gds_socket") | |
192 | s) | |
193 | (lambda _ #f))) | |
194 | (error "Couldn't connect to GDS by TCP or Unix domain socket"))) | |
169ccff5 NJ |
195 | (write-form (list 'name (getpid) (apply client-name application-name)))))) |
196 | ||
197 | (define (client-name . application-name) | |
198 | (let loop ((args (append application-name (program-arguments)))) | |
199 | (if (null? args) | |
200 | (format #f "PID ~A" (getpid)) | |
201 | (let ((arg (car args))) | |
202 | (cond ((string-match "^(.*[/\\])?guile(\\..*)?$" arg) | |
203 | (loop (cdr args))) | |
204 | ((string-match "^-" arg) | |
205 | (loop (cdr args))) | |
206 | (else | |
207 | (format #f "~A (PID ~A)" arg (getpid)))))))) | |
ea19f0b3 NJ |
208 | |
209 | (if (not (defined? 'make-mutex)) | |
210 | (begin | |
211 | (define (make-mutex) #f) | |
212 | (define lock-mutex noop) | |
213 | (define unlock-mutex noop))) | |
214 | ||
215 | (define write-mutex (make-mutex)) | |
216 | ||
217 | (define (write-form form) | |
218 | ;; Write any form FORM to GDS. | |
219 | (lock-mutex write-mutex) | |
220 | (write form gds-port) | |
221 | (newline gds-port) | |
222 | (force-output gds-port) | |
223 | (unlock-mutex write-mutex)) | |
224 | ||
225 | (define (stack->emacs-readable stack) | |
226 | ;; Return Emacs-readable representation of STACK. | |
227 | (map (lambda (index) | |
228 | (frame->emacs-readable (stack-ref stack index))) | |
229 | (iota (min (stack-length stack) | |
230 | (cadr (memq 'depth (debug-options))))))) | |
231 | ||
232 | (define (frame->emacs-readable frame) | |
233 | ;; Return Emacs-readable representation of FRAME. | |
234 | (if (frame-procedure? frame) | |
235 | (list 'application | |
236 | (with-output-to-string | |
237 | (lambda () | |
238 | (display (if (frame-real? frame) " " "t ")) | |
239 | (write-frame-short/application frame))) | |
240 | (source->emacs-readable frame)) | |
241 | (list 'evaluation | |
242 | (with-output-to-string | |
243 | (lambda () | |
244 | (display (if (frame-real? frame) " " "t ")) | |
245 | (write-frame-short/expression frame))) | |
246 | (source->emacs-readable frame)))) | |
247 | ||
248 | (define (source->emacs-readable frame) | |
249 | ;; Return Emacs-readable representation of the filename, line and | |
250 | ;; column source properties of SOURCE. | |
251 | (or (frame->source-position frame) 'nil)) | |
252 | ||
253 | (define (flags->emacs-readable flags) | |
254 | ;; Return Emacs-readable representation of trap FLAGS. | |
255 | (let ((prev #f)) | |
256 | (map (lambda (flag) | |
257 | (let ((erf (if (and (keyword? flag) | |
258 | (not (eq? prev #:return))) | |
259 | (keyword->symbol flag) | |
260 | (format #f "~S" flag)))) | |
261 | (set! prev flag) | |
262 | erf)) | |
263 | flags))) | |
264 | ||
265 | (define (eval-in-frame stack index expr) | |
266 | (write-form | |
267 | (list 'eval-result | |
268 | (format #f "~S" | |
269 | (catch #t | |
270 | (lambda () | |
271 | (local-eval (with-input-from-string expr read) | |
272 | (memoized-environment | |
273 | (frame-source (stack-ref stack | |
274 | index))))) | |
275 | (lambda args | |
276 | (cons 'ERROR args))))))) | |
277 | ||
278 | (set! (behaviour-ordering gds-debug-trap) 100) | |
279 | ||
280 | ;;; Code below here adds support for interaction between the GDS | |
281 | ;;; client program and the Emacs frontend even when not stopped in the | |
282 | ;;; debugger. | |
283 | ||
284 | ;; A mutex to control attempts by multiple threads to read protocol | |
285 | ;; back from the frontend. | |
286 | (define gds-read-mutex (make-mutex)) | |
287 | ||
288 | ;; Read a protocol instruction from the frontend. | |
289 | (define (gds-read) | |
290 | ;; Acquire the read mutex. | |
291 | (lock-mutex gds-read-mutex) | |
292 | ;; Tell the front end something that identifies us as a thread. | |
293 | (write-form `(thread-id ,(get-thread-id))) | |
294 | ;; Now read, then release the mutex and return what was read. | |
295 | (let ((x (catch #t | |
296 | (lambda () (read gds-port)) | |
297 | (lambda ignored the-eof-object)))) | |
298 | (unlock-mutex gds-read-mutex) | |
299 | x)) | |
300 | ||
301 | (define (gds-accept-input exit-on-continue) | |
302 | ;; If reading from the GDS connection returns EOF, we will throw to | |
303 | ;; this catch. | |
304 | (catch 'server-eof | |
305 | (lambda () | |
306 | (let loop ((protocol (gds-read))) | |
307 | (if (or (eof-object? protocol) | |
308 | (and exit-on-continue | |
309 | (eq? (car protocol) 'continue))) | |
310 | (throw 'server-eof)) | |
311 | (safely-handle-nondebug-protocol protocol) | |
312 | (loop (gds-read)))) | |
313 | (lambda ignored #f))) | |
314 | ||
315 | (define (safely-handle-nondebug-protocol protocol) | |
316 | ;; This catch covers any internal errors in the GDS code or | |
317 | ;; protocol. | |
318 | (catch #t | |
319 | (lambda () | |
320 | (lazy-catch #t | |
321 | (lambda () | |
322 | (handle-nondebug-protocol protocol)) | |
323 | save-lazy-trap-context-and-rethrow)) | |
324 | (lambda (key . args) | |
325 | (write-form | |
326 | `(eval-results (error . ,(format #f "~s" protocol)) | |
327 | ,(if last-lazy-trap-context 't 'nil) | |
328 | "GDS Internal Error | |
329 | Please report this to <neil@ossau.uklinux.net>, ideally including: | |
330 | - a description of the scenario in which this error occurred | |
331 | - which versions of Guile and guile-debugging you are using | |
332 | - the error stack, which you can get by clicking on the link below, | |
333 | and then cut and paste into your report. | |
334 | Thanks!\n\n" | |
335 | ,(list (with-output-to-string | |
336 | (lambda () | |
337 | (write key) | |
338 | (display ": ") | |
339 | (write args) | |
340 | (newline))))))))) | |
341 | ||
342 | ;; The key that is used to signal a read error changes from 1.6 to | |
343 | ;; 1.8; here we cover all eventualities by discovering the key | |
344 | ;; dynamically. | |
345 | (define read-error-key | |
346 | (catch #t | |
347 | (lambda () | |
348 | (with-input-from-string "(+ 3 4" read)) | |
349 | (lambda (key . args) | |
350 | key))) | |
351 | ||
352 | (define (handle-nondebug-protocol protocol) | |
353 | (case (car protocol) | |
354 | ||
355 | ((eval) | |
356 | (set! last-lazy-trap-context #f) | |
091baf9e | 357 | (apply (lambda (correlator module port-name line column code flags) |
ea19f0b3 NJ |
358 | (with-input-from-string code |
359 | (lambda () | |
360 | (set-port-filename! (current-input-port) port-name) | |
361 | (set-port-line! (current-input-port) line) | |
362 | (set-port-column! (current-input-port) column) | |
363 | (let ((m (and module (resolve-module-from-root module)))) | |
364 | (catch read-error-key | |
365 | (lambda () | |
366 | (let loop ((exprs '()) (x (read))) | |
367 | (if (eof-object? x) | |
368 | ;; Expressions to be evaluated have all | |
369 | ;; been read. Now evaluate them. | |
370 | (let loop2 ((exprs (reverse! exprs)) | |
371 | (results '()) | |
372 | (n 1)) | |
373 | (if (null? exprs) | |
374 | (write-form `(eval-results ,correlator | |
375 | ,(if last-lazy-trap-context 't 'nil) | |
376 | ,@results)) | |
377 | (loop2 (cdr exprs) | |
378 | (append results (gds-eval (car exprs) m | |
379 | (if (and (null? (cdr exprs)) | |
380 | (= n 1)) | |
381 | #f n))) | |
382 | (+ n 1)))) | |
383 | ;; Another complete expression read; add | |
384 | ;; it to the list. | |
385 | (begin | |
386 | (for-each-breakpoint setup-after-read x) | |
091baf9e NJ |
387 | (if (and (pair? x) |
388 | (memq 'debug flags)) | |
389 | (install-trap (make <source-trap> | |
390 | #:expression x | |
391 | #:behaviour gds-debug-trap))) | |
ea19f0b3 NJ |
392 | (loop (cons x exprs) (read)))))) |
393 | (lambda (key . args) | |
394 | (write-form `(eval-results | |
395 | ,correlator | |
396 | ,(if last-lazy-trap-context 't 'nil) | |
397 | ,(with-output-to-string | |
398 | (lambda () | |
399 | (display ";;; Reading expressions") | |
400 | (display " to evaluate\n") | |
401 | (apply display-error #f | |
402 | (current-output-port) args))) | |
403 | ("error-in-read")))))))) | |
404 | (if (string? port-name) | |
405 | (without-traps | |
406 | (lambda () | |
407 | (for-each-breakpoint setup-after-eval port-name))))) | |
408 | (cdr protocol))) | |
409 | ||
410 | ((complete) | |
411 | (let ((matches (apropos-internal | |
412 | (string-append "^" (regexp-quote (cadr protocol)))))) | |
413 | (cond ((null? matches) | |
414 | (write-form '(completion-result nil))) | |
415 | (else | |
416 | ;;(write matches (current-error-port)) | |
417 | ;;(newline (current-error-port)) | |
418 | (let ((match | |
419 | (let loop ((match (symbol->string (car matches))) | |
420 | (matches (cdr matches))) | |
421 | ;;(write match (current-error-port)) | |
422 | ;;(newline (current-error-port)) | |
423 | ;;(write matches (current-error-port)) | |
424 | ;;(newline (current-error-port)) | |
425 | (if (null? matches) | |
426 | match | |
427 | (if (string-prefix=? match | |
428 | (symbol->string (car matches))) | |
429 | (loop match (cdr matches)) | |
430 | (loop (substring match 0 | |
431 | (- (string-length match) 1)) | |
432 | matches)))))) | |
433 | (if (string=? match (cadr protocol)) | |
434 | (write-form `(completion-result | |
435 | ,(map symbol->string matches))) | |
436 | (write-form `(completion-result | |
437 | ,match)))))))) | |
438 | ||
439 | ((debug-lazy-trap-context) | |
440 | (if last-lazy-trap-context | |
441 | (gds-debug-trap last-lazy-trap-context) | |
442 | (error "There is no stack available to show"))) | |
443 | ||
444 | ((set-breakpoint) | |
445 | ;; Create or update a breakpoint object according to the | |
446 | ;; definition. If the target code is already loaded, note that | |
447 | ;; this may immediately install a trap. | |
448 | (let* ((num (cadr protocol)) | |
449 | (def (caddr protocol)) | |
450 | (behaviour (case (list-ref def 0) | |
451 | ((debug) gds-debug-trap) | |
452 | ((trace) gds-trace-trap) | |
453 | ((trace-subtree) gds-trace-subtree) | |
454 | (else (error "Unsupported behaviour:" | |
455 | (list-ref def 0))))) | |
456 | (bp (hash-ref breakpoints num))) | |
457 | (trc 'existing-bp bp) | |
458 | (if bp | |
459 | (update-breakpoint bp (list-ref def 3)) | |
460 | (begin | |
461 | (set! bp | |
462 | (case (list-ref def 1) | |
463 | ((in) | |
464 | (break-in (string->symbol (list-ref def 3)) | |
465 | (list-ref def 2) | |
466 | #:behaviour behaviour)) | |
467 | ((at) | |
468 | (break-at (list-ref def 2) | |
469 | (car (list-ref def 3)) | |
470 | (cdr (list-ref def 3)) | |
471 | #:behaviour behaviour)) | |
472 | (else | |
473 | (error "Unsupported breakpoint type:" | |
474 | (list-ref def 1))))) | |
475 | ;; Install an observer that will tell the frontend about | |
476 | ;; future changes in this breakpoint's status. | |
477 | (slot-set! bp 'observer | |
478 | (lambda () | |
479 | (write-form `(breakpoint | |
480 | ,num | |
481 | ,@(map trap-description | |
482 | (slot-ref bp 'traps)))))) | |
483 | ;; Add this to the breakpoint hash, and return the | |
484 | ;; breakpoint number and status to the front end. | |
485 | (hash-set! breakpoints num bp))) | |
486 | ;; Call the breakpoint's observer now. | |
487 | ((slot-ref bp 'observer)))) | |
488 | ||
489 | ((delete-breakpoint) | |
490 | (let* ((num (cadr protocol)) | |
491 | (bp (hash-ref breakpoints num))) | |
492 | (if bp | |
493 | (begin | |
494 | (hash-remove! breakpoints num) | |
495 | (delete-breakpoint bp))))) | |
496 | ||
497 | ;;; ((describe-breakpoints) | |
498 | ;;; ;; Describe all breakpoints. | |
499 | ;;; (let ((desc | |
500 | ;;; (with-output-to-string | |
501 | ;;; (lambda () | |
502 | ;;; (hash-fold (lambda (num bp acc) | |
503 | ;;; (format #t | |
504 | ;;; "Breakpoint ~a ~a (~a):\n" | |
505 | ;;; (class-name (class-of bp)) | |
506 | ;;; num | |
507 | ;;; (slot-ref bp 'status)) | |
508 | ;;; (for-each (lambda (trap) | |
509 | ;;; (write (trap-description trap)) | |
510 | ;;; (newline)) | |
511 | ;;; (slot-ref bp 'traps))) | |
512 | ;;; #f | |
513 | ;;; breakpoints))))) | |
514 | ;;; (write-form (list 'info-result desc)))) | |
515 | ||
516 | (else | |
517 | (error "Unexpected protocol:" protocol)))) | |
518 | ||
519 | (define breakpoints (make-hash-table 11)) | |
520 | ||
521 | (define (resolve-module-from-root name) | |
522 | (save-module-excursion | |
523 | (lambda () | |
524 | (set-current-module the-root-module) | |
525 | (resolve-module name)))) | |
526 | ||
527 | (define (gds-eval x m part) | |
528 | ;; Consumer to accept possibly multiple values and present them for | |
529 | ;; Emacs as a list of strings. | |
530 | (define (value-consumer . values) | |
531 | (if (unspecified? (car values)) | |
532 | '() | |
533 | (map (lambda (value) | |
534 | (with-output-to-string (lambda () (write value)))) | |
535 | values))) | |
536 | ;; Now do evaluation. | |
537 | (let ((intro (if part | |
538 | (format #f ";;; Evaluating expression ~A" part) | |
539 | ";;; Evaluating")) | |
540 | (value #f)) | |
541 | (let* ((do-eval (if m | |
542 | (lambda () | |
543 | (display intro) | |
544 | (display " in module ") | |
545 | (write (module-name m)) | |
546 | (newline) | |
547 | (set! value | |
548 | (call-with-values (lambda () | |
549 | (start-stack 'gds-eval-stack | |
550 | (eval x m))) | |
551 | value-consumer))) | |
552 | (lambda () | |
553 | (display intro) | |
554 | (display " in current module ") | |
555 | (write (module-name (current-module))) | |
556 | (newline) | |
557 | (set! value | |
558 | (call-with-values (lambda () | |
559 | (start-stack 'gds-eval-stack | |
560 | (primitive-eval x))) | |
561 | value-consumer))))) | |
562 | (output | |
563 | (with-output-to-string | |
564 | (lambda () | |
565 | (catch #t | |
566 | (lambda () | |
567 | (lazy-catch #t | |
568 | do-eval | |
569 | save-lazy-trap-context-and-rethrow)) | |
570 | (lambda (key . args) | |
571 | (case key | |
572 | ((misc-error signal unbound-variable numerical-overflow) | |
573 | (apply display-error #f | |
574 | (current-output-port) args) | |
575 | (set! value '("error-in-evaluation"))) | |
576 | (else | |
577 | (display "EXCEPTION: ") | |
578 | (display key) | |
579 | (display " ") | |
580 | (write args) | |
581 | (newline) | |
582 | (set! value | |
583 | '("unhandled-exception-in-evaluation")))))))))) | |
584 | (list output value)))) | |
585 | ||
586 | (define last-lazy-trap-context #f) | |
587 | ||
588 | (define (save-lazy-trap-context-and-rethrow key . args) | |
589 | (set! last-lazy-trap-context | |
590 | (throw->trap-context key args save-lazy-trap-context-and-rethrow)) | |
591 | (apply throw key args)) | |
592 | ||
593 | (define (run-utility) | |
ea19f0b3 NJ |
594 | (set-gds-breakpoints) |
595 | (write (getpid)) | |
596 | (newline) | |
597 | (force-output) | |
598 | (named-module-use! '(guile-user) '(ice-9 session)) | |
599 | (gds-accept-input #f)) | |
600 | ||
601 | (define (set-gds-breakpoints) | |
602 | (connect-to-gds) | |
603 | (write-form '(get-breakpoints)) | |
604 | (gds-accept-input #t)) | |
605 | ||
606 | (define-method (trap-description (trap <trap>)) | |
607 | (let loop ((description (list (class-name (class-of trap)))) | |
608 | (next 'installed?)) | |
609 | (case next | |
610 | ((installed?) | |
611 | (loop (if (slot-ref trap 'installed) | |
612 | (cons 'installed description) | |
613 | description) | |
614 | 'conditional?)) | |
615 | ((conditional?) | |
616 | (loop (if (slot-ref trap 'condition) | |
617 | (cons 'conditional description) | |
618 | description) | |
619 | 'skip-count)) | |
620 | ((skip-count) | |
621 | (loop (let ((skip-count (slot-ref trap 'skip-count))) | |
622 | (if (zero? skip-count) | |
623 | description | |
624 | (cons* skip-count 'skip-count description))) | |
625 | 'single-shot?)) | |
626 | ((single-shot?) | |
627 | (loop (if (slot-ref trap 'single-shot) | |
628 | (cons 'single-shot description) | |
629 | description) | |
630 | 'done)) | |
631 | (else | |
632 | (reverse! description))))) | |
633 | ||
634 | (define-method (trap-description (trap <procedure-trap>)) | |
635 | (let ((description (next-method))) | |
636 | (set-cdr! description | |
637 | (cons (procedure-name (slot-ref trap 'procedure)) | |
638 | (cdr description))) | |
639 | description)) | |
640 | ||
641 | (define-method (trap-description (trap <source-trap>)) | |
642 | (let ((description (next-method))) | |
643 | (set-cdr! description | |
644 | (cons (format #f "~s" (slot-ref trap 'expression)) | |
645 | (cdr description))) | |
646 | description)) | |
647 | ||
648 | (define-method (trap-description (trap <location-trap>)) | |
649 | (let ((description (next-method))) | |
650 | (set-cdr! description | |
651 | (cons* (slot-ref trap 'file-regexp) | |
652 | (slot-ref trap 'line) | |
653 | (slot-ref trap 'column) | |
654 | (cdr description))) | |
655 | description)) | |
656 | ||
657 | (define (gds-trace-trap trap-context) | |
658 | (connect-to-gds) | |
659 | (gds-do-trace trap-context) | |
660 | (at-exit (tc:depth trap-context) gds-do-trace)) | |
661 | ||
662 | (define (gds-do-trace trap-context) | |
663 | (write-form (list 'trace | |
664 | (format #f | |
665 | "~3@a: ~a" | |
666 | (trace/stack-real-depth trap-context) | |
667 | (trace/info trap-context))))) | |
668 | ||
669 | (define (gds-trace-subtree trap-context) | |
670 | (connect-to-gds) | |
671 | (gds-do-trace trap-context) | |
672 | (let ((step-trap (make <step-trap> #:behaviour gds-do-trace))) | |
673 | (install-trap step-trap) | |
674 | (at-exit (tc:depth trap-context) | |
675 | (lambda (trap-context) | |
676 | (uninstall-trap step-trap))))) | |
677 | ||
678 | ;;; (ice-9 gds-client) ends here. |