;;; Guile VM debugging facilities ;;; Copyright (C) 2001, 2009, 2010, 2011, 2013 Free Software Foundation, Inc. ;;; ;;; This library is free software; you can redistribute it and/or ;;; modify it under the terms of the GNU Lesser General Public ;;; License as published by the Free Software Foundation; either ;;; version 3 of the License, or (at your option) any later version. ;;; ;;; This library is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ;;; Lesser General Public License for more details. ;;; ;;; You should have received a copy of the GNU Lesser General Public ;;; License along with this library; if not, write to the Free Software ;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ;;; Code: (define-module (system vm inspect) #:use-module (system base pmatch) #:use-module (system base syntax) #:use-module (system vm vm) #:use-module (system vm frame) #:use-module (system vm disassembler) #:use-module (ice-9 rdelim) #:use-module (ice-9 pretty-print) #:use-module (ice-9 format) #:use-module (system vm program) #:export (inspect)) (define (reverse-hashq h) (let ((ret (make-hash-table))) (hash-for-each (lambda (k v) (hashq-set! ret v (cons k (hashq-ref ret v '())))) h) ret)) (define (catch-bad-arguments thunk bad-args-thunk) (catch 'wrong-number-of-args (lambda () (catch 'keyword-argument-error thunk (lambda (k . args) (bad-args-thunk)))) (lambda (k . args) (bad-args-thunk)))) (define (read-args prompt) (define (read* reader) (repl-reader prompt reader)) (define (next) (read* read-char)) (define (cmd chr) (cond ((eof-object? chr) (list chr)) ((char=? chr #\newline) (cmd (next))) ((char-whitespace? chr) (cmd (next))) (else (unread-char chr) (let ((tok (read* read))) (args (list tok) (next)))))) (define (args out chr) (cond ((eof-object? chr) (reverse out)) ((char=? chr #\newline) (reverse out)) ((char-whitespace? chr) (args out (next))) (else (unread-char chr) (let ((tok (read* read))) (args (cons tok out) (next)))))) (cmd (next))) ;;; ;;; Inspector ;;; (define (inspect x) (define-syntax-rule (define-command ((mod cname alias ...) . args) body ...) (define cname (let ((c (lambda* args body ...))) (set-procedure-property! c 'name 'cname) (module-define! mod 'cname c) (module-add! mod 'alias (module-local-variable mod 'cname)) ... c))) (let ((commands (make-module))) (define (prompt) (format #f "~20@y inspect> " x)) (define-command ((commands quit q continue cont c)) "Quit the inspector." (throw 'quit)) (define-command ((commands print p)) "Print the current object using `pretty-print'." (pretty-print x)) (define-command ((commands write w)) "Print the current object using `write'." (write x)) (define-command ((commands display d)) "Print the current object using `display'." (display x)) (define-command ((commands disassemble x)) "Disassemble the current object, which should be a procedure." (catch #t (lambda () (disassemble-program x)) (lambda args (format #t "Error disassembling object: ~a\n" args)))) (define-command ((commands help h ?) #:optional cmd) "Show this help message." (let ((rhash (reverse-hashq (module-obarray commands)))) (define (help-cmd cmd) (let* ((v (module-local-variable commands cmd)) (p (variable-ref v)) (canonical-name (procedure-name p))) ;; la la la (format #t "~a~{ ~:@(~a~)~}~?~%~a~&~%" canonical-name (program-lambda-list p) "~#[~:;~40t(aliases: ~@{~a~^, ~})~]" (delq canonical-name (hashq-ref rhash v)) (procedure-documentation p)))) (cond (cmd (cond ((and (symbol? cmd) (module-local-variable commands cmd)) (help-cmd cmd)) (else (format #t "Invalid command ~s.~%" cmd) (format #t "Try `help' for a list of commands~%")))) (else (let ((names (sort (hash-map->list (lambda (k v) (procedure-name (variable-ref k))) rhash) (lambda (x y) (stringstring x) (symbol->string y)))))) (format #t "Available commands:~%~%") (for-each help-cmd names)))))) (define (handle cmd . args) (cond ((and (symbol? cmd) (module-local-variable commands cmd)) => (lambda (var) (let ((proc (variable-ref var))) (catch-bad-arguments (lambda () (apply (variable-ref var) args)) (lambda () (format (current-error-port) "Invalid arguments to ~a. Try `help ~a'.~%" (procedure-name proc) (procedure-name proc))))))) ; ((and (integer? cmd) (exact? cmd)) ; (nth cmd)) ((eof-object? cmd) (newline) (throw 'quit)) (else (format (current-error-port) "~&Unknown command: ~a. Try `help'.~%" cmd) *unspecified*))) (catch 'quit (lambda () (let loop () (apply handle (save-module-excursion (lambda () (set-current-module commands) (read-args prompt)))) (loop))) (lambda (k . args) (apply values args)))))