; .EnTete "Le-Lisp (c) version 15.2" " " "Les De'buggers" ; .EnPied "debug.ll" "L-%" " " ; .Annexe L "Utilitaire permettant d'explorer la pile" ; .nr % 1 ; ; .Centre "*****************************************************************" ; .Centre " Ce fichier est en lecture seule hors du projet ALE de l'INRIA. " ; .Centre " Il est maintenu par ILOG SA, 2 Avenue Gallie'ni, 94250 Gentilly " ; .Centre " (c) Le-Lisp est une marque de'pose'e de l'INRIA " ; .Centre "*****************************************************************" ; .Centre "$Header: debug.ll,v 4.3 88/04/07 18:27:59 nuyens Exp $" (unless (>= (version) 15.2) (error 'load 'erricf 'debug)) (unless (featurep 'virtty) (initty)) ; Dans le package #:system:debug (setq #:sys-package:colon '#:system:debug) ; .Section "Interface avec le monde" ; Globales re'glant le debug ; lignes maxi de pretty print (defvar #:system:debug-line 5) ; niveaux maxi de pile (defvar #:system:stack-depth 5) ; L'indicateur de debug (defvar #:system:debug ()) ; Impression du contenu de la pile (defun printstack nl ; ((&opt n 32767) (&opt l (cstack))) ... (let* ((l (if (and (consp nl) (consp (cdr nl))) (cadr nl) (cstack))) (m (if (consp nl) ; nombre de blocs a imprimer (min (car nl) (length l)) (length l))) (n (length l)) ; nume'ros des blocs frame) (mapc (lambda (f) (when (:is-hidden-block f) (decr n))) l) (setq m (min m n)) (incr n) (while (gt m 0) (nextl l frame) (unless (:is-hidden-block frame) (decr n) (decr m) (prin " [stack ") (when (< n 100) (princn #\sp)) (when (< n 10) (princn #\sp)) (prin n "] (") (selectq (car frame) ; sur le type du bloc (1 ; type lambda (1 llink fval lparam v1 .. vn) (prin (or (:findfn (caddr frame)) 'let))) (2 ; type label (2 fct1 ofval1 oftyp1 ...) (prin 'flet " " (cadr frame))) (3 ; type e'chappement (3 tag-name) (prin 'tag " "(cadr frame))) (4 ; type itsoft (4 llink nom etat forme funct) (prin 'itsoft " " (caddr frame))) (5 ; type lock (5 fval) (prin 'lock " " (or (symbolp (cadr frame)) (:findfn (cadr frame)) "(lambda ...)"))) (6 ; type protect (5 progn) (prin 'protect)) (7 ; type sys-protect (6) (prin 'sys-protect)) (8 ; type schedule (8 XXX) (prin 'schedule)) (9 ; type tagbody (9 et1 corp1 ... etN corpN) (prin 'tagbody)) (10 ; type bloc (10 slot) (prin 'block)) (11) (t ; type erronne (prin #- #:system:foreign-language "*** bloc inconnu :" #+ #:system:foreign-language "*** unknown block:" (car frame)))) (print " ...)"))))) (de :is-hidden-block (frame) (or (eq (car frame) 7) ; sysprotect (eq (car frame) 11) ; progn (eq (car frame) 4)) ; itsoft ) ; Passage en/Sortie du mode debug (df debug #:system:l (cond ((atom #:system:l) ; retourne l'indicateur courant #:system:debug) ((memq (car #:system:l) '(t ())) ; positionnement global (setq #:system:debug (car #:system:l))) (t ; positionnement temporaire (let ((#:system:debug (car #:system:l))) (eval (car #:system:l)))))) ; Cre'ation d'une boucle d'inspection (defun break () (if #:system:debug (let ((#:system:debug t) (#:sys-package:itsoft (cons '#.#:sys-package:colon (remq '#.#:sys-package:colon #:sys-package:itsoft)))) (clockalarm 0) (tag continue (with ((inchan ()) (outchan ())) (:break-loop (cstack))) (err))) (err))) ; Lancement d'une commande du de'buggeur (defun debug-command (:char) (eprogn (cdr (cassq :char :commands)))) ; .Section "Implantation" ; Boucle d'inspection (defvar :current-form) (defvar :current-function) (defvar :current-error-form) (defvar :error-message) (defvar :break-number 0) (defvar :resetfn-alist) (de :break-loop (:stack) (let (:error-message :current-function :current-form :current-error-form (:resetfn-alist (mapcoblist (lambda (s) (when (and (neq (valfn s) 0) (getprop s 'resetfn)) (ncons (cons (cdr (getprop s 'resetfn)) s)))))) current-window (:break-number (add1 :break-number))) (protect (progn (when (and (featurep 'window) (current-window)) (setq current-window (current-window)) (current-window (:create-break-window :break-number))) (with ((prompt (if (gt :break-number 1) (catenate :break-number ">? ") ">? "))) (:init-stack (:stack-in-error :stack)) (debug-command #/e) (debug-command #/.) (protect (untilexit break (itsoft 'toplevel ())) (untilexit #:system:debug (:up-stack))))) (when (and (featurep 'window) (current-window)) (kill-window (current-window)) (current-window current-window))))) ; Interaction ; Le toplevel (de :toplevel () (tag #:system:debug (catcherror t (print "= " (eval (read)))))) ; De'codage des commandes (defun :bol () (super-itsoft '#.#:sys-package:colon 'bol ()) (let* ((:inbuf (inbuf)) (:c (sref (inbuf) 0))) (when (and (assq :c :commands) (eq 3 (inmax))) (sset :inbuf 0 #\cr) (sset :inbuf 1 #\lf) (debug-command :c)))) ; Initialisations ; Initialisation de la fene↑tre (de :create-break-window (n) (create-window '#:window:tty (div (bitxmax) 3) (mul (mul n 2) (height-space)) (scale (bitxmax) 2 3) (scale (bitymax) 2 3) (catenate "Le←Lisp : Break Loop #" (string n)) 1 1))) ; Initialisation de la pile (de :find-syserror (stack) (when stack (if (and (eq 4 (caar stack)) (eq 'syserror (caddar stack))) stack (:find-syserror (cdr stack))))) (de :find-break (stack) (when stack (if (and (eq 1 (caar stack)) (if (eq (typefn 'break) 'expr) (eq (valfn 'break) (caddar stack)) (equal (valfn 'break) (loc (caddar stack))))) stack (:find-break (cdr stack))))) (de :cut-to-toplevel (stack) (if (:is-at-toplevel stack) (displace stack '(())) (let (frame) (until (or (null (cdr stack)) (:is-at-toplevel (cdr stack))) (nextl stack)) (when (consp stack) (rplacd stack ()))))) (de :is-at-toplevel (stack) (or (and (eq 4 (caar stack)) (eq 'toplevel (caddar stack))) (and (eq 7 (caar stack)) ; sysprot (nextl stack) (or (eq 7 (caar stack)) (eq 5 (caar stack))) (nextl stack) (and (eq 3 (caar stack)) (eq '#:system:error-tag (cadar stack))) (nextl stack) (if (eq 11 (caar stack)) (nextl stack) t) (and (eq 3 (caar stack)) (eq '#:system:toplevel-tag (cadar stack))) (nextl stack) (if (eq 1 (caar stack)) (nextl stack) t) (and (eq 4 (caar stack)) (eq 'toplevel (caddar stack)))))) (de :stack-in-error (stack) (:cut-to-toplevel stack) (let ((stack (or (:find-syserror stack) (:find-break stack)))) (cond ((and (eq 1 (caar stack)) (if (eq (typefn 'break) 'expr) (eq (valfn 'break) (caddar stack)) (equal (valfn 'break) (loc (caddar stack))))) ; break au sommet (setq :error-message '(break break ()) :current-error-form '(break))) ((eq 4 (caar stack)) ; syserror au sommet (setq :error-message (nth 10 (car stack))) (setq :current-error-form ; break step: forme dans errmess (if (eq 'step (car :error-message)) (caddr :error-message) (nth 4 (car stack))))) (t (setq :error-message '(debug errerr ())))) (nextl stack) (:remove-fn '(cstep unstep step #:step:steploop #:step:stepeval) stack))) (de :remove-fn (lfn stack) (setq stack (cons () stack) lfn (mapcar 'valfn lfn)) (let ((bstack stack) (fstack stack)) (while (setq fstack (:find-function (cdr fstack))) (ifn (:is-in-lvalfn (caddr (car fstack)) lfn) (setq stack fstack) (rplacd stack (cdr fstack)))) (setq stack bstack) (cdr bstack))) (de :is-in-lvalfn (valfn lvalfn) (any (lambda (vfn) (or (eq vfn valfn) (equal (loc valfn) vfn))) lvalfn)) (defun :init-stack (stack) (when (:is-a-struct-access (car stack)) ; hack #:system:structaccess (nextl stack)) (setq :current-form (ncons stack)) (setq :current-function (ncons (:find-function stack)))) ; Les commandes (defvar :commands '((#/v "show variables" (:print-current-variables)) (#/h "print top of stack" (:printstack #:system:stack-depth)) (#/H "print complete stack" (:printstack)) (#/e "show error message" (:print-error)) (#/. "show current stack frame" (:print-current-function)) (#/+ "down stack" (:down-stack) (:print-current-function)) (#/- "up stack" (:up-stack) (:print-current-function)) (#/t "back to toplevel" (exit #:system:toplevel-tag)) (#/q "exit inspection loop" (exit break)) (#/r "resume and correct error" (:continue)) (#/c "continue" (exit continue)) (#/z "step traced functions" (setq #:trace:step-in-trace-flag t) (:continue)) (#/? "list commands" (:help)))) ; Commandes d'impression (de :printstack n (if n (setq n (car n)) (setq n (length (car :current-form)))) (printstack n (car :current-form))) (de :print-current-variables () (:print-variables (car :current-form))) (defun :print-variables (:s) (cond ((:has-function-definition (car :s)) (:print-arguments (nth 3 (car :s)))) ((eq 1 (caar :s)) (:print-arguments (nth 3 (car :s))) (:bind/unbind (car :s)) (:print-variables (cdr :s)) (:bind/unbind (car :s))) (:s (:print-variables (cdr :s))))) (defun :print-arguments (:larg) (cond ((or (eq :larg '&nobind) (null :larg))) ((symbolp :larg) (print " " :larg "=" (symeval :larg))) ((consp :larg) (:print-arguments (car :larg)) (:print-arguments (cdr :larg))))) (de :print-error () (apply 'printerror :error-message) (selectq (cadr :error-message) ((errwna ; "mauvais nombre d'arguments" errbpa ; "mauvais parametre" errilb) ; "liaison illegale" (let ((d (:getdef (car :error-message)))) (:print-filtered (list (car d) (cadr d) (:hilited:make (caddr d)))))) )) (defun :print-current-function () (let ((current-definition (:getdef (:findfn (caddr (caar :current-function)))))) (when current-definition (:print-filtered (:hilite-expr (:current-expr current-definition) current-definition))))) ; Commandes de de'placement dans la pile (defun :down-stack () (let* ((:next-form (cdr (car :current-function))) (:next-function (:find-function :next-form))) (ifn :next-function (exit #:system:debug) (newl :current-form :next-form) (newl :current-function :next-function) (let ((:previous-form (cadr :current-form))) (while (neq :previous-form (car :current-form)) (:bind/unbind (nextl :previous-form))))))) (de :up-stack () (ifn (cdr :current-function) (exit #:system:debug) (let ((:rebind-frames (:up-frame-list (cadr :current-form) (car :current-form)))) (nextl :current-function) (nextl :current-form) (while :rebind-frames (:bind/unbind (nextl :rebind-frames)))))) (de :up-frame-list (f1 f2) (let ((res ())) (until (eq (car f1) (car f2)) (newl res (nextl f1))) res)) ; Commandes diverses (de :continue () (selectq (cadr :error-message) (break (exit continue)) (errudv (prinflush (prompt) "(setq " (caddr :error-message) " '") (with ((prompt "")) (let ((form `(setq ,(caddr :error-message) ',(read)))) (exit continue (eval form))))) (errudf (prinflush "Function ") (exit continue (read))) (t (print "This error can not be resumed")))) (defun :help () (mapc (lambda ((c doc . rest)) (print "; " (ascii c) ": " doc)) :commands)) ; De'pilage d'un block de pile (defun :bind/unbind (:frame) (when (eq 1 (car :frame)) ; bloc lambda ; type lambda (1 llink fval lparam vn ... v1) (let ((:lval (nreverse (cddddr :frame)))) (:exchange-arguments (cadddr :frame) :lval) (rplacd (cdddr :frame) (nreverse :lval))))) (defun :exchange-arguments (:larg :lval) (cond ((or (eq :larg '&nobind) (null :larg))) ((symbolp :larg) (rplaca :lval (prog1 (:cval :larg) (:scval :larg (car :lval))))) ((consp :larg) (:exchange-arguments (car :larg) (if (consp (car :larg)) (car :lval) :lval)) (:exchange-arguments (cdr :larg) (cdr :lval))))) (defvar :v) (defun :scval (:s :v) (if (boundp ':v) (set :s :v) (makunbound :s))) (defun :cval (:s) (if (boundp :s) (symeval :s) '←undef←)) (de :current-expr (definition) (let ((:frame-list (:up-frame-list (car :current-form) (car :current-function)))) (while :frame-list (setq definition (:find-expr (nextl :frame-list) definition))) (when (null (cdr :current-form)) (setq definition (:find-error definition))) definition)) ; Trouver l'expression ayant cre'e' le bloc frame dans sexpr (de :find-expr (frame sexpr) (or (:find-tree (or (selectq (car frame) ; sur le type du bloc (1 ; type lambda (1 llink fval lparam v1 ... vn) (lambda (expr) (and (consp (car expr)) (eq (cdar expr) (caddr frame))))) ; type label (2 fct1 ofval1 oftyp1 ... oftypn) (3 ; type e'chappement (3 tag-name) (lambda (expr) (and (memq (car expr) '(tag untilexit)) (consp (cdr expr)) (eq (cadr expr) (cadr frame))))) ; type itsoft (4 llink nom etat forme funct) (5 ; type lock (5 fval) (lambda (expr) (and (eq (car expr) 'lock) (consp (cdr expr)) (eq (cadr expr) (cadr frame))))) (6 ; type protect (6 progn) (lambda (expr) (and (eq (car expr) 'protect) (consp (cdr expr)) (eq (cddr expr) (cadr frame))))) ; type sys-protect (7) (8 ; type schedule (8 XXX) (lambda (expr) (eq (car expr) 'schedule))) (9 ; type tagbody (9 et1 corp1 ... etN corpN) ) ; type bloc (10 slot) (11 ; type progn (11 progn) (lambda (expr) (exit found (caadr frame))))) (lambda (expr) (exit found ()))) sexpr) sexpr)) ; Trouver l'expression qui a provoque' l'erreur dans expr (de :find-error (expr) (or (when (memq (cadr :error-message) '(errudf errbal errwna errilb break)) (:find-tree (lambda (expr) (eq expr :current-error-form)) expr)) (when (eq (cadr :error-message) 'errudv) (:find-tree (lambda (expr) (when (eq (car expr) (caddr :error-message)) (exit found (car expr)))) expr)) (:find-tree (lambda (expr) (and (eq (car expr) (car :error-message)) (:find-tree (lambda (expr) (eq expr :current-error-form)) expr))) expr) expr)) ; .SSection "Utilitaires" ; Recherche et substitution dans un arbre (de :find-tree (:fn :tree) (tag found (:find-tree1 :fn :tree))) (de :find-tree1 (:fn :tree) (when (consp :tree) (when (funcall :fn :tree) (exit found :tree)) (while (consp :tree) (:find-tree1 :fn (nextl :tree))))) ; Substitution avec test EQ (defun substq (n o s) (cond ((atom s) (if (eq s o) n s)) ((consp s) (cons (if (eq (car s) o) n (substq n o (car s))) (if (eq (cdr s) o) n (substq n o (cdr s))))))) ; Manipuler la pile sous forme de CSTACK (de :find-function (stack) (until (or (:has-function-definition (car stack)) (null stack)) (nextl stack)) stack) (defun :has-function-definition (frame) (and (eq (car frame) 1) (:findfn (caddr frame)))) (de :is-a-struct-access (frame) (and (eq (car frame) 1) (let ((valfn (caddr frame))) (and (consp valfn) (eq (car valfn) '&nobind) (consp (cdr valfn)) (consp (cadr valfn)) (eq (caadr valfn) '#:system:structaccess))))) ; Re'cupe'rer la de'finition d'une fonction (de :getdef (f) (cond ((memq f #:trace:trace) (:get-plist-def f 'trace)) ((getprop f 'resetfn) (:get-plist-def f 'resetfn)) (t (getdef f)))) (de :findfn (valfn) (or (findfn valfn) (cassoc valfn :resetfn-alist))) (de :get-plist-def (symbol prop) (makedef symbol (car (getprop symbol prop)) (cdr (getprop symbol prop)))) ; .SSection "Gestion des objets mis en valeur" (de :hilite-expr (expr1 expr2) (ifn expr1 expr2 (substq (:hilited:make expr1) expr1 expr2))) (defun :hilited:make (o) (tcons ':hilited o)) (defun :hilited:prin (f) (with ((tyattrib t)) (pprin (cdr f)))) (synonymq :hilited:pretty :hilited:prin) ; Limitation de l'impression hilite'e (defvar :outlist) (defvar :hibegin) (defvar :hiend) (defvar :nlines) (defvar #:tty:system:hilited:tyattrib ()) (de #:tty:system:hilited:tyattrib (x) (if x (setq :hibegin (cons :nlines (outpos))) (setq :hiend (cons :nlines (outpos))))) (de :hilited:eol () (incr :nlines) (newl :outlist (substring (outbuf) 0 (outpos))) (fillstring (outbuf) 0 #\sp) (outpos (lmargin))) (de :print-filtered (x) (let ((:nlines 0) (:hibegin ()) (:hiend ()) (:outlist ())) (let ((#:sys-package:itsoft ':hilited) (#:sys-package:tty '#:tty:system:hilited)) (pprint x)) (ifn (and :hibegin :hiend) (setq :hibegin (cons 0 0) :hiend (cons 0 0))) (:flush-hilited))) (de :flush-hilited () (setq :outlist (nreverse :outlist)) (unless (le :nlines #:system:debug-line) (let ((nscroll (min (sub1 (car :hibegin)) (sub (add1 (car :hiend)) #:system:debug-line)))) (when (gt nscroll 0) (rplaca :hibegin (sub (car :hibegin) nscroll)) (rplaca :hiend (min (sub (car :hiend) nscroll) (sub1 #:system:debug-line))) (rplacd :outlist (nthcdr (add1 nscroll) :outlist)) (rplaca :outlist (catenate (car :outlist) " ..."))) (when (gt (length :outlist) #:system:debug-line) (let ((boutlist (nthcdr (sub1 #:system:debug-line) :outlist))) (rplacd boutlist ()) (rplaca boutlist (catenate (car boutlist) " ...")) (when (gt (car :hiend) (sub1 #:system:debug-line)) (rplaca :hiend (sub1 #:system:debug-line)) (rplacd :hiend (slen (car boutlist)))))))) (let ((#:system:print-for-read ())) (with ((outchan t)) (with ((rmargin 78)) (repeat (car :hibegin) (print (nextl :outlist))) (prin (substring (car :outlist) 0 (cdr :hibegin))) (tyattrib t) (cond ((eq (car :hiend) (car :hibegin)) (prin (substring (car :outlist) (cdr :hibegin) (sub (cdr :hiend) (cdr :hibegin))))) (t (print (substring (nextl :outlist) (cdr :hibegin))) (repeat (sub1 (sub (car :hiend) (car :hibegin))) (tyattrib t) (print (nextl :outlist))) (tyattrib t) (prin (substring (car :outlist) 0 (cdr :hiend))))) (tyattrib ()) (print (substring (nextl :outlist) (cdr :hiend))) (while :outlist (print (nextl :outlist)))))))