1 ;;;; functions and macros to define and deal with internal errors
2 ;;;; (i.e. problems that can be signaled from assembler code)
4 ;;;; This software is part of the SBCL system. See the README file for
7 ;;;; This software is derived from the CMU CL system, which was
8 ;;;; written at Carnegie Mellon University and released into the
9 ;;;; public domain. The software is in the public domain and is
10 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
11 ;;;; files for more information.
13 (in-package "SB!KERNEL")
18 (let ((n (1+ (position-if 'stringp sb
!c
:+backend-internal-errors
+
19 :key
#'car
:from-end t
))))
21 (declaim ((simple-vector ,n
) **internal-error-handlers
**))
22 (!defglobal
**internal-error-handlers
**
23 ,(make-array n
:initial-element
0))))))
26 (eval-when (:compile-toplevel
:execute
)
27 (sb!xc
:defmacro deferr
(name args
&rest body
)
28 (multiple-value-bind (llks required optional rest
) (parse-lambda-list args
)
29 (declare (ignore llks
))
31 (let ((max (+ (length required
) (length optional
))))
33 (error "Update (DEFUN INTERNAL-ERROR) for ~D error arguments" max
))))
34 `(setf (svref **internal-error-handlers
** ,(error-number-or-lose name
))
35 (named-lambda ,(string name
) (,@args
)
36 (declare (optimize (sb!c
::verify-arg-count
0)))
37 ,@body
)))) ; EVAL-WHEN
39 ;;; Backtrace code may want to know the error that caused
40 ;;; interruption, but there are other means to get code interrupted
41 ;;; and inspecting code around PC for the error number may yield wrong
43 (defvar *current-internal-error
* nil
)
44 (defvar *current-internal-trap-number
*)
45 (defvar *current-internal-error-args
*)
47 #!+undefined-fun-restarts
48 (defun restart-undefined (name fdefn-or-symbol context
)
49 (multiple-value-bind (tn-offset pc-offset
)
51 (sb!c
::decode-restart-location context
)
52 (car *current-internal-error-args
*))
53 (labels ((retry-value (value)
55 (fdefn (fdefn-fun value
))
57 (let ((fdefn (symbol-fdefn value
)))
62 (try (make-condition 'retry-undefined-function
64 :format-control
"Bad value when restarting ~s: ~s"
65 :format-arguments
(list name value
))
67 (try (make-condition 'retry-undefined-function
69 :format-control
(if (fdefn-p value
)
70 "~S is still undefined"
71 "Can't replace ~s with ~s because it is undefined")
72 :format-arguments
(list name value
))
74 (set-value (function retrying
)
76 (retry-value function
)
77 (sb!di
::sub-set-debug-var-slot
79 (retry-value function
)
80 *current-internal-error-context
*)))
81 (try (condition &optional retrying
)
83 ;; The #'abc case from SAFE-FDEFN-FUN, CONTEXT
84 ;; specifies the offset from the error location
85 ;; where it can retry checking the FDEFN
87 (restart-case (error condition
)
89 :report
(lambda (stream)
90 (format stream
"Retry using ~s." name
))
91 (set-value fdefn-or-symbol retrying
))
93 :report
(lambda (stream)
94 (format stream
"Use specified function."))
95 :interactive read-evaluated-form
96 (set-value value retrying
)))
98 (sb!vm
::incf-context-pc
*current-internal-error-context
*
101 (restart-case (error condition
)
103 :report
(lambda (stream)
104 (format stream
"Retry calling ~s." name
))
105 (set-value fdefn-or-symbol retrying
))
107 :report
(lambda (stream)
108 (format stream
"Call specified function."))
109 :interactive read-evaluated-form
110 (set-value value retrying
))
111 (return-value (&rest values
)
112 :report
(lambda (stream)
113 (format stream
"Return specified values."))
114 :interactive mv-read-evaluated-form
115 (set-value (lambda (&rest args
)
116 (declare (ignore args
))
117 (values-list values
))
120 :report
(lambda (stream)
121 (format stream
"Return zero values."))
122 (set-value (lambda (&rest args
)
123 (declare (ignore args
))
126 (try (make-condition 'undefined-function
:name name
)))))
128 (deferr undefined-fun-error
(fdefn-or-symbol)
129 (let ((name (etypecase fdefn-or-symbol
130 (symbol fdefn-or-symbol
)
131 (fdefn (let ((name (fdefn-name fdefn-or-symbol
)))
132 ;; fasteval stores weird things in the NAME slot
133 ;; of fdefns of special forms. Have to grab the
134 ;; special form name out of that.
135 (cond #!+(and sb-fasteval immobile-code
)
136 ((and (listp name
) (functionp (car name
)))
137 (cadr (%fun-name
(car name
))))
140 #!+undefined-fun-restarts
142 (cond #!+undefined-fun-restarts
143 ((or (= *current-internal-trap-number
* sb
!vm
:cerror-trap
)
144 (integerp (setf context
(sb!di
:error-context
))))
145 (restart-undefined name fdefn-or-symbol context
))
147 (error 'undefined-function
:name name
)))))
149 #!+(or arm arm64 x86-64
)
150 (deferr undefined-alien-fun-error
(address)
151 (error 'undefined-alien-function-error
153 (and (integerp address
)
154 (sap-foreign-symbol (int-sap address
)))))
156 #!-
(or arm arm64 x86-64
)
157 (defun undefined-alien-fun-error ()
158 (error 'undefined-alien-function-error
))
160 (deferr invalid-arg-count-error
(nargs)
161 (error 'simple-program-error
162 :format-control
"invalid number of arguments: ~S"
163 :format-arguments
(list nargs
)))
165 (deferr local-invalid-arg-count-error
(nargs name
)
166 (error 'simple-program-error
167 :format-control
"~S called with invalid number of arguments: ~S"
168 :format-arguments
(list name nargs
)))
170 (deferr bogus-arg-to-values-list-error
(list)
171 (error 'simple-type-error
175 "~@<attempt to use VALUES-LIST on a dotted list: ~2I~_~S~:>"
176 :format-arguments
(list list
)))
178 (deferr unbound-symbol-error
(symbol)
179 (error 'unbound-variable
:name symbol
))
181 (deferr invalid-unwind-error
()
182 (error 'simple-control-error
184 "attempt to RETURN-FROM a block or GO to a tag that no longer exists"))
186 (deferr unseen-throw-tag-error
(tag)
187 (let ((text "attempt to THROW to a tag that does not exist: ~S"))
190 (binding* ((frame (find-interrupted-frame))
191 (name (sb!di
:debug-fun-name
(sb!di
:frame-debug-fun frame
)))
192 (down (and (eq name
'sb
!c
::unwind
) ; is this tautological ?
193 (sb!di
:frame-down frame
)) :exit-if-null
))
194 (case (sb!di
:debug-fun-name
(sb!di
:frame-debug-fun down
))
196 (setq text
"attempt to RETURN-FROM an exited block: ~S"
197 ;; block name was wrapped in a cons
200 ;; FIXME: can we reverse-engineer the tag name from
201 ;; the object that was thrown, for a better diagnostic?
202 (setq text
"attempt to GO into an exited tagbody")))))
203 (error 'simple-control-error
204 :format-control text
:format-arguments
(list tag
))))
206 (deferr nil-fun-returned-error
(function)
207 (error 'simple-control-error
209 "A function with declared result type NIL returned:~% ~S"
210 :format-arguments
(list function
)))
212 (deferr nil-array-accessed-error
(array)
213 (error 'nil-array-accessed-error
214 :datum array
:expected-type
'(not (array nil
))))
216 (deferr division-by-zero-error
(this that
)
217 (error 'division-by-zero
219 :operands
(list this that
)))
221 (deferr object-not-type-error
(object type
)
222 (if (invalid-array-p object
)
223 (invalid-array-error object
)
224 (error (if (and (%instancep object
)
225 (layout-invalid (%instance-layout object
)))
230 :context
(sb!di
:error-context
))))
232 (deferr layout-invalid-error
(object layout
)
233 (error 'layout-invalid
235 :expected-type
(layout-classoid layout
)))
237 (deferr odd-key-args-error
()
238 (error 'simple-program-error
239 :format-control
"odd number of &KEY arguments"))
241 (deferr unknown-key-arg-error
(key-name)
242 (let ((context (sb!di
:error-context
)))
243 (if (integerp context
)
245 (error 'unknown-keyword-argument
:name key-name
)
247 :report
(lambda (stream)
248 (format stream
"Ignore all unknown keywords"))
249 (sb!vm
::incf-context-pc
*current-internal-error-context
*
251 (error 'unknown-keyword-argument
:name key-name
))))
253 ;; TODO: make the arguments (ARRAY INDEX &optional BOUND)
254 ;; and don't need the bound for vectors. Just read it.
255 (deferr invalid-array-index-error
(array bound index
)
256 (invalid-array-index-error array index bound
))
258 (deferr tls-exhausted-error
()
259 ;; There is nothing we can do about it. A number of entries in the
260 ;; tls could be reserved and made available for recovery but since
261 ;; tls indices are never reused it would be kind of silly and
262 ;; without it signalling an error is more than likely to end in a
264 (%primitive print
"Thread local storage exhausted.")
267 ;;;; INTERNAL-ERROR signal handler
269 ;;; This is needed for restarting XEPs, which do not bind anything but
270 ;;; also do not save their own BSP, and we need to discard the
271 ;;; bindings made by the error handling machinery.
272 #!+unwind-to-frame-and-call-vop
273 (defvar *interr-current-bsp
* nil
)
275 (defun internal-error (context continuable
)
276 (declare (type system-area-pointer context
))
277 (declare (ignore continuable
))
278 (/show0
"entering INTERNAL-ERROR, CONTEXT=..")
280 (let (#!+unwind-to-frame-and-call-vop
281 (*interr-current-bsp
*
282 ;; Needs to be done before anything is bound
283 (%primitive sb
!c
:current-binding-pointer
)))
284 (infinite-error-protect
285 (/show0
"about to bind ALIEN-CONTEXT")
286 (let* ((alien-context (sap-alien context
(* os-context-t
)))
287 #!+c-stack-is-control-stack
288 (fp-and-pc (make-array 2 :element-type
'word
)))
289 #!+c-stack-is-control-stack
290 (declare (truly-dynamic-extent fp-and-pc
))
291 #!+c-stack-is-control-stack
292 (setf (aref fp-and-pc
0) (sb!vm
:context-register alien-context sb
!vm
::cfp-offset
)
293 (aref fp-and-pc
1) (sb!sys
:sap-int
(sb!vm
:context-pc alien-context
)))
294 (let (#!+c-stack-is-control-stack
295 (*saved-fp-and-pcs
* (cons fp-and-pc
*saved-fp-and-pcs
*)))
296 #!+c-stack-is-control-stack
297 (declare (truly-dynamic-extent *saved-fp-and-pcs
*))
298 (/show0
"about to bind ERROR-NUMBER and ARGUMENTS"))
299 (multiple-value-bind (error-number arguments
300 *current-internal-trap-number
*)
301 (sb!vm
:internal-error-args alien-context
)
302 (with-interrupt-bindings
303 (let ((sb!debug
:*stack-top-hint
* (find-interrupted-frame))
304 (*current-internal-error
* error-number
)
305 (*current-internal-error-args
* arguments
)
306 (*current-internal-error-context
* alien-context
)
307 (fp (int-sap (sb!vm
:context-register alien-context
308 sb
!vm
::cfp-offset
))))
309 (if (and (>= error-number
(length **internal-error-handlers
**))
310 (< error-number
(length sb
!c
:+backend-internal-errors
+)))
311 (let ((context (sb!di
:error-context
)))
312 (if (typep context
'(cons (eql :struct-read
)))
313 ;; This was shoehorned into being a "type error"
314 ;; which isn't the best way to explain it to the user.
315 ;; However, from an API stance, it makes some sense to signal
316 ;; a TYPE-ERROR since there may be existing code that catches
317 ;; unbound slots errors as type-errors. Our tests certainly do,
318 ;; but perhaps only as an artifact of the implementation.
319 (destructuring-bind (struct-name . slot-name
) (cdr context
)
320 ;; Infer the slot type, but fail safely. The message is enough,
321 ;; and the required type is pretty much irrelevant.
322 (let* ((dd (find-defstruct-description struct-name
))
323 (dsd (and dd
(find slot-name
(dd-slots dd
) :key
#'dsd-name
))))
324 (error 'simple-type-error
325 :format-control
"Accessed uninitialized slot ~S of structure ~S"
326 :format-arguments
(list slot-name struct-name
)
327 :datum
(make-unbound-marker)
328 :expected-type
(if dsd
(dsd-type dsd
) 't
))))
330 :datum
(sb!di
::sub-access-debug-var-slot
331 fp
(first arguments
) alien-context
)
333 (car (svref sb
!c
:+backend-internal-errors
+
337 (and (typep error-number
338 '#.
`(mod ,(length **internal-error-handlers
**)))
339 (svref **internal-error-handlers
** error-number
))))
342 ;; INTERNAL-ERROR-ARGS supplies the right amount of arguments
344 `(sb!di
::sub-access-debug-var-slot
345 fp
(nth ,n arguments
) alien-context
)))
346 (ecase (length arguments
)
347 (0 (funcall handler
))
348 (1 (funcall handler
(arg 0)))
349 (2 (funcall handler
(arg 0) (arg 1)))
350 (3 (funcall handler
(arg 0) (arg 1) (arg 2))))))
351 ((eql handler
0) ; if (DEFERR x) was inadvertently omitted
354 "unknown internal error, ~D, args=~S"
357 (mapcar (lambda (sc-offset)
358 (sb!di
::sub-access-debug-var-slot
359 fp sc-offset alien-context
))
363 :format-control
"internal error ~D: ~A; args=~S"
367 (mapcar (lambda (sc-offset)
368 (sb!di
::sub-access-debug-var-slot
369 fp sc-offset alien-context
))
370 arguments
))))))))))))))
372 (defun control-stack-exhausted-error ()
373 (let ((sb!debug
:*stack-top-hint
* nil
))
374 (infinite-error-protect
375 (format *error-output
*
376 "Control stack guard page temporarily disabled: proceed with caution~%")
377 (error 'control-stack-exhausted
))))
379 (defun binding-stack-exhausted-error ()
380 (let ((sb!debug
:*stack-top-hint
* nil
))
381 (infinite-error-protect
382 (format *error-output
*
383 "Binding stack guard page temporarily disabled: proceed with caution~%")
384 (error 'binding-stack-exhausted
))))
386 (defun alien-stack-exhausted-error ()
387 (let ((sb!debug
:*stack-top-hint
* nil
))
388 (infinite-error-protect
389 (format *error-output
*
390 "Alien stack guard page temporarily disabled: proceed with caution~%")
391 (error 'alien-stack-exhausted
))))
393 ;;; KLUDGE: we keep a single HEAP-EXHAUSTED-ERROR object around, so
394 ;;; that we don't need to allocate it when running out of
395 ;;; memory. Similarly we pass the amounts in special variables as
396 ;;; there may be multiple threads running into trouble at the same
397 ;;; time. The condition is created by GC-REINIT.
398 (define-load-time-global *heap-exhausted-error-condition
*
399 (make-condition 'heap-exhausted-error
))
400 (defvar *heap-exhausted-error-available-bytes
*)
401 (defvar *heap-exhausted-error-requested-bytes
*)
403 (defun heap-exhausted-error (available requested
)
404 ;; Double word aligned bytes, can be passed as fixnums to avoid
405 ;; allocating bignums on the C side.
406 (declare (fixnum available requested
))
407 (infinite-error-protect
408 (let ((*heap-exhausted-error-available-bytes
*
409 (ash available sb
!vm
:n-fixnum-tag-bits
))
410 (*heap-exhausted-error-requested-bytes
*
411 (ash requested sb
!vm
:n-fixnum-tag-bits
)))
412 (error *heap-exhausted-error-condition
*))))
414 (defun undefined-alien-variable-error ()
415 (declare (optimize allow-non-returning-tail-call
))
416 (error 'undefined-alien-variable-error
))
419 (define-alien-variable current-memory-fault-address unsigned
)
422 (defun memory-fault-error ()
423 (let ((sb!debug
:*stack-top-hint
* (find-interrupted-frame)))
424 (error 'memory-fault-error
425 :address current-memory-fault-address
)))
427 ;;; This is SIGTRAP / EXCEPTION_BREAKPOINT that runtime could not deal
428 ;;; with. Prior to Windows we just had a Lisp side handler for
429 ;;; SIGTRAP, but now we need to deal with this portably.
430 (defun unhandled-trap-error (context-sap)
431 (declare (type system-area-pointer context-sap
))
432 (infinite-error-protect
433 (let ((context (sap-alien context-sap
(* os-context-t
))))
434 (error 'breakpoint-error
436 :address
(sap-int (sb!vm
:context-pc context
))))))