3 ;; Copyright (C) 2001, 2009, 2010 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA
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 #:autoload (system base language) (lookup-language language-reader)
33 #:autoload (system vm trace) (vm-trace)
34 #:autoload (system vm profile) (vm-profile)
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))
48 ;;; Meta command interface
51 (define *command-table*
52 '((help (help h) (show s) (apropos a) (describe d))
53 (module (module m) (import use) (load l) (binding b))
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)
62 (traps) (delete del) (disable) (enable))
63 (inspect (inspect i) (pretty-print pp))
64 (system (gc) (statistics stat) (option o)
65 (quit q continue cont))))
68 '((show (warranty w) (copying c) (version v))))
70 (define (group-name g) (car g))
71 (define (group-commands g) (cdr g))
73 (define *command-module* (current-module))
74 (define (command-name c) (car c))
75 (define (command-abbrevs c) (cdr c))
76 (define (command-procedure c) (module-ref *command-module* (command-name c)))
77 (define (command-doc c) (procedure-documentation (command-procedure c)))
79 (define (command-usage c)
80 (let ((doc (command-doc c)))
81 (substring doc 0 (string-index doc #\newline))))
83 (define (command-summary c)
84 (let* ((doc (command-doc c))
85 (start (1+ (string-index doc #\newline))))
86 (cond ((string-index doc #\newline start)
87 => (lambda (end) (substring doc start end)))
88 (else (substring doc start)))))
90 (define (lookup-group name)
91 (assq name *command-table*))
93 (define* (lookup-command key #:optional (table *command-table*))
94 (let loop ((groups table) (commands '()))
95 (cond ((and (null? groups) (null? commands)) #f)
97 (loop (cdr groups) (cdar groups)))
98 ((memq key (car commands)) (car commands))
99 (else (loop groups (cdr commands))))))
101 (define* (display-group group #:optional (abbrev? #t))
102 (format #t "~:(~A~) Commands~:[~; [abbrev]~]:~2%" (group-name group) abbrev?)
103 (for-each (lambda (c)
104 (display-summary (command-usage c)
105 (if abbrev? (command-abbrevs c) '())
106 (command-summary c)))
107 (group-commands group))
110 (define (display-command command)
112 (display (command-doc command))
115 (define (display-summary usage abbrevs summary)
116 (let* ((usage-len (string-length usage))
117 (abbrevs (if (pair? abbrevs)
118 (format #f "[,~A~{ ,~A~}]" (car abbrevs) (cdr abbrevs))
120 (abbrevs-len (string-length abbrevs)))
121 (format #t " ,~A~A~A - ~A\n"
125 (error "abbrevs too long" abbrevs))
126 ((> (+ usage-len abbrevs-len) 32)
127 (format #f "~%~v_" (+ 2 (- 32 abbrevs-len))))
129 (format #f "~v_" (- 32 abbrevs-len usage-len))))
133 (define (read-command repl)
135 (lambda () (read (repl-inport repl)))
138 ((,subr ,msg ,args . ,rest)
139 (format #t "Throw to key `~a' while reading command:\n" key)
140 (display-error #f (current-output-port) subr msg args rest))
142 (format #t "Throw to key `~a' with args `~s' while reading command.\n"
148 (let ((orig-read-line read-line))
150 (orig-read-line (repl-inport repl)))))
152 (define (meta-command repl)
153 (let ((command (read-command repl)))
155 ((eq? command *unspecified*)) ; read error, already signalled; pass.
156 ((not (symbol? command))
157 (format #t "Meta-command not a symbol: ~s~%" command))
158 ((lookup-command command)
159 => (lambda (c) ((command-procedure c) repl)))
161 (format #t "Unknown meta command: ~A~%" command)))))
163 (define-syntax define-meta-command
165 ((_ (name repl (expression0 ...) . datums) docstring b0 b1 ...)
168 (define (handle-read-error form-name key args)
170 ((,subr ,msg ,args . ,rest)
171 (format #t "Throw to key `~a' while reading ~@[argument `~A' of ~]command `~A':\n"
173 (display-error #f (current-output-port) subr msg args rest))
175 (format #t "Throw to key `~a' with args `~s' while reading ~@[ argument `~A' of ~]command `~A'.\n"
176 key args form-name 'name)))
179 (% (let* ((expression0
183 (lambda* (#:optional (port (repl-inport repl)))
184 ((language-reader (repl-language repl))
185 port (current-module)))))
187 (handle-read-error 'expression0 k args))))
189 (apply (lambda* datums
190 (with-output-to-port (repl-outport repl)
191 (lambda () b0 b1 ...)))
194 (let ((port (open-input-string (read-line repl))))
196 (let ((x (read port)))
199 (lp (cons x out)))))))
201 (handle-read-error #f k args)))))
202 (lambda (k) #f)))) ; the abort handler
204 ((_ (name repl . datums) docstring b0 b1 ...)
205 (define-meta-command (name repl () . datums)
206 docstring b0 b1 ...))))
214 (define-meta-command (help repl . args)
215 "help [all | GROUP | [-c] COMMAND]
218 With one argument, tries to look up the argument as a group name, giving
219 help on that group if successful. Otherwise tries to look up the
220 argument as a command, giving help on the command.
222 If there is a command whose name is also a group name, use the ,help
223 -c COMMAND form to give help on the command instead of the group.
225 Without any argument, a list of help commands and command groups
229 (display-group (lookup-group 'help))
230 (display "Command Groups:\n\n")
231 (display-summary "help all" #f "List all commands")
232 (for-each (lambda (g)
233 (let* ((name (symbol->string (group-name g)))
234 (usage (string-append "help " name))
235 (header (string-append "List " name " commands")))
236 (display-summary usage #f header)))
237 (cdr *command-table*))
240 "Type `,help -c COMMAND' to show documentation of a particular command.")
243 (for-each display-group *command-table*))
244 ((,group) (guard (lookup-group group))
245 (display-group (lookup-group group)))
246 ((,command) (guard (lookup-command command))
247 (display-command (lookup-command command)))
248 ((-c ,command) (guard (lookup-command command))
249 (display-command (lookup-command command)))
251 (format #t "Unknown command or group: ~A~%" command))
253 (format #t "Unknown command: ~A~%" command))
255 (format #t "Bad arguments: ~A~%" args))))
257 (define-meta-command (show repl . args)
259 Gives information about Guile.
261 With one argument, tries to show a particular piece of information;
263 currently supported topics are `warranty' (or `w'), `copying' (or `c'),
264 and `version' (or `v').
266 Without any argument, a list of topics is displayed."
269 (display-group (car *show-table*) #f)
271 ((,topic) (guard (lookup-command topic *show-table*))
272 ((command-procedure (lookup-command topic *show-table*)) repl))
274 (format #t "Unknown topic: ~A~%" command))
276 (format #t "Bad arguments: ~A~%" args))))
278 (define (warranty repl)
280 Details on the lack of warranty."
284 (define (copying repl)
290 (define (version repl)
292 Version information."
296 (define guile:apropos apropos)
297 (define-meta-command (apropos repl regexp)
299 Find bindings/modules/packages."
300 (guile:apropos (->string regexp)))
302 (define-meta-command (describe repl (form))
304 Show description/documentation."
305 (display (object-documentation (repl-eval repl (repl-parse repl form))))
308 (define-meta-command (option repl . args)
310 List/show/set options."
313 (for-each (lambda (spec)
314 (format #t " ~A~24t~A\n" (car spec) (cadr spec)))
315 (repl-options repl)))
317 (display (repl-option-ref repl key))
320 (repl-option-set! repl key val))))
322 (define-meta-command (quit repl)
332 (define-meta-command (module repl . args)
334 Change modules / Show current module."
336 (() (puts (module-name (current-module))))
337 ((,mod-name) (guard (list? mod-name))
338 (set-current-module (resolve-module mod-name)))
339 (,mod-name (set-current-module (resolve-module mod-name)))))
341 (define-meta-command (import repl . args)
343 Import modules / List those imported."
346 (let ((mod (resolve-interface name)))
348 (module-use! (current-module) mod)
349 (format #t "No such module: ~A~%" name))))
351 (for-each puts (map module-name (module-uses (current-module))))
352 (for-each use args))))
354 (define guile:load load)
355 (define-meta-command (load repl file)
357 Load a file in the current module."
358 (guile:load (->string file)))
360 (define-meta-command (binding repl)
362 List current bindings."
363 (module-for-each (lambda (k v) (format #t "~23A ~A\n" k v))
368 ;;; Language commands
371 (define-meta-command (language repl name)
374 (let ((lang (lookup-language name))
375 (cur (repl-language repl)))
376 (format #t "Happy hacking with ~a! To switch back, type `,L ~a'.\n"
377 (language-title lang) (language-name cur))
378 (set! (repl-language repl) lang)))
385 (define-meta-command (compile repl (form))
387 Generate compiled code."
388 (let ((x (repl-compile repl (repl-parse repl form))))
389 (cond ((objcode? x) (guile:disassemble x))
390 (else (repl-print repl x)))))
392 (define guile:compile-file compile-file)
393 (define-meta-command (compile-file repl file . opts)
396 (guile:compile-file (->string file) #:opts opts))
398 (define (guile:disassemble x)
399 ((@ (language assembly disassemble) disassemble) x))
401 (define-meta-command (disassemble repl (form))
403 Disassemble a compiled procedure."
404 (guile:disassemble (repl-eval repl (repl-parse repl form))))
406 (define-meta-command (disassemble-file repl file)
407 "disassemble-file FILE
409 (guile:disassemble (load-objcode (->string file))))
416 (define-meta-command (time repl (form))
419 (let* ((gc-start (gc-run-time))
421 (result (repl-eval repl (repl-parse repl form)))
423 (gc-end (gc-run-time)))
424 (define (get proc start end)
425 (exact->inexact (/ (- (proc end) (proc start)) internal-time-units-per-second)))
426 (repl-print repl result)
427 (display "clock utime stime cutime cstime gctime\n")
428 (format #t "~5,2F ~5,2F ~5,2F ~6,2F ~6,2F ~6,2F\n"
429 (get tms:clock tms-start tms-end)
430 (get tms:utime tms-start tms-end)
431 (get tms:stime tms-start tms-end)
432 (get tms:cutime tms-start tms-end)
433 (get tms:cstime tms-start tms-end)
434 (get identity gc-start gc-end))
437 (define-meta-command (profile repl (form) . opts)
442 (repl-prepare-eval-thunk repl (repl-parse repl form))
445 (define-meta-command (trace repl (form) . opts)
448 ;; FIXME: doc options, or somehow deal with them better
451 (repl-prepare-eval-thunk repl (repl-parse repl form))
459 (define-syntax define-stack-command
462 ((_ (name repl . args) docstring body body* ...)
463 #`(define-meta-command (name repl . args)
465 (let ((debug (repl-debug repl)))
468 ((#,(datum->syntax #'repl 'frames)
469 (identifier-syntax (debug-frames debug)))
470 (#,(datum->syntax #'repl 'message)
471 (identifier-syntax (debug-error-message debug)))
472 (#,(datum->syntax #'repl 'index)
474 (id (debug-index debug))
475 ((set! id exp) (set! (debug-index debug) exp))))
476 (#,(datum->syntax #'repl 'cur)
478 (vector-ref #,(datum->syntax #'repl 'frames)
479 #,(datum->syntax #'repl 'index)))))
481 (format #t "Nothing to debug.~%"))))))))
483 (define-stack-command (backtrace repl #:optional count
484 #:key (width 72) full?)
485 "backtrace [COUNT] [#:width W] [#:full? F]
488 Print a backtrace of all stack frames, or innermost COUNT frames.
489 If COUNT is negative, the last COUNT frames will be shown."
495 (define-stack-command (up repl #:optional (count 1))
497 Select a calling stack frame.
499 Select and print stack frames that called this one.
500 An argument says how many frames up to go."
502 ((or (not (integer? count)) (<= count 0))
503 (format #t "Invalid argument to `up': expected a positive integer for COUNT.~%"))
504 ((>= (+ count index) (vector-length frames))
506 ((= index (1- (vector-length frames)))
507 (format #t "Already at outermost frame.\n"))
509 (set! index (1- (vector-length frames)))
510 (print-frame cur #:index index))))
512 (set! index (+ count index))
513 (print-frame cur #:index index))))
515 (define-stack-command (down repl #:optional (count 1))
517 Select a called stack frame.
519 Select and print stack frames called by this one.
520 An argument says how many frames down to go."
522 ((or (not (integer? count)) (<= count 0))
523 (format #t "Invalid argument to `down': expected a positive integer for COUNT.~%"))
524 ((< (- index count) 0)
527 (format #t "Already at innermost frame.\n"))
530 (print-frame cur #:index index))))
532 (set! index (- index count))
533 (print-frame cur #:index index))))
535 (define-stack-command (frame repl #:optional idx)
539 Show the selected frame.
540 With an argument, select a frame by index, then show it."
544 ((or (not (integer? idx)) (< idx 0))
545 (format #t "Invalid argument to `frame': expected a non-negative integer for IDX.~%"))
546 ((< idx (vector-length frames))
548 (print-frame cur #:index index))
550 (format #t "No such frame.~%"))))
551 (else (print-frame cur #:index index))))
553 (define-stack-command (procedure repl)
555 Print the procedure for the selected frame."
556 (repl-print repl (frame-procedure cur)))
558 (define-stack-command (locals repl)
560 Show local variables.
562 Show locally-bound variables in the selected frame."
565 (define-stack-command (error-message repl)
569 Display the message associated with the error that started the current
571 (format #t "~a~%" (if (string? message) message "No error message")))
573 (define-meta-command (break repl (form))
575 Break on calls to PROCEDURE.
577 Starts a recursive prompt when PROCEDURE is called."
578 (let ((proc (repl-eval repl (repl-parse repl form))))
579 (if (not (procedure? proc))
580 (error "Not a procedure: ~a" proc)
581 (let ((idx (add-trap-at-procedure-call! proc)))
582 (format #t "Trap ~a: ~a.~%" idx (trap-name idx))))))
584 (define-meta-command (break-at-source repl file line)
585 "break-at-source FILE LINE
586 Break when control reaches the given source location.
588 Starts a recursive prompt when control reaches line LINE of file FILE.
589 Note that the given source location must be inside a procedure."
590 (let ((file (if (symbol? file) (symbol->string file) file)))
591 (let ((idx (add-trap-at-source-location! file line)))
592 (format #t "Trap ~a: ~a.~%" idx (trap-name idx)))))
594 (define-meta-command (tracepoint repl (form))
595 "tracepoint PROCEDURE
596 Add a tracepoint to PROCEDURE.
598 A tracepoint will print out the procedure and its arguments, when it is
599 called, and its return value(s) when it returns."
600 (let ((proc (repl-eval repl (repl-parse repl form))))
601 (if (not (procedure? proc))
602 (error "Not a procedure: ~a" proc)
603 (let ((idx (add-trace-at-procedure-call! proc)))
604 (format #t "Trap ~a: ~a.~%" idx (trap-name idx))))))
606 (define-meta-command (traps repl)
608 Show the set of currently attached traps.
610 Show the set of currently attached traps (breakpoints and tracepoints)."
611 (let ((traps (list-traps)))
613 (format #t "No traps set.~%")
614 (for-each (lambda (idx)
615 (format #t " ~a: ~a~a~%"
617 (if (trap-enabled? idx) "" " (disabled)")))
620 (define-meta-command (delete repl idx)
625 (if (not (integer? idx))
626 (error "expected a trap index (a non-negative integer)" idx)
629 (define-meta-command (disable repl idx)
634 (if (not (integer? idx))
635 (error "expected a trap index (a non-negative integer)" idx)
636 (disable-trap! idx)))
638 (define-meta-command (enable repl idx)
643 (if (not (integer? idx))
644 (error "expected a trap index (a non-negative integer)" idx)
650 ;;; Inspection commands
653 (define-stack-command (inspect repl (form))
655 Inspect the result(s) of evaluating EXP."
656 (call-with-values (repl-prepare-eval-thunk repl (repl-parse repl form))
658 (for-each %inspect args))))
660 (define-meta-command (pretty-print repl (form))
662 Pretty-print the result(s) of evaluating EXP."
663 (call-with-values (repl-prepare-eval-thunk repl (repl-parse repl form))
667 (run-hook before-print-hook x)
677 (define-meta-command (gc repl)
682 (define-meta-command (statistics repl)
685 (let ((this-tms (times))
686 (this-gcs (gc-stats))
687 (last-tms (repl-tm-stats repl))
688 (last-gcs (repl-gc-stats repl)))
690 (let ((this-times (assq-ref this-gcs 'gc-times))
691 (last-times (assq-ref last-gcs 'gc-times)))
692 (display-diff-stat "GC times:" #t this-times last-times "times")
695 (let ((this-cells (assq-ref this-gcs 'cells-allocated))
696 (this-heap (assq-ref this-gcs 'cell-heap-size))
697 (this-bytes (assq-ref this-gcs 'bytes-malloced))
698 (this-malloc (assq-ref this-gcs 'gc-malloc-threshold)))
699 (display-stat-title "Memory size:" "current" "limit")
700 (display-stat "heap" #f this-cells this-heap "cells")
701 (display-stat "malloc" #f this-bytes this-malloc "bytes")
704 (let ((this-marked (assq-ref this-gcs 'cells-marked))
705 (last-marked (assq-ref last-gcs 'cells-marked))
706 (this-swept (assq-ref this-gcs 'cells-swept))
707 (last-swept (assq-ref last-gcs 'cells-swept)))
708 (display-stat-title "Cells collected:" "diff" "total")
709 (display-diff-stat "marked" #f this-marked last-marked "cells")
710 (display-diff-stat "swept" #f this-swept last-swept "cells")
713 (let ((this-mark (assq-ref this-gcs 'gc-mark-time-taken))
714 (last-mark (assq-ref last-gcs 'gc-mark-time-taken))
715 (this-total (assq-ref this-gcs 'gc-time-taken))
716 (last-total (assq-ref last-gcs 'gc-time-taken)))
717 (display-stat-title "GC time taken:" "diff" "total")
718 (display-time-stat "mark" this-mark last-mark)
719 (display-time-stat "total" this-total last-total)
721 ;; Process time spent
722 (let ((this-utime (tms:utime this-tms))
723 (last-utime (tms:utime last-tms))
724 (this-stime (tms:stime this-tms))
725 (last-stime (tms:stime last-tms))
726 (this-cutime (tms:cutime this-tms))
727 (last-cutime (tms:cutime last-tms))
728 (this-cstime (tms:cstime this-tms))
729 (last-cstime (tms:cstime last-tms)))
730 (display-stat-title "Process time spent:" "diff" "total")
731 (display-time-stat "user" this-utime last-utime)
732 (display-time-stat "system" this-stime last-stime)
733 (display-time-stat "child user" this-cutime last-cutime)
734 (display-time-stat "child system" this-cstime last-cstime)
738 (set! (repl-tm-stats repl) this-tms)
739 (set! (repl-gc-stats repl) this-gcs)))
741 (define (display-stat title flag field1 field2 unit)
742 (let ((str (format #f "~~20~AA ~~10@A /~~10@A ~~A~~%" (if flag "" "@"))))
743 (format #t str title field1 field2 unit)))
745 (define (display-stat-title title field1 field2)
746 (display-stat title #t field1 field2 ""))
748 (define (display-diff-stat title flag this last unit)
749 (display-stat title flag (- this last) this unit))
751 (define (display-time-stat title this last)
753 (format #f "~10,2F" (exact->inexact (/ num internal-time-units-per-second))))
754 (display-stat title #f (conv (- this last)) (conv this) "s"))
756 (define (display-mips-stat title this-time this-clock last-time last-clock)
757 (define (mips time clock)
758 (if (= time 0) "----" (format #f "~10,2F" (/ clock time 1000000.0))))
759 (display-stat title #f
760 (mips (- this-time last-time) (- this-clock last-clock))
761 (mips this-time this-clock) "mips"))