(in-package "SB!DI")
-(file-comment
- "$Header$")
-
;;; FIXME: There are an awful lot of package prefixes in this code.
;;; Couldn't we have SB-DI use the SB-C and SB-VM packages?
\f
()
#!+sb-doc
(:documentation
- "All debug-conditions inherit from this type. These are serious conditions
+ "All DEBUG-CONDITIONs inherit from this type. These are serious conditions
that must be handled, but they are not programmer errors."))
(define-condition no-debug-info (debug-condition)
- ()
+ ((code-component :reader no-debug-info-code-component
+ :initarg :code-component))
#!+sb-doc
- (:documentation "There is absolutely no debugging information available.")
+ (:documentation "There is no usable debugging information available.")
(:report (lambda (condition stream)
(declare (ignore condition))
(fresh-line stream)
- (write-line "No debugging information available." stream))))
+ (format stream
+ "no debug information available for ~S~%"
+ (no-debug-info-code-component condition)))))
(define-condition no-debug-function-returns (debug-condition)
((debug-function :reader no-debug-function-returns-debug-function
:initarg :debug-function))
#!+sb-doc
(:documentation
- "The system could not return values from a frame with debug-function since
+ "The system could not return values from a frame with DEBUG-FUNCTION since
it lacked information about returning values.")
(:report (lambda (condition stream)
(let ((fun (debug-function-function
"All programmer errors from using the interface for building debugging
tools inherit from this type."))
-(define-condition unhandled-condition (debug-error)
- ((condition :reader unhandled-condition-condition :initarg :condition))
+(define-condition unhandled-debug-condition (debug-error)
+ ((condition :reader unhandled-debug-condition-condition :initarg :condition))
(:report (lambda (condition stream)
(format stream "~&unhandled DEBUG-CONDITION:~%~A"
- (unhandled-condition-condition condition)))))
+ (unhandled-debug-condition-condition condition)))))
(define-condition unknown-code-location (debug-error)
((code-location :reader unknown-code-location-code-location
(frame :reader frame-function-mismatch-frame :initarg :frame)
(form :reader frame-function-mismatch-form :initarg :form))
(:report (lambda (condition stream)
- (format stream
- "~&Form was preprocessed for ~S,~% but called on ~S:~% ~S"
- (frame-function-mismatch-code-location condition)
- (frame-function-mismatch-frame condition)
- (frame-function-mismatch-form condition)))))
-
-;;; This signals debug-conditions. If they go unhandled, then signal an
-;;; unhandled-condition error.
+ (format
+ stream
+ "~&Form was preprocessed for ~S,~% but called on ~S:~% ~S"
+ (frame-function-mismatch-code-location condition)
+ (frame-function-mismatch-frame condition)
+ (frame-function-mismatch-form condition)))))
+
+;;; This signals debug-conditions. If they go unhandled, then signal
+;;; an UNHANDLED-DEBUG-CONDITION error.
;;;
;;; ??? Get SIGNAL in the right package!
(defmacro debug-signal (datum &rest arguments)
`(let ((condition (make-condition ,datum ,@arguments)))
(signal condition)
- (error 'unhandled-condition :condition condition)))
+ (error 'unhandled-debug-condition :condition condition)))
\f
;;;; structures
;;;;
#!-sb-fluid (declaim (inline cstack-pointer-valid-p))
(defun cstack-pointer-valid-p (x)
(declare (type system-area-pointer x))
- #!-x86
+ #!-x86 ; stack grows toward high address values
(and (sap< x (current-sp))
- (sap<= #!-gengc (sb!alien:alien-sap
- (sb!alien:extern-alien "control_stack" (* t)))
+ (sap<= #!-gengc (int-sap control-stack-start)
#!+gengc (mutator-control-stack-base)
x)
(zerop (logand (sap-int x) #b11)))
- #!+x86 ;; stack grows to low address values
+ #!+x86 ; stack grows toward low address values
(and (sap>= x (current-sp))
- (sap> (sb!alien:alien-sap (sb!alien:extern-alien "control_stack_end"
- (* t)))
- x)
+ (sap> (int-sap control-stack-end) x)
(zerop (logand (sap-int x) #b11))))
#!+(or gengc x86)
;; Not the first page which is unmapped.
(>= (sap-int ra) 4096)
;; Not a Lisp stack pointer.
- (or (sap< ra (current-sp))
- (sap>= ra (sb!alien:alien-sap
- (sb!alien:extern-alien "control_stack_end" (* t)))))))
+ (not (cstack-pointer-valid-p ra))))
;;; Try to find a valid previous stack. This is complex on the x86 as
;;; it can jump between C and Lisp frames. To help find a valid frame
;;; XXX Should probably check whether it has reached the bottom of the
;;; stack.
;;;
-;;; XXX Should handle interrupted frames, both Lisp and C. At present it
-;;; manages to find a fp trail, see linux hack below.
-(defun x86-call-context (fp &key (depth 8))
+;;; XXX Should handle interrupted frames, both Lisp and C. At present
+;;; it manages to find a fp trail, see linux hack below.
+(defun x86-call-context (fp &key (depth 0))
(declare (type system-area-pointer fp)
(fixnum depth))
;;(format t "*CC ~S ~S~%" fp depth)
lisp-ocfp lisp-ra c-ocfp c-ra)
;; Look forward another step to check their validity.
(let ((lisp-path-fp (x86-call-context lisp-ocfp
- :depth (- depth 1)))
- (c-path-fp (x86-call-context c-ocfp :depth (- depth 1))))
+ :depth (1+ depth)))
+ (c-path-fp (x86-call-context c-ocfp :depth (1+ depth))))
(cond ((and lisp-path-fp c-path-fp)
- ;; Both still seem valid - choose the smallest.
- #+nil (format t "debug: both still valid ~S ~S ~S ~S~%"
- lisp-ocfp lisp-ra c-ocfp c-ra)
- (if (sap< lisp-ocfp c-ocfp)
- (values lisp-ra lisp-ocfp)
- (values c-ra c-ocfp)))
+ ;; Both still seem valid - choose the lisp frame.
+ #+nil (when (zerop depth)
+ (format t "debug: both still valid ~S ~S ~S ~S~%"
+ lisp-ocfp lisp-ra c-ocfp c-ra))
+ #+freebsd
+ (if (sap> lisp-ocfp c-ocfp)
+ (values lisp-ra lisp-ocfp)
+ (values c-ra c-ocfp))
+ #-freebsd
+ (values lisp-ra lisp-ocfp))
(lisp-path-fp
;; The lisp convention is looking good.
#+nil (format t "*C lisp-ocfp ~S ~S~%" lisp-ocfp lisp-ra)
(without-gcing
(let* ((component-ptr (component-ptr-from-pc
(sb!vm:context-pc context)))
- (code (if (sap= component-ptr (int-sap #x0))
- nil ; FIXME: UNLESS might be clearer than IF.
- (component-from-component-ptr component-ptr))))
+ (code (unless (sap= component-ptr (int-sap #x0))
+ (component-from-component-ptr component-ptr))))
(when (null code)
(return (values code 0 context)))
(let* ((code-header-len (* (get-header-data code)
(unless (<= 0 pc-offset
(* (code-header-ref code sb!vm:code-code-size-slot)
sb!vm:word-bytes))
- ;; We were in an assembly routine. Therefore, use the LRA as
- ;; the pc.
+ ;; We were in an assembly routine. Therefore, use the
+ ;; LRA as the pc.
+ ;;
+ ;; FIXME: Should this be WARN or ERROR or what?
(format t "** pc-offset ~S not in code obj ~S?~%"
pc-offset code))
(return
(let ((info (%code-debug-info component)))
(cond
((not info)
- (debug-signal 'no-debug-info))
+ (debug-signal 'no-debug-info :code-component component))
((eq info :bogus-lra)
(make-bogus-debug-function "function end breakpoint"))
(t
(elsewhere-p
(>= pc (sb!c::compiled-debug-function-elsewhere-pc
(svref function-map 0)))))
- ;; FIXME: I don't think SB!C is the home package of INDEX.
- (declare (type sb!c::index i))
+ (declare (type sb!int:index i))
(loop
(when (or (= i len)
(< pc (if elsewhere-p
(let* ((locations
(dotimes (k (sb!c::read-var-integer blocks i)
(result locations-buffer))
- (let ((kind (svref sb!c::compiled-code-location-kinds
+ (let ((kind (svref sb!c::*compiled-code-location-kinds*
(aref+ blocks i)))
(pc (+ last-pc
(sb!c::read-var-integer blocks i)))
(if (logtest flags sb!c::minimal-debug-function-setf-bit)
`(setf ,base)
base))
- :kind (svref sb!c::minimal-debug-function-kinds
+ :kind (svref sb!c::*minimal-debug-function-kinds*
(ldb sb!c::minimal-debug-function-kind-byte options))
:variables
(when vars-p
(coerce (cdr (res)) 'simple-vector))))
-;;; This variable maps minimal debug-info function maps to an unpacked
-;;; version thereof.
+;;; a map from minimal DEBUG-INFO function maps to unpacked
+;;; versions thereof
(defvar *uncompacted-function-maps* (make-hash-table :test 'eq))
-;;; Return a function-map for a given compiled-debug-info object. If
+;;; Return a FUNCTION-MAP for a given COMPILED-DEBUG-info object. If
;;; the info is minimal, and has not been parsed, then parse it.
;;;
-;;; FIXME: Now that we no longer use the minimal-debug-function
+;;; FIXME: Now that we no longer use the MINIMAL-DEBUG-FUNCTION
;;; representation, calls to this function can be replaced by calls to
;;; the bare COMPILED-DEBUG-INFO-FUNCTION-MAP slot accessor function,
;;; and this function and everything it calls become dead code which
\f
;;;; CODE-LOCATIONs
-;;; If we're sure of whether code-location is known, return t or nil.
-;;; If we're :unsure, then try to fill in the code-location's slots.
+;;; If we're sure of whether code-location is known, return T or NIL.
+;;; If we're :UNSURE, then try to fill in the code-location's slots.
;;; This determines whether there is any debug-block information, and
;;; if code-location is known.
;;;
;;; ??? IF this conses closures every time it's called, then break off the
-;;; :unsure part to get the HANDLER-CASE into another function.
+;;; :UNSURE part to get the HANDLER-CASE into another function.
(defun code-location-unknown-p (basic-code-location)
- #!+sb-doc
- "Returns whether basic-code-location is unknown. It returns nil when the
- code-location is known."
(ecase (code-location-%unknown-p basic-code-location)
((t) t)
((nil) nil)
(handler-case (not (fill-in-code-location basic-code-location))
(no-debug-blocks () t))))))
+;;; Return the DEBUG-BLOCK containing code-location if it is available.
+;;; Some debug policies inhibit debug-block information, and if none
+;;; is available, then this signals a NO-DEBUG-BLOCKS condition.
(defun code-location-debug-block (basic-code-location)
- #!+sb-doc
- "Returns the debug-block containing code-location if it is available. Some
- debug policies inhibit debug-block information, and if none is available,
- then this signals a no-debug-blocks condition."
(let ((block (code-location-%debug-block basic-code-location)))
(if (eq block :unparsed)
(etypecase basic-code-location
(interpreted-code-location-ir1-node basic-code-location))))))
block)))
-;;; This stores and returns BASIC-CODE-LOCATION's debug-block. It
-;;; determines the correct one using the code-location's pc. This uses
+;;; Store and return BASIC-CODE-LOCATION's debug-block. We determines
+;;; the correct one using the code-location's pc. We use
;;; DEBUG-FUNCTION-DEBUG-BLOCKS to return the cached block information
-;;; or signal a 'no-debug-blocks condition. The blocks are sorted by
+;;; or signal a NO-DEBUG-BLOCKS condition. The blocks are sorted by
;;; their first code-location's pc, in ascending order. Therefore, as
;;; soon as we find a block that starts with a pc greater than
;;; basic-code-location's pc, we know the previous block contains the
(let ((live-set (compiled-code-location-%live-set code-location)))
(cond ((eq live-set :unparsed)
(unless (fill-in-code-location code-location)
- ;; This check should be unnecessary. We're missing debug info
- ;; the compiler should have dumped.
+ ;; This check should be unnecessary. We're missing
+ ;; debug info the compiler should have dumped.
;;
;; FIXME: This error and comment happen over and over again.
;; Make them a shared function.
(compiled-code-location-%live-set code-location))
(t live-set)))))
+;;; true if OBJ1 and OBJ2 are the same place in the code
(defun code-location= (obj1 obj2)
- #!+sb-doc
- "Returns whether obj1 and obj2 are the same place in the code."
(etypecase obj1
(compiled-code-location
(etypecase obj2
(= (compiled-code-location-pc obj1)
(compiled-code-location-pc obj2)))
-;;; This fills in CODE-LOCATION's :unparsed slots. It returns t or nil
+;;; Fill in CODE-LOCATION's :UNPARSED slots, returning T or NIL
;;; depending on whether the code-location was known in its
;;; debug-function's debug-block information. This may signal a
;;; NO-DEBUG-BLOCKS condition due to DEBUG-FUNCTION-DEBUG-BLOCKS, and
(or (compiled-debug-var-save-sc-offset debug-var)
(compiled-debug-var-sc-offset debug-var))))))
+;;; a helper function for working with possibly-invalid values:
+;;; Do (MAKE-LISP-OBJ VAL) only if the value looks valid.
+;;;
+;;; (Such values can arise in registers on machines with conservative
+;;; GC, and might also arise in debug variable locations when
+;;; those variables are invalid.)
+(defun make-valid-lisp-obj (val)
+ (/show0 "entering MAKE-VALID-LISP-OBJ, VAL=..")
+ #!+sb-show (%primitive print (sb!impl::hexstr val))
+ (if (or
+ ;; fixnum
+ (zerop (logand val 3))
+ ;; character
+ (and (zerop (logand val #xffff0000)) ; Top bits zero
+ (= (logand val #xff) sb!vm:base-char-type)) ; Char tag
+ ;; unbound marker
+ (= val sb!vm:unbound-marker-type)
+ ;; pointer
+ (and (logand val 1)
+ ;; Check that the pointer is valid. XXX Could do a better
+ ;; job. FIXME: e.g. by calling out to an is_valid_pointer
+ ;; routine in the C runtime support code
+ (or (< sb!vm:read-only-space-start val
+ (* sb!vm:*read-only-space-free-pointer*
+ sb!vm:word-bytes))
+ (< sb!vm:static-space-start val
+ (* sb!vm:*static-space-free-pointer*
+ sb!vm:word-bytes))
+ (< sb!vm:dynamic-space-start val
+ (sap-int (dynamic-space-free-pointer))))))
+ (make-lisp-obj val)
+ :invalid-object))
+
;;; CMU CL had
;;; (DEFUN SUB-ACCESS-DEBUG-VAR-SLOT (FP SC-OFFSET &OPTIONAL ESCAPED) ..)
;;; code for this case.
#!+x86
(defun sub-access-debug-var-slot (fp sc-offset &optional escaped)
(declare (type system-area-pointer fp))
+ (/show0 "entering SUB-ACCESS-DEBUG-VAR-SLOT, FP,SC-OFFSET,ESCAPED=..")
+ #!+sb-show (%primitive print (sb!impl::hexstr fp))
+ #!+sb-show (%primitive print (sb!impl::hexstr sc-offset))
+ #!+sb-show (%primitive print (sb!impl::hexstr escaped))
(macrolet ((with-escaped-value ((var) &body forms)
`(if escaped
- (let ((,var (sb!vm:context-register
- escaped (sb!c:sc-offset-offset sc-offset))))
- ,@forms)
- :invalid-value-for-unescaped-register-storage))
+ (let ((,var (sb!vm:context-register
+ escaped
+ (sb!c:sc-offset-offset sc-offset))))
+ (/show0 "in escaped case, ,VAR value=..")
+ #!+sb-show (%primitive print (sb!impl::hexstr ,var))
+ ,@forms)
+ :invalid-value-for-unescaped-register-storage))
(escaped-float-value (format)
`(if escaped
- (sb!vm:context-float-register
- escaped (sb!c:sc-offset-offset sc-offset) ',format)
- :invalid-value-for-unescaped-register-storage))
+ (sb!vm:context-float-register
+ escaped (sb!c:sc-offset-offset sc-offset) ',format)
+ :invalid-value-for-unescaped-register-storage))
(escaped-complex-float-value (format)
`(if escaped
- (complex
- (sb!vm:context-float-register
- escaped (sb!c:sc-offset-offset sc-offset) ',format)
- (sb!vm:context-float-register
- escaped (1+ (sb!c:sc-offset-offset sc-offset)) ',format))
- :invalid-value-for-unescaped-register-storage))
- ;; The debug variable locations are not always valid, and
- ;; on the x86 locations can contain raw values. To
- ;; prevent later problems from invalid objects, they are
- ;; filtered here.
- (make-valid-lisp-obj (val)
- `(if (or
- ;; fixnum
- (zerop (logand ,val 3))
- ;; character
- (and (zerop (logand ,val #xffff0000)) ; Top bits zero
- (= (logand ,val #xff) sb!vm:base-char-type)) ; Char tag
- ;; unbound marker
- (= ,val sb!vm:unbound-marker-type)
- ;; pointer
- (and (logand ,val 1)
- ;; Check that the pointer is valid. XXX Could do a
- ;; better job.
- (or (< (sb!impl::read-only-space-start) ,val
- (* sb!impl::*read-only-space-free-pointer*
- sb!vm:word-bytes))
- (< (sb!impl::static-space-start) ,val
- (* sb!impl::*static-space-free-pointer*
- sb!vm:word-bytes))
- (< (sb!impl::current-dynamic-space-start) ,val
- (sap-int (dynamic-space-free-pointer))))))
- (make-lisp-obj ,val)
- :invalid-object)))
+ (complex
+ (sb!vm:context-float-register
+ escaped (sb!c:sc-offset-offset sc-offset) ',format)
+ (sb!vm:context-float-register
+ escaped (1+ (sb!c:sc-offset-offset sc-offset)) ',format))
+ :invalid-value-for-unescaped-register-storage)))
(ecase (sb!c:sc-offset-scn sc-offset)
((#.sb!vm:any-reg-sc-number #.sb!vm:descriptor-reg-sc-number)
+ (/show0 "case of ANY-REG-SC-NUMBER or DESCRIPTOR-REG-SC-NUMBER")
(without-gcing
(with-escaped-value (val)
+ (/show0 "VAL=..")
+ #!+sb-show (%primitive print (sb!impl::hexstr val))
(make-valid-lisp-obj val))))
(#.sb!vm:base-char-reg-sc-number
+ (/show0 "case of BASE-CHAR-REG-SC-NUMBER")
(with-escaped-value (val)
(code-char val)))
(#.sb!vm:sap-reg-sc-number
+ (/show0 "case of SAP-REG-SC-NUMBER")
(with-escaped-value (val)
(int-sap val)))
(#.sb!vm:signed-reg-sc-number
+ (/show0 "case of SIGNED-REG-SC-NUMBER")
(with-escaped-value (val)
(if (logbitp (1- sb!vm:word-bits) val)
(logior val (ash -1 sb!vm:word-bits))
val)))
(#.sb!vm:unsigned-reg-sc-number
+ (/show0 "case of UNSIGNED-REG-SC-NUMBER")
(with-escaped-value (val)
val))
(#.sb!vm:single-reg-sc-number
+ (/show0 "case of SINGLE-REG-SC-NUMBER")
(escaped-float-value single-float))
(#.sb!vm:double-reg-sc-number
+ (/show0 "case of DOUBLE-REG-SC-NUMBER")
(escaped-float-value double-float))
#!+long-float
(#.sb!vm:long-reg-sc-number
+ (/show0 "case of LONG-REG-SC-NUMBER")
(escaped-float-value long-float))
(#.sb!vm:complex-single-reg-sc-number
+ (/show0 "case of COMPLEX-SINGLE-REG-SC-NUMBER")
(escaped-complex-float-value single-float))
(#.sb!vm:complex-double-reg-sc-number
+ (/show0 "case of COMPLEX-DOUBLE-REG-SC-NUMBER")
(escaped-complex-float-value double-float))
#!+long-float
(#.sb!vm:complex-long-reg-sc-number
+ (/show0 "case of COMPLEX-LONG-REG-SC-NUMBER")
(escaped-complex-float-value long-float))
(#.sb!vm:single-stack-sc-number
+ (/show0 "case of SINGLE-STACK-SC-NUMBER")
(sap-ref-single fp (- (* (1+ (sb!c:sc-offset-offset sc-offset))
sb!vm:word-bytes))))
(#.sb!vm:double-stack-sc-number
+ (/show0 "case of DOUBLE-STACK-SC-NUMBER")
(sap-ref-double fp (- (* (+ (sb!c:sc-offset-offset sc-offset) 2)
sb!vm:word-bytes))))
#!+long-float
(#.sb!vm:long-stack-sc-number
+ (/show0 "case of LONG-STACK-SC-NUMBER")
(sap-ref-long fp (- (* (+ (sb!c:sc-offset-offset sc-offset) 3)
sb!vm:word-bytes))))
(#.sb!vm:complex-single-stack-sc-number
+ (/show0 "case of COMPLEX-STACK-SC-NUMBER")
(complex
(sap-ref-single fp (- (* (1+ (sb!c:sc-offset-offset sc-offset))
sb!vm:word-bytes)))
(sap-ref-single fp (- (* (+ (sb!c:sc-offset-offset sc-offset) 2)
sb!vm:word-bytes)))))
(#.sb!vm:complex-double-stack-sc-number
+ (/show0 "case of COMPLEX-DOUBLE-STACK-SC-NUMBER")
(complex
(sap-ref-double fp (- (* (+ (sb!c:sc-offset-offset sc-offset) 2)
sb!vm:word-bytes)))
sb!vm:word-bytes)))))
#!+long-float
(#.sb!vm:complex-long-stack-sc-number
+ (/show0 "case of COMPLEX-LONG-STACK-SC-NUMBER")
(complex
(sap-ref-long fp (- (* (+ (sb!c:sc-offset-offset sc-offset) 3)
sb!vm:word-bytes)))
(sap-ref-long fp (- (* (+ (sb!c:sc-offset-offset sc-offset) 6)
sb!vm:word-bytes)))))
(#.sb!vm:control-stack-sc-number
+ (/show0 "case of CONTROL-STACK-SC-NUMBER")
(stack-ref fp (sb!c:sc-offset-offset sc-offset)))
(#.sb!vm:base-char-stack-sc-number
+ (/show0 "case of BASE-CHAR-STACK-SC-NUMBER")
(code-char
(sap-ref-32 fp (- (* (1+ (sb!c:sc-offset-offset sc-offset))
sb!vm:word-bytes)))))
(#.sb!vm:unsigned-stack-sc-number
+ (/show0 "case of UNSIGNED-STACK-SC-NUMBER")
(sap-ref-32 fp (- (* (1+ (sb!c:sc-offset-offset sc-offset))
sb!vm:word-bytes))))
(#.sb!vm:signed-stack-sc-number
+ (/show0 "case of SIGNED-STACK-SC-NUMBER")
(signed-sap-ref-32 fp (- (* (1+ (sb!c:sc-offset-offset sc-offset))
sb!vm:word-bytes))))
(#.sb!vm:sap-stack-sc-number
+ (/show0 "case of SAP-STACK-SC-NUMBER")
(sap-ref-sap fp (- (* (1+ (sb!c:sc-offset-offset sc-offset))
sb!vm:word-bytes)))))))
:code-location loc :form form :frame frame))
(funcall res frame))))))
+;;; Evaluate FORM in the lexical context of FRAME's current code
+;;; location, returning the results of the evaluation.
(defun eval-in-frame (frame form)
(declare (type frame frame))
- #!+sb-doc
- "Evaluate Form in the lexical context of Frame's current code location,
- returning the results of the evaluation."
(funcall (preprocess-for-eval form (frame-code-location frame)) frame))
\f
;;;; breakpoints
;;;; user-visible interface
+;;; Create and return a breakpoint. When program execution encounters
+;;; the breakpoint, the system calls HOOK-FUNCTION. HOOK-FUNCTION takes the
+;;; current frame for the function in which the program is running and the
+;;; breakpoint object.
+;;;
+;;; WHAT and KIND determine where in a function the system invokes
+;;; HOOK-FUNCTION. WHAT is either a code-location or a debug-function.
+;;; KIND is one of :CODE-LOCATION, :FUNCTION-START, or :FUNCTION-END.
+;;; Since the starts and ends of functions may not have code-locations
+;;; representing them, designate these places by supplying WHAT as a
+;;; debug-function and KIND indicating the :FUNCTION-START or
+;;; :FUNCTION-END. When WHAT is a debug-function and kind is
+;;; :FUNCTION-END, then hook-function must take two additional
+;;; arguments, a list of values returned by the function and a
+;;; FUNCTION-END-COOKIE.
+;;;
+;;; INFO is information supplied by and used by the user.
+;;;
+;;; FUNCTION-END-COOKIE is a function. To implement :FUNCTION-END
+;;; breakpoints, the system uses starter breakpoints to establish the
+;;; :FUNCTION-END breakpoint for each invocation of the function. Upon
+;;; each entry, the system creates a unique cookie to identify the
+;;; invocation, and when the user supplies a function for this
+;;; argument, the system invokes it on the frame and the cookie. The
+;;; system later invokes the :FUNCTION-END breakpoint hook on the same
+;;; cookie. The user may save the cookie for comparison in the hook
+;;; function.
+;;;
+;;; Signal an error if WHAT is an unknown code-location.
(defun make-breakpoint (hook-function what
&key (kind :code-location) info function-end-cookie)
- #!+sb-doc
- "This creates and returns a breakpoint. When program execution encounters
- the breakpoint, the system calls hook-function. Hook-function takes the
- current frame for the function in which the program is running and the
- breakpoint object.
- What and kind determine where in a function the system invokes
- hook-function. What is either a code-location or a debug-function. Kind is
- one of :code-location, :function-start, or :function-end. Since the starts
- and ends of functions may not have code-locations representing them,
- designate these places by supplying what as a debug-function and kind
- indicating the :function-start or :function-end. When what is a
- debug-function and kind is :function-end, then hook-function must take two
- additional arguments, a list of values returned by the function and a
- function-end-cookie.
- Info is information supplied by and used by the user.
- Function-end-cookie is a function. To implement :function-end breakpoints,
- the system uses starter breakpoints to establish the :function-end breakpoint
- for each invocation of the function. Upon each entry, the system creates a
- unique cookie to identify the invocation, and when the user supplies a
- function for this argument, the system invokes it on the frame and the
- cookie. The system later invokes the :function-end breakpoint hook on the
- same cookie. The user may save the cookie for comparison in the hook
- function.
- This signals an error if what is an unknown code-location."
(etypecase what
(code-location
(when (code-location-unknown-p what)
;; This is the debug-function associated with the cookie.
debug-fun)
-;;; This maps bogus-lra-components to cookies, so
+;;; This maps bogus-lra-components to cookies, so that
;;; HANDLE-FUNCTION-END-BREAKPOINT can find the appropriate cookie for the
;;; breakpoint hook.
(defvar *function-end-cookies* (make-hash-table :test 'eq))
(let ((fun (breakpoint-cookie-fun bpt)))
(when fun (funcall fun frame cookie))))))))))
+;;; This takes a FUNCTION-END-COOKIE and a frame, and it returns
+;;; whether the cookie is still valid. A cookie becomes invalid when
+;;; the frame that established the cookie has exited. Sometimes cookie
+;;; holders are unaware of cookie invalidation because their
+;;; :FUNCTION-END breakpoint hooks didn't run due to THROW'ing.
+;;;
+;;; This takes a frame as an efficiency hack since the user probably
+;;; has a frame object in hand when using this routine, and it saves
+;;; repeated parsing of the stack and consing when asking whether a
+;;; series of cookies is valid.
(defun function-end-cookie-valid-p (frame cookie)
- #!+sb-doc
- "This takes a function-end-cookie and a frame, and it returns whether the
- cookie is still valid. A cookie becomes invalid when the frame that
- established the cookie has exited. Sometimes cookie holders are unaware
- of cookie invalidation because their :function-end breakpoint hooks didn't
- run due to THROW'ing. This takes a frame as an efficiency hack since the
- user probably has a frame object in hand when using this routine, and it
- saves repeated parsing of the stack and consing when asking whether a
- series of cookies is valid."
(let ((lra (function-end-cookie-bogus-lra cookie))
(lra-sc-offset (sb!c::compiled-debug-function-return-pc
(compiled-debug-function-compiler-debug-fun
#!+gengc sb!vm::ra-save-offset
lra-sc-offset)))
(return t)))))
-
+\f
;;;; ACTIVATE-BREAKPOINT
+;;; Cause the system to invoke the breakpoint's hook-function until
+;;; the next call to DEACTIVATE-BREAKPOINT or DELETE-BREAKPOINT. The
+;;; system invokes breakpoint hook functions in the opposite order
+;;; that you activate them.
(defun activate-breakpoint (breakpoint)
- #!+sb-doc
- "This causes the system to invoke the breakpoint's hook-function until the
- next call to DEACTIVATE-BREAKPOINT or DELETE-BREAKPOINT. The system invokes
- breakpoint hook functions in the opposite order that you activate them."
(when (eq (breakpoint-status breakpoint) :deleted)
(error "cannot activate a deleted breakpoint: ~S" breakpoint))
(unless (eq (breakpoint-status breakpoint) :active)
(compiled-debug-function
(let ((starter (breakpoint-start-helper breakpoint)))
(unless (eq (breakpoint-status starter) :active)
- ;; May already be active by some other :function-end breakpoint.
+ ;; may already be active by some other :FUNCTION-END breakpoint
(activate-compiled-function-start-breakpoint starter)))
(setf (breakpoint-status breakpoint) :active))
(interpreted-debug-function
(setf (breakpoint-data-breakpoints data)
(append (breakpoint-data-breakpoints data) (list breakpoint)))
(setf (breakpoint-internal-data breakpoint) data)))
-
+\f
;;;; DEACTIVATE-BREAKPOINT
(defun deactivate-breakpoint (breakpoint)
(delete-breakpoint-data data))))
(setf (breakpoint-status breakpoint) :inactive)
breakpoint)
-
+\f
;;;; BREAKPOINT-INFO
(defun breakpoint-info (breakpoint)
(let ((other (breakpoint-unknown-return-partner breakpoint)))
(when other
(setf (breakpoint-%info other) value))))
-
+\f
;;;; BREAKPOINT-ACTIVE-P and DELETE-BREAKPOINT
(defun breakpoint-active-p (breakpoint)
(breakpoint-what breakpoint))
nil))))))
breakpoint)
-
+\f
;;;; C call out stubs
;;; This actually installs the break instruction in the component. It
;;; debugging-tool break instruction. This does NOT handle all breaks;
;;; for example, it does not handle breaks for internal errors.
(defun handle-breakpoint (offset component signal-context)
+ (/show0 "entering HANDLE-BREAKPOINT")
(let ((data (breakpoint-data component offset nil)))
(unless data
(error "unknown breakpoint in ~S at offset ~S"
;;; This handles code-location and debug-function :FUNCTION-START
;;; breakpoints.
(defun handle-breakpoint-aux (breakpoints data offset component signal-context)
+ (/show0 "entering HANDLE-BREAKPOINT-AUX")
(unless breakpoints
(error "internal error: breakpoint that nobody wants"))
(unless (member data *executing-breakpoint-hooks*)
bpt)))))
(defun handle-function-end-breakpoint (offset component context)
+ (/show0 "entering HANDLE-FUNCTION-END-BREAKPOINT")
(let ((data (breakpoint-data component offset nil)))
(unless data
(error "unknown breakpoint in ~S at offset ~S"
;;; [old C code] or HANDLE-FUNCTION-END-BREAKPOINT calls this directly
;;; [new C code].
(defun handle-function-end-breakpoint-aux (breakpoints data signal-context)
+ (/show0 "entering HANDLE-FUNCTION-END-BREAKPOINT-AUX")
(delete-breakpoint-data data)
(let* ((scp
(locally
#!+x86 sb!vm::ebx-offset)))
(nargs (make-lisp-obj
(sb!vm:context-register scp sb!vm::nargs-offset)))
- (reg-arg-offsets '#.sb!vm::register-arg-offsets)
+ (reg-arg-offsets '#.sb!vm::*register-arg-offsets*)
(results nil))
(without-gcing
(dotimes (arg-num nargs)
(stack-ref ocfp arg-num))
results)))
(nreverse results)))
-
-;;;; MAKE-BOGUS-LRA (used for :function-end breakpoints)
+\f
+;;;; MAKE-BOGUS-LRA (used for :FUNCTION-END breakpoints)
(defconstant
bogus-lra-constants