narrowing stacks to prompts; backtrace shows frames from start-stack
[bpt/guile.git] / module / system / vm / debug.scm
CommitLineData
af988bbf 1;;; Guile VM debugging facilities
ac99cb0c 2
a589525d 3;;; Copyright (C) 2001, 2009, 2010 Free Software Foundation, Inc.
587cd3bf
LC
4;;;
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 3 of the License, or (at your option) any later version.
9;;;
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.
14;;;
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
ac99cb0c
KN
18
19;;; Code:
20
af988bbf 21(define-module (system vm debug)
391d2902 22 #:use-module (system base pmatch)
8239263f 23 #:use-module (system base syntax)
1a1a10d3
AW
24 #:use-module (system vm vm)
25 #:use-module (system vm frame)
f6fe5fe2
AW
26 #:use-module (ice-9 rdelim)
27 #:use-module (ice-9 pretty-print)
1a1a10d3 28 #:use-module (ice-9 format)
e106eca6 29 #:use-module ((system vm inspect) #:select ((inspect . %inspect)))
f6fe5fe2 30 #:use-module (system vm program)
1ad7fef5 31 #:export (run-debugger debug-pre-unwind-handler))
af988bbf
KN
32
33\f
f6fe5fe2
AW
34(define (reverse-hashq h)
35 (let ((ret (make-hash-table)))
36 (hash-for-each
37 (lambda (k v)
38 (hashq-set! ret v (cons k (hashq-ref ret v '()))))
39 h)
40 ret))
41
42(define (catch-bad-arguments thunk bad-args-thunk)
43 (catch 'wrong-number-of-args
44 (lambda ()
45 (catch 'keyword-argument-error
46 thunk
47 (lambda (k . args)
48 (bad-args-thunk))))
49 (lambda (k . args)
50 (bad-args-thunk))))
51
52(define (read-args prompt)
53 (define (read* reader)
54 (repl-reader prompt reader))
55 (define (next)
56 (read* read-char))
57 (define (cmd chr)
58 (cond
59 ((eof-object? chr) (list chr))
60 ((char=? chr #\newline) (cmd (next)))
61 ((char-whitespace? chr) (cmd (next)))
62 (else
63 (unread-char chr)
64 (let ((tok (read* read)))
65 (args (list tok) (next))))))
66 (define (args out chr)
67 (cond
68 ((eof-object? chr) (reverse out))
69 ((char=? chr #\newline) (reverse out))
70 ((char-whitespace? chr) (args out (next)))
71 (else
72 (unread-char chr)
73 (let ((tok (read* read)))
74 (args (cons tok out) (next))))))
75 (cmd (next)))
76
0c2a05c3
AW
77(define* (print-locals frame #:optional (port (current-output-port))
78 #:key (width 72) (per-line-prefix ""))
79 (let ((bindings (frame-bindings frame)))
80 (cond
81 ((null? bindings)
82 (format port "~aNo local variables.~%" per-line-prefix))
83 (else
84 (format port "~aLocal variables:~%" per-line-prefix)
85 (for-each
86 (lambda (binding)
87 (format port "~a~4d ~a~:[~; (boxed)~] = ~v:@y\n"
88 per-line-prefix
89 (binding:index binding)
90 (binding:name binding)
91 (binding:boxed? binding)
92 width
93 (let ((x (frame-local-ref frame (binding:index binding))))
94 (if (binding:boxed? binding)
95 (variable-ref x)
96 x))))
97 (frame-bindings frame))))))
98
06dcb9df
AW
99(define* (print-frames frames
100 #:optional (port (current-output-port))
101 #:key (width 72) (full? #f) (forward? #f) count)
102 (let* ((len (vector-length frames))
103 (lower-idx (if (or (not count) (positive? count))
104 0
105 (max 0 (+ len count))))
106 (upper-idx (if (and count (negative? count))
107 (1- len)
108 (1- (if count (min count len) len))))
109 (inc (if forward? 1 -1)))
110 (let lp ((i (if forward? lower-idx upper-idx))
111 (last-file ""))
112 (if (<= lower-idx i upper-idx)
113 (let* ((frame (vector-ref frames i))
114 (source (frame-source frame))
115 (file (and source
116 (or (source:file source)
117 "current input")))
118 (line (and=> source source:line)))
119 (if (and file (not (equal? file last-file)))
120 (format port "~&In ~a:~&" file))
121 (format port "~:[~*~6_~;~5d:~]~3d ~v:@y~%" line line
122 i width (frame-call-representation frame))
123 (if full?
124 (print-locals frame #:width width
125 #:per-line-prefix " "))
126 (lp (+ i inc) (or file last-file)))))))
0c2a05c3
AW
127
128
af988bbf
KN
129;;;
130;;; Debugger
131;;;
132
1ad7fef5 133(define-record <debugger> vm level breakpoints module)
af988bbf 134
1ad7fef5
AW
135(define (make-debugger-module)
136 (let ((m (make-fresh-user-module)))
137 m))
138
139(define vm-debugger
140 (let ((prop (make-object-property)))
141 (lambda (vm)
142 (or (prop vm)
f6fe5fe2 143 (let ((debugger (make-debugger vm 0 '() (make-debugger-module))))
1ad7fef5
AW
144 (set! (prop vm) debugger)
145 debugger)))))
146
06dcb9df 147(define* (run-debugger stack frames i #:optional (vm (the-vm)))
1ad7fef5
AW
148 (let* ((db (vm-debugger vm))
149 (level (debugger-level db)))
f6fe5fe2
AW
150 (dynamic-wind
151 (lambda () (set! (debugger-level db) (1+ level)))
06dcb9df 152 (lambda () (debugger-repl db stack frames i))
f6fe5fe2 153 (lambda () (set! (debugger-level db) level)))))
1ad7fef5 154
06dcb9df
AW
155(define (debugger-repl db stack frames index)
156 (let ((top (vector-ref frames 0))
157 (cur (vector-ref frames index))
e106eca6
AW
158 (level (debugger-level db))
159 (last #f))
d7a4096d 160 (define (frame-at-index idx)
06dcb9df
AW
161 (and (< idx (vector-length frames))
162 (vector-ref frames idx)))
d7a4096d 163 (define (show-frame)
1c5e8122
AW
164 ;; #2 0x009600e0 in do_std_select (args=0xbfffd9e0) at threads.c:1668
165 ;; 1668 select (select_args->nfds,
166 (format #t "#~2a 0x~8,'0x in ~60@y~%"
167 index
168 (frame-instruction-pointer cur)
169 (frame-call-representation cur)))
f6fe5fe2
AW
170
171 (define-syntax define-command
172 (syntax-rules ()
173 ((_ ((mod cname alias ...) . args) body ...)
174 (define cname
175 (let ((c (lambda* args body ...)))
176 (set-procedure-property! c 'name 'cname)
177 (module-define! mod 'cname c)
178 (module-add! mod 'alias (module-local-variable mod 'cname))
179 ...
180 c)))))
181
182 (let ((commands (make-module)))
183 (define (prompt)
184 (format #f "~a~a debug> "
185 (if (= level 1)
186 ""
187 (format #f "~a:" level))
188 index))
189
190 (define (print* . vals)
191 (define (print x)
192 (run-hook before-print-hook x)
e106eca6 193 (set! last x)
f6fe5fe2
AW
194 (pretty-print x))
195 (if (and (pair? vals)
196 (not (and (null? (cdr vals))
197 (unspecified? (car vals)))))
198 (for-each print vals)))
199
0c2a05c3
AW
200 (define-command ((commands backtrace bt) #:optional count
201 #:key (width 72) full?)
06dcb9df
AW
202 "Print a backtrace of all stack frames, or innermost COUNT frames.
203If COUNT is negative, the last COUNT frames will be shown."
204 (print-frames frames
205 #:count count
0c2a05c3
AW
206 #:width width
207 #:full? full?))
d7a4096d
AW
208
209 (define-command ((commands up) #:optional (count 1))
210 "Select and print stack frames that called this one.
211An argument says how many frames up to go"
06dcb9df
AW
212 (cond
213 ((or (not (integer? count)) (<= count 0))
214 (format #t "Invalid argument to `up': expected a positive integer for COUNT.~%"))
215 ((>= (+ count index) (vector-length frames))
216 (cond
217 ((= index (1- (vector-length frames)))
218 (format #t "Already at outermost frame.\n"))
219 (else
220 (set! index (1- (vector-length frames)))
221 (set! cur (vector-ref frames index))
222 (show-frame))))
223 (else
224 (set! index (+ count index))
225 (set! cur (vector-ref frames index))
226 (show-frame))))
227
d7a4096d
AW
228 (define-command ((commands down) #:optional (count 1))
229 "Select and print stack frames called by this one.
230An argument says how many frames down to go"
231 (cond
232 ((or (not (integer? count)) (<= count 0))
233 (format #t "Invalid argument to `down': expected a positive integer for COUNT.~%"))
06dcb9df
AW
234 ((< (- index count) 0)
235 (cond
236 ((zero? index)
237 (format #t "Already at innermost frame.\n"))
238 (else
239 (set! index 0)
240 (set! cur (vector-ref frames index))
241 (show-frame))))
d7a4096d 242 (else
06dcb9df
AW
243 (set! index (- index count))
244 (set! cur (vector-ref frames index))
d7a4096d 245 (show-frame))))
06dcb9df 246
d7a4096d
AW
247 (define-command ((commands frame f) #:optional idx)
248 "Show the selected frame.
249With an argument, select a frame by index, then show it."
250 (cond
251 (idx
252 (cond
253 ((or (not (integer? idx)) (< idx 0))
254 (format #t "Invalid argument to `frame': expected a non-negative integer for IDX.~%"))
255 ((frame-at-index idx)
256 => (lambda (f)
257 (set! cur f)
258 (set! index idx)
259 (show-frame)))
260 (else
261 (format #t "No such frame.~%"))))
262 (else (show-frame))))
263
e106eca6
AW
264 (define-command ((commands procedure proc))
265 "Print the procedure for the selected frame."
266 (print* (frame-procedure cur)))
267
268 (define-command ((commands inspect i))
269 "Launch the inspector on the last-printed object."
270 (%inspect last))
271
1c5e8122
AW
272 (define-command ((commands locals))
273 "Show locally-bound variables in the selected frame."
0c2a05c3 274 (print-locals cur))
1c5e8122 275
f6fe5fe2
AW
276 (define-command ((commands quit q continue cont c))
277 "Quit the debugger and let the program continue executing."
278 (throw 'quit))
279
f6fe5fe2
AW
280 (define-command ((commands help h ?) #:optional cmd)
281 "Show this help message."
282 (let ((rhash (reverse-hashq (module-obarray commands))))
283 (define (help-cmd cmd)
284 (let* ((v (module-local-variable commands cmd))
285 (p (variable-ref v))
286 (canonical-name (procedure-name p)))
287 ;; la la la
288 (format #t "~a~{ ~:@(~a~)~}~?~%~a~&~%"
289 canonical-name (program-lambda-list p)
290 "~#[~:;~40t(aliases: ~@{~a~^, ~})~]"
291 (delq canonical-name (hashq-ref rhash v))
292 (procedure-documentation p))))
293 (cond
294 (cmd
295 (cond
296 ((and (symbol? cmd) (module-local-variable commands cmd))
297 (help-cmd cmd))
298 (else
299 (format #t "Invalid command ~s.~%" cmd)
300 (format #t "Try `help' for a list of commands~%"))))
301 (else
302 (let ((names (sort
303 (hash-map->list
304 (lambda (k v)
305 (procedure-name (variable-ref k)))
306 rhash)
307 (lambda (x y)
308 (string<? (symbol->string x)
309 (symbol->string y))))))
310 (format #t "Available commands:~%~%")
311 (for-each help-cmd names))))))
312
313 (define (handle cmd . args)
314 (cond
315 ((and (symbol? cmd)
316 (module-local-variable commands cmd))
317 => (lambda (var)
318 (let ((proc (variable-ref var)))
319 (catch-bad-arguments
320 (lambda ()
321 (apply (variable-ref var) args))
322 (lambda ()
d7a4096d
AW
323 (format (current-error-port)
324 "Invalid arguments to ~a. Try `help ~a'.~%"
325 (procedure-name proc) (procedure-name proc)))))))
f6fe5fe2 326 ((and (integer? cmd) (exact? cmd))
2d8c757c 327 (frame cmd))
f6fe5fe2
AW
328 ((eof-object? cmd)
329 (newline)
330 (throw 'quit))
331 (else
332 (format (current-error-port)
333 "~&Unknown command: ~a. Try `help'.~%" cmd)
334 *unspecified*)))
335
336 (catch 'quit
337 (lambda ()
338 (let loop ()
d7a4096d
AW
339 (apply
340 handle
341 (save-module-excursion
342 (lambda ()
343 (set-current-module commands)
344 (read-args prompt))))
f6fe5fe2
AW
345 (loop)))
346 (lambda (k . args)
347 (apply values args))))))
1ad7fef5 348
d7a4096d 349
441891f3 350;; TODO:
1ad7fef5
AW
351;;
352;; eval expression in context of frame
1ad7fef5 353;; set local variable in frame
1ad7fef5
AW
354;; step until next instruction
355;; step until next function call/return
356;; step until return from frame
357;; step until different source line
358;; step until greater source line
359;; watch expression
360;; break on a function
361;; remove breakpoints
362;; set printing width
363;; display a truncated backtrace
364;; go to a frame by index
365;; (reuse gdb commands perhaps)
1ad7fef5
AW
366;; disassemble a function
367;; disassemble the current function
368;; inspect any object
369;; hm, trace via reassigning global vars. tricksy.
370;; (state associated with vm ?)
ac99cb0c 371
06dcb9df
AW
372(define (stack->vector stack)
373 (let* ((len (stack-length stack))
374 (v (make-vector len)))
375 (if (positive? len)
376 (let lp ((i 0) (frame (stack-ref stack 0)))
377 (if (< i len)
378 (begin
379 (vector-set! v i frame)
380 (lp (1+ i) (frame-previous frame))))))
381 v))
382
1ad7fef5 383(define (debug-pre-unwind-handler key . args)
06dcb9df
AW
384 ;; Narrow the stack by three frames: make-stack, this one, and the throw
385 ;; handler.
386 (cond
387 ((make-stack #t 3) =>
388 (lambda (stack)
389 (pmatch args
390 ((,subr ,msg ,args . ,rest)
391 (format #t "Throw to key `~a':\n" key)
392 (display-error stack (current-output-port) subr msg args rest))
393 (else
394 (format #t "Throw to key `~a' with args `~s'." key args)))
395 (format #t "Entering the debugger. Type `bt' for a backtrace or `c' to continue.\n")
396 (run-debugger stack
397 (stack->vector
398 ;; by default, narrow to the most recent start-stack
399 (make-stack (stack-ref stack 0) 0
400 (and (pair? (fluid-ref %stacks))
401 (cdar (fluid-ref %stacks)))))
402 0))))
37e9bc8a 403 (save-stack debug-pre-unwind-handler)
1ad7fef5 404 (apply throw key args))