1 ;;;; Testing the behavior of foreign calls trying to unwind the stack. Uses win32-stack-unwind.c.
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; While most of SBCL is derived from the CMU CL system, the test
7 ;;;; files (like this one) were written from scratch after the fork
10 ;;;; This software is in the public domain and is provided with
11 ;;;; absolutely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
14 #-win32
(quit :unix-status
104) ;; This is extremely win32-specific.
16 (use-package :sb-alien
)
18 ;;; Callbacks are not part of the exported interface yet -- when they are this can
20 (import 'sb-alien
::alien-lambda
)
22 (defun run-compiler ()
23 (let ((proc (run-program "gcc" '("win32-stack-unwind.c"
24 "-mno-cygwin" "-shared"
25 "-o" "win32-stack-unwind.dll")
27 (unless (zerop (process-exit-code proc
))
28 (error "Bad exit code: ~S"
29 (process-exit-code proc
)))))
33 (load-shared-object "win32-stack-unwind.dll")
36 (defvar *current-test-callback
*)
38 (defparameter *test-callback-thunk
*
39 (sb-alien::alien-callback
41 #'(lambda () (funcall *current-test-callback
*))))
43 (defun establish-return-frame (callback)
44 "Establish an SEH frame for use as a target with PERFORM-TEST-UNWIND and invoke CALLBACK via FUNCALL"
45 ;; We don't use a LET here because we don't want to accidentally
46 ;; correct a blown binding stack pointer just yet.
47 (setf *current-test-callback
* callback
)
48 (alien-funcall (extern-alien "establish_return_frame"
49 (function void
(* (function void
))))
50 (alien-sap *test-callback-thunk
*))
51 (makunbound '*current-test-callback
*)
54 (defun perform-test-unwind ()
55 "Perform an RtlUnwind to the surrounding ESTABLISH-RETURN-FRAME frame."
56 (alien-funcall (extern-alien "perform_test_unwind" (function void
))))
59 ;;; An attempt to detect and clean up latent fatalities in the
60 ;;; post-test environent.
62 (defmacro with-test-environment
(args &body body
)
63 (declare (ignore args
))
64 (let ((old-bsp (gensym))
68 `(let ((*standard-input
* *standard-input
*))
69 (let ((,old-bsp
(+ sb-vm
::*binding-stack-pointer
* 2))
70 (,old-cuwp sb-vm
::*current-unwind-protect-block
*)
71 (,old-ccb sb-vm
:*current-catch-block
*)
72 (,old-asp sb-vm
::*alien-stack
*))
74 (let ((result (progn ,@body
))
76 (when (not (eql ,old-bsp sb-vm
::*binding-stack-pointer
*))
78 (format t
"~A ~A~%" ,old-bsp sb-vm
::*binding-stack-pointer
*)
79 (push :bsp-fail extra-results
))
80 (when (not (eql ,old-cuwp sb-vm
::*current-unwind-protect-block
*))
81 (push :cuwp-fail extra-results
))
82 (when (not (eql ,old-ccb sb-vm
:*current-catch-block
*))
83 (push :ccb-fail extra-results
))
84 (when (not (eql ,old-asp sb-vm
::*alien-stack
*))
85 (push :asp-fail extra-results
))
86 (setf sb-vm
::*current-unwind-protect-block
* ,old-cuwp
)
87 (setf sb-vm
:*current-catch-block
* ,old-ccb
)
88 (setf sb-vm
::*alien-stack
* ,old-asp
)
89 (list* result extra-results
))
96 (with-test (:name
#1=:base-case
)
97 ;; Tests that the unwind test machinery works.
99 (with-test-environment ()
100 (establish-return-frame (lambda () (perform-test-unwind)))
102 (format t
"~S result: ~S~%" #1# result
)
103 (assert (eql :success
(car result
)))))
105 (with-test (:name
#1=:special-binding
)
106 ;; Tests that special bindings are undone properly during
109 (with-test-environment ()
110 (let ((foo :success
))
111 (declare (special foo
))
112 (establish-return-frame (lambda ()
114 (declare (special foo
))
115 (perform-test-unwind))))
117 (format t
"~S result: ~S~%" #1# result
)
118 (assert (eql :success
(car result
)))))
120 (with-test (:name
#1=:unwind-protect
)
121 ;; Tests that unwind-protect forms are run during unwind.
123 (with-test-environment ()
125 (establish-return-frame (lambda ()
127 (perform-test-unwind)
128 (setf result
:success
))))
130 (format t
"~S result: ~S~%" #1# result
)
131 (assert (eql :success
(car result
)))))
133 (with-test (:name
#1=:unwind-protect-nlx
)
134 ;; Tests that unwind-protect forms that are run during unwind
135 ;; can do a non-local exit to abort the unwind.
137 (with-test-environment ()
139 (establish-return-frame (lambda ()
142 (perform-test-unwind)
144 (setf result
:success
)))
146 (format t
"~S result: ~S~%" #1# result
)
147 (assert (eql :success
(car result
)))))
149 (with-test (:name
#1=:no-unwind
)
150 ;; Basic smoke test of establish-return-frame.
152 (with-test-environment ()
153 (establish-return-frame (lambda ()))
155 (format t
"~S result: ~S~%" #1# result
)
156 (assert (eql :success
(car result
)))))
158 (with-test (:name
#1=:no-unwind-error
)
159 ;; Tests that EXCEPTION_BREAKPOINT is caught and handled
160 ;; correctly within callbacks.
162 (with-test-environment ()
163 (establish-return-frame (lambda ()
165 (some-undefined-function)
166 (undefined-function ()))))
168 (format t
"~S result: ~S~%" #1# result
)
169 (assert (eql :success
(car result
)))))
171 (with-test (:name
#1=:unwind-foreign-frame
)
172 ;; Tests that unwinding a foreign SEH frame isn't completely
175 (with-test-environment ()
177 (establish-return-frame (lambda () (return :success
)))))))
178 (format t
"~S result: ~S~%" #1# result
)
179 (assert (eql :success
(car result
)))))
181 (with-test (:name
#1=:unwind-protect-unwind-foreign-frame
)
182 ;; Tests that an unwind-protect block is allowed to unwind
183 ;; past the original unwind target.
185 (with-test-environment ()
187 (establish-return-frame (lambda ()
189 (perform-test-unwind)
190 (return :success
))))))))
191 (format t
"~S result: ~S~%" #1# result
)
192 (assert (eql :success
(car result
)))))
194 (with-test (:name
#1=:unwind-error
)
195 ;; Another test for unwinding an SEH frame.
197 (with-test-environment ()
199 (establish-return-frame (lambda ()
203 (format t
"~S result: ~S~%" #1# result
)
204 (assert (eql :success
(car result
)))))