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 ;;;; absoluely no warranty. See the COPYING and CREDITS files for
12 ;;;; more information.
15 (defconstant min-code-header-bytes
16 (let ((min-header-words (* 2 (ceiling sb-vm
:code-constants-offset
2))))
17 (* min-header-words sb-vm
:n-word-bytes
)))
19 ;;; A newly opened region must not start on a page that has 0 bytes available.
20 ;;; The effect of that was to cause start_addr to be the next page's address,
21 ;;; where the OPEN_REGION_PAGE_FLAG was already set on each page in the region
22 ;;; including the one that was completely full. This caused a failure when
23 ;;; closing the region because find_page_index(start_addr) was not the *first*
24 ;;; page on which the open flag should be removed.
25 ;;; Strangely, the assertion that caught this was far removed from the
26 ;;; point of failure, in conservative_root_p()
28 (with-test (:name
:gc-region-pickup
:skipped-on
(not (or :x86
:x86-64
)))
29 (flet ((allocate-code-bytes (nbytes)
30 ;; Make a code component occupying exactly NBYTES bytes in total.
31 (assert (zerop (mod nbytes
(* 2 sb-vm
:n-word-bytes
))))
32 (assert (>= nbytes min-code-header-bytes
))
33 (sb-c::allocate-code-object nil
0 (- nbytes min-code-header-bytes
)))
35 (declare (type (simple-array sb-ext
:word
(4)) a
))
36 ;; Return array of 4: free-ptr, end-addr, last-page, start-addr
39 (deref (extern-alien "gc_alloc_region" (array unsigned
12))
40 ;; code region is the third region in the GC global
41 ;; regions. Each region is described by 4 words.
43 (let ((a (make-array 4 :element-type
'sb-ext
:word
))
46 #-
64-bit
(- (* 4 sb-vm
:gencgc-card-bytes
) (* 2 sb-vm
:n-word-bytes
)))
49 (symbol-macrolet ((free-ptr (aref a
0))
51 (last-page (aref a
2))
52 (start-addr (aref a
3)))
53 (gc) ; This will leave the code region in a closed state
55 (assert (= free-ptr end-addr
))
56 ;; Allocate a teency amount to start a new region
57 (sb-c::allocate-code-object nil
0 0)
59 (setq saved-region-start start-addr
60 saved-region-end end-addr
)
61 (multiple-value-bind (n-chunks remainder
)
62 (floor (- end-addr free-ptr
) code-size
)
63 ;; (Maybe) use up a few bytes more so that the larger objects
64 ;; exactly consume the entirety of the region.
65 (when (plusp remainder
)
66 (allocate-code-bytes (max remainder min-code-header-bytes
))
68 (multiple-value-setq (n-chunks remainder
)
69 (floor (- end-addr free-ptr
) code-size
)))
70 (when (plusp remainder
)
71 ;; This happens only if the MAX expression above bumped the
72 ;; remainder up, so now there is a different remainder.
73 (allocate-code-bytes remainder
))
74 (dotimes (i (1- n-chunks
))
75 (allocate-code-bytes code-size
))
76 ;; Now make two more objects, one consuming almost the entirety
77 ;; of the region, and one touching just the final page.
78 (allocate-code-bytes (- code-size
128))
79 (let ((c (allocate-code-bytes 128)))
81 ;; The region should be the same region we started with,
83 (assert (= start-addr saved-region-start
))
84 (assert (= end-addr saved-region-end
))
85 ;; It should be totally full
86 (assert (= free-ptr end-addr
))
87 ;; Create an object to open a new region where the last one
88 ;; ended. It should start on the next completely empty page,
89 ;; not the prior totally full page.
90 (allocate-code-bytes 128)
93 ;; Return C (so that it has to be kept live on the stack).
96 ;;; This test pertains only to the compact-instance-header feature.
97 #-compact-instance-header
(exit :code
104)
99 ;;; Everything from here down to the WITH-TEST is the setup to try
100 ;;; to hit "implausible layout" in verify_gc() which would occur
101 ;;; prior to the associated fix in update_page_write_prot().
102 ;;; GC has to use care if the sole pointer to a layout is the header
103 ;;; of an obsolete instance, so not to miss any old -> young pointers
104 ;;; where the layout pointer is in the high half of the header word.
105 (setf (extern-alien "verify_gens" char
) 0)
109 (defun change-foo-layout (myfoo)
110 ;; Slam a new layout (a copy of the existing one) into a FOO
111 ;; The new layout will be *younger* than the FOO itself,
112 ;; which is exactly the situation that tickled the GC bug.
113 (flet ((copy-layout (layout)
114 ;; don't just COPY-STRUCTURE - that would place it in dynamic space
116 (sb-kernel:make-layout
117 :classoid
(sb-kernel:layout-classoid layout
))))
120 (- (sb-kernel:get-lisp-obj-address layout
)
121 sb-vm
:instance-pointer-lowtag
))
122 sb-vm
:n-word-bytes
; do not copy the header!
124 (- (sb-kernel:get-lisp-obj-address new-layout
)
125 sb-vm
:instance-pointer-lowtag
))
126 sb-vm
:n-word-bytes
; correspondingly with above
127 (* (1+ (sb-kernel:%instance-length layout
))
130 (setf (sb-kernel:%instance-layout myfoo
)
131 (copy-layout (sb-kernel:%instance-layout myfoo
)))
134 (defconstant n-conses-per-page
135 (/ sb-vm
:gencgc-card-bytes
(* 2 sb-vm
:n-word-bytes
)))
138 (make-array (1+ (* 2 n-conses-per-page
))))
140 ;;; In order to get a FOO on a page by itself, we pad the page
141 ;;; with conses of immediate values, before and after.
142 ;;; So it doesn't matter where the FOO is, but there won't be
143 ;;; other objects with pointers in them (the conses don't have pointers).
144 ;;; And we point at all those conses from a single vector
145 ;;; so that scavenging is forced to linearize them onto one page,
146 ;;; along with the FOO, after discovering that the vector is live.
150 (setf (svref *junk
* (incf j
)) (cons 1 2)))
151 (setf (svref *junk
* (incf j
)) (make-foo))
153 (setf (svref *junk
* (incf j
)) (cons 1 2)))))
155 (filljunk n-conses-per-page
)
157 ;; Promote *junk* and all its referenced objects into generation 1
158 ;; With luck, they should be in order, and the page on which the FOO
159 ;; resides should have some leading and trailing conses.
160 ;; (The conses ensure that nothing else is on the page impeding
161 ;; validity of the test)
163 (assert (= (sb-kernel:generation-of
*junk
*) 1))
165 ;;; This test is very contrived, but this bug was observed in real life,
166 ;;; having something to do with SB-PCL::CHECK-WRAPPER-VALIDITY.
167 (with-test (:name
:gc-anonymous-layout
)
168 ;;; The page on which the FOO instance resides should be WPed
169 ;;; and should have nothing else but conses on it.
170 (let* ((foo (svref *junk
* n-conses-per-page
))
171 (page (sb-vm::find-page-index
(sb-kernel:get-lisp-obj-address foo
)))
172 (gen (slot (deref sb-vm
::page-table page
) 'sb-vm
::gen
))
173 (flags (slot (deref sb-vm
::page-table page
) 'sb-vm
::flags
))
174 (wp (logbitp 3 flags
))
175 (page-addr (+ sb-vm
:dynamic-space-start
176 (* sb-vm
:gencgc-card-bytes page
)))
178 (declare (ignorable gen wp
))
179 ; (format t "~&page ~d: gen=~d flags=~b (wp=~a)~%" page gen flags wp))
180 ;; Check that the page holding the FOO has those conses and nothing else
181 (sb-vm::map-objects-in-range
182 (lambda (obj type size
)
183 (declare (ignore type size
))
184 (unless (typep obj
'(or (cons (eql 1) (eql 2)) foo
))
186 (sb-kernel:%make-lisp-obj page-addr
)
187 (sb-kernel:%make-lisp-obj
(+ page-addr sb-vm
:gencgc-card-bytes
)))
188 (assert aok
) ; page should have nothing but a foo and the conses
190 (change-foo-layout foo
)
192 ;; Assert that we didn't accidentally copy the header word of the layout,
193 ;; which would place it in generation 1 (and probably break other parts of GC)
194 (assert (= (sb-kernel:generation-of
(sb-kernel:%instance-layout foo
)) 0))
196 ;; And the page with FOO must have gotten touched
197 (assert (not (logbitp 3 (slot (deref sb-vm
::page-table page
) 'sb-vm
::flags
))))
199 ;; It requires *two* GCs, not one, to cause this bug.
200 ;; The first GC sees that the page with the FOO on it was touched,
201 ;; and so GC is required to scavenge the whole page.
202 ;; This scavenge pass enlivens the wonky layout that we created,
203 ;; but it INCORRECTLY would re-protect the page, because there did
204 ;; not seem to be any pointer to younger. (The conses are immediates,
205 ;; the FOO has no slots, and its compact header was opaque)
208 (format t
"~&page ~d: wp=~a~%"
210 (logbitp 3 (slot (deref sb-vm
::page-table page
) 'sb-vm
::flags
)))
212 ;; This GC would fail in the verify step because it trashes the apparently
213 ;; orphaned layout, which actually does have a referer.