X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fcode%2Fdebug.lisp;h=1ec043bb54457066e285a1d99e1e21b78302ff66;hb=d0511d2a94e7d2d346e2f4acc38ff84cd99a74b1;hp=ba1c054d00ab76cc77c9e2121ab11b970e5d2da3;hpb=d45e8a2e9167150c8283783152d2449bd8d59d2d;p=sbcl.git diff --git a/src/code/debug.lisp b/src/code/debug.lisp index ba1c054..1ec043b 100644 --- a/src/code/debug.lisp +++ b/src/code/debug.lisp @@ -13,13 +13,43 @@ ;;;; variables and constants -(defvar *debug-print-level* 3 +;;; things to consider when tweaking these values: +;;; * We're afraid to just default them to NIL and NIL, in case the +;;; user inadvertently causes a hairy data structure to be printed +;;; when he inadvertently enters the debugger. +;;; * We don't want to truncate output too much. These days anyone +;;; can easily run their Lisp in a windowing system or under Emacs, +;;; so it's not the end of the world even if the worst case is a +;;; few thousand lines of output. +;;; * As condition :REPORT methods are converted to use the pretty +;;; printer, they acquire *PRINT-LEVEL* constraints, so e.g. under +;;; sbcl-0.7.1.28's old value of *DEBUG-PRINT-LEVEL*=3, an +;;; ARG-COUNT-ERROR printed as +;;; error while parsing arguments to DESTRUCTURING-BIND: +;;; invalid number of elements in +;;; # +;;; to satisfy lambda list +;;; #: +;;; exactly 2 expected, but 5 found +;;; +;;; FIXME: These variables were deprecated in late February 2004, and +;;; can probably be removed in about a year. +(defvar *debug-print-level* 5 + #!+sb-doc + "(This is deprecated in favor of *DEBUG-PRINT-VARIABLE-ALIST*.) + +*PRINT-LEVEL* for the debugger") +(defvar *debug-print-length* 7 #!+sb-doc - "*PRINT-LEVEL* for the debugger") + "(This is deprecated in favor of *DEBUG-PRINT-VARIABLE-ALIST*.) -(defvar *debug-print-length* 5 +*PRINT-LENGTH* for the debugger") + +(defvar *debug-print-variable-alist* nil #!+sb-doc - "*PRINT-LENGTH* for the debugger") + "an association list describing new bindings for special variables +(typically *PRINT-FOO* variables) to be used within the debugger, e.g. +((*PRINT-LENGTH* . 10) (*PRINT-LEVEL* . 6) (*PRINT-PRETTY* . NIL))") (defvar *debug-readtable* ;; KLUDGE: This can't be initialized in a cold toplevel form, @@ -54,38 +84,32 @@ "Should the debugger display beginner-oriented help messages?") (defun debug-prompt (stream) - - ;; old behavior, will probably go away in sbcl-0.7.x - (format stream "~%~D" (sb!di:frame-number *current-frame*)) - (dotimes (i *debug-command-level*) - (write-char #\] stream)) - (write-char #\space stream) - - ;; planned new behavior, delayed since it will break ILISP - #+nil + (sb!thread::get-foreground) (format stream - "~%~D~:[~;[~D~]] " + "~%~W~:[~;[~W~]] " (sb!di:frame-number *current-frame*) (> *debug-command-level* 1) *debug-command-level*)) (defparameter *debug-help-string* -"The prompt is right square brackets, the number indicating how many - recursive command loops you are in. -Any command may be uniquely abbreviated. +"The debug prompt is square brackets, with number(s) indicating the current + control stack level and, if you've entered the debugger recursively, how + deeply recursed you are. +Any command -- including the name of a restart -- may be uniquely abbreviated. The debugger rebinds various special variables for controlling i/o, sometimes to defaults (much like WITH-STANDARD-IO-SYNTAX does) and sometimes to its own special values, e.g. SB-DEBUG:*DEBUG-PRINT-LEVEL*. -Debug commands do not affect * and friends, but evaluation in the debug loop - does affect these variables. +Debug commands do not affect *, //, and similar variables, but evaluation in + the debug loop does affect these variables. SB-DEBUG:*FLUSH-DEBUG-ERRORS* controls whether errors at the debug prompt - drop you into deeper into the debugger. + drop you deeper into the debugger. Getting in and out of the debugger: RESTART invokes restart numbered as shown (prompt if not given). ERROR prints the error condition and restart cases. - The name of any restart, or its number, is a valid command, and is the same - as using RESTART to invoke that restart. + The number of any restart, or its name, or a unique abbreviation for its + name, is a valid command, and is the same as using RESTART to invoke + that restart. Changing frames: U up frame D down frame @@ -112,12 +136,20 @@ Breakpoints and steps: STEP [n] Step to the next location or step n times. Function and macro commands: - (SB-DEBUG:DEBUG-RETURN expression) - Exit the debugger, returning expression's values from the current frame. (SB-DEBUG:ARG n) Return the n'th argument in the current frame. (SB-DEBUG:VAR string-or-symbol [id]) - Returns the value of the specified variable in the current frame.") + Returns the value of the specified variable in the current frame. + +Other commands: + RETURN expr + [EXPERIMENTAL] Return the values resulting from evaluation of expr + from the current frame, if this frame was compiled with a sufficiently + high DEBUG optimization quality. + SLURP + Discard all pending input on *STANDARD-INPUT*. (This can be + useful when the debugger was invoked to handle an error in + deeply nested input syntax, and now the reader is confused.)") ;;; This is used to communicate to DEBUG-LOOP that we are at a step breakpoint. (define-condition step-condition (simple-condition) ()) @@ -157,8 +189,8 @@ Function and macro commands: (declaim (type integer *number-of-steps*)) ;;; This is used when listing and setting breakpoints. -(defvar *default-breakpoint-debug-function* nil) -(declaim (type (or list sb!di:debug-function) *default-breakpoint-debug-function*)) +(defvar *default-breakpoint-debug-fun* nil) +(declaim (type (or list sb!di:debug-fun) *default-breakpoint-debug-fun*)) ;;;; code location utilities @@ -194,11 +226,10 @@ Function and macro commands: (setf next-list (next-code-locations (first next-list)))) next-list))) -;;; Returns a list of code-locations of the possible breakpoints of the -;;; debug-function passed. -(defun possible-breakpoints (debug-function) +;;; Return a list of code-locations of the possible breakpoints of DEBUG-FUN. +(defun possible-breakpoints (debug-fun) (let ((possible-breakpoints nil)) - (sb!di:do-debug-function-blocks (debug-block debug-function) + (sb!di:do-debug-fun-blocks (debug-block debug-fun) (unless (sb!di:debug-block-elsewhere-p debug-block) (if *only-block-start-locations* (push (first-code-location debug-block) possible-breakpoints) @@ -208,8 +239,8 @@ Function and macro commands: (push code-location possible-breakpoints)))))) (nreverse possible-breakpoints))) -;;; Searches the info-list for the item passed (code-location, -;;; debug-function, or breakpoint-info). If the item passed is a debug +;;; Search the info-list for the item passed (CODE-LOCATION, +;;; DEBUG-FUN, or BREAKPOINT-INFO). If the item passed is a debug ;;; function then kind will be compared if it was specified. The kind ;;; if also compared if a breakpoint-info is passed since it's in the ;;; breakpoint. The info structure is returned if found. @@ -220,19 +251,19 @@ Function and macro commands: (cond ((sb!di:code-location-p place) (find place info-list :key #'breakpoint-info-place - :test #'(lambda (x y) (and (sb!di:code-location-p y) - (sb!di:code-location= x y))))) + :test (lambda (x y) (and (sb!di:code-location-p y) + (sb!di:code-location= x y))))) (t (find place info-list - :test #'(lambda (x-debug-function y-info) - (let ((y-place (breakpoint-info-place y-info)) - (y-breakpoint (breakpoint-info-breakpoint - y-info))) - (and (sb!di:debug-function-p y-place) - (eq x-debug-function y-place) - (or (not kind) - (eq kind (sb!di:breakpoint-kind - y-breakpoint)))))))))) + :test (lambda (x-debug-fun y-info) + (let ((y-place (breakpoint-info-place y-info)) + (y-breakpoint (breakpoint-info-breakpoint + y-info))) + (and (sb!di:debug-fun-p y-place) + (eq x-debug-fun y-place) + (or (not kind) + (eq kind (sb!di:breakpoint-kind + y-breakpoint)))))))))) ;;; If LOC is an unknown location, then try to find the block start ;;; location. Used by source printing to some information instead of @@ -254,31 +285,32 @@ Function and macro commands: ;;;; the BREAKPOINT-INFO structure ;;; info about a made breakpoint -(defstruct (breakpoint-info (:copier nil)) +(defstruct (breakpoint-info (:copier nil) + (:constructor %make-breakpoint-info)) ;; where we are going to stop - (place (required-argument) - :type (or sb!di:code-location sb!di:debug-function)) - ;; the breakpoint returned by sb!di:make-breakpoint - (breakpoint (required-argument) :type sb!di:breakpoint) + (place (missing-arg) + :type (or sb!di:code-location sb!di:debug-fun) + :read-only t) + ;; the breakpoint returned by SB!DI:MAKE-BREAKPOINT + (breakpoint (missing-arg) :type sb!di:breakpoint :read-only t) ;; the function returned from SB!DI:PREPROCESS-FOR-EVAL. If result is ;; non-NIL, drop into the debugger. - (break #'identity :type function) - ;; the function returned from sb!di:preprocess-for-eval. If result is + (break #'identity :type function :read-only t) + ;; the function returned from SB!DI:PREPROCESS-FOR-EVAL. If result is ;; non-NIL, eval (each) print and print results. - (condition #'identity :type function) - ;; the list of functions from sb!di:preprocess-for-eval to evaluate. - ;; Results are conditionally printed. Car of each element is the - ;; function, cdr is the form it goes with. - (print nil :type list) + (condition #'identity :type function :read-only t) + ;; the list of functions from SB!DI:PREPROCESS-FOR-EVAL to evaluate. + ;; Results are conditionally printed. CAR of each element is the + ;; function, CDR is the form it goes with. + (print nil :type list :read-only t) ;; the number used when listing the possible breakpoints within a - ;; function. Could also be a symbol such as start or end. - (code-location-number (required-argument) :type (or symbol integer)) - ;; the number used when listing the breakpoints active and to delete - ;; breakpoints - (breakpoint-number (required-argument) :type integer)) - -;;; Return a new BREAKPOINT-INFO structure with the info passed. -(defun create-breakpoint-info (place breakpoint code-location-number + ;; function; or could also be a symbol such as START or END + (code-location-selector (missing-arg) :type (or symbol integer) :read-only t) + ;; the number used when listing the active breakpoints, and when + ;; deleting breakpoints + (breakpoint-number (missing-arg) :type integer :read-only t)) + +(defun create-breakpoint-info (place breakpoint code-location-selector &key (break #'identity) (condition #'identity) (print nil)) (setf *breakpoints* @@ -290,40 +322,40 @@ Function and macro commands: (first breakpoints))))) i)))) - (make-breakpoint-info :place place :breakpoint breakpoint - :code-location-number code-location-number - :breakpoint-number breakpoint-number - :break break :condition condition :print print))) + (%make-breakpoint-info :place place + :breakpoint breakpoint + :code-location-selector code-location-selector + :breakpoint-number breakpoint-number + :break break + :condition condition + :print print))) -;;; Print the breakpoint info for the breakpoint-info structure passed. (defun print-breakpoint-info (breakpoint-info) (let ((place (breakpoint-info-place breakpoint-info)) - (bp-number (breakpoint-info-breakpoint-number breakpoint-info)) - (loc-number (breakpoint-info-code-location-number breakpoint-info))) + (bp-number (breakpoint-info-breakpoint-number breakpoint-info))) (case (sb!di:breakpoint-kind (breakpoint-info-breakpoint breakpoint-info)) (:code-location (print-code-location-source-form place 0) (format t "~&~S: ~S in ~S" bp-number - loc-number - (sb!di:debug-function-name (sb!di:code-location-debug-function - place)))) - (:function-start - (format t "~&~S: FUNCTION-START in ~S" bp-number - (sb!di:debug-function-name place))) - (:function-end - (format t "~&~S: FUNCTION-END in ~S" bp-number - (sb!di:debug-function-name place)))))) + (breakpoint-info-code-location-selector breakpoint-info) + (sb!di:debug-fun-name (sb!di:code-location-debug-fun place)))) + (:fun-start + (format t "~&~S: FUN-START in ~S" bp-number + (sb!di:debug-fun-name place))) + (:fun-end + (format t "~&~S: FUN-END in ~S" bp-number + (sb!di:debug-fun-name place)))))) -;;;; MAIN-HOOK-FUNCTION for steps and breakpoints +;;;; MAIN-HOOK-FUN for steps and breakpoints ;;; This must be passed as the hook function. It keeps track of where ;;; STEP breakpoints are. -(defun main-hook-function (current-frame breakpoint &optional return-vals - function-end-cookie) - (setf *default-breakpoint-debug-function* - (sb!di:frame-debug-function current-frame)) +(defun main-hook-fun (current-frame breakpoint &optional return-vals + fun-end-cookie) + (setf *default-breakpoint-debug-fun* + (sb!di:frame-debug-fun current-frame)) (dolist (step-info *step-breakpoints*) (sb!di:delete-breakpoint (breakpoint-info-breakpoint step-info)) (let ((bp-info (location-in-list step-info *breakpoints*))) @@ -347,7 +379,7 @@ Function and macro commands: (print-common-info () (build-string (with-output-to-string (*standard-output*) - (when function-end-cookie + (when fun-end-cookie (format t "~%Return values: ~S" return-vals)) (when condition (when (breakpoint-info-print bp-hit-info) @@ -388,7 +420,7 @@ Function and macro commands: (break string) (format t "~A" string))) (t - (break "error in main-hook-function: unknown breakpoint")))))) + (break "unknown breakpoint")))))) ;;; Set breakpoints at the next possible code-locations. After calling ;;; this, either (CONTINUE) if in the debugger or just let program flow @@ -410,17 +442,17 @@ Function and macro commands: (when bp-info (sb!di:deactivate-breakpoint (breakpoint-info-breakpoint bp-info)))) - (let ((bp (sb!di:make-breakpoint #'main-hook-function code-location + (let ((bp (sb!di:make-breakpoint #'main-hook-fun code-location :kind :code-location))) (sb!di:activate-breakpoint bp) (push (create-breakpoint-info code-location bp 0) *step-breakpoints*)))) (t - (let* ((debug-function (sb!di:frame-debug-function *current-frame*)) - (bp (sb!di:make-breakpoint #'main-hook-function debug-function - :kind :function-end))) + (let* ((debug-fun (sb!di:frame-debug-fun *current-frame*)) + (bp (sb!di:make-breakpoint #'main-hook-fun debug-fun + :kind :fun-end))) (sb!di:activate-breakpoint bp) - (push (create-breakpoint-info debug-function bp 0) + (push (create-breakpoint-info debug-fun bp 0) *step-breakpoints*)))))))) ;;;; STEP @@ -428,9 +460,10 @@ Function and macro commands: ;;; ANSI specifies that this macro shall exist, even if only as a ;;; trivial placeholder like this. (defmacro step (form) - "a trivial placeholder implementation of the CL:STEP macro required by - the ANSI spec" - `(progn + "This is a trivial placeholder implementation of the CL:STEP macro required + by the ANSI spec, simply expanding to `(LET () ,FORM). A more featureful + version would be welcome, we just haven't written it." + `(let () ,form)) ;;;; BACKTRACE @@ -439,7 +472,7 @@ Function and macro commands: (*standard-output* *debug-io*)) #!+sb-doc "Show a listing of the call stack going down from the current frame. In the - debugger, the current frame is indicated by the prompt. Count is how many + debugger, the current frame is indicated by the prompt. COUNT is how many frames to show." (fresh-line *standard-output*) (do ((frame (if *in-the-debugger* *current-frame* (sb!di:top-frame)) @@ -449,6 +482,20 @@ Function and macro commands: (print-frame-call frame :number t)) (fresh-line *standard-output*) (values)) + +(defun backtrace-as-list (&optional (count most-positive-fixnum)) + #!+sb-doc "Return a list representing the current BACKTRACE." + (do ((reversed-result nil) + (frame (if *in-the-debugger* *current-frame* (sb!di:top-frame)) + (sb!di:frame-down frame)) + (count count (1- count))) + ((or (null frame) (zerop count)) + (nreverse reversed-result)) + (push (frame-call-as-list frame) reversed-result))) + +(defun frame-call-as-list (frame) + (cons (sb!di:debug-fun-name (sb!di:frame-debug-fun frame)) + (frame-args-as-list frame))) ;;;; frame printing @@ -486,53 +533,73 @@ Function and macro commands: ) ; EVAL-WHEN ;;; This is used in constructing arg lists for debugger printing when -;;; the arg list is unavailable, some arg is unavailable or unused, -;;; etc. +;;; the arg list is unavailable, some arg is unavailable or unused, etc. (defstruct (unprintable-object (:constructor make-unprintable-object (string)) (:print-object (lambda (x s) - (print-unreadable-object (x s :type t) + (print-unreadable-object (x s) (write-string (unprintable-object-string x) s)))) (:copier nil)) string) -;;; Print FRAME with verbosity level 1. If we hit a &REST arg, then -;;; print as many of the values as possible, punting the loop over -;;; lambda-list variables since any other arguments will be in the -;;; &REST arg's list of values. -(defun print-frame-call-1 (frame) - (let* ((d-fun (sb!di:frame-debug-function frame)) - (loc (sb!di:frame-code-location frame)) - (results (list (sb!di:debug-function-name d-fun)))) +;;; Extract the function argument values for a debug frame. +(defun frame-args-as-list (frame) + (let ((debug-fun (sb!di:frame-debug-fun frame)) + (loc (sb!di:frame-code-location frame)) + (reversed-result nil)) (handler-case - (dolist (ele (sb!di:debug-function-lambda-list d-fun)) - (lambda-list-element-dispatch ele - :required ((push (frame-call-arg ele loc frame) results)) - :optional ((push (frame-call-arg (second ele) loc frame) results)) - :keyword ((push (second ele) results) - (push (frame-call-arg (third ele) loc frame) results)) - :deleted ((push (frame-call-arg ele loc frame) results)) - :rest ((lambda-var-dispatch (second ele) loc + (progn + (dolist (ele (sb!di:debug-fun-lambda-list debug-fun)) + (lambda-list-element-dispatch ele + :required ((push (frame-call-arg ele loc frame) reversed-result)) + :optional ((push (frame-call-arg (second ele) loc frame) + reversed-result)) + :keyword ((push (second ele) reversed-result) + (push (frame-call-arg (third ele) loc frame) + reversed-result)) + :deleted ((push (frame-call-arg ele loc frame) reversed-result)) + :rest ((lambda-var-dispatch (second ele) loc nil (progn - (setf results + (setf reversed-result (append (reverse (sb!di:debug-var-value (second ele) frame)) - results)) + reversed-result)) (return)) (push (make-unprintable-object "unavailable &REST argument") - results))))) + reversed-result))))) + ;; As long as we do an ordinary return (as opposed to SIGNALing + ;; a CONDITION) from the DOLIST above: + (nreverse reversed-result)) (sb!di:lambda-list-unavailable () - (push (make-unprintable-object "lambda list unavailable") results))) - (pprint-logical-block (*standard-output* nil) - (let ((x (nreverse (mapcar #'ensure-printable-object results)))) - (format t "(~@<~S~{ ~_~S~}~:>)" (first x) (rest x)))) - (when (sb!di:debug-function-kind d-fun) + (make-unprintable-object "unavailable lambda list"))))) + +;;; Print FRAME with verbosity level 1. If we hit a &REST arg, then +;;; print as many of the values as possible, punting the loop over +;;; lambda-list variables since any other arguments will be in the +;;; &REST arg's list of values. +(defun print-frame-call-1 (frame) + (let ((debug-fun (sb!di:frame-debug-fun frame))) + + (pprint-logical-block (*standard-output* nil :prefix "(" :suffix ")") + (let ((args (ensure-printable-object (frame-args-as-list frame)))) + ;; Since we go to some trouble to make nice informative function + ;; names like (PRINT-OBJECT :AROUND (CLOWN T)), let's make sure + ;; that they aren't truncated by *PRINT-LENGTH* and *PRINT-LEVEL*. + (let ((*print-length* nil) + (*print-level* nil)) + (prin1 (ensure-printable-object (sb!di:debug-fun-name debug-fun)))) + ;; For the function arguments, we can just print normally. + (if (listp args) + (format t "~{ ~_~S~}" args) + (format t " ~S" args)))) + + (when (sb!di:debug-fun-kind debug-fun) (write-char #\[) - (prin1 (sb!di:debug-function-kind d-fun)) + (prin1 (sb!di:debug-fun-kind debug-fun)) (write-char #\])))) (defun ensure-printable-object (object) @@ -552,7 +619,7 @@ Function and macro commands: ;;; Prints a representation of the function call causing FRAME to ;;; exist. VERBOSITY indicates the level of information to output; -;;; zero indicates just printing the debug-function's name, and one +;;; zero indicates just printing the DEBUG-FUN's name, and one ;;; indicates displaying call-like, one-liner format with argument ;;; values. (defun print-frame-call (frame &key (verbosity 1) (number nil)) @@ -585,19 +652,118 @@ Function and macro commands: of this variable to the function because it binds *DEBUGGER-HOOK* to NIL around the invocation.") +(defvar *invoke-debugger-hook* nil + #!+sb-doc + "This is either NIL or a designator for a function of two arguments, + to be run when the debugger is about to be entered. The function is + run with *INVOKE-DEBUGGER-HOOK* bound to NIL to minimize recursive + errors, and receives as arguments the condition that triggered + debugger entry and the previous value of *INVOKE-DEBUGGER-HOOK* + + This mechanism is an SBCL extension similar to the standard *DEBUGGER-HOOK*. + In contrast to *DEBUGGER-HOOK*, it is observed by INVOKE-DEBUGGER even when + called by BREAK.") + ;;; These are bound on each invocation of INVOKE-DEBUGGER. (defvar *debug-restarts*) (defvar *debug-condition*) - +(defvar *nested-debug-condition*) + +;;; Oh, what a tangled web we weave when we preserve backwards +;;; compatibility with 1968-style use of global variables to control +;;; per-stream i/o properties; there's really no way to get this +;;; quite right, but we do what we can. +(defun funcall-with-debug-io-syntax (fun &rest rest) + (declare (type function fun)) + ;; Try to force the other special variables into a useful state. + (let (;; Protect from WITH-STANDARD-IO-SYNTAX some variables where + ;; any default we might use is less useful than just reusing + ;; the global values. + (original-package *package*) + (original-print-pretty *print-pretty*)) + (with-standard-io-syntax + (let (;; We want the printer and reader to be in a useful state, + ;; regardless of where the debugger was invoked in the + ;; program. WITH-STANDARD-IO-SYNTAX did much of what we + ;; want, but + ;; * It doesn't affect our internal special variables + ;; like *CURRENT-LEVEL-IN-PRINT*. + ;; * It isn't customizable. + ;; * It doesn't set *PRINT-READABLY* to the same value + ;; as the toplevel default. + ;; * It sets *PACKAGE* to COMMON-LISP-USER, which is not + ;; helpful behavior for a debugger. + ;; * There's no particularly good debugger default for + ;; *PRINT-PRETTY*, since T is usually what you want + ;; -- except absolutely not what you want when you're + ;; debugging failures in PRINT-OBJECT logic. + ;; We try to address all these issues with explicit + ;; rebindings here. + (sb!kernel:*current-level-in-print* 0) + (*package* original-package) + (*print-pretty* original-print-pretty) + (*print-readably* nil) + ;; Clear the circularity machinery to try to to reduce the + ;; pain from sharing the circularity table across all + ;; streams; if these are not rebound here, then setting + ;; *PRINT-CIRCLE* within the debugger when debugging in a + ;; state where something circular was being printed (e.g., + ;; because the debugger was entered on an error in a + ;; PRINT-OBJECT method) makes a hopeless mess. Binding them + ;; here does seem somewhat ugly because it makes it more + ;; difficult to debug the printing-of-circularities code + ;; itself; however, as far as I (WHN, 2004-05-29) can see, + ;; that's almost entirely academic as long as there's one + ;; shared *C-H-T* for all streams (i.e., it's already + ;; unreasonably difficult to debug print-circle machinery + ;; given the buggy crosstalk between the debugger streams + ;; and the stream you're trying to watch), and any fix for + ;; that buggy arrangement will likely let this hack go away + ;; naturally. + (sb!impl::*circularity-hash-table* . nil) + (sb!impl::*circularity-counter* . nil) + ;; These rebindings are now (as of early 2004) deprecated, + ;; with the new *PRINT-VAR-ALIST* mechanism preferred. + (*print-length* *debug-print-length*) + (*print-level* *debug-print-level*) + (*readtable* *debug-readtable*)) + (progv + ;; (Why NREVERSE? PROGV makes the later entries have + ;; precedence over the earlier entries. + ;; *DEBUG-PRINT-VARIABLE-ALIST* is called an alist, so it's + ;; expected that its earlier entries have precedence. And + ;; the earlier-has-precedence behavior is mostly more + ;; convenient, so that programmers can use PUSH or LIST* to + ;; customize *DEBUG-PRINT-VARIABLE-ALIST*.) + (nreverse (mapcar #'car *debug-print-variable-alist*)) + (nreverse (mapcar #'cdr *debug-print-variable-alist*)) + (apply fun rest)))))) + +;;; the ordinary ANSI case of INVOKE-DEBUGGER, when not suppressed by +;;; command-line --disable-debugger option (defun invoke-debugger (condition) #!+sb-doc "Enter the debugger." + (let ((old-hook *debugger-hook*)) (when old-hook (let ((*debugger-hook* nil)) (funcall old-hook condition old-hook)))) - (sb!unix:unix-sigsetmask 0) + (let ((old-hook *invoke-debugger-hook*)) + (when old-hook + (let ((*invoke-debugger-hook* nil)) + (funcall old-hook condition old-hook)))) + + ;; Note: CMU CL had (SB-UNIX:UNIX-SIGSETMASK 0) here, to reset the + ;; signal state in the case that we wind up in the debugger as a + ;; result of something done by a signal handler. It's not + ;; altogether obvious that this is necessary, and indeed SBCL has + ;; not been doing it since 0.7.8.5. But nobody seems altogether + ;; convinced yet + ;; -- dan 2003.11.11, based on earlier comment of WHN 2002-09-28 + ;; We definitely want *PACKAGE* to be of valid type. + ;; ;; Elsewhere in the system, we use the SANE-PACKAGE function for ;; this, but here causing an exception just as we're trying to handle ;; an exception would be confusing, so instead we use a special hack. @@ -608,114 +774,190 @@ Function and macro commands: "The value of ~S was not an undeleted PACKAGE. It has been reset to ~S." '*package* *package*)) - (let (;; Save *PACKAGE* to protect it from WITH-STANDARD-IO-SYNTAX. - (original-package *package*)) - (with-standard-io-syntax - (let* ((*debug-condition* condition) - (*debug-restarts* (compute-restarts condition)) - ;; We want the i/o subsystem to be in a known, useful - ;; state, regardless of where the debugger was invoked in - ;; the program. WITH-STANDARD-IO-SYNTAX does some of that, - ;; but - ;; 1. It doesn't affect our internal special variables - ;; like *CURRENT-LEVEL*. - ;; 2. It isn't customizable. - ;; 3. It doesn't set *PRINT-READABLY* or *PRINT-PRETTY* - ;; to the same value as the toplevel default. - ;; 4. It sets *PACKAGE* to COMMON-LISP-USER, which is not - ;; helpful behavior for a debugger. - ;; We try to remedy all these problems with explicit - ;; rebindings here. - (sb!kernel:*current-level* 0) - (*print-length* *debug-print-length*) - (*print-level* *debug-print-level*) - (*readtable* *debug-readtable*) - (*print-readably* nil) - (*print-pretty* t) - (*package* original-package)) - - ;; Before we start our own output, finish any pending output. - ;; Otherwise, if the user tried to track the progress of - ;; his program using PRINT statements, he'd tend to lose - ;; the last line of output or so, and get confused. - (flush-standard-output-streams) - - ;; (The initial output here goes to *ERROR-OUTPUT*, because the - ;; initial output is not interactive, just an error message, - ;; and when people redirect *ERROR-OUTPUT*, they could - ;; reasonably expect to see error messages logged there, - ;; regardless of what the debugger does afterwards.) - (handler-case - (format *error-output* - "~2&~@~%" - (type-of *debug-condition*) - *debug-condition*) - (error (condition) - (format *error-output* - "~&(caught ~S trying to print ~S when entering debugger)~%" - (type-of condition) - '*debug-condition*))) - - ;; After the initial error/condition/whatever announcement to - ;; *ERROR-OUTPUT*, we become interactive, and should talk on - ;; *DEBUG-IO* from now on. (KLUDGE: This is a normative - ;; statement, not a description of reality.:-| There's a lot of - ;; older debugger code which was written to do i/o on whatever - ;; stream was in fashion at the time, and not all of it has - ;; been converted to behave this way. -- WHN 2000-11-16) - (let (;; FIXME: The first two bindings here seem wrong, - ;; violating the principle of least surprise, and making - ;; it impossible for the user to do reasonable things - ;; like using PRINT at the debugger prompt to send output - ;; to the program's ordinary (possibly - ;; redirected-to-a-file) *STANDARD-OUTPUT*, or using - ;; PEEK-CHAR or some such thing on the program's ordinary - ;; (possibly also redirected) *STANDARD-INPUT*. - (*standard-input* *debug-io*) - (*standard-output* *debug-io*) - ;; This seems reasonable: e.g. if the user has redirected - ;; *ERROR-OUTPUT* to some log file, it's probably wrong - ;; to send errors which occur in interactive debugging to - ;; that file, and right to send them to *DEBUG-IO*. - (*error-output* *debug-io*)) - (unless (typep condition 'step-condition) - (when *debug-beginner-help-p* - (format *debug-io* - "~%~@~2%" - '*debug-condition* - '*debug-beginner-help-p*)) - (show-restarts *debug-restarts* *debug-io*)) - (internal-debug)))))) + (type-of *debug-condition*) + (sb!thread:current-thread-id) + *debug-condition*) + (error (condition) + (setf *nested-debug-condition* condition) + (let ((ndc-type (type-of *nested-debug-condition*))) + (format *error-output* + "~&~@<(A ~S was caught when trying to print ~S when ~ + entering the debugger. Printing was aborted and the ~ + ~S was stored in ~S.)~@:>~%" + ndc-type + '*debug-condition* + ndc-type + '*nested-debug-condition*)) + (when (typep condition 'cell-error) + ;; what we really want to know when it's e.g. an UNBOUND-VARIABLE: + (format *error-output* + "~&(CELL-ERROR-NAME ~S) = ~S~%" + '*debug-condition* + (cell-error-name *debug-condition*))))) + + (let ((background-p (sb!thread::debugger-wait-until-foreground-thread + *debug-io*))) + + ;; After the initial error/condition/whatever announcement to + ;; *ERROR-OUTPUT*, we become interactive, and should talk on + ;; *DEBUG-IO* from now on. (KLUDGE: This is a normative + ;; statement, not a description of reality.:-| There's a lot of + ;; older debugger code which was written to do i/o on whatever + ;; stream was in fashion at the time, and not all of it has + ;; been converted to behave this way. -- WHN 2000-11-16) + + (unwind-protect + (let (;; FIXME: Rebinding *STANDARD-OUTPUT* here seems wrong, + ;; violating the principle of least surprise, and making + ;; it impossible for the user to do reasonable things + ;; like using PRINT at the debugger prompt to send output + ;; to the program's ordinary (possibly + ;; redirected-to-a-file) *STANDARD-OUTPUT*. (CMU CL + ;; used to rebind *STANDARD-INPUT* here too, but that's + ;; been fixed already.) + (*standard-output* *debug-io*) + ;; This seems reasonable: e.g. if the user has redirected + ;; *ERROR-OUTPUT* to some log file, it's probably wrong + ;; to send errors which occur in interactive debugging to + ;; that file, and right to send them to *DEBUG-IO*. + (*error-output* *debug-io*)) + (unless (typep condition 'step-condition) + (when *debug-beginner-help-p* + (format *debug-io* + "~%~@~2%")) + (show-restarts *debug-restarts* *debug-io*)) + (internal-debug)) + (when background-p + (sb!thread::release-foreground)))))) + +;;; this function is for use in *INVOKE-DEBUGGER-HOOK* when ordinary +;;; ANSI behavior has been suppressed by the "--disable-debugger" +;;; command-line option +(defun debugger-disabled-hook (condition me) + (declare (ignore me)) + ;; There is no one there to interact with, so report the + ;; condition and terminate the program. + (flet ((failure-quit (&key recklessly-p) + (/show0 "in FAILURE-QUIT (in --disable-debugger debugger hook)") + (quit :unix-status 1 :recklessly-p recklessly-p))) + ;; This HANDLER-CASE is here mostly to stop output immediately + ;; (and fall through to QUIT) when there's an I/O error. Thus, + ;; when we're run under a shell script or something, we can die + ;; cleanly when the script dies (and our pipes are cut), instead + ;; of falling into ldb or something messy like that. Similarly, we + ;; can terminate cleanly even if BACKTRACE dies because of bugs in + ;; user PRINT-OBJECT methods. + (handler-case + (progn + (format *error-output* + "~&~@~2%" + (type-of condition) + (sb!thread:current-thread-id) + condition) + ;; Flush *ERROR-OUTPUT* even before the BACKTRACE, so that + ;; even if we hit an error within BACKTRACE (e.g. a bug in + ;; the debugger's own frame-walking code, or a bug in a user + ;; PRINT-OBJECT method) we'll at least have the CONDITION + ;; printed out before we die. + (finish-output *error-output*) + ;; (Where to truncate the BACKTRACE is of course arbitrary, but + ;; it seems as though we should at least truncate it somewhere.) + (sb!debug:backtrace 128 *error-output*) + (format + *error-output* + "~%unhandled condition in --disable-debugger mode, quitting~%") + (finish-output *error-output*) + (failure-quit)) + (condition () + ;; We IGNORE-ERRORS here because even %PRIMITIVE PRINT can + ;; fail when our output streams are blown away, as e.g. when + ;; we're running under a Unix shell script and it dies somehow + ;; (e.g. because of a SIGINT). In that case, we might as well + ;; just give it up for a bad job, and stop trying to notify + ;; the user of anything. + ;; + ;; Actually, the only way I've run across to exercise the + ;; problem is to have more than one layer of shell script. + ;; I have a shell script which does + ;; time nice -10 sh make.sh "$1" 2>&1 | tee make.tmp + ;; and the problem occurs when I interrupt this with Ctrl-C + ;; under Linux 2.2.14-5.0 and GNU bash, version 1.14.7(1). + ;; I haven't figured out whether it's bash, time, tee, Linux, or + ;; what that is responsible, but that it's possible at all + ;; means that we should IGNORE-ERRORS here. -- WHN 2001-04-24 + (ignore-errors + (%primitive print + "Argh! error within --disable-debugger error handling")) + (failure-quit :recklessly-p t))))) + +;;; halt-on-failures and prompt-on-failures modes, suitable for +;;; noninteractive and interactive use respectively +(defun disable-debugger () + (when (eql *invoke-debugger-hook* nil) + (setf *debug-io* *error-output* + *invoke-debugger-hook* 'debugger-disabled-hook))) + +(defun enable-debugger () + (when (eql *invoke-debugger-hook* 'debugger-disabled-hook) + (setf *invoke-debugger-hook* nil))) + +(setf *debug-io* *query-io*) (defun show-restarts (restarts s) - (when restarts - (format s "~&restarts:~%") - (let ((count 0) - (names-used '(nil)) - (max-name-len 0)) - (dolist (restart restarts) - (let ((name (restart-name restart))) - (when name - (let ((len (length (princ-to-string name)))) - (when (> len max-name-len) - (setf max-name-len len)))))) - (unless (zerop max-name-len) - (incf max-name-len 3)) - (dolist (restart restarts) - (let ((name (restart-name restart))) - (cond ((member name names-used) - (format s "~& ~2D: ~@VT~A~%" count max-name-len restart)) - (t - (format s "~& ~2D: [~VA] ~A~%" - count (- max-name-len 3) name restart) - (push name names-used)))) - (incf count))))) + (cond ((null restarts) + (format s + "~&(no restarts: If you didn't do this on purpose, ~ + please report it as a bug.)~%")) + (t + (format s "~&restarts (invokable by number or by ~ + possibly-abbreviated name):~%") + (let ((count 0) + (names-used '(nil)) + (max-name-len 0)) + (dolist (restart restarts) + (let ((name (restart-name restart))) + (when name + (let ((len (length (princ-to-string name)))) + (when (> len max-name-len) + (setf max-name-len len)))))) + (unless (zerop max-name-len) + (incf max-name-len 3)) + (dolist (restart restarts) + (let ((name (restart-name restart))) + (cond ((member name names-used) + (format s "~& ~2D: ~V@T~A~%" count max-name-len restart)) + (t + (format s "~& ~2D: [~VA] ~A~%" + count (- max-name-len 3) name restart) + (push name names-used)))) + (incf count)))))) + +(defvar *debug-loop-fun* #'debug-loop-fun + "a function taking no parameters that starts the low-level debug loop") ;;; This calls DEBUG-LOOP, performing some simple initializations ;;; before doing so. INVOKE-DEBUGGER calls this to actually get into @@ -728,8 +970,7 @@ reset to ~S." (*read-suppress* nil)) (unless (typep *debug-condition* 'step-condition) (clear-input *debug-io*)) - #!-mp (debug-loop) - #!+mp (sb!mp:without-scheduling (debug-loop)))) + (funcall *debug-loop-fun*))) ;;;; DEBUG-LOOP @@ -740,93 +981,60 @@ reset to ~S." "When set, avoid calling INVOKE-DEBUGGER recursively when errors occur while executing in the debugger.") -(defun debug-loop () +(defun debug-loop-fun () (let* ((*debug-command-level* (1+ *debug-command-level*)) (*real-stack-top* (sb!di:top-frame)) (*stack-top* (or *stack-top-hint* *real-stack-top*)) (*stack-top-hint* nil) (*current-frame* *stack-top*)) - (handler-bind ((sb!di:debug-condition (lambda (condition) - (princ condition *debug-io*) - (throw 'debug-loop-catcher nil)))) + (handler-bind ((sb!di:debug-condition + (lambda (condition) + (princ condition *debug-io*) + (/show0 "handling d-c by THROWing DEBUG-LOOP-CATCHER") + (throw 'debug-loop-catcher nil)))) (fresh-line) (print-frame-call *current-frame* :verbosity 2) (loop (catch 'debug-loop-catcher - (handler-bind ((error #'(lambda (condition) - (when *flush-debug-errors* - (clear-input *debug-io*) - (princ condition) - ;; FIXME: Doing input on *DEBUG-IO* - ;; and output on T seems broken. - (format t - "~&error flushed (because ~ - ~S is set)" - '*flush-debug-errors*) - (throw 'debug-loop-catcher nil))))) - ;; We have to bind level for the restart function created by + (handler-bind ((error (lambda (condition) + (when *flush-debug-errors* + (clear-input *debug-io*) + (princ condition) + ;; FIXME: Doing input on *DEBUG-IO* + ;; and output on T seems broken. + (format t + "~&error flushed (because ~ + ~S is set)" + '*flush-debug-errors*) + (/show0 "throwing DEBUG-LOOP-CATCHER") + (throw 'debug-loop-catcher nil))))) + ;; We have to bind LEVEL for the restart function created by ;; WITH-SIMPLE-RESTART. (let ((level *debug-command-level*) (restart-commands (make-restart-commands))) (with-simple-restart (abort - "Reduce debugger level (to debug level ~D)." + "~@" level) (debug-prompt *debug-io*) (force-output *debug-io*) - (let ((input (sb!int:get-stream-command *debug-io*))) - (cond (input - (let ((cmd-fun (debug-command-p - (sb!int:stream-command-name input) - restart-commands))) - (cond - ((not cmd-fun) - (error "unknown stream-command: ~S" input)) - ((consp cmd-fun) - (error "ambiguous debugger command: ~S" cmd-fun)) - (t - (apply cmd-fun - (sb!int:stream-command-args input)))))) + (let* ((exp (read *debug-io*)) + (cmd-fun (debug-command-p exp restart-commands))) + (cond ((not cmd-fun) + (debug-eval-print exp)) + ((consp cmd-fun) + (format t "~&Your command, ~S, is ambiguous:~%" + exp) + (dolist (ele cmd-fun) + (format t " ~A~%" ele))) (t - (let* ((exp (read)) - (cmd-fun (debug-command-p exp - restart-commands))) - (cond ((not cmd-fun) - (debug-eval-print exp)) - ((consp cmd-fun) - (format t - "~&Your command, ~S, is ambiguous:~%" - exp) - (dolist (ele cmd-fun) - (format t " ~A~%" ele))) - (t - (funcall cmd-fun))))))))))))))) - -;;; FIXME: As far as I know, the CMU CL X86 codebase has never -;;; supported access to the environment of the debugged function. It -;;; would be really, really nice to make that work! (Until then, -;;; non-NIL *AUTO-EVAL-IN-FRAME* seems to be useless, and as of -;;; sbcl-0.6.10 it even seemed to be actively harmful, since the -;;; debugger gets confused when trying to unwind the frames which -;;; arise in SIGINT interrupts. So it's set to NIL.) -(defvar *auto-eval-in-frame* nil - #!+sb-doc - "When set, evaluations in the debugger's command loop occur relative - to the current frame's environment without the need of debugger - forms that explicitly control this kind of evaluation. In an ideal - world, the default would be T, but since unfortunately the X86 - debugger support isn't good enough to make this useful, the - default is NIL instead.") + (funcall cmd-fun)))))))))))) ;;; FIXME: We could probably use INTERACTIVE-EVAL for much of this logic. (defun debug-eval-print (expr) (/noshow "entering DEBUG-EVAL-PRINT" expr) (/noshow (fboundp 'compile)) - (/noshow (and (fboundp 'compile) *auto-eval-in-frame*)) (setq +++ ++ ++ + + - - expr) - (let* ((values (multiple-value-list - (if (and (fboundp 'compile) *auto-eval-in-frame*) - (sb!di:eval-in-frame *current-frame* -) - (eval -)))) + (let* ((values (multiple-value-list (eval -))) (*standard-output* *debug-io*)) (/noshow "done with EVAL in DEBUG-EVAL-PRINT") (fresh-line) @@ -852,17 +1060,17 @@ reset to ~S." (sb!xc:defmacro define-var-operation (ref-or-set &optional value-var) `(let* ((temp (etypecase name - (symbol (sb!di:debug-function-symbol-variables - (sb!di:frame-debug-function *current-frame*) + (symbol (sb!di:debug-fun-symbol-vars + (sb!di:frame-debug-fun *current-frame*) name)) (simple-string (sb!di:ambiguous-debug-vars - (sb!di:frame-debug-function *current-frame*) + (sb!di:frame-debug-fun *current-frame*) name)))) (location (sb!di:frame-code-location *current-frame*)) ;; Let's only deal with valid variables. - (vars (remove-if-not #'(lambda (v) - (eq (sb!di:debug-var-validity v location) - :valid)) + (vars (remove-if-not (lambda (v) + (eq (sb!di:debug-var-validity v location) + :valid)) temp))) (declare (list vars)) (cond ((null vars) @@ -903,9 +1111,9 @@ reset to ~S." ;; name. ((and (not exact) (find-if-not - #'(lambda (v) - (string= (sb!di:debug-var-symbol-name v) - (sb!di:debug-var-symbol-name (car vars)))) + (lambda (v) + (string= (sb!di:debug-var-symbol-name v) + (sb!di:debug-var-symbol-name (car vars)))) (cdr vars))) (error "specification ambiguous:~%~{ ~A~%~}" (mapcar #'sb!di:debug-var-symbol-name @@ -918,7 +1126,7 @@ reset to ~S." (let ((v (find id vars :key #'sb!di:debug-var-id))) (unless v (error - "invalid variable ID, ~D: should have been one of ~S" + "invalid variable ID, ~W: should have been one of ~S" id (mapcar #'sb!di:debug-var-id vars))) ,(ecase ref-or-set @@ -958,10 +1166,14 @@ reset to ~S." (define-var-operation :set value)) ;;; This returns the COUNT'th arg as the user sees it from args, the -;;; result of SB!DI:DEBUG-FUNCTION-LAMBDA-LIST. If this returns a +;;; result of SB!DI:DEBUG-FUN-LAMBDA-LIST. If this returns a ;;; potential DEBUG-VAR from the lambda-list, then the second value is ;;; T. If this returns a keyword symbol or a value from a rest arg, ;;; then the second value is NIL. +;;; +;;; FIXME: There's probably some way to merge the code here with +;;; FRAME-ARGS-AS-LIST. (A fair amount of logic is already shared +;;; through LAMBDA-LIST-ELEMENT-DISPATCH, but I suspect more could be.) (declaim (ftype (function (index list)) nth-arg)) (defun nth-arg (count args) (let ((n count)) @@ -978,8 +1190,7 @@ reset to ~S." :rest ((let ((var (second ele))) (lambda-var-dispatch var (sb!di:frame-code-location *current-frame*) - (error "unused &REST argument before n'th -argument") + (error "unused &REST argument before n'th argument") (dolist (value (sb!di:debug-var-value var *current-frame*) (error @@ -993,12 +1204,12 @@ argument") (defun arg (n) #!+sb-doc - "Returns the N'th argument's value if possible. Argument zero is the first + "Return the N'th argument's value if possible. Argument zero is the first argument in a frame's default printed representation. Count keyword/value pairs as separate arguments." (multiple-value-bind (var lambda-var-p) - (nth-arg n (handler-case (sb!di:debug-function-lambda-list - (sb!di:frame-debug-function *current-frame*)) + (nth-arg n (handler-case (sb!di:debug-fun-lambda-list + (sb!di:frame-debug-fun *current-frame*)) (sb!di:lambda-list-unavailable () (error "No argument values are available.")))) (if lambda-var-p @@ -1044,7 +1255,7 @@ argument") (let* ((name (if (symbolp form) (symbol-name form) - (format nil "~D" form))) + (format nil "~W" form))) (len (length name)) (res nil)) (declare (simple-string name) @@ -1076,7 +1287,7 @@ argument") (setf (car cmds) (caar cmds)))))))) ;;; Return a list of debug commands (in the same format as -;;; *debug-commands*) that invoke each active restart. +;;; *DEBUG-COMMANDS*) that invoke each active restart. ;;; ;;; Two commands are made for each restart: one for the number, and ;;; one for the restart name (unless it's been shadowed by an earlier @@ -1087,8 +1298,10 @@ argument") (dolist (restart restarts) (let ((name (string (restart-name restart)))) (let ((restart-fun - #'(lambda () (invoke-restart-interactively restart)))) - (push (cons (format nil "~d" num) restart-fun) commands) + (lambda () + (/show0 "in restart-command closure, about to i-r-i") + (invoke-restart-interactively restart)))) + (push (cons (prin1-to-string num) restart-fun) commands) (unless (or (null (restart-name restart)) (find name commands :key #'car :test #'string=)) (push (cons name restart-fun) commands)))) @@ -1165,34 +1378,35 @@ argument") ;;; and "terminate the Lisp system" as the SB-EXT:QUIT function.) ;;; ;;;(!def-debug-command "QUIT" () -;;; (throw 'sb!impl::top-level-catcher nil)) +;;; (throw 'sb!impl::toplevel-catcher nil)) ;;; CMU CL supported this GO debug command, but SBCL doesn't -- in -;;; SBCL you just type the CONTINUE restart name instead (or "RESTART -;;; CONTINUE", that's OK too). - +;;; SBCL you just type the CONTINUE restart name instead (or "C" or +;;; "RESTART CONTINUE", that's OK too). ;;;(!def-debug-command "GO" () ;;; (continue *debug-condition*) ;;; (error "There is no restart named CONTINUE.")) (!def-debug-command "RESTART" () + (/show0 "doing RESTART debug-command") (let ((num (read-if-available :prompt))) (when (eq num :prompt) (show-restarts *debug-restarts* *debug-io*) (write-string "restart: ") (force-output) - (setf num (read *standard-input*))) + (setf num (read *debug-io*))) (let ((restart (typecase num (unsigned-byte (nth num *debug-restarts*)) (symbol (find num *debug-restarts* :key #'restart-name - :test #'(lambda (sym1 sym2) - (string= (symbol-name sym1) - (symbol-name sym2))))) + :test (lambda (sym1 sym2) + (string= (symbol-name sym1) + (symbol-name sym2))))) (t (format t "~S is invalid as a restart name.~%" num) (return-from restart-debug-command nil))))) + (/show0 "got RESTART") (if restart (invoke-restart-interactively restart) ;; FIXME: Even if this isn't handled by WARN, it probably @@ -1229,7 +1443,7 @@ argument") (!def-debug-command-alias "P" "PRINT") (!def-debug-command "LIST-LOCALS" () - (let ((d-fun (sb!di:frame-debug-function *current-frame*))) + (let ((d-fun (sb!di:frame-debug-fun *current-frame*))) (if (sb!di:debug-var-info-available d-fun) (let ((*standard-output* *debug-io*) (location (sb!di:frame-code-location *current-frame*)) @@ -1242,7 +1456,7 @@ argument") (setf any-p t) (when (eq (sb!di:debug-var-validity v location) :valid) (setf any-valid-p t) - (format t "~S~:[#~D~;~*~] = ~S~%" + (format t "~S~:[#~W~;~*~] = ~S~%" (sb!di:debug-var-symbol v) (zerop (sb!di:debug-var-id v)) (sb!di:debug-var-id v) @@ -1293,39 +1507,39 @@ argument") *cached-readtable* nil)) *before-save-initializations*) -;;; We also cache the last top-level form that we printed a source for +;;; We also cache the last toplevel form that we printed a source for ;;; so that we don't have to do repeated reads and calls to ;;; FORM-NUMBER-TRANSLATIONS. -(defvar *cached-top-level-form-offset* nil) -(declaim (type (or index null) *cached-top-level-form-offset*)) -(defvar *cached-top-level-form*) +(defvar *cached-toplevel-form-offset* nil) +(declaim (type (or index null) *cached-toplevel-form-offset*)) +(defvar *cached-toplevel-form*) (defvar *cached-form-number-translations*) ;;; Given a code location, return the associated form-number -;;; translations and the actual top-level form. We check our cache --- +;;; translations and the actual top level form. We check our cache --- ;;; if there is a miss, we dispatch on the kind of the debug source. -(defun get-top-level-form (location) +(defun get-toplevel-form (location) (let ((d-source (sb!di:code-location-debug-source location))) (if (and (eq d-source *cached-debug-source*) - (eql (sb!di:code-location-top-level-form-offset location) - *cached-top-level-form-offset*)) - (values *cached-form-number-translations* *cached-top-level-form*) - (let* ((offset (sb!di:code-location-top-level-form-offset location)) + (eql (sb!di:code-location-toplevel-form-offset location) + *cached-toplevel-form-offset*)) + (values *cached-form-number-translations* *cached-toplevel-form*) + (let* ((offset (sb!di:code-location-toplevel-form-offset location)) (res (ecase (sb!di:debug-source-from d-source) - (:file (get-file-top-level-form location)) + (:file (get-file-toplevel-form location)) (:lisp (svref (sb!di:debug-source-name d-source) offset))))) - (setq *cached-top-level-form-offset* offset) + (setq *cached-toplevel-form-offset* offset) (values (setq *cached-form-number-translations* (sb!di:form-number-translations res offset)) - (setq *cached-top-level-form* res)))))) + (setq *cached-toplevel-form* res)))))) -;;; Locate the source file (if it still exists) and grab the top-level -;;; form. If the file is modified, we use the top-level-form offset +;;; Locate the source file (if it still exists) and grab the top level +;;; form. If the file is modified, we use the top level form offset ;;; instead of the recorded character offset. -(defun get-file-top-level-form (location) +(defun get-file-toplevel-form (location) (let* ((d-source (sb!di:code-location-debug-source location)) - (tlf-offset (sb!di:code-location-top-level-form-offset location)) + (tlf-offset (sb!di:code-location-toplevel-form-offset location)) (local-tlf-offset (- tlf-offset (sb!di:debug-source-root-number d-source))) (char-offset @@ -1364,10 +1578,10 @@ argument") (setq *cached-readtable* (copy-readtable)) (set-dispatch-macro-character #\# #\. - #'(lambda (stream sub-char &rest rest) - (declare (ignore rest sub-char)) - (let ((token (read stream t nil t))) - (format nil "#.~S" token))) + (lambda (stream sub-char &rest rest) + (declare (ignore rest sub-char)) + (let ((token (read stream t nil t))) + (format nil "#.~S" token))) *cached-readtable*)) (let ((*readtable* *cached-readtable*)) (read *cached-source-stream*)))) @@ -1375,7 +1589,7 @@ argument") (defun print-code-location-source-form (location context) (let* ((location (maybe-block-start-location location)) (form-num (sb!di:code-location-form-number location))) - (multiple-value-bind (translations form) (get-top-level-form location) + (multiple-value-bind (translations form) (get-toplevel-form location) (unless (< form-num (length translations)) (error "The source path no longer exists.")) (prin1 (sb!di:source-path-context form @@ -1396,23 +1610,23 @@ argument") ;;; *POSSIBLE-BREAKPOINTS* to the code-locations which can then be ;;; used by sbreakpoint. (!def-debug-command "LIST-LOCATIONS" () - (let ((df (read-if-available *default-breakpoint-debug-function*))) + (let ((df (read-if-available *default-breakpoint-debug-fun*))) (cond ((consp df) - (setf df (sb!di:function-debug-function (eval df))) - (setf *default-breakpoint-debug-function* df)) + (setf df (sb!di:fun-debug-fun (eval df))) + (setf *default-breakpoint-debug-fun* df)) ((or (eq ':c df) - (not *default-breakpoint-debug-function*)) - (setf df (sb!di:frame-debug-function *current-frame*)) - (setf *default-breakpoint-debug-function* df))) + (not *default-breakpoint-debug-fun*)) + (setf df (sb!di:frame-debug-fun *current-frame*)) + (setf *default-breakpoint-debug-fun* df))) (setf *possible-breakpoints* (possible-breakpoints df))) (let ((continue-at (sb!di:frame-code-location *current-frame*))) - (let ((active (location-in-list *default-breakpoint-debug-function* - *breakpoints* :function-start)) + (let ((active (location-in-list *default-breakpoint-debug-fun* + *breakpoints* :fun-start)) (here (sb!di:code-location= - (sb!di:debug-function-start-location - *default-breakpoint-debug-function*) continue-at))) + (sb!di:debug-fun-start-location + *default-breakpoint-debug-fun*) continue-at))) (when (or active here) - (format t "::FUNCTION-START ") + (format t "::FUN-START ") (when active (format t " *Active*")) (when here (format t " *Continue here*")))) @@ -1423,8 +1637,8 @@ argument") (when prev-location (let ((this-num (1- this-num))) (if (= prev-num this-num) - (format t "~&~D: " prev-num) - (format t "~&~D-~D: " prev-num this-num))) + (format t "~&~W: " prev-num) + (format t "~&~W-~W: " prev-num this-num))) (print-code-location-source-form prev-location 0) (when *print-location-kind* (format t "~S " (sb!di:code-location-kind prev-location))) @@ -1440,9 +1654,9 @@ argument") (not prev-location) (not (eq (sb!di:code-location-debug-source code-location) (sb!di:code-location-debug-source prev-location))) - (not (eq (sb!di:code-location-top-level-form-offset + (not (eq (sb!di:code-location-toplevel-form-offset code-location) - (sb!di:code-location-top-level-form-offset + (sb!di:code-location-toplevel-form-offset prev-location))) (not (eq (sb!di:code-location-form-number code-location) (sb!di:code-location-form-number prev-location)))) @@ -1451,10 +1665,10 @@ argument") (incf this-num)))) - (when (location-in-list *default-breakpoint-debug-function* + (when (location-in-list *default-breakpoint-debug-fun* *breakpoints* - :function-end) - (format t "~&::FUNCTION-END *Active* ")))) + :fun-end) + (format t "~&::FUN-END *Active* ")))) (!def-debug-command-alias "LL" "LIST-LOCATIONS") @@ -1467,7 +1681,7 @@ argument") (print-functions nil) (function nil) (bp) - (place *default-breakpoint-debug-function*)) + (place *default-breakpoint-debug-fun*)) (flet ((get-command-line () (let ((command-line nil) (unique '(nil))) @@ -1485,27 +1699,27 @@ argument") (:break (setf break (pop command-line))) (:function (setf function (eval (pop command-line))) - (setf *default-breakpoint-debug-function* - (sb!di:function-debug-function function)) - (setf place *default-breakpoint-debug-function*) + (setf *default-breakpoint-debug-fun* + (sb!di:fun-debug-fun function)) + (setf place *default-breakpoint-debug-fun*) (setf *possible-breakpoints* (possible-breakpoints - *default-breakpoint-debug-function*)))))) - (setup-function-start () - (let ((code-loc (sb!di:debug-function-start-location place))) - (setf bp (sb!di:make-breakpoint #'main-hook-function + *default-breakpoint-debug-fun*)))))) + (setup-fun-start () + (let ((code-loc (sb!di:debug-fun-start-location place))) + (setf bp (sb!di:make-breakpoint #'main-hook-fun place - :kind :function-start)) + :kind :fun-start)) (setf break (sb!di:preprocess-for-eval break code-loc)) (setf condition (sb!di:preprocess-for-eval condition code-loc)) (dolist (form print) (push (cons (sb!di:preprocess-for-eval form code-loc) form) print-functions)))) - (setup-function-end () + (setup-fun-end () (setf bp - (sb!di:make-breakpoint #'main-hook-function + (sb!di:make-breakpoint #'main-hook-fun place - :kind :function-end)) + :kind :fun-end)) (setf break ;; FIXME: These and any other old (COERCE `(LAMBDA ..) ..) ;; forms should be converted to shiny new (LAMBDA ..) forms. @@ -1524,8 +1738,7 @@ argument") print-functions))) (setup-code-location () (setf place (nth index *possible-breakpoints*)) - (setf bp (sb!di:make-breakpoint #'main-hook-function - place + (setf bp (sb!di:make-breakpoint #'main-hook-fun place :kind :code-location)) (dolist (form print) (push (cons @@ -1537,9 +1750,9 @@ argument") (set-vars-from-command-line (get-command-line)) (cond ((or (eq index :start) (eq index :s)) - (setup-function-start)) + (setup-fun-start)) ((or (eq index :end) (eq index :e)) - (setup-function-end)) + (setup-fun-end)) (t (setup-code-location))) (sb!di:activate-breakpoint bp) @@ -1591,22 +1804,42 @@ argument") (!def-debug-command "DESCRIBE" () (let* ((curloc (sb!di:frame-code-location *current-frame*)) - (debug-fun (sb!di:code-location-debug-function curloc)) - (function (sb!di:debug-function-function debug-fun))) + (debug-fun (sb!di:code-location-debug-fun curloc)) + (function (sb!di:debug-fun-fun debug-fun))) (if function (describe function) (format t "can't figure out the function for this frame")))) + +(!def-debug-command "SLURP" () + (loop while (read-char-no-hang *standard-input*))) + +(!def-debug-command "RETURN" (&optional + (return (read-prompting-maybe + "return: "))) + (let ((tag (find-if (lambda (x) + (and (typep (car x) 'symbol) + (not (symbol-package (car x))) + (string= (car x) "SB-DEBUG-CATCH-TAG"))) + (sb!di::frame-catches *current-frame*)))) + (if tag + (throw (car tag) + (funcall (sb!di:preprocess-for-eval + return + (sb!di:frame-code-location *current-frame*)) + *current-frame*)) + (format t "~@")))) ;;;; debug loop command utilities -(defun read-prompting-maybe (prompt &optional (in *standard-input*) - (out *standard-output*)) - (unless (sb!int:listen-skip-whitespace in) - (princ prompt out) - (force-output out)) - (read in)) +(defun read-prompting-maybe (prompt) + (unless (sb!int:listen-skip-whitespace *debug-io*) + (princ prompt) + (force-output)) + (read *debug-io*)) -(defun read-if-available (default &optional (stream *standard-input*)) - (if (sb!int:listen-skip-whitespace stream) - (read stream) +(defun read-if-available (default) + (if (sb!int:listen-skip-whitespace *debug-io*) + (read *debug-io*) default))