repl: terminal-width by default
[bpt/guile.git] / module / system / repl / command.scm
1 ;;; Repl commands
2
3 ;; Copyright (C) 2001, 2009, 2010, 2011 Free Software Foundation, Inc.
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
18 ;; 02110-1301 USA
19
20 ;;; Code:
21
22 (define-module (system repl command)
23 #:use-module (system base syntax)
24 #:use-module (system base pmatch)
25 #:use-module (system base compile)
26 #:use-module (system repl common)
27 #:use-module (system repl debug)
28 #:use-module (system vm objcode)
29 #:use-module (system vm program)
30 #:use-module (system vm trap-state)
31 #:use-module (system vm vm)
32 #:use-module ((system vm frame) #:select (frame-return-values))
33 #:autoload (system base language) (lookup-language language-reader)
34 #:autoload (system vm trace) (call-with-trace)
35 #:use-module (ice-9 format)
36 #:use-module (ice-9 session)
37 #:use-module (ice-9 documentation)
38 #:use-module (ice-9 and-let-star)
39 #:use-module (ice-9 rdelim)
40 #:use-module (ice-9 control)
41 #:use-module ((ice-9 pretty-print) #:select ((pretty-print . pp)))
42 #:use-module ((system vm inspect) #:select ((inspect . %inspect)))
43 #:use-module (statprof)
44 #:export (meta-command define-meta-command))
45
46 \f
47 ;;;
48 ;;; Meta command interface
49 ;;;
50
51 (define *command-table*
52 '((help (help h) (show) (apropos a) (describe d))
53 (module (module m) (import use) (load l) (reload re) (binding b) (in))
54 (language (language L))
55 (compile (compile c) (compile-file cc)
56 (disassemble x) (disassemble-file xx))
57 (profile (time t) (profile pr) (trace tr))
58 (debug (backtrace bt) (up) (down) (frame fr)
59 (procedure proc) (locals) (error-message error)
60 (break br bp) (break-at-source break-at bs)
61 (step s) (step-instruction si)
62 (next n) (next-instruction ni)
63 (finish)
64 (tracepoint tp)
65 (traps) (delete del) (disable) (enable)
66 (registers regs))
67 (inspect (inspect i) (pretty-print pp))
68 (system (gc) (statistics stat) (option o)
69 (quit q continue cont))))
70
71 (define *show-table*
72 '((show (warranty w) (copying c) (version v))))
73
74 (define terminal-width
75 (let ((set-width #f))
76 (case-lambda
77 (()
78 (or set-width
79 (let ((w (false-if-exception (string->number (getenv "COLUMNS")))))
80 (and (integer? w) (exact? w) (> w 0) w))
81 72))
82 ((w)
83 (if (or (not w) (and (integer? w) (exact? w) (> w 0)))
84 (set! set-width w)
85 (error "Expected a column number (a positive integer)" w))))))
86
87
88 (define (group-name g) (car g))
89 (define (group-commands g) (cdr g))
90
91 (define *command-infos* (make-hash-table))
92 (define (command-name c) (car c))
93 (define (command-abbrevs c) (cdr c))
94 (define (command-info c) (hashq-ref *command-infos* (command-name c)))
95 (define (command-procedure c) (command-info-procedure (command-info c)))
96 (define (command-doc c) (procedure-documentation (command-procedure c)))
97
98 (define (make-command-info proc arguments-reader)
99 (cons proc arguments-reader))
100
101 (define (command-info-procedure info)
102 (car info))
103
104 (define (command-info-arguments-reader info)
105 (cdr info))
106
107 (define (command-usage c)
108 (let ((doc (command-doc c)))
109 (substring doc 0 (string-index doc #\newline))))
110
111 (define (command-summary c)
112 (let* ((doc (command-doc c))
113 (start (1+ (string-index doc #\newline))))
114 (cond ((string-index doc #\newline start)
115 => (lambda (end) (substring doc start end)))
116 (else (substring doc start)))))
117
118 (define (lookup-group name)
119 (assq name *command-table*))
120
121 (define* (lookup-command key #:optional (table *command-table*))
122 (let loop ((groups table) (commands '()))
123 (cond ((and (null? groups) (null? commands)) #f)
124 ((null? commands)
125 (loop (cdr groups) (cdar groups)))
126 ((memq key (car commands)) (car commands))
127 (else (loop groups (cdr commands))))))
128
129 (define* (display-group group #:optional (abbrev? #t))
130 (format #t "~:(~A~) Commands~:[~; [abbrev]~]:~2%" (group-name group) abbrev?)
131 (for-each (lambda (c)
132 (display-summary (command-usage c)
133 (if abbrev? (command-abbrevs c) '())
134 (command-summary c)))
135 (group-commands group))
136 (newline))
137
138 (define (display-command command)
139 (display "Usage: ")
140 (display (command-doc command))
141 (newline))
142
143 (define (display-summary usage abbrevs summary)
144 (let* ((usage-len (string-length usage))
145 (abbrevs (if (pair? abbrevs)
146 (format #f "[,~A~{ ,~A~}]" (car abbrevs) (cdr abbrevs))
147 ""))
148 (abbrevs-len (string-length abbrevs)))
149 (format #t " ,~A~A~A - ~A\n"
150 usage
151 (cond
152 ((> abbrevs-len 32)
153 (error "abbrevs too long" abbrevs))
154 ((> (+ usage-len abbrevs-len) 32)
155 (format #f "~%~v_" (+ 2 (- 32 abbrevs-len))))
156 (else
157 (format #f "~v_" (- 32 abbrevs-len usage-len))))
158 abbrevs
159 summary)))
160
161 (define (read-command repl)
162 (catch #t
163 (lambda () (read))
164 (lambda (key . args)
165 (pmatch args
166 ((,subr ,msg ,args . ,rest)
167 (format #t "Throw to key `~a' while reading command:\n" key)
168 (display-error #f (current-output-port) subr msg args rest))
169 (else
170 (format #t "Throw to key `~a' with args `~s' while reading command.\n"
171 key args)))
172 (force-output)
173 *unspecified*)))
174
175 (define (read-command-arguments c repl)
176 ((command-info-arguments-reader (command-info c)) repl))
177
178 (define (meta-command repl)
179 (let ((command (read-command repl)))
180 (cond
181 ((eq? command *unspecified*)) ; read error, already signalled; pass.
182 ((not (symbol? command))
183 (format #t "Meta-command not a symbol: ~s~%" command))
184 ((lookup-command command)
185 => (lambda (c)
186 (and=> (read-command-arguments c repl)
187 (lambda (args) (apply (command-procedure c) repl args)))))
188 (else
189 (format #t "Unknown meta command: ~A~%" command)))))
190
191 (define (add-meta-command! name category proc argument-reader)
192 (hashq-set! *command-infos* name (make-command-info proc argument-reader))
193 (if category
194 (let ((entry (assq category *command-table*)))
195 (if entry
196 (set-cdr! entry (append (cdr entry) (list (list name))))
197 (set! *command-table*
198 (append *command-table*
199 (list (list category (list name)))))))))
200
201 (define-syntax define-meta-command
202 (syntax-rules ()
203 ((_ ((name category) repl (expression0 ...) . datums) docstring b0 b1 ...)
204 (add-meta-command!
205 'name
206 'category
207 (lambda* (repl expression0 ... . datums)
208 docstring
209 b0 b1 ...)
210 (lambda (repl)
211 (define (handle-read-error form-name key args)
212 (pmatch args
213 ((,subr ,msg ,args . ,rest)
214 (format #t "Throw to key `~a' while reading ~@[argument `~A' of ~]command `~A':\n"
215 key form-name 'name)
216 (display-error #f (current-output-port) subr msg args rest))
217 (else
218 (format #t "Throw to key `~a' with args `~s' while reading ~@[ argument `~A' of ~]command `~A'.\n"
219 key args form-name 'name)))
220 (abort))
221 (% (let* ((expression0
222 (catch #t
223 (lambda ()
224 (repl-reader
225 ""
226 (lambda* (#:optional (port (current-input-port)))
227 ((language-reader (repl-language repl))
228 port (current-module)))))
229 (lambda (k . args)
230 (handle-read-error 'expression0 k args))))
231 ...)
232 (append
233 (list expression0 ...)
234 (catch #t
235 (lambda ()
236 (let ((port (open-input-string (read-line))))
237 (let lp ((out '()))
238 (let ((x (read port)))
239 (if (eof-object? x)
240 (reverse out)
241 (lp (cons x out)))))))
242 (lambda (k . args)
243 (handle-read-error #f k args)))))
244 (lambda (k) #f))))) ; the abort handler
245
246 ((_ ((name category) repl . datums) docstring b0 b1 ...)
247 (define-meta-command ((name category) repl () . datums)
248 docstring b0 b1 ...))
249
250 ((_ (name repl (expression0 ...) . datums) docstring b0 b1 ...)
251 (define-meta-command ((name #f) repl (expression0 ...) . datums)
252 docstring b0 b1 ...))
253
254 ((_ (name repl . datums) docstring b0 b1 ...)
255 (define-meta-command ((name #f) repl () . datums)
256 docstring b0 b1 ...))))
257
258
259 \f
260 ;;;
261 ;;; Help commands
262 ;;;
263
264 (define-meta-command (help repl . args)
265 "help [all | GROUP | [-c] COMMAND]
266 Show help.
267
268 With one argument, tries to look up the argument as a group name, giving
269 help on that group if successful. Otherwise tries to look up the
270 argument as a command, giving help on the command.
271
272 If there is a command whose name is also a group name, use the ,help
273 -c COMMAND form to give help on the command instead of the group.
274
275 Without any argument, a list of help commands and command groups
276 are displayed."
277 (pmatch args
278 (()
279 (display-group (lookup-group 'help))
280 (display "Command Groups:\n\n")
281 (display-summary "help all" #f "List all commands")
282 (for-each (lambda (g)
283 (let* ((name (symbol->string (group-name g)))
284 (usage (string-append "help " name))
285 (header (string-append "List " name " commands")))
286 (display-summary usage #f header)))
287 (cdr *command-table*))
288 (newline)
289 (display
290 "Type `,help -c COMMAND' to show documentation of a particular command.")
291 (newline))
292 ((all)
293 (for-each display-group *command-table*))
294 ((,group) (guard (lookup-group group))
295 (display-group (lookup-group group)))
296 ((,command) (guard (lookup-command command))
297 (display-command (lookup-command command)))
298 ((-c ,command) (guard (lookup-command command))
299 (display-command (lookup-command command)))
300 ((,command)
301 (format #t "Unknown command or group: ~A~%" command))
302 ((-c ,command)
303 (format #t "Unknown command: ~A~%" command))
304 (else
305 (format #t "Bad arguments: ~A~%" args))))
306
307 (define-meta-command (show repl . args)
308 "show [TOPIC]
309 Gives information about Guile.
310
311 With one argument, tries to show a particular piece of information;
312
313 currently supported topics are `warranty' (or `w'), `copying' (or `c'),
314 and `version' (or `v').
315
316 Without any argument, a list of topics is displayed."
317 (pmatch args
318 (()
319 (display-group (car *show-table*) #f)
320 (newline))
321 ((,topic) (guard (lookup-command topic *show-table*))
322 ((command-procedure (lookup-command topic *show-table*)) repl))
323 ((,command)
324 (format #t "Unknown topic: ~A~%" command))
325 (else
326 (format #t "Bad arguments: ~A~%" args))))
327
328 ;;; `warranty', `copying' and `version' are "hidden" meta-commands, only
329 ;;; accessible via `show'. They have an entry in *command-infos* but not
330 ;;; in *command-table*.
331
332 (define-meta-command (warranty repl)
333 "show warranty
334 Details on the lack of warranty."
335 (display *warranty*)
336 (newline))
337
338 (define-meta-command (copying repl)
339 "show copying
340 Show the LGPLv3."
341 (display *copying*)
342 (newline))
343
344 (define-meta-command (version repl)
345 "show version
346 Version information."
347 (display *version*)
348 (newline))
349
350 (define-meta-command (apropos repl regexp)
351 "apropos REGEXP
352 Find bindings/modules/packages."
353 (apropos (->string regexp)))
354
355 (define-meta-command (describe repl (form))
356 "describe OBJ
357 Show description/documentation."
358 (display (object-documentation (repl-eval repl (repl-parse repl form))))
359 (newline))
360
361 (define-meta-command (option repl . args)
362 "option [KEY VALUE]
363 List/show/set options."
364 (pmatch args
365 (()
366 (for-each (lambda (spec)
367 (format #t " ~A~24t~A\n" (car spec) (cadr spec)))
368 (repl-options repl)))
369 ((,key)
370 (display (repl-option-ref repl key))
371 (newline))
372 ((,key ,val)
373 (repl-option-set! repl key val))))
374
375 (define-meta-command (quit repl)
376 "quit
377 Quit this session."
378 (throw 'quit))
379
380 \f
381 ;;;
382 ;;; Module commands
383 ;;;
384
385 (define-meta-command (module repl . args)
386 "module [MODULE]
387 Change modules / Show current module."
388 (pmatch args
389 (() (puts (module-name (current-module))))
390 ((,mod-name) (guard (list? mod-name))
391 (set-current-module (resolve-module mod-name)))
392 (,mod-name (set-current-module (resolve-module mod-name)))))
393
394 (define-meta-command (import repl . args)
395 "import [MODULE ...]
396 Import modules / List those imported."
397 (let ()
398 (define (use name)
399 (let ((mod (resolve-interface name)))
400 (if mod
401 (module-use! (current-module) mod)
402 (format #t "No such module: ~A~%" name))))
403 (if (null? args)
404 (for-each puts (map module-name (module-uses (current-module))))
405 (for-each use args))))
406
407 (define-meta-command (load repl file)
408 "load FILE
409 Load a file in the current module."
410 (load (->string file)))
411
412 (define-meta-command (reload repl . args)
413 "reload [MODULE]
414 Reload the given module, or the current module if none was given."
415 (pmatch args
416 (() (reload-module (current-module)))
417 ((,mod-name) (guard (list? mod-name))
418 (reload-module (resolve-module mod-name)))
419 (,mod-name (reload-module (resolve-module mod-name)))))
420
421 (define-meta-command (binding repl)
422 "binding
423 List current bindings."
424 (module-for-each (lambda (k v) (format #t "~23A ~A\n" k v))
425 (current-module)))
426
427 (define-meta-command (in repl module command-or-expression . args)
428 "in MODULE COMMAND-OR-EXPRESSION
429 Evaluate an expression or command in the context of module."
430 (let ((m (resolve-module module #:ensure #f)))
431 (if m
432 (pmatch command-or-expression
433 (('unquote ,command) (guard (lookup-command command))
434 (save-module-excursion
435 (lambda ()
436 (set-current-module m)
437 (apply (command-procedure (lookup-command command)) repl args))))
438 (,expression
439 (guard (null? args))
440 (repl-print repl (eval expression m)))
441 (else
442 (format #t "Invalid arguments to `in': expected a single expression or a command.\n")))
443 (format #t "No such module: ~s\n" module))))
444
445 \f
446 ;;;
447 ;;; Language commands
448 ;;;
449
450 (define-meta-command (language repl name)
451 "language LANGUAGE
452 Change languages."
453 (let ((lang (lookup-language name))
454 (cur (repl-language repl)))
455 (format #t "Happy hacking with ~a! To switch back, type `,L ~a'.\n"
456 (language-title lang) (language-name cur))
457 (set! (repl-language repl) lang)))
458
459 \f
460 ;;;
461 ;;; Compile commands
462 ;;;
463
464 (define-meta-command (compile repl (form))
465 "compile EXP
466 Generate compiled code."
467 (let ((x (repl-compile repl (repl-parse repl form))))
468 (cond ((objcode? x) (guile:disassemble x))
469 (else (repl-print repl x)))))
470
471 (define-meta-command (compile-file repl file . opts)
472 "compile-file FILE
473 Compile a file."
474 (compile-file (->string file) #:opts opts))
475
476 (define (guile:disassemble x)
477 ((@ (language assembly disassemble) disassemble) x))
478
479 (define-meta-command (disassemble repl (form))
480 "disassemble EXP
481 Disassemble a compiled procedure."
482 (guile:disassemble (repl-eval repl (repl-parse repl form))))
483
484 (define-meta-command (disassemble-file repl file)
485 "disassemble-file FILE
486 Disassemble a file."
487 (guile:disassemble (load-objcode (->string file))))
488
489 \f
490 ;;;
491 ;;; Profile commands
492 ;;;
493
494 (define-meta-command (time repl (form))
495 "time EXP
496 Time execution."
497 (let* ((gc-start (gc-run-time))
498 (tms-start (times))
499 (result (repl-eval repl (repl-parse repl form)))
500 (tms-end (times))
501 (gc-end (gc-run-time)))
502 (define (get proc start end)
503 (exact->inexact (/ (- (proc end) (proc start)) internal-time-units-per-second)))
504 (repl-print repl result)
505 (display "clock utime stime cutime cstime gctime\n")
506 (format #t "~5,2F ~5,2F ~5,2F ~6,2F ~6,2F ~6,2F\n"
507 (get tms:clock tms-start tms-end)
508 (get tms:utime tms-start tms-end)
509 (get tms:stime tms-start tms-end)
510 (get tms:cutime tms-start tms-end)
511 (get tms:cstime tms-start tms-end)
512 (get identity gc-start gc-end))
513 result))
514
515 (define-meta-command (profile repl (form) . opts)
516 "profile EXP
517 Profile execution."
518 ;; FIXME opts
519 (apply statprof
520 (repl-prepare-eval-thunk repl (repl-parse repl form))
521 opts))
522
523 (define-meta-command (trace repl (form) . opts)
524 "trace EXP
525 Trace execution."
526 ;; FIXME: doc options, or somehow deal with them better
527 (apply call-with-trace
528 (repl-prepare-eval-thunk repl (repl-parse repl form))
529 opts))
530
531 \f
532 ;;;
533 ;;; Debug commands
534 ;;;
535
536 (define-syntax define-stack-command
537 (lambda (x)
538 (syntax-case x ()
539 ((_ (name repl . args) docstring body body* ...)
540 #`(define-meta-command (name repl . args)
541 docstring
542 (let ((debug (repl-debug repl)))
543 (if debug
544 (letrec-syntax
545 ((#,(datum->syntax #'repl 'frames)
546 (identifier-syntax (debug-frames debug)))
547 (#,(datum->syntax #'repl 'message)
548 (identifier-syntax (debug-error-message debug)))
549 (#,(datum->syntax #'repl 'for-trap?)
550 (identifier-syntax (debug-for-trap? debug)))
551 (#,(datum->syntax #'repl 'index)
552 (identifier-syntax
553 (id (debug-index debug))
554 ((set! id exp) (set! (debug-index debug) exp))))
555 (#,(datum->syntax #'repl 'cur)
556 (identifier-syntax
557 (vector-ref #,(datum->syntax #'repl 'frames)
558 #,(datum->syntax #'repl 'index)))))
559 body body* ...)
560 (format #t "Nothing to debug.~%"))))))))
561
562 (define-stack-command (backtrace repl #:optional count
563 #:key (width (terminal-width)) full?)
564 "backtrace [COUNT] [#:width W] [#:full? F]
565 Print a backtrace.
566
567 Print a backtrace of all stack frames, or innermost COUNT frames.
568 If COUNT is negative, the last COUNT frames will be shown."
569 (print-frames frames
570 #:count count
571 #:width width
572 #:full? full?
573 #:for-trap? for-trap?))
574
575 (define-stack-command (up repl #:optional (count 1))
576 "up [COUNT]
577 Select a calling stack frame.
578
579 Select and print stack frames that called this one.
580 An argument says how many frames up to go."
581 (cond
582 ((or (not (integer? count)) (<= count 0))
583 (format #t "Invalid argument to `up': expected a positive integer for COUNT.~%"))
584 ((>= (+ count index) (vector-length frames))
585 (cond
586 ((= index (1- (vector-length frames)))
587 (format #t "Already at outermost frame.\n"))
588 (else
589 (set! index (1- (vector-length frames)))
590 (print-frame cur #:index index
591 #:next-source? (and (zero? index) for-trap?)))))
592 (else
593 (set! index (+ count index))
594 (print-frame cur #:index index
595 #:next-source? (and (zero? index) for-trap?)))))
596
597 (define-stack-command (down repl #:optional (count 1))
598 "down [COUNT]
599 Select a called stack frame.
600
601 Select and print stack frames called by this one.
602 An argument says how many frames down to go."
603 (cond
604 ((or (not (integer? count)) (<= count 0))
605 (format #t "Invalid argument to `down': expected a positive integer for COUNT.~%"))
606 ((< (- index count) 0)
607 (cond
608 ((zero? index)
609 (format #t "Already at innermost frame.\n"))
610 (else
611 (set! index 0)
612 (print-frame cur #:index index #:next-source? for-trap?))))
613 (else
614 (set! index (- index count))
615 (print-frame cur #:index index
616 #:next-source? (and (zero? index) for-trap?)))))
617
618 (define-stack-command (frame repl #:optional idx)
619 "frame [IDX]
620 Show a frame.
621
622 Show the selected frame.
623 With an argument, select a frame by index, then show it."
624 (cond
625 (idx
626 (cond
627 ((or (not (integer? idx)) (< idx 0))
628 (format #t "Invalid argument to `frame': expected a non-negative integer for IDX.~%"))
629 ((< idx (vector-length frames))
630 (set! index idx)
631 (print-frame cur #:index index
632 #:next-source? (and (zero? index) for-trap?)))
633 (else
634 (format #t "No such frame.~%"))))
635 (else (print-frame cur #:index index
636 #:next-source? (and (zero? index) for-trap?)))))
637
638 (define-stack-command (procedure repl)
639 "procedure
640 Print the procedure for the selected frame."
641 (repl-print repl (frame-procedure cur)))
642
643 (define-stack-command (locals repl #:key (width (terminal-width)))
644 "locals
645 Show local variables.
646
647 Show locally-bound variables in the selected frame."
648 (print-locals cur #:width width))
649
650 (define-stack-command (error-message repl)
651 "error-message
652 Show error message.
653
654 Display the message associated with the error that started the current
655 debugging REPL."
656 (format #t "~a~%" (if (string? message) message "No error message")))
657
658 (define-meta-command (break repl (form))
659 "break PROCEDURE
660 Break on calls to PROCEDURE.
661
662 Starts a recursive prompt when PROCEDURE is called."
663 (let ((proc (repl-eval repl (repl-parse repl form))))
664 (if (not (procedure? proc))
665 (error "Not a procedure: ~a" proc)
666 (let ((idx (add-trap-at-procedure-call! proc)))
667 (format #t "Trap ~a: ~a.~%" idx (trap-name idx))))))
668
669 (define-meta-command (break-at-source repl file line)
670 "break-at-source FILE LINE
671 Break when control reaches the given source location.
672
673 Starts a recursive prompt when control reaches line LINE of file FILE.
674 Note that the given source location must be inside a procedure."
675 (let ((file (if (symbol? file) (symbol->string file) file)))
676 (let ((idx (add-trap-at-source-location! file line)))
677 (format #t "Trap ~a: ~a.~%" idx (trap-name idx)))))
678
679 (define (repl-pop-continuation-resumer repl msg)
680 ;; Capture the dynamic environment with this prompt thing. The
681 ;; result is a procedure that takes a frame.
682 (% (call-with-values
683 (lambda ()
684 (abort
685 (lambda (k)
686 ;; Call frame->stack-vector before reinstating the
687 ;; continuation, so that we catch the %stacks fluid at
688 ;; the time of capture.
689 (lambda (frame)
690 (k frame
691 (frame->stack-vector
692 (frame-previous frame)))))))
693 (lambda (from stack)
694 (format #t "~a~%" msg)
695 (let ((vals (frame-return-values from)))
696 (if (null? vals)
697 (format #t "No return values.~%")
698 (begin
699 (format #t "Return values:~%")
700 (for-each (lambda (x) (repl-print repl x)) vals))))
701 ((module-ref (resolve-interface '(system repl repl)) 'start-repl)
702 #:debug (make-debug stack 0 msg #t))))))
703
704 (define-stack-command (finish repl)
705 "finish
706 Run until the current frame finishes.
707
708 Resume execution, breaking when the current frame finishes."
709 (let ((handler (repl-pop-continuation-resumer
710 repl (format #f "Return from ~a" cur))))
711 (add-ephemeral-trap-at-frame-finish! cur handler)
712 (throw 'quit)))
713
714 (define (repl-next-resumer msg)
715 ;; Capture the dynamic environment with this prompt thing. The
716 ;; result is a procedure that takes a frame.
717 (% (let ((stack (abort
718 (lambda (k)
719 ;; Call frame->stack-vector before reinstating the
720 ;; continuation, so that we catch the %stacks fluid
721 ;; at the time of capture.
722 (lambda (frame)
723 (k (frame->stack-vector frame)))))))
724 (format #t "~a~%" msg)
725 ((module-ref (resolve-interface '(system repl repl)) 'start-repl)
726 #:debug (make-debug stack 0 msg #t)))))
727
728 (define-stack-command (step repl)
729 "step
730 Step until control reaches a different source location.
731
732 Step until control reaches a different source location."
733 (let ((msg (format #f "Step into ~a" cur)))
734 (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
735 #:into? #t #:instruction? #f)
736 (throw 'quit)))
737
738 (define-stack-command (step-instruction repl)
739 "step-instruction
740 Step until control reaches a different instruction.
741
742 Step until control reaches a different VM instruction."
743 (let ((msg (format #f "Step into ~a" cur)))
744 (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
745 #:into? #t #:instruction? #t)
746 (throw 'quit)))
747
748 (define-stack-command (next repl)
749 "next
750 Step until control reaches a different source location in the current frame.
751
752 Step until control reaches a different source location in the current frame."
753 (let ((msg (format #f "Step into ~a" cur)))
754 (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
755 #:into? #f #:instruction? #f)
756 (throw 'quit)))
757
758 (define-stack-command (next-instruction repl)
759 "next-instruction
760 Step until control reaches a different instruction in the current frame.
761
762 Step until control reaches a different VM instruction in the current frame."
763 (let ((msg (format #f "Step into ~a" cur)))
764 (add-ephemeral-stepping-trap! cur (repl-next-resumer msg)
765 #:into? #f #:instruction? #t)
766 (throw 'quit)))
767
768 (define-meta-command (tracepoint repl (form))
769 "tracepoint PROCEDURE
770 Add a tracepoint to PROCEDURE.
771
772 A tracepoint will print out the procedure and its arguments, when it is
773 called, and its return value(s) when it returns."
774 (let ((proc (repl-eval repl (repl-parse repl form))))
775 (if (not (procedure? proc))
776 (error "Not a procedure: ~a" proc)
777 (let ((idx (add-trace-at-procedure-call! proc)))
778 (format #t "Trap ~a: ~a.~%" idx (trap-name idx))))))
779
780 (define-meta-command (traps repl)
781 "traps
782 Show the set of currently attached traps.
783
784 Show the set of currently attached traps (breakpoints and tracepoints)."
785 (let ((traps (list-traps)))
786 (if (null? traps)
787 (format #t "No traps set.~%")
788 (for-each (lambda (idx)
789 (format #t " ~a: ~a~a~%"
790 idx (trap-name idx)
791 (if (trap-enabled? idx) "" " (disabled)")))
792 traps))))
793
794 (define-meta-command (delete repl idx)
795 "delete IDX
796 Delete a trap.
797
798 Delete a trap."
799 (if (not (integer? idx))
800 (error "expected a trap index (a non-negative integer)" idx)
801 (delete-trap! idx)))
802
803 (define-meta-command (disable repl idx)
804 "disable IDX
805 Disable a trap.
806
807 Disable a trap."
808 (if (not (integer? idx))
809 (error "expected a trap index (a non-negative integer)" idx)
810 (disable-trap! idx)))
811
812 (define-meta-command (enable repl idx)
813 "enable IDX
814 Enable a trap.
815
816 Enable a trap."
817 (if (not (integer? idx))
818 (error "expected a trap index (a non-negative integer)" idx)
819 (enable-trap! idx)))
820
821 (define-stack-command (registers repl)
822 "registers
823 Print registers.
824
825 Print the registers of the current frame."
826 (print-registers cur))
827
828 (define-meta-command (width repl #:optional x)
829 "width [X]
830 Set debug output width.
831
832 Set the number of screen columns in the output from `backtrace' and
833 `locals'."
834 (terminal-width x)
835 (format #t "Set screen width to ~a columns.~%" (terminal-width)))
836
837
838 \f
839 ;;;
840 ;;; Inspection commands
841 ;;;
842
843 (define-meta-command (inspect repl (form))
844 "inspect EXP
845 Inspect the result(s) of evaluating EXP."
846 (call-with-values (repl-prepare-eval-thunk repl (repl-parse repl form))
847 (lambda args
848 (for-each %inspect args))))
849
850 (define-meta-command (pretty-print repl (form))
851 "pretty-print EXP
852 Pretty-print the result(s) of evaluating EXP."
853 (call-with-values (repl-prepare-eval-thunk repl (repl-parse repl form))
854 (lambda args
855 (for-each
856 (lambda (x)
857 (run-hook before-print-hook x)
858 (pp x))
859 args))))
860
861 \f
862 ;;;
863 ;;; System commands
864 ;;;
865
866 (define-meta-command (gc repl)
867 "gc
868 Garbage collection."
869 (gc))
870
871 (define-meta-command (statistics repl)
872 "statistics
873 Display statistics."
874 (let ((this-tms (times))
875 (this-gcs (gc-stats))
876 (last-tms (repl-tm-stats repl))
877 (last-gcs (repl-gc-stats repl)))
878 ;; GC times
879 (let ((this-times (assq-ref this-gcs 'gc-times))
880 (last-times (assq-ref last-gcs 'gc-times)))
881 (display-diff-stat "GC times:" #t this-times last-times "times")
882 (newline))
883 ;; Memory size
884 (let ((this-heap (assq-ref this-gcs 'heap-size))
885 (this-free (assq-ref this-gcs 'heap-free-size)))
886 (display-stat-title "Memory size:" "current" "limit")
887 (display-stat "heap" #f (- this-heap this-free) this-heap "bytes")
888 (newline))
889 ;; Cells collected
890 (let ((this-alloc (assq-ref this-gcs 'heap-total-allocated))
891 (last-alloc (assq-ref last-gcs 'heap-total-allocated)))
892 (display-stat-title "Bytes allocated:" "diff" "total")
893 (display-diff-stat "allocated" #f this-alloc last-alloc "bytes")
894 (newline))
895 ;; GC time taken
896 (let ((this-total (assq-ref this-gcs 'gc-time-taken))
897 (last-total (assq-ref last-gcs 'gc-time-taken)))
898 (display-stat-title "GC time taken:" "diff" "total")
899 (display-time-stat "total" this-total last-total)
900 (newline))
901 ;; Process time spent
902 (let ((this-utime (tms:utime this-tms))
903 (last-utime (tms:utime last-tms))
904 (this-stime (tms:stime this-tms))
905 (last-stime (tms:stime last-tms))
906 (this-cutime (tms:cutime this-tms))
907 (last-cutime (tms:cutime last-tms))
908 (this-cstime (tms:cstime this-tms))
909 (last-cstime (tms:cstime last-tms)))
910 (display-stat-title "Process time spent:" "diff" "total")
911 (display-time-stat "user" this-utime last-utime)
912 (display-time-stat "system" this-stime last-stime)
913 (display-time-stat "child user" this-cutime last-cutime)
914 (display-time-stat "child system" this-cstime last-cstime)
915 (newline))
916 ;; Save statistics
917 ;; Save statistics
918 (set! (repl-tm-stats repl) this-tms)
919 (set! (repl-gc-stats repl) this-gcs)))
920
921 (define (display-stat title flag field1 field2 unit)
922 (let ((fmt (format #f "~~20~AA ~~10@A /~~10@A ~~A~~%" (if flag "" "@"))))
923 (format #t fmt title field1 field2 unit)))
924
925 (define (display-stat-title title field1 field2)
926 (display-stat title #t field1 field2 ""))
927
928 (define (display-diff-stat title flag this last unit)
929 (display-stat title flag (- this last) this unit))
930
931 (define (display-time-stat title this last)
932 (define (conv num)
933 (format #f "~10,2F" (exact->inexact (/ num internal-time-units-per-second))))
934 (display-stat title #f (conv (- this last)) (conv this) "s"))
935
936 (define (display-mips-stat title this-time this-clock last-time last-clock)
937 (define (mips time clock)
938 (if (= time 0) "----" (format #f "~10,2F" (/ clock time 1000000.0))))
939 (display-stat title #f
940 (mips (- this-time last-time) (- this-clock last-clock))
941 (mips this-time this-clock) "mips"))