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 vm)
31 #:autoload (system base language) (lookup-language language-reader)
32 #:autoload (system vm trace) (vm-trace)
33 #:autoload (system vm profile) (vm-profile)
34 #:use-module (ice-9 format)
35 #:use-module (ice-9 session)
36 #:use-module (ice-9 documentation)
37 #:use-module (ice-9 and-let-star)
38 #:use-module (ice-9 rdelim)
39 #:use-module (ice-9 control)
40 #:use-module ((ice-9 pretty-print) #:select ((pretty-print . pp)))
41 #:use-module ((system vm inspect) #:select ((inspect . %inspect)))
42 #:use-module (statprof)
43 #:export (meta-command))
47 ;;; Meta command interface
50 (define *command-table*
51 '((help (help h) (show s) (apropos a) (describe d))
52 (module (module m) (import use) (load l) (binding b))
53 (language (language L))
54 (compile (compile c) (compile-file cc)
55 (disassemble x) (disassemble-file xx))
56 (profile (time t) (profile pr) (trace tr))
57 (debug (backtrace bt) (up) (down) (frame fr)
58 (procedure proc) (locals) (error-message error))
59 (inspect (inspect i) (pretty-print pp))
60 (system (gc) (statistics stat) (option o)
61 (quit q continue cont))))
64 '((show (warranty w) (copying c) (version v))))
66 (define (group-name g) (car g))
67 (define (group-commands g) (cdr g))
69 (define *command-module* (current-module))
70 (define (command-name c) (car c))
71 (define (command-abbrevs c) (cdr c))
72 (define (command-procedure c) (module-ref *command-module* (command-name c)))
73 (define (command-doc c) (procedure-documentation (command-procedure c)))
75 (define (command-usage c)
76 (let ((doc (command-doc c)))
77 (substring doc 0 (string-index doc #\newline))))
79 (define (command-summary c)
80 (let* ((doc (command-doc c))
81 (start (1+ (string-index doc #\newline))))
82 (cond ((string-index doc #\newline start)
83 => (lambda (end) (substring doc start end)))
84 (else (substring doc start)))))
86 (define (lookup-group name)
87 (assq name *command-table*))
89 (define* (lookup-command key #:optional (table *command-table*))
90 (let loop ((groups table) (commands '()))
91 (cond ((and (null? groups) (null? commands)) #f)
93 (loop (cdr groups) (cdar groups)))
94 ((memq key (car commands)) (car commands))
95 (else (loop groups (cdr commands))))))
97 (define* (display-group group #:optional (abbrev? #t))
98 (format #t "~:(~A~) Commands~:[~; [abbrev]~]:~2%" (group-name group) abbrev?)
100 (display-summary (command-usage c)
101 (if abbrev? (command-abbrevs c) '())
102 (command-summary c)))
103 (group-commands group))
106 (define (display-command command)
108 (display (command-doc command))
111 (define (display-summary usage abbrevs summary)
112 (let* ((usage-len (string-length usage))
113 (abbrevs (if (pair? abbrevs)
114 (format #f "[,~A~{ ,~A~}]" (car abbrevs) (cdr abbrevs))
116 (abbrevs-len (string-length abbrevs)))
117 (format #t " ,~A~A~A - ~A\n"
121 (error "abbrevs too long" abbrevs))
122 ((> (+ usage-len abbrevs-len) 32)
123 (format #f "~%~v_" (+ 2 (- 32 abbrevs-len))))
125 (format #f "~v_" (- 32 abbrevs-len usage-len))))
129 (define (read-command repl)
131 (lambda () (read (repl-inport repl)))
134 ((,subr ,msg ,args . ,rest)
135 (format #t "Throw to key `~a' while reading command:\n" key)
136 (display-error #f (current-output-port) subr msg args rest))
138 (format #t "Throw to key `~a' with args `~s' while reading command.\n"
144 (let ((orig-read-line read-line))
146 (orig-read-line (repl-inport repl)))))
148 (define (meta-command repl)
149 (let ((command (read-command repl)))
151 ((eq? command *unspecified*)) ; read error, already signalled; pass.
152 ((not (symbol? command))
153 (format #t "Meta-command not a symbol: ~s~%" command))
154 ((lookup-command command)
155 => (lambda (c) ((command-procedure c) repl)))
157 (format #t "Unknown meta command: ~A~%" command)))))
159 (define-syntax define-meta-command
161 ((_ (name repl (expression0 ...) . datums) docstring b0 b1 ...)
164 (define (handle-read-error form-name key args)
166 ((,subr ,msg ,args . ,rest)
167 (format #t "Throw to key `~a' while reading ~@[argument `~A' of ~]command `~A':\n"
169 (display-error #f (current-output-port) subr msg args rest))
171 (format #t "Throw to key `~a' with args `~s' while reading ~@[ argument `~A' of ~]command `~A'.\n"
172 key args form-name 'name)))
175 (% (let* ((expression0
179 (lambda* (#:optional (port (repl-inport repl)))
180 ((language-reader (repl-language repl))
181 port (current-module)))))
183 (handle-read-error 'expression0 k args))))
185 (apply (lambda* datums
186 (with-output-to-port (repl-outport repl)
187 (lambda () b0 b1 ...)))
190 (let ((port (open-input-string (read-line repl))))
192 (let ((x (read port)))
195 (lp (cons x out)))))))
197 (handle-read-error #f k args)))))
198 (lambda (k) #f)))) ; the abort handler
200 ((_ (name repl . datums) docstring b0 b1 ...)
201 (define-meta-command (name repl () . datums)
202 docstring b0 b1 ...))))
210 (define-meta-command (help repl . args)
211 "help [all | GROUP | [-c] COMMAND]
214 With one argument, tries to look up the argument as a group name, giving
215 help on that group if successful. Otherwise tries to look up the
216 argument as a command, giving help on the command.
218 If there is a command whose name is also a group name, use the ,help
219 -c COMMAND form to give help on the command instead of the group.
221 Without any argument, a list of help commands and command groups
225 (display-group (lookup-group 'help))
226 (display "Command Groups:\n\n")
227 (display-summary "help all" #f "List all commands")
228 (for-each (lambda (g)
229 (let* ((name (symbol->string (group-name g)))
230 (usage (string-append "help " name))
231 (header (string-append "List " name " commands")))
232 (display-summary usage #f header)))
233 (cdr *command-table*))
236 "Type `,help -c COMMAND' to show documentation of a particular command.")
239 (for-each display-group *command-table*))
240 ((,group) (guard (lookup-group group))
241 (display-group (lookup-group group)))
242 ((,command) (guard (lookup-command command))
243 (display-command (lookup-command command)))
244 ((-c ,command) (guard (lookup-command command))
245 (display-command (lookup-command command)))
247 (format #t "Unknown command or group: ~A~%" command))
249 (format #t "Unknown command: ~A~%" command))
251 (format #t "Bad arguments: ~A~%" args))))
253 (define-meta-command (show repl . args)
255 Gives information about Guile.
257 With one argument, tries to show a particular piece of information;
259 currently supported topics are `warranty' (or `w'), `copying' (or `c'),
260 and `version' (or `v').
262 Without any argument, a list of topics is displayed."
265 (display-group (car *show-table*) #f)
267 ((,topic) (guard (lookup-command topic *show-table*))
268 ((command-procedure (lookup-command topic *show-table*)) repl))
270 (format #t "Unknown topic: ~A~%" command))
272 (format #t "Bad arguments: ~A~%" args))))
274 (define (warranty repl)
276 Details on the lack of warranty."
280 (define (copying repl)
286 (define (version repl)
288 Version information."
292 (define guile:apropos apropos)
293 (define-meta-command (apropos repl regexp)
295 Find bindings/modules/packages."
296 (guile:apropos (->string regexp)))
298 (define-meta-command (describe repl (form))
300 Show description/documentation."
301 (display (object-documentation (repl-eval repl (repl-parse repl form))))
304 (define-meta-command (option repl . args)
306 List/show/set options."
309 (for-each (lambda (spec)
310 (format #t " ~A~24t~A\n" (car spec) (cadr spec)))
311 (repl-options repl)))
313 (display (repl-option-ref repl key))
316 (repl-option-set! repl key val))))
318 (define-meta-command (quit repl)
328 (define-meta-command (module repl . args)
330 Change modules / Show current module."
332 (() (puts (module-name (current-module))))
333 ((,mod-name) (guard (list? mod-name))
334 (set-current-module (resolve-module mod-name)))
335 (,mod-name (set-current-module (resolve-module mod-name)))))
337 (define-meta-command (import repl . args)
339 Import modules / List those imported."
342 (let ((mod (resolve-interface name)))
344 (module-use! (current-module) mod)
345 (format #t "No such module: ~A~%" name))))
347 (for-each puts (map module-name (module-uses (current-module))))
348 (for-each use args))))
350 (define guile:load load)
351 (define-meta-command (load repl file)
353 Load a file in the current module."
354 (guile:load (->string file)))
356 (define-meta-command (binding repl)
358 List current bindings."
359 (module-for-each (lambda (k v) (format #t "~23A ~A\n" k v))
364 ;;; Language commands
367 (define-meta-command (language repl name)
370 (let ((lang (lookup-language name))
371 (cur (repl-language repl)))
372 (format #t "Happy hacking with ~a! To switch back, type `,L ~a'.\n"
373 (language-title lang) (language-name cur))
374 (set! (repl-language repl) lang)))
381 (define-meta-command (compile repl (form))
383 Generate compiled code."
384 (let ((x (repl-compile repl (repl-parse repl form))))
385 (cond ((objcode? x) (guile:disassemble x))
386 (else (repl-print repl x)))))
388 (define guile:compile-file compile-file)
389 (define-meta-command (compile-file repl file . opts)
392 (guile:compile-file (->string file) #:opts opts))
394 (define (guile:disassemble x)
395 ((@ (language assembly disassemble) disassemble) x))
397 (define-meta-command (disassemble repl (form))
399 Disassemble a compiled procedure."
400 (guile:disassemble (repl-eval repl (repl-parse repl form))))
402 (define-meta-command (disassemble-file repl file)
403 "disassemble-file FILE
405 (guile:disassemble (load-objcode (->string file))))
412 (define-meta-command (time repl (form))
415 (let* ((gc-start (gc-run-time))
417 (result (repl-eval repl (repl-parse repl form)))
419 (gc-end (gc-run-time)))
420 (define (get proc start end)
421 (exact->inexact (/ (- (proc end) (proc start)) internal-time-units-per-second)))
422 (repl-print repl result)
423 (display "clock utime stime cutime cstime gctime\n")
424 (format #t "~5,2F ~5,2F ~5,2F ~6,2F ~6,2F ~6,2F\n"
425 (get tms:clock tms-start tms-end)
426 (get tms:utime tms-start tms-end)
427 (get tms:stime tms-start tms-end)
428 (get tms:cutime tms-start tms-end)
429 (get tms:cstime tms-start tms-end)
430 (get identity gc-start gc-end))
433 (define-meta-command (profile repl (form) . opts)
438 (make-program (repl-compile repl (repl-parse repl form)))
441 (define-meta-command (trace repl (form) . opts)
444 ;; FIXME: doc options, or somehow deal with them better
447 (make-program (repl-compile repl (repl-parse repl form)))
455 (define-syntax define-stack-command
458 ((_ (name repl . args) docstring body body* ...)
459 #`(define-meta-command (name repl . args)
461 (let ((debug (repl-debug repl)))
464 ((#,(datum->syntax #'repl 'frames)
465 (identifier-syntax (debug-frames debug)))
466 (#,(datum->syntax #'repl 'message)
467 (identifier-syntax (debug-error-message debug)))
468 (#,(datum->syntax #'repl 'index)
470 (id (debug-index debug))
471 ((set! id exp) (set! (debug-index debug) exp))))
472 (#,(datum->syntax #'repl 'cur)
474 (vector-ref #,(datum->syntax #'repl 'frames)
475 #,(datum->syntax #'repl 'index)))))
477 (format #t "Nothing to debug.~%"))))))))
479 (define-stack-command (error-message repl)
483 Display the message associated with the error that started the current
485 (format #t "~a~%" (if (string? message) message "No error message")))
487 (define-stack-command (backtrace repl #:optional count
488 #:key (width 72) full?)
489 "backtrace [COUNT] [#:width W] [#:full? F]
492 Print a backtrace of all stack frames, or innermost COUNT frames.
493 If COUNT is negative, the last COUNT frames will be shown."
499 (define-stack-command (up repl #:optional (count 1))
501 Select a calling stack frame.
503 Select and print stack frames that called this one.
504 An argument says how many frames up to go."
506 ((or (not (integer? count)) (<= count 0))
507 (format #t "Invalid argument to `up': expected a positive integer for COUNT.~%"))
508 ((>= (+ count index) (vector-length frames))
510 ((= index (1- (vector-length frames)))
511 (format #t "Already at outermost frame.\n"))
513 (set! index (1- (vector-length frames)))
514 (print-frame cur #:index index))))
516 (set! index (+ count index))
517 (print-frame cur #:index index))))
519 (define-stack-command (down repl #:optional (count 1))
521 Select a called stack frame.
523 Select and print stack frames called by this one.
524 An argument says how many frames down to go."
526 ((or (not (integer? count)) (<= count 0))
527 (format #t "Invalid argument to `down': expected a positive integer for COUNT.~%"))
528 ((< (- index count) 0)
531 (format #t "Already at innermost frame.\n"))
534 (print-frame cur #:index index))))
536 (set! index (- index count))
537 (print-frame cur #:index index))))
539 (define-stack-command (frame repl #:optional idx)
543 Show the selected frame.
544 With an argument, select a frame by index, then show it."
548 ((or (not (integer? idx)) (< idx 0))
549 (format #t "Invalid argument to `frame': expected a non-negative integer for IDX.~%"))
550 ((< idx (vector-length frames))
552 (print-frame cur #:index index))
554 (format #t "No such frame.~%"))))
555 (else (print-frame cur #:index index))))
557 (define-stack-command (procedure repl)
559 Print the procedure for the selected frame."
560 (repl-print repl (frame-procedure cur)))
562 (define-stack-command (locals repl)
564 Show local variables.
566 Show locally-bound variables in the selected frame."
571 ;;; Inspection commands
574 (define-stack-command (inspect repl (form))
576 Inspect the result(s) of evaluating EXP."
577 (call-with-values (make-program (repl-compile repl (repl-parse repl form)))
579 (for-each %inspect args))))
581 (define-meta-command (pretty-print repl (form))
583 Pretty-print the result(s) of evaluating EXP."
584 (call-with-values (make-program (repl-compile repl (repl-parse repl form)))
588 (run-hook before-print-hook x)
598 (define-meta-command (gc repl)
603 (define-meta-command (statistics repl)
606 (let ((this-tms (times))
607 (this-gcs (gc-stats))
608 (last-tms (repl-tm-stats repl))
609 (last-gcs (repl-gc-stats repl)))
611 (let ((this-times (assq-ref this-gcs 'gc-times))
612 (last-times (assq-ref last-gcs 'gc-times)))
613 (display-diff-stat "GC times:" #t this-times last-times "times")
616 (let ((this-cells (assq-ref this-gcs 'cells-allocated))
617 (this-heap (assq-ref this-gcs 'cell-heap-size))
618 (this-bytes (assq-ref this-gcs 'bytes-malloced))
619 (this-malloc (assq-ref this-gcs 'gc-malloc-threshold)))
620 (display-stat-title "Memory size:" "current" "limit")
621 (display-stat "heap" #f this-cells this-heap "cells")
622 (display-stat "malloc" #f this-bytes this-malloc "bytes")
625 (let ((this-marked (assq-ref this-gcs 'cells-marked))
626 (last-marked (assq-ref last-gcs 'cells-marked))
627 (this-swept (assq-ref this-gcs 'cells-swept))
628 (last-swept (assq-ref last-gcs 'cells-swept)))
629 (display-stat-title "Cells collected:" "diff" "total")
630 (display-diff-stat "marked" #f this-marked last-marked "cells")
631 (display-diff-stat "swept" #f this-swept last-swept "cells")
634 (let ((this-mark (assq-ref this-gcs 'gc-mark-time-taken))
635 (last-mark (assq-ref last-gcs 'gc-mark-time-taken))
636 (this-total (assq-ref this-gcs 'gc-time-taken))
637 (last-total (assq-ref last-gcs 'gc-time-taken)))
638 (display-stat-title "GC time taken:" "diff" "total")
639 (display-time-stat "mark" this-mark last-mark)
640 (display-time-stat "total" this-total last-total)
642 ;; Process time spent
643 (let ((this-utime (tms:utime this-tms))
644 (last-utime (tms:utime last-tms))
645 (this-stime (tms:stime this-tms))
646 (last-stime (tms:stime last-tms))
647 (this-cutime (tms:cutime this-tms))
648 (last-cutime (tms:cutime last-tms))
649 (this-cstime (tms:cstime this-tms))
650 (last-cstime (tms:cstime last-tms)))
651 (display-stat-title "Process time spent:" "diff" "total")
652 (display-time-stat "user" this-utime last-utime)
653 (display-time-stat "system" this-stime last-stime)
654 (display-time-stat "child user" this-cutime last-cutime)
655 (display-time-stat "child system" this-cstime last-cstime)
659 (set! (repl-tm-stats repl) this-tms)
660 (set! (repl-gc-stats repl) this-gcs)))
662 (define (display-stat title flag field1 field2 unit)
663 (let ((str (format #f "~~20~AA ~~10@A /~~10@A ~~A~~%" (if flag "" "@"))))
664 (format #t str title field1 field2 unit)))
666 (define (display-stat-title title field1 field2)
667 (display-stat title #t field1 field2 ""))
669 (define (display-diff-stat title flag this last unit)
670 (display-stat title flag (- this last) this unit))
672 (define (display-time-stat title this last)
674 (format #f "~10,2F" (exact->inexact (/ num internal-time-units-per-second))))
675 (display-stat title #f (conv (- this last)) (conv this) "s"))
677 (define (display-mips-stat title this-time this-clock last-time last-clock)
678 (define (mips time clock)
679 (if (= time 0) "----" (format #f "~10,2F" (/ clock time 1000000.0))))
680 (display-stat title #f
681 (mips (- this-time last-time) (- this-clock last-clock))
682 (mips this-time this-clock) "mips"))