1 ;;;; heap-grovelling memory usage stuff
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
15 ;;;; type format database
17 ;;; FIXME: this structure seems to no longer serve a purpose.
18 ;;; We'd do as well with a simple-vector of (or symbol cons saetp).
19 (defstruct (room-info (:constructor make-room-info
(name))
21 (name nil
:type symbol
:read-only t
)) ; the name of this type
22 (declaim (freeze-type room-info
))
24 (defun room-info-type-name (info)
25 (if (specialized-array-element-type-properties-p info
)
26 (saetp-primitive-type-name info
)
27 (room-info-name info
)))
29 (defconstant tiny-boxed-size-mask
#xFF
)
30 (defun compute-room-infos ()
31 (let ((infos (make-array 256 :initial-element nil
)))
32 (dolist (obj *primitive-objects
*)
33 (let ((widetag (primitive-object-widetag obj
))
34 (lowtag (primitive-object-lowtag obj
))
35 (name (primitive-object-name obj
)))
36 (when (and (member lowtag
'(other-pointer-lowtag fun-pointer-lowtag
37 instance-pointer-lowtag
))
38 (not (member widetag
'(t nil simple-fun-widetag
))))
39 (setf (svref infos
(symbol-value widetag
)) (make-room-info name
)))))
41 (let ((info (make-room-info 'array-header
)))
42 (dolist (code (list #+sb-unicode complex-character-string-widetag
43 complex-base-string-widetag simple-array-widetag
44 complex-bit-vector-widetag complex-vector-widetag
45 complex-array-widetag
))
46 (setf (svref infos code
) info
)))
48 (dotimes (i (length *specialized-array-element-type-properties
*))
49 (let ((saetp (aref *specialized-array-element-type-properties
* i
)))
50 (setf (svref infos
(saetp-typecode saetp
)) saetp
)))
52 (let ((cons-info (make-room-info 'cons
)))
53 ;; A cons consists of two words, both of which may be either a
54 ;; pointer or immediate data. According to the runtime this means
55 ;; either a fixnum, a character, an unbound-marker, a single-float
56 ;; on a 64-bit system, or a pointer.
57 (dotimes (i (ash 1 (- n-widetag-bits n-fixnum-tag-bits
)))
58 (setf (svref infos
(ash i n-fixnum-tag-bits
)) cons-info
))
60 (dotimes (i (ash 1 (- n-widetag-bits n-lowtag-bits
)))
61 (setf (svref infos
(logior (ash i n-lowtag-bits
) instance-pointer-lowtag
))
63 (setf (svref infos
(logior (ash i n-lowtag-bits
) list-pointer-lowtag
))
65 (setf (svref infos
(logior (ash i n-lowtag-bits
) fun-pointer-lowtag
))
67 (setf (svref infos
(logior (ash i n-lowtag-bits
) other-pointer-lowtag
))
70 (setf (svref infos character-widetag
) cons-info
)
72 (setf (svref infos unbound-marker-widetag
) cons-info
)
74 ;; Single-floats are immediate data on 64-bit systems.
75 #+64-bit
(setf (svref infos single-float-widetag
) cons-info
))
79 (define-load-time-global *room-info
* (compute-room-infos))
80 (declaim (type (simple-vector 256) *room-info
*))
82 (defconstant-eqx +heap-spaces
+
83 '((:dynamic
"Dynamic space" dynamic-usage
)
85 (:immobile
"Immobile space" sb-kernel
::immobile-space-usage
)
86 (:read-only
"Read-only space" sb-kernel
::read-only-space-usage
)
87 (:static
"Static space" sb-kernel
::static-space-usage
))
90 (defconstant-eqx +stack-spaces
+
91 '((:control-stack
"Control stack" sb-kernel
::control-stack-usage
)
92 (:binding-stack
"Binding stack" sb-kernel
::binding-stack-usage
))
95 (defconstant-eqx +all-spaces
+ (append +heap-spaces
+ +stack-spaces
+) #'equal
)
97 (defconstant-eqx +heap-space-keywords
+ (mapcar #'first
+heap-spaces
+) #'equal
)
98 (deftype spaces
() `(member .
,+heap-space-keywords
+))
101 ;;;; MAP-ALLOCATED-OBJECTS
103 ;;; Return the lower limit and current free-pointer of SPACE as fixnums
104 ;;; whose raw bits (at the register level) represent a pointer.
105 ;;; This makes it "off" by a factor of (EXPT 2 N-FIXNUM-TAG-BITS) - and/or
106 ;;; possibly negative - if you look at the value in Lisp,
107 ;;; but avoids potentially needing a bignum on 32-bit machines.
108 ;;; 64-bit machines have no problem since most current generation CPUs
109 ;;; use an address width that is narrower than 64 bits.
110 ;;; This function is private because of the wacky representation.
111 (defun %space-bounds
(space)
112 (macrolet ((bounds (a b
) `(values (%make-lisp-obj
,a
) (%make-lisp-obj
,b
))))
114 (:static
;; These bounds are appropriate for computing the space usage
115 ;; but NOT for computing iteration bounds, because there are
116 ;; "unformatted" words preceding the lowest addressable object.
117 (bounds static-space-start
118 (sap-int *static-space-free-pointer
*)))
120 (bounds read-only-space-start
121 (sap-int *read-only-space-free-pointer
*)))
124 (bounds fixedobj-space-start
125 (sap-int *fixedobj-space-free-pointer
*)))
128 (bounds text-space-start
129 (sap-int *text-space-free-pointer
*)))
131 (bounds dynamic-space-start
132 (sap-int (dynamic-space-free-pointer)))))))
134 ;;; Return the total number of bytes used in SPACE.
135 (defun space-bytes (space)
136 (if (eq space
:immobile
)
137 (+ (space-bytes :immobile-fixed
)
138 (space-bytes :immobile-variable
))
139 (multiple-value-bind (start end
) (%space-bounds space
)
140 (ash (- end start
) n-fixnum-tag-bits
))))
142 (defun instance-length (instance) ; excluding header, not aligned to even
143 ;; Add 1 if expressed length PLUS header (total number of words) would be
144 ;; an even number, and the hash state bits indicate hashed-and-moved.
145 (+ (%instance-length instance
)
146 ;; Compute 1 or 0 depending whether the instance was physically extended
147 ;; by one word for the stable hash value. Extension occurs when and only when
148 ;; the hash state is hashed-and-moved, and the apparent total number of words
149 ;; inclusive of header (and exclusive of extension) is even. ANDing the least
150 ;; significant bit of the payload size with HASH-SLOT-PRESENT arrives at the
151 ;; desired boolean value. If apparent size is odd in hashed-and-moved state,
152 ;; the physical size undergoes no change.
153 (let ((header-word (instance-header-word instance
)))
154 (logand (ash header-word
(- instance-length-shift
))
155 (ash header-word
(- hash-slot-present-flag
))
158 ;;; Iterate over all the objects in the contiguous block of memory
159 ;;; with the low address at START and the high address just before
160 ;;; END, calling FUN with the object, the object's type code, and the
161 ;;; object's total size in bytes, including any header and padding.
162 ;;; START and END are untagged, aligned memory addresses interpreted
163 ;;; as FIXNUMs (unlike SAPs or tagged addresses, these will not cons).
164 (defun map-objects-in-range (fun start end
&optional
(strict-bound t
))
165 (declare (type function fun
))
166 (declare (dynamic-extent fun
))
167 (let ((start (descriptor-sap start
))
168 (end (descriptor-sap end
)))
170 (if (sap>= start end
) (return))
171 (let ((word (sap-ref-word start
0)))
173 ((= (logand word widetag-mask
) filler-widetag
) ; pseudo-object
174 (let ((size (ash (filler-nwords word
) word-shift
)))
175 (setq start
(sap+ start size
))))
176 ((= word most-positive-word
)
177 ;; has to be a pseudo-cons resulting from removing an insignificant
178 ;; sign word of a bignum. Don't call FUN
179 (setq start
(sap+ start
(* 2 n-word-bytes
))))
182 ((widetag (widetag@baseptr start
))
183 (obj (lispobj@baseptr start widetag
))
185 ;; PRIMITIVE-OBJECT-SIZE works on conses, but they're exceptions already
186 ;; because of absence of a widetag, so may as well not call the sizer.
188 (values list-pointer-lowtag
(* 2 n-word-bytes
))
189 (values widetag
(primitive-object-size obj
)))))
190 ;; SIZE is surely a fixnum. Non-fixnum would imply at least
191 ;; a 512MB object if 32-bit words, and is inconceivable if 64-bit.
192 ;; But check to be sure.
193 (aver (not (logtest (the fixnum size
) lowtag-mask
)))
194 (funcall fun obj typecode size
)
195 (setq start
(sap+ start size
)))))))
197 ;; If START is not eq to END, then we have blown past our endpoint.
199 (unless (sap= start end
)
200 ;; don't make things go more wrong than they already are.
201 (alien-funcall (extern-alien "printf" (function void system-area-pointer
))
202 (vector-sap #.
(format nil
"map-objects-in-range failure~%")))
205 (aver (sap= start end
)))))
208 (define-alien-variable "allocation_bitmap" (* unsigned-char
))
211 (defun map-objects-in-discontiguous-range (fun start end generation-mask
)
212 (declare (type function fun
)
213 (type fixnum start end
))
214 (declare (dynamic-extent fun
))
215 ;; START/END are passed as fixnum-encoded raw words to ensure no boxing
216 (let* ((start (get-lisp-obj-address start
))
217 (end (get-lisp-obj-address end
))
218 (first-byte (floor (- start dynamic-space-start
)
219 (ash 8 n-lowtag-bits
)))
220 (last-byte (ceiling (- end dynamic-space-start n-lowtag-bits
)
221 (ash 8 n-lowtag-bits
))))
222 (loop for byte from first-byte to last-byte
224 (when (logbitp bit
(deref allocation-bitmap byte
))
225 (let ((position (+ dynamic-space-start
226 (ash (+ (* byte
8) bit
) n-lowtag-bits
))))
227 (when (and (<= start position
) (< position end
))
228 ;; As in MAP-OBJECTS-IN-RANGE.
230 ((widetag (widetag@baseptr
(int-sap position
)))
231 (obj (lispobj@baseptr
(int-sap position
) widetag
))
234 (values list-pointer-lowtag
(* 2 n-word-bytes
))
235 (values widetag
(primitive-object-size obj
)))))
236 (aver (not (logtest (the fixnum size
) lowtag-mask
)))
237 ;; TODO: Each line has exactly one generation; should
238 ;; check that in the outer loop instead.
239 ;; This code SHOULD work but does not:
240 ;; (let ((gen (the (not null) (generation-of obj))))
241 ;; (when (logbitp gen generation-mask)
242 ;; So it was using the 'default' arg to gc_gen_of.
243 ;; But why??? We're in a generational space aren't we?
244 (let ((gen (generation-of obj
)))
245 (when (and gen
(logbitp gen generation-mask
))
246 (funcall fun obj typecode size
)))))))))))
248 ;;; Access to the GENCGC page table for better precision in
249 ;;; MAP-ALLOCATED-OBJECTS
250 (define-alien-variable "next_free_page" sb-kernel
::page-index-t
)
254 (deftype immobile-subspaces
() '(member :fixed
:variable
))
255 (declaim (ftype (sfunction (function &rest immobile-subspaces
) null
)
256 map-immobile-objects
))
257 (defun map-immobile-objects (function &rest subspaces
) ; Perform no filtering
258 (declare (dynamic-extent function
))
259 (do-rest-arg ((subspace) subspaces
)
260 (multiple-value-bind (start end
) (%space-bounds subspace
)
261 (map-objects-in-range function start end
)))))
264 MAP-ALLOCATED-OBJECTS is fundamentally unsafe to use if the user-supplied
265 function allocates anything. Consider what can happens when NEXT-FREE-PAGE
266 points to a partially filled page
, and one more object is created extending
267 an allocation region that began on the formerly
"last" page
:
269 0x10027cfff0: 0x00000000000000d9 <-- this was Lisp
's view of
270 0x10027cfff8: 0x0000000000000006 the last page
(page 1273)
271 ---- page boundary ----
272 0x10027d0000: 0x0000001000005ecf <-- next_free_page moves here
(page 1274)
273 0x10027d0008: 0x00000000000000ba
274 0x10027d0010: 0x0000000000000040
275 0x10027d0018: 0x0000000000000000
277 Lisp did not think that the page starting at
0x10027d0000 was allocated
,
278 so it believes the stopping point is page
1273. When we read the bytes-used
279 on that page
, we see a totally full page
, but do not consider adjoining any
280 additional pages into the contiguous block.
281 However the object
, a vector
, that started on page
1273 ends on page
1274,
282 causing MAP-OBJECTS-IN-RANGE to assert that it overran
0x10027d0000.
284 We could try a few things to mitigate this
:
285 * Try to
"chase" the value of next-free-page. This is literally impossible -
286 it
's a moving target
, and it
's extremely likely to exhaust memory doing so
,
287 especially if the supplied lambda is an interpreted function.
288 (Each object scanned causes consing of more bytes
, and we never
289 "catch up" to the moving next-free-page
)
291 * If the page that we
're looking at is full but the FINALLY clause is hit
,
292 don
't stop looking for more pages in that one case. Instead keep looking
293 for the end of the contiguous block
, but stop as soon as any potential
294 stopping point is found
; don't chase next-free-page. This is tricky
295 as well and just about as infeasible.
297 * Pass a flag to MAP-OBJECTS-IN-RANGE specifying that it
's OK to
298 surpass the expected bound - silently accept our fate.
299 This is what we do since it
's simple
, and seems to work.
302 ;;; Iterate over all the objects allocated in each of the SPACES, calling FUN
303 ;;; with the object, the object's type code, and the object's total size in
304 ;;; bytes, including any header and padding. As a special case, if exactly one
305 ;;; space named :ALL is requested, then map over the known spaces.
306 (defun map-allocated-objects (fun &rest spaces
)
307 (declare (type function fun
)
308 ;; KLUDGE: rest-arg and self calls do not play nice and it'll get consed
309 (optimize (sb-c::recognize-self-calls
0)))
310 (declare (dynamic-extent fun
))
311 (when (and (= (length spaces
) 1) (eq (first spaces
) :all
))
312 (return-from map-allocated-objects
313 (map-allocated-objects fun
315 #+immobile-space
:immobile
317 ;; You can't specify :ALL and also a list of spaces. Check that up front.
318 (do-rest-arg ((space) spaces
) (the spaces space
))
319 (flet ((do-1-space (space)
322 ;; Static space starts with NIL, which requires special
323 ;; handling, as the header and alignment are slightly off.
324 (funcall fun nil symbol-widetag
(* sizeof-nil-in-words n-word-bytes
))
325 (let ((start (%make-lisp-obj
(+ static-space-start static-space-objects-offset
)))
326 (end (%make-lisp-obj
(sap-int *static-space-free-pointer
*))))
327 (map-objects-in-range fun start end
)))
329 ;; Read-only space (and dynamic space on cheneygc) is a block
330 ;; of contiguous allocations.
331 (multiple-value-bind (start end
) (%space-bounds space
)
332 (map-objects-in-range fun start end
)))
335 (with-system-mutex (*allocator-mutex
*)
336 (map-immobile-objects fun
:variable
))
337 ;; Filter out padding words
338 (dx-flet ((filter (obj type size
)
339 (unless (= type list-pointer-lowtag
)
340 (funcall fun obj type size
))))
341 (map-immobile-objects #'filter
:fixed
))))))
342 (do-rest-arg ((space) spaces
)
343 (if (eq space
:dynamic
)
344 (without-gcing (walk-dynamic-space fun
#b1111111
0 0))
345 (do-1-space space
)))))
347 ;;; Using the mask bits you can make many different match conditions resulting
348 ;;; from a product of {boxed,unboxed,code,any} x {large,non-large,both}
349 ;;; e.g. mask = #b10011" constraint = "#b10010"
350 ;;; matches "large & (unboxed | code)"
352 ;;; I think, when iterating over only code, that if we grab the code_allocator_lock
353 ;;; and free_pages_lock, that this can be made reliable (both crash-free and
354 ;;; guaranteed to visit all chosen objects) despite other threads running.
355 ;;; As things are it is only "maybe" reliable, regardless of the parameters.
356 (defun walk-dynamic-space (fun generation-mask
357 page-type-mask page-type-constraint
)
358 (declare (function fun
)
359 (type (unsigned-byte 7) generation-mask
)
360 (type (unsigned-byte 5) page-type-mask page-type-constraint
))
361 ;; Dynamic space on gencgc requires walking the GC page tables
362 ;; in order to determine what regions contain objects.
364 ;; We explicitly presume that any pages in an allocation region
365 ;; that are in-use have a BYTES-USED of GENCGC-PAGE-BYTES
366 ;; (indicating a full page) or an otherwise-valid BYTES-USED.
367 ;; We also presume that the pages of an open allocation region
368 ;; after the first page, and any pages that are unallocated,
369 ;; have a BYTES-USED of zero. GENCGC seems to guarantee this.
371 ;; Our procedure is to scan forward through the page table,
372 ;; maintaining an "end pointer" until we reach a page where
373 ;; BYTES-USED is not GENCGC-PAGE-BYTES or we reach
374 ;; NEXT-FREE-PAGE. We then MAP-OBJECTS-IN-RANGE if the range
375 ;; is not empty, and proceed to the next page (unless we've hit
378 ;; FIXME: WITHOUT-GCING prevents a GC flip, but doesn't prevent
379 ;; closing allocation regions and opening new ones. This may
380 ;; prove to be an issue with concurrent systems, or with
381 ;; spectacularly poor timing for closing an allocation region
382 ;; in a single-threaded system.
383 (close-thread-alloc-region)
384 (do ((initial-next-free-page next-free-page
)
385 (base (int-sap dynamic-space-start
))
388 (end-page-bytes-used 0))
389 ((> start-page initial-next-free-page
))
390 ;; The type constraint on page indices is probably too generous,
391 ;; but it does its job of producing efficient code.
392 (declare (type (integer 0 (#.
(/ (ash 1 n-machine-word-bits
) gencgc-page-bytes
)))
393 start-page end-page
))
394 (setq end-page start-page
)
395 (loop (setq end-page-bytes-used
396 (ash (ash (slot (deref page-table end-page
) 'words-used
*) -
1)
398 ;; See 'page_ends_contiguous_block_p' in gencgc.c
399 (when (or (< end-page-bytes-used gencgc-page-bytes
)
400 (= (slot (deref page-table
(1+ end-page
)) 'start
) 0))
403 (let ((start (sap+ base
(truly-the signed-word
404 (logand (* start-page gencgc-page-bytes
)
405 most-positive-word
))))
406 (end (sap+ base
(truly-the signed-word
407 (logand (+ (* end-page gencgc-page-bytes
)
409 most-positive-word
)))))
410 (when (sap> end start
)
411 (let ((flags (slot (deref page-table start-page
) 'flags
)))
412 ;; The GEN slot is declared as (SIGNED 8) which does not satisfy the
413 ;; type restriction on the first argument to LOGBITP.
414 ;; Masking it to 3 bits fixes that, and allows using the other 5 bits
415 ;; for something potentially.
417 (when (and (logbitp (logand (slot (deref page-table start-page
) 'gen
) 7)
419 (= (logand flags page-type-mask
) page-type-constraint
))
420 ;; FIXME: should exclude (0 . 0) conses on PAGE_TYPE_{BOXED,UNBOXED}
421 ;; resulting from zeroing the tail of a bignum or vector etc.
422 (map-objects-in-range
424 (%make-lisp-obj
(sap-int start
))
425 (%make-lisp-obj
(sap-int end
))
426 (< start-page initial-next-free-page
)))
427 ;; Generations of pages are basically meaningless (except
428 ;; for pseudo-static pages) so we test generations of lines.
430 (when (= (logand flags page-type-mask
) page-type-constraint
)
431 (map-objects-in-discontiguous-range
433 (%make-lisp-obj
(sap-int start
))
434 (%make-lisp-obj
(sap-int end
))
436 (setq start-page
(1+ end-page
))))
438 ;; Users are often surprised to learn that a just-consed object can't
439 ;; necessarily be seen by MAP-ALLOCATED-OBJECTS, so close the region
440 ;; to update the page table.
441 ;; Since we're in WITHOUT-GCING, there can be no interrupts.
442 ;; Moreover it's probably not safe in the least to walk any thread's
443 ;; allocation region, unless the observer and observed aren't consing.
444 (defun close-thread-alloc-region ()
445 (alien-funcall (extern-alien "close_current_thread_tlab" (function void
)))
452 (declaim (ftype (function (immobile-subspaces) (values t t t
&optional
))
453 immobile-fragmentation-information
))
454 (defun immobile-fragmentation-information (subspace)
455 (binding* (((start free-pointer
) (%space-bounds subspace
))
456 (used-bytes (ash (- free-pointer start
) n-fixnum-tag-bits
))
459 (if (eq subspace
:fixed
)
460 (map-immobile-objects
461 (lambda (obj type size
)
462 (declare (ignore obj
))
463 (when (= type list-pointer-lowtag
) (incf hole-bytes size
)))
466 (map-immobile-objects
467 (lambda (obj type size
)
468 (declare (ignore obj type
))
469 (incf sum-sizes size
))
471 (setq hole-bytes
(- used-bytes sum-sizes
))))
472 (values holes hole-bytes used-bytes
)))
474 (defun show-fragmentation (&key
(subspaces '(:fixed
:variable
))
475 (stream *standard-output
*))
476 (dolist (subspace subspaces
)
477 (format stream
"~(~A~) subspace fragmentation:~%" subspace
)
478 (multiple-value-bind (holes hole-bytes total-space-used
)
479 (immobile-fragmentation-information subspace
)
480 (loop for
(start . size
) in holes
481 do
(format stream
"~2@T~X..~X ~8:D~%" start
(+ start size
) size
))
482 (format stream
"~2@T~18@<~:D hole~:P~> ~8:D (~,2,2F% of ~:D ~
484 (length holes
) hole-bytes
485 (/ hole-bytes total-space-used
) total-space-used
))))
487 (defun sb-kernel::immobile-space-usage
()
488 (binding* (((nil fixed-hole-bytes fixed-used-bytes
)
489 (immobile-fragmentation-information :fixed
))
490 ((nil variable-hole-bytes variable-used-bytes
)
491 (immobile-fragmentation-information :variable
))
492 (total-used-bytes (+ fixed-used-bytes variable-used-bytes
))
493 (total-hole-bytes (+ fixed-hole-bytes variable-hole-bytes
)))
494 (values total-used-bytes total-hole-bytes
)))
497 ;;; Return a list of 3-lists (bytes object type-name) for the objects
498 ;;; allocated in Space.
499 (defun type-breakdown (space)
500 (declare (muffle-conditions compiler-note
))
501 (let ((sizes (make-array 256 :initial-element
0 :element-type
'word
))
502 (counts (make-array 256 :initial-element
0 :element-type
'word
)))
503 (map-allocated-objects
504 (lambda (obj type size
)
505 (declare (word size
) (optimize (speed 3)) (ignore obj
))
506 (incf (aref sizes type
) size
)
507 (incf (aref counts type
)))
510 (let ((totals (make-hash-table :test
'eq
)))
512 (let ((total-count (aref counts i
)))
513 (unless (zerop total-count
)
514 (let* ((total-size (aref sizes i
))
515 (name (room-info-type-name (aref *room-info
* i
)))
516 (found (ensure-gethash name totals
(list 0 0 name
))))
517 (incf (first found
) total-size
)
518 (incf (second found
) total-count
)))))
520 (collect ((totals-list))
521 (maphash (lambda (k v
)
525 (sort (totals-list) #'> :key
#'first
)))))
527 ;;; Handle the summary printing for MEMORY-USAGE. Totals is a list of lists
528 ;;; (space-name . totals-for-space), where totals-for-space is the list
529 ;;; returned by TYPE-BREAKDOWN.
530 (defun print-summary (spaces totals
)
531 (let ((summary (make-hash-table :test
'eq
))
532 (space-count (length spaces
)))
533 (dolist (space-total totals
)
534 (dolist (total (cdr space-total
))
535 (push (cons (car space-total
) total
)
536 (gethash (third total
) summary
))))
538 (collect ((summary-totals))
539 (maphash (lambda (k v
)
542 (declare (unsigned-byte sum
))
543 (dolist (space-total v
)
544 (incf sum
(first (cdr space-total
))))
545 (summary-totals (cons sum v
))))
548 (format t
"~2&Summary of space~P: ~(~{~A ~}~)~%" space-count spaces
)
549 (let ((summary-total-bytes 0)
550 (summary-total-objects 0))
551 (declare (unsigned-byte summary-total-bytes summary-total-objects
))
552 (dolist (space-totals
553 (mapcar #'cdr
(sort (summary-totals) #'> :key
#'car
)))
554 (let ((total-objects 0)
557 (declare (unsigned-byte total-objects total-bytes
))
559 (dolist (space-total space-totals
)
560 (let ((total (cdr space-total
)))
561 (setq name
(third total
))
562 (incf total-bytes
(first total
))
563 (incf total-objects
(second total
))
564 (spaces (cons (car space-total
) (first total
)))))
565 (format t
"~%~A:~% ~:D bytes, ~:D object~:P"
566 name total-bytes total-objects
)
567 (unless (= 1 space-count
)
568 (dolist (space (spaces))
569 (format t
", ~D% ~(~A~)"
570 (round (* (cdr space
) 100) total-bytes
) (car space
))))
572 (incf summary-total-bytes total-bytes
)
573 (incf summary-total-objects total-objects
))))
574 (format t
"~%Summary total:~% ~:D bytes, ~:D objects.~%"
575 summary-total-bytes summary-total-objects
)))))
577 (declaim (ftype (sfunction (index &key
(:comma-interval
(and (integer 1) index
))) index
)
578 decimal-with-grouped-digits-width
))
579 (defun decimal-with-grouped-digits-width (value &key
(comma-interval 3))
580 (let ((digits (length (write-to-string value
:base
10))))
581 (+ digits
(floor (1- digits
) comma-interval
))))
583 ;;; Report object usage for a single space.
584 (defun report-space-total (space-info cutoff
)
585 (declare (list space-info
) (type (or single-float null
) cutoff
))
586 (destructuring-bind (space . types
) space-info
587 (format t
"~2&Breakdown for ~(~A~) space:~%" space
)
588 (let* ((total-bytes (reduce #'+ (mapcar #'first types
)))
589 (bytes-width (decimal-with-grouped-digits-width total-bytes
))
590 (total-objects (reduce #'+ (mapcar #'second types
)))
591 (objects-width (decimal-with-grouped-digits-width total-objects
))
592 (cutoff-point (if cutoff
593 (truncate (* (float total-bytes
) cutoff
))
596 (reported-objects 0))
597 (declare (unsigned-byte total-objects total-bytes cutoff-point
598 reported-objects reported-bytes
))
599 (flet ((type-usage (bytes objects name
&optional note
)
600 (format t
" ~V:D bytes for ~V:D ~(~A~) object~2:*~P~*~
602 bytes-width bytes objects-width objects name note
)))
603 (loop for
(bytes objects name
) in types do
604 (when (<= bytes cutoff-point
)
605 (type-usage (- total-bytes reported-bytes
)
606 (- total-objects reported-objects
)
609 (incf reported-bytes bytes
)
610 (incf reported-objects objects
)
611 (type-usage bytes objects name
))
613 (type-usage total-bytes total-objects space
"space total")))))
615 ;;; Print information about the heap memory in use. PRINT-SPACES is a
616 ;;; list of the spaces to print detailed information for.
617 ;;; COUNT-SPACES is a list of the spaces to scan. For either one, T
618 ;;; means all spaces (i.e. :STATIC, :DYNAMIC and :READ-ONLY.) If
619 ;;; PRINT-SUMMARY is true, then summary information will be printed.
620 ;;; The defaults print only summary information for dynamic space. If
621 ;;; true, CUTOFF is a fraction of the usage in a report below which
622 ;;; types will be combined as OTHER.
623 (defun memory-usage (&key print-spaces
(count-spaces '(:dynamic
#+immobile-space
:immobile
))
624 (print-summary t
) cutoff
)
625 (declare (type (or single-float null
) cutoff
))
626 (let* ((spaces (if (eq count-spaces t
) +heap-space-keywords
+ count-spaces
))
627 (totals (mapcar (lambda (space)
628 (cons space
(type-breakdown space
)))
631 (dolist (space-total totals
)
632 (when (or (eq print-spaces t
)
633 (member (car space-total
) print-spaces
))
634 (report-space-total space-total cutoff
)))
636 (when print-summary
(print-summary spaces totals
)))
640 ;;; Print a breakdown by instance type of all the instances allocated
641 ;;; in SPACE. If TOP-N is true, print only information for the
642 ;;; TOP-N types with largest usage.
643 (defun instance-usage (space &key
(top-n 15))
644 (declare (type spaces space
) (type (or fixnum null
) top-n
))
645 (format t
"~2&~@[Top ~W ~]~(~A~) instance types:~%" top-n space
)
646 (let ((totals (make-hash-table :test
'eq
))
649 (declare (unsigned-byte total-objects total-bytes
))
650 (map-allocated-objects
651 (lambda (obj type size
)
652 (declare (optimize (speed 3)))
653 (when (or (eql type instance-widetag
)
654 (eql type funcallable-instance-widetag
))
657 (let* ((layout (if (eql type funcallable-instance-widetag
)
659 (%instance-layout obj
)))
660 (classoid (if (zerop (get-lisp-obj-address layout
))
661 ;; Don't crash on partially allocated instances
663 (layout-classoid layout
)))
664 (found (ensure-gethash classoid totals
(cons 0 0)))
666 (declare (fixnum size
))
667 (incf total-bytes size
)
668 (incf (the fixnum
(car found
)))
669 (incf (the fixnum
(cdr found
)) size
)))))
671 (let* ((sorted (sort (%hash-table-alist totals
) #'> :key
#'cddr
))
672 (interesting (if top-n
673 (subseq sorted
0 (min (length sorted
) top-n
))
675 (bytes-width (decimal-with-grouped-digits-width total-bytes
))
676 (objects-width (decimal-with-grouped-digits-width total-objects
))
677 (totals-label (format nil
"~:(~A~) instance total" space
))
678 (types-width (reduce #'max interesting
680 (let ((type (first info
)))
686 (with-output-to-string (stream)
687 (sb-ext:print-symbol-with-prefix
688 stream
(classoid-name type
))))))))
689 :initial-value
(length totals-label
)))
692 (declare (unsigned-byte printed-bytes printed-objects
))
693 (flet ((type-usage (type objects bytes
)
696 (format t
" ~V@<~A~> ~V:D bytes, ~V:D object~:P.~%"
697 (1+ types-width
) type bytes-width bytes
698 objects-width objects
))
700 (format t
" ~V@<~/sb-ext:print-symbol-with-prefix/~> ~
701 ~V:D bytes, ~V:D object~:P.~%"
702 (1+ types-width
) (classoid-name type
) bytes-width bytes
703 objects-width objects
)))))
704 (loop for
(type .
(objects . bytes
)) in interesting
705 do
(incf printed-bytes bytes
)
706 (incf printed-objects objects
)
707 (type-usage type objects bytes
))
709 (let ((residual-objects (- total-objects printed-objects
))
710 (residual-bytes (- total-bytes printed-bytes
)))
711 (unless (zerop residual-objects
)
712 (type-usage "Other types" residual-objects residual-bytes
)))
713 (type-usage totals-label total-objects total-bytes
))))
716 ;;;; PRINT-ALLOCATED-OBJECTS
718 ;;; This function is sheer madness. You're better off using
719 ;;; LIST-ALLOCATED-OBJECTS and then iterating over that, to avoid
720 ;;; seeing all the junk created while doing this thing.
721 (defun print-allocated-objects (space &key
(percent 0) (pages 5)
722 type larger smaller count
723 (stream *standard-output
*))
724 (declare (type (integer 0 99) percent
) (type index pages
)
725 (type stream stream
) (type spaces space
)
726 (type (or index null
) type larger smaller count
))
727 (multiple-value-bind (start end
) (%space-bounds space
)
728 (when (eq space
:static
)
729 (setq start
(%make-lisp-obj
(+ static-space-start static-space-objects-offset
))))
730 (let* ((space-start (ash start n-fixnum-tag-bits
))
731 (space-end (ash end n-fixnum-tag-bits
))
732 (space-size (- space-end space-start
))
733 (pagesize sb-c
:+backend-page-bytes
+)
734 (start (+ space-start
(round (* space-size percent
) 100)))
735 (printed-conses (make-hash-table :test
'eq
))
739 (declare (type word last-page start
)
740 (fixnum pages-so-far count-so-far pagesize
))
741 (labels ((note-conses (x)
742 (unless (or (atom x
) (gethash x printed-conses
))
743 (setf (gethash x printed-conses
) t
)
744 (note-conses (car x
))
745 (note-conses (cdr x
)))))
746 (map-allocated-objects
747 (lambda (obj obj-type size
)
748 (let ((addr (get-lisp-obj-address obj
)))
749 (when (>= addr start
)
751 (> count-so-far count
)
752 (> pages-so-far pages
))
753 (return-from print-allocated-objects
(values)))
756 (let ((this-page (* (the (values word t
)
757 (truncate addr pagesize
))
759 (declare (type word this-page
))
760 (when (/= this-page last-page
)
761 (when (< pages-so-far pages
)
762 ;; FIXME: What is this? (ERROR "Argh..")? or
763 ;; a warning? or code that can be removed
764 ;; once the system is stable? or what?
765 (format stream
"~2&**** Page ~W, address ~X:~%"
767 (setq last-page this-page
)
768 (incf pages-so-far
))))
770 (when (and (or (not type
) (eql obj-type type
))
771 (or (not smaller
) (<= size smaller
))
772 (or (not larger
) (>= size larger
)))
775 (#.code-header-widetag
776 (let ((dinfo (%code-debug-info obj
)))
777 (format stream
"~&Code object: ~S~%"
778 (if dinfo
; BUG: what if this is in the asm code ?
779 (sb-c::compiled-debug-info-name dinfo
)
782 (format stream
"~&~S~%" obj
))
783 (#.list-pointer-lowtag
784 (unless (gethash obj printed-conses
)
786 (let ((*print-circle
* t
)
789 (format stream
"~&~S~%" obj
))))
792 (let ((str (write-to-string obj
:level
5 :length
10
794 (unless (eql type instance-widetag
)
795 (format stream
"~S: " (type-of obj
)))
796 (format stream
"~A~%"
797 (subseq str
0 (min (length str
) 60))))))))))
801 ;;;; LIST-ALLOCATED-OBJECTS, LIST-REFERENCING-OBJECTS
803 (defun list-allocated-objects (space &key type larger smaller count
805 (declare (type (or (eql :all
) spaces
) space
)
806 (type (or (unsigned-byte 8) null
) type
)
807 (type (or index null
) larger smaller count
)
808 (type (or function null
) test
))
809 (declare (dynamic-extent test
))
811 (return-from list-allocated-objects nil
))
812 ;; This function was pretty much random as to what subset of the heap it
813 ;; visited- it might see half the heap, 1/10th of the heap, who knows, because
814 ;; it stopped based on hitting a sentinel cons cell made just prior to the loop.
815 ;; That stopping condition was totally wrong because allocation does not occur
816 ;; linearly. Taking 2 passes (first count, then store) stands a chance of
817 ;; getting a reasonable point-in-time view as long as other threads are not consing
818 ;; like crazy. If the user-supplied TEST function conses at all, then the result is
819 ;; still very arbitrary - including possible duplication of objects if we visit
820 ;; something and then see it again after GC transports it higher. The only way to
821 ;; allow consing in the predicate would be to use dedicated "arenas" for new
822 ;; allocations, that being a concept which we do not now - and may never - support.
823 (flet ((wantp (obj widetag size
)
824 (and (or (not type
) (eql widetag type
))
825 (or (not smaller
) (<= size smaller
))
826 (or (not larger
) (>= size larger
))
827 (or (not test
) (funcall test obj
)))))
828 ;; Unless COUNT is smallish, always start by counting. Don't just trust the user
829 ;; because s/he might specify :COUNT huge-num which is acceptable provided that
830 ;; huge-num is an INDEX which could either exhaust the heap, or at least be
831 ;; wasteful if but a tiny handful of objects would actually satisfy WANTP.
832 (let* ((output (make-array
833 (if (typep count
'(integer 0 100000))
836 (map-allocated-objects
837 (lambda (obj widetag size
)
838 (when (wantp obj widetag size
) (incf n
)))
843 (map-allocated-objects
844 (lambda (obj widetag size
)
845 (when (wantp obj widetag size
)
846 (setf (aref output index
) obj
)
847 (when (= (incf index
) (length output
))
848 (return-from done
))))
851 (cond ((= index
(length output
)) ; easy case
852 (coerce output
'list
))
853 (t ; didn't fill the array
855 (dotimes (i index
(res))
856 (res (svref output i
))))))))
857 (fill output
0) ; assist GC a bit
860 ;;; Calls FUNCTION with all objects that have (possibly conservative)
861 ;;; references to them on current stack.
862 ;;; This is for use by SB-INTROSPECT. (Other consumers, at your own risk)
863 ;;; Note that we do not call MAKE-LISP-OBJ in the errorp=nil mode, as it
864 ;;; potentially uses FORMAT and MAKE-UNPRINTABLE-OBJECT with each invocation.
865 ;;; And see the cautionary remarks above that function regarding its dangerous
866 ;;; nature (more so on precise GC). On conservative GC we should be OK here
867 ;;; because we know that there's a stack reference.
868 (defun map-stack-references (function)
869 (declare (type function function
))
870 (declare (dynamic-extent function
))
871 (macrolet ((iter (step limit test
)
872 `(do ((sp (current-sp) (sap+ sp
(,step n-word-bytes
)))
873 (limit (sb-di::descriptor-sap
,limit
))
876 (let ((word (sap-ref-word sp
0)))
877 ;; Explicitly skip non-pointer words. The callable that
878 ;; SB-INTROSPECT provides ignores immediate values anyway.
879 (when (and (is-lisp-pointer word
)
880 (not (zerop (sb-di::valid-tagged-pointer-p
(int-sap word
)))))
881 (let ((obj (%make-lisp-obj word
)))
882 (unless (memq obj seen
)
884 (funcall function obj
))))))))
885 #+stack-grows-downward-not-upward
(iter + *control-stack-end
* sap
>)
886 #-stack-grows-downward-not-upward
(iter -
*control-stack-start
* sap
<)))
888 ;;; Invoke FUNCTOID (a macro or function) on OBJ and any values in MORE.
889 ;;; Note that neither OBJ nor items in MORE undergo ONCE-ONLY treatment.
890 ;;; The fact that FUNCTOID can be a macro allows treatment of its first argument
891 ;;; as a generalized place in the manner of SETF, allowing read/write access.
892 ;;; CLAUSES are used to modify the output of this macro. See example uses
894 ;;; HIGH EXPERIMENTAL: PROCEED AT YOUR OWN RISK.
895 (defmacro do-referenced-object
((obj functoid
&rest more
) &rest alterations
896 &aux
(n-matched-alterations 0))
897 (labels ((make-case (type &rest actions
)
898 (apply #'make-case
* type
899 (mapcar (lambda (action) `(,functoid
,action
,@more
))
901 (make-case* (type &rest actions
)
902 (let* ((found (assoc type alterations
:test
'equal
))
903 (alteration (or (cdr found
) '(:extend
))))
905 (incf n-matched-alterations
))
906 (ecase (car alteration
)
907 (:override
(list `(,type
,@(cdr alteration
))))
908 (:extend
(list `(,type
,@actions
,@(cdr alteration
))))
909 (:delete
))))) ; no clause
912 ;; Until the compiler can learn how to efficiently compile jump tables
913 ;; by widetag, test in descending order of popularity.
914 ;; These two are in fact generally the most frequently occurring type.
915 ,.
(make-case 'cons
`(car ,obj
) `(cdr ,obj
))
916 ,.
(make-case* 'instance
917 ;; %INSTANCE-LAYOUT is defknown'ed to return a LAYOUT,
918 ;; but heap walking might encounter an instance with no layout,
919 ;; hence the need to access the slot opaquely.
920 `(unless (eql 0 #+compact-instance-header
(%primitive %instance-layout
,obj
)
921 #-compact-instance-header
(%instance-ref
,obj
0))
922 (,functoid
(%instance-layout
,obj
) ,@more
)
923 (do-instance-tagged-slot (.i.
,obj
)
924 (,functoid
(%instance-ref
,obj .i.
) ,@more
))))
927 ,.
(make-case* 'closure
928 `(,functoid
(%closure-fun
,obj
) ,@more
)
929 `(do-closure-values (.o.
,obj
:include-extra-values t
)
930 ;; FIXME: doesn't allow setf, but of course there is
931 ;; no closure-index-set anyway, so .O. might be unused
932 ;; if functoid is a macro that does nothing.
933 (,functoid .o.
,@more
)))
934 ,.
(make-case* 'funcallable-instance
936 ;; As for INSTANCE, allow the functoid to see the access form
937 (,functoid
(%fun-layout
,obj
) ,@more
)
938 (,functoid
(%funcallable-instance-fun
,obj
) ,@more
)
939 ;; Unfortunately for FUNCALLABLE-INSTANCEs, the relation
940 ;; between layout bitmap indices and indices as given to
941 ;; FUNCALLABLE-INSTANCE-INFO is not so obvious, and it's
942 ;; both tricky and unnecessary to generalize iteration.
944 to
(- (get-closure-length ,obj
) funcallable-instance-info-offset
)
945 do
(,functoid
(%funcallable-instance-info
,obj .i.
) ,@more
))))
946 .
,(make-case 'function
))) ; in case there was code provided for it
948 ;; TODO: the generated code is pretty horrible. OTHER-POINTER-LOWTAG
949 ;; is known at this point, but tested N times.
951 ,.
(make-case* 'simple-vector
952 `(dotimes (.i.
(length ,obj
))
953 (,functoid
(data-vector-ref ,obj .i.
) ,@more
)))
954 ;; Fancy arrays aren't highly popular, but this case must precede ARRAY
955 ;; because ARRAY weeds out all other arrays, namely the ones that
956 ;; hold no pointers: simple-string, simple-bit-vector, etc.
957 ,.
(make-case '(satisfies array-header-p
)
959 `(%array-displaced-p
,obj
)
960 `(%array-displaced-from
,obj
))
962 ,.
(make-case* 'symbol
963 `(,functoid
(%primitive sb-c
:fast-symbol-global-value
,obj
) ,@more
)
964 `(,functoid
(symbol-%info
,obj
) ,@more
)
965 `(,functoid
(symbol-name ,obj
) ,@more
)
966 `(,functoid
(symbol-package ,obj
) ,@more
))
970 ;; While it looks like we could easily allow a pointer to a movable object
971 ;; in the fdefn-raw-addr slot, it is not exactly trivial- at a bare minimum,
972 ;; translating the raw-addr to a lispobj might have to be pseudoatomic,
973 ;; since we don't know what object to pin when reconstructing it.
974 ;; For simple-funs in dynamic space, it doesn't have to be pseudoatomic
975 ;; because a reference to the interior of code pins the code.
976 ;; Closure trampolines would be fine as well. That leaves funcallable instances
977 ;; as the pain point. Those could go on pages of code as well, but see the
978 ;; comment in conservative_root_p() in gencgc as to why that alone
979 ;; would be inadequate- we require a properly tagged descriptor
980 ;; to enliven any object other than code.
981 #+(and immobile-code x86-64
)
983 (alien-funcall (extern-alien "decode_fdefn_rawfun" (function unsigned unsigned
))
984 (logandc2 (get-lisp-obj-address ,obj
) lowtag-mask
))))
985 ,.
(make-case* 'code-component
986 `(loop for .i. from
2 below
(code-header-words ,obj
)
987 do
(,functoid
(code-header-ref ,obj .i.
) ,@more
)))
988 ,.
(make-case '(or float
(complex float
) bignum
989 #+sb-simd-pack simd-pack
990 #+sb-simd-pack-256 simd-pack-256
991 system-area-pointer
)) ; nothing to do
992 ,.
(make-case 'weak-pointer
993 #+weak-vector-readbarrier
994 `(if (weak-vector-p ,obj
)
995 (dotimes (.i.
(weak-vector-len ,obj
))
996 (,functoid
(weak-vector-ref ,obj .i.
) ,@more
))
997 (weak-pointer-value ,obj
))
998 #-weak-vector-readbarrier
999 `(weak-pointer-value ,obj
))
1000 ,.
(make-case 'ratio
`(%numerator
,obj
) `(%denominator
,obj
))
1001 ;; Visitor won't be invoked on (COMPLEX float)
1002 ,.
(make-case '(complex rational
) `(%realpart
,obj
) `(%imagpart
,obj
))
1003 ;; Caller can do anything in the fallback case.
1005 (when (> (length alterations
) n-matched-alterations
)
1006 (error "DO-REFERENCED-OBJECT usage error")))))
1008 ;;; Return T if and only if THIS references THAT.
1009 ;;; code-components are considered to reference their embedded
1010 ;;; simple-funs for this purpose; if THIS is a simple-fun, it is ignored.
1011 (defun references-p (this that
)
1012 (declare (optimize (sb-c::aref-trapping
0)))
1013 (macrolet ((test (x) `(when (eq ,x that
) (go win
))))
1015 (do-referenced-object (this test
)
1018 (dotimes (i (code-n-entries this
))
1019 (let ((f (%code-entry-point this i
)))
1024 (case (widetag-of this
)
1025 (#.value-cell-widetag
1026 (test (value-cell-ref this
)))
1028 (bug "Unknown object type #x~x addr=~x"
1030 (get-lisp-obj-address this
))))))
1031 (return-from references-p nil
)
1033 (return-from references-p t
))))
1035 ;;; If OBJ points (directly or indirectly) to something in some arena,
1036 ;;; then return the pointed-to arena-allocated thing.
1037 ;;; Cribbed from DEEP-SIZE in tests/do-refs.impure
1039 (defun points-to-arena (obj)
1040 (flet ((leafp (x) (typep x
'(or package symbol fdefn layout classoid
))))
1041 (let ((worklist (list obj
))
1042 (seen (make-hash-table :test
'eq
)))
1043 (setf (gethash obj seen
) t
)
1044 (flet ((visit (thing)
1045 (when (is-lisp-pointer (get-lisp-obj-address thing
))
1046 (unless (or (leafp thing
) (gethash thing seen
))
1047 (when (find-containing-arena (get-lisp-obj-address thing
))
1048 (return-from points-to-arena thing
))
1049 (push thing worklist
)
1050 (setf (gethash thing seen
) t
)))))
1052 (unless worklist
(return))
1053 (let ((x (pop worklist
)))
1054 (do-referenced-object (x visit
))))))))
1056 ;;; This interface allows one either to be agnostic of the referencing space,
1057 ;;; or specify exactly one space, but not specify a list of spaces.
1058 ;;; An upward-compatible change would be to assume a list, and call ENSURE-LIST.
1059 (defun map-referencing-objects (fun space object
)
1060 (declare (type (or (eql :all
) spaces
) space
))
1061 (declare (dynamic-extent fun
))
1063 (map-allocated-objects
1064 (lambda (referer widetag size
)
1065 (declare (ignore widetag size
))
1066 ;; Don't count a self-reference as a reference
1067 (when (and (neq referer object
)
1068 (references-p referer object
))
1069 (push referer list
)))
1071 (mapc (%coerce-callable-to-fun fun
) list
)))
1073 (defun list-referencing-objects (space object
)
1075 (map-referencing-objects
1076 (lambda (obj) (res obj
)) space object
)
1081 (defun room-minimal-info ()
1082 (multiple-value-bind (names name-width
1083 used-bytes used-bytes-width
1085 (loop for
(nil name function
) in
+all-spaces
+
1086 for
(space-used-bytes space-overhead-bytes
)
1087 = (multiple-value-list (funcall function
))
1088 collect name into names
1089 collect space-used-bytes into used-bytes
1090 collect space-overhead-bytes into overhead-bytes
1091 maximizing
(length name
) into name-maximum
1092 maximizing space-used-bytes into used-bytes-maximum
1093 finally
(return (values
1095 used-bytes
(decimal-with-grouped-digits-width
1098 (loop for name in names
1099 for space-used-bytes in used-bytes
1100 for space-overhead-bytes in overhead-bytes
1101 do
(format t
"~V@<~A usage is:~> ~V:D bytes~@[ (~:D bytes ~
1103 (+ name-width
10) name used-bytes-width space-used-bytes
1104 space-overhead-bytes
)))
1106 (format t
"Control and binding stack usage is for the current thread ~
1108 (format t
"Garbage collection is currently ~:[enabled~;DISABLED~].~%"
1111 (defun room-intermediate-info ()
1113 (memory-usage :count-spaces
'(:dynamic
#+immobile-space
:immobile
)
1116 :print-summary nil
))
1118 (defun room-maximal-info ()
1119 (let ((spaces '(:dynamic
#+immobile-space
:immobile
:static
)))
1121 (memory-usage :count-spaces spaces
)
1122 (dolist (space spaces
)
1123 (instance-usage space
:top-n
10))))
1125 (defun room (&optional
(verbosity :default
))
1126 "Print to *STANDARD-OUTPUT* information about the state of internal
1127 storage and its management. The optional argument controls the
1128 verbosity of output. If it is T, ROOM prints out a maximal amount of
1129 information. If it is NIL, ROOM prints out a minimal amount of
1130 information. If it is :DEFAULT or it is not supplied, ROOM prints out
1131 an intermediate amount of information."
1135 (room-maximal-info))
1137 (room-minimal-info))
1139 (room-intermediate-info)))
1142 #+nil
; for debugging
1143 (defun show-dynamic-space-code (&optional
(stream *standard-output
*)
1144 &aux
(n-code-bytes 0)
1145 (total-pages next-free-page
)
1147 (make-array total-pages
:element-type
'bit
1148 :initial-element
0)))
1149 (flet ((dump-page (page-num)
1150 (format stream
"~&Page ~D~%" page-num
)
1151 (let ((where (+ dynamic-space-start
(* page-num gencgc-page-bytes
)))
1154 (let* ((obj (let ((sap (int-sap where
)))
1155 (lispobj@baseptr sap
(widetag@baseptr sap
))))
1156 (size (primitive-object-size obj
)))
1157 (when (code-component-p obj
)
1158 (incf n-code-bytes size
))
1159 (when (if (and (consp obj
) (eq (car obj
) 0) (eq (cdr obj
) 0))
1161 (progn (write-char #\. stream
) nil
)
1162 (setq seen-filler t
))
1163 (progn (setq seen-filler nil
) t
))
1164 (let ((*print-pretty
* nil
))
1165 (format stream
"~& ~X ~4X ~S " where size obj
)))
1167 (loop for index from page-num to
(find-page-index (1- where
))
1168 do
(setf (sbit pages index
) 1)))
1169 (let ((next-page (find-page-index where
)))
1170 (cond ((= (logand where
(1- gencgc-page-bytes
)) 0)
1171 (format stream
"~&-- END OF PAGE --~%")
1173 ((eq next-page page-num
))
1175 (setq page-num next-page seen-filler nil
))))))))
1177 (loop while
(< i total-pages
)
1178 do
(let ((type (slot (deref page-table i
) 'flags
)))
1179 (if (= (logand type
7) 7)
1180 (setq i
(dump-page i
))
1182 (let* ((n-pages (count 1 pages
))
1183 (tot (* n-pages gencgc-page-bytes
))
1184 (waste (- tot n-code-bytes
)))
1185 (format t
"~&Used-bytes=~D Pages=~D Waste=~D (~F%)~%"
1186 n-code-bytes n-pages waste
1187 (* 100 (/ waste tot
))))))
1189 #+nil
; for debugging
1190 (defun show-immobile-spaces (which)
1191 (flet ((show (obj type size
)
1192 (declare (ignore type size
))
1193 (let ((*print-pretty
* nil
) (*print-length
* 3))
1194 (format t
"~x: ~s~%" (get-lisp-obj-address obj
) obj
))))
1195 (when (or (eq which
:fixed
) (eq which
:both
))
1196 (format t
"Fixedobj space~%==============~%")
1197 (map-objects-in-range #'show
1198 (%make-lisp-obj fixedobj-space-start
)
1199 (%make-lisp-obj
(sap-int *fixedobj-space-free-pointer
*))))
1200 (when (or (eq which
:variable
) (eq which
:both
))
1201 (format t
"Text space~%=============~%")
1202 (map-objects-in-range #'show
1203 (%make-lisp-obj text-space-start
)
1204 (%make-lisp-obj
(sap-int *text-space-free-pointer
*))))))
1206 ;;; Show objects in a much simpler way than print-allocated-objects.
1207 ;;; Probably don't use this for generation 0 of dynamic space. Other spaces are ok.
1208 ;;; (And this is removed from the image; it's meant for developers)
1209 (defun show-generation-objs (gen space
)
1210 (let ((*print-pretty
* nil
))
1211 (map-allocated-objects
1212 (lambda (obj type size
)
1213 (declare (ignore type
))
1214 (when (= (generation-of obj
) gen
)
1215 (format t
"~x ~3x ~s~%" (get-lisp-obj-address obj
) size obj
)))
1218 ;;; Unfortunately this is a near total copy of the test in gc.impure.lisp
1219 (defun !ensure-genesis-code
/data-separation
()
1222 (close-thread-alloc-region)
1223 (+ next-free-page
50)))
1224 (code-bits (make-array n-bits
:element-type
'bit
:initial-element
0))
1225 (data-bits (make-array n-bits
:element-type
'bit
:initial-element
0))
1226 (total-code-size 0))
1227 (map-allocated-objects
1228 (lambda (obj type size
)
1229 (declare ((and fixnum
(integer 1)) size
))
1230 (unless (= type funcallable-instance-widetag
)
1231 ;; M-A-O disables GC, therefore GET-LISP-OBJ-ADDRESS is safe
1232 (let ((obj-addr (get-lisp-obj-address obj
))
1233 (array (cond ((= type code-header-widetag
)
1234 (incf total-code-size size
)
1238 (other-array (cond ((= type code-header-widetag
)
1242 ;; If Lisp allocates to cons pages, then this check is always valid.
1243 ;; If not, then at first glance we could weaken the check to allow
1244 ;; gen0 conses on MIXED pages, but even that is not enough- pinned conses
1245 ;; will promote but keep their MIXED page type. So don't bother with this.
1247 (let* ((type (logandc2 (slot (deref page-table
(find-page-index obj-addr
)) 'flags
)
1248 32)) ; OPEN_REGION_PAGE_FLAG (due to finalizer thread perhaps)
1250 (or (= type
#b101
) ; PAGE_TYPE_CONS
1251 (and (eq (car obj
) 0) (eq (cdr obj
) 0)))
1254 (error "Object @ ~x (gen~D) is on page-type ~b~%"
1255 obj-addr
(generation-of obj
) type
)))
1256 ;; This is not the most efficient way to update the bit arrays,
1257 ;; but the simplest and clearest for sure. (The loop could avoided
1258 ;; if the current page is the same as the previously seen page)
1259 (loop for index from
(find-page-index obj-addr
)
1260 to
(find-page-index (truly-the word
1261 (+ (logandc2 obj-addr lowtag-mask
)
1263 do
(cond ((= (sbit other-array index
) 1)
1264 (format t
"~&broken on page index ~d base ~x~%"
1266 (+ dynamic-space-start
(* index gencgc-page-bytes
)))
1267 (alien-funcall (extern-alien "ldb_monitor" (function void
))))
1269 (setf (sbit array index
) 1)))))))
1271 ;;; Because pseudo-static objects can not move nor be freed,
1272 ;;; this is a valid test that genesis separated code and data.
1273 #+x86-64
; FIXME: flaky on other platforms.
1274 (!ensure-genesis-code
/data-separation
)
1276 ;;; Make sure that every KEY-INFO is in the hashset.
1277 ;;; We don't dump any from genesis, which is a good thing.
1278 (let ((cache (sb-impl::hashset-storage sb-kernel
::*key-info-hashset
*))
1280 (list-allocated-objects :all
:type instance-widetag
1281 :test
#'sb-kernel
:key-info-p
)))
1282 (dolist (x list
) (aver (sb-impl::weak-hashset-linear-find
/eq x cache
))))
1285 (defun show-tls-map ()
1287 (sort (list-allocated-objects
1289 :type symbol-widetag
1290 :test
(lambda (x) (plusp (sb-kernel:symbol-tls-index x
))))
1292 :key
#'sb-kernel
:symbol-tls-index
))
1295 (let ((n (ash (sb-kernel:symbol-tls-index x
) (- word-shift
))))
1296 (when (and (> n primitive-thread-object-length
)
1298 (format t
"(unused)~%"))
1299 (format t
"~5d = ~s~%" n x
)
1302 (flet ((print-it (obj type size
)
1303 (declare (ignore type size
))
1304 (let ((*print-level
* 2) (*print-lines
* 1))
1305 (format t
"~x ~s~%" (get-lisp-obj-address obj
) obj
))))
1306 (defun print-all-code ()
1307 (walk-dynamic-space #'print-it
#x7f
#b01111
#b00111
))
1308 (defun print-large-code ()
1309 (walk-dynamic-space #'print-it
#x7f
#b11111
#b10111
))
1310 (defun print-large-unboxed ()
1311 (walk-dynamic-space #'print-it
#x7f
#b11111
#b10010
))
1312 ;;; Use this only if you know what you're doing. It can fail because a page
1313 ;;; that needs to continue onto the next page will cause the "overrun" check
1315 (defun print-page-contents (page)
1317 (+ dynamic-space-start
(* gencgc-page-bytes page
)))
1319 (+ start gencgc-page-bytes
)))
1320 (map-objects-in-range #'print-it
(%make-lisp-obj start
) (%make-lisp-obj end
)))))
1322 (defun map-code-objects (fun)
1323 (declare (dynamic-extent fun
))
1324 (dx-flet ((filter (obj type size
)
1325 (declare (ignore size
))
1326 (when (= type code-header-widetag
)
1327 (funcall fun obj
))))
1330 (with-system-mutex (*allocator-mutex
*)
1331 (map-objects-in-range #'filter
1332 (ash text-space-start
(- n-fixnum-tag-bits
))
1333 (%make-lisp-obj
(sap-int *text-space-free-pointer
*))))
1334 (alien-funcall (extern-alien "close_code_region" (function void
)))
1335 (walk-dynamic-space #'filter
1336 #b1111111
; all generations
1337 #b111
#b111
)))) ; type mask and constraint
1339 (export 'code-from-serialno
)
1340 (defun code-from-serialno (serial)
1341 (dx-flet ((visit (obj)
1342 (when (= (%code-serialno obj
) serial
)
1343 (return-from code-from-serialno obj
))))
1344 (map-code-objects #'visit
)))
1346 (defun show-all-layouts ()
1347 (let ((l (list-allocated-objects :all
:test
#'sb-kernel
::layout-p
))
1348 zero trailing-raw trailing-tagged vanilla
)
1350 (let ((m (layout-bitmap x
)))
1351 (cond ((eql m
+layout-all-tagged
+) (push x vanilla
))
1352 ((eql m
0) (push x zero
))
1353 ((minusp m
) (push x trailing-tagged
))
1354 (t (push x trailing-raw
)))))
1355 (flet ((legend (newline str list
)
1356 (when newline
(terpri))
1357 (let ((s (format nil str
(length list
))))
1358 (format t
"~A~%~A~%" s
(make-string (length s
) :initial-element
#\-
)))))
1360 (legend nil
"Zero bitmap (~d):" zero
)
1361 (dolist (x zero
) (format t
"~a~%" (classoid-name (layout-classoid x
)))))
1363 (legend t
"Trailing raw (~d):" trailing-raw
)
1364 (dolist (x trailing-raw
)
1365 (let ((m (layout-bitmap x
)))
1366 (format t
"~30a 0...~v,'0b~%"
1367 (classoid-name (layout-classoid x
))
1368 (acond ((layout-info x
) (1+ (dd-length it
))) (t 32))
1370 (when trailing-tagged
1371 (legend t
"Trailing tagged (~d):" trailing-tagged
)
1372 (dolist (x trailing-tagged
)
1373 (let ((m (layout-bitmap x
)))
1374 (format t
"~30a 1...~b~%"
1375 (classoid-name (layout-classoid x
))
1376 (acond ((layout-info x
) (ldb (byte (dd-length it
) 0) m
))
1377 (t (ldb (byte 32 0) m
)))))))
1378 (legend t
"Default: (~d) [not shown]" vanilla
))))
1381 (defun find-poisoned-vectors (&aux result
)
1382 (dolist (v (list-allocated-objects :all
:type simple-vector-widetag
)
1384 (when (dotimes (i (length v
))
1385 (declare (optimize (sb-c::aref-trapping
0)))
1386 (let ((val (svref v i
)))
1387 (when (= (get-lisp-obj-address val
) unwritten-vector-element-marker
)
1389 (push (make-weak-pointer v
) result
)
1390 (let* ((origin (vector-extra-data v
))
1391 (code (sb-di::code-header-from-pc
(ash origin -
3)))
1392 (*print-array
* nil
))
1393 (format t
"g~d ~a ~a~%" (sb-kernel:generation-of v
) v code
)))))
1396 (defun symbol-from-tls-index (index)
1397 ;; Possible TODO: a weak vector indexed by symbol would make this function
1398 ;; more quick, more reliable, and also maybe make it easier to recycle TLS indices
1399 (unless (zerop index
)
1400 ;; Search interned symbols first since that's probably enough
1401 (do-all-symbols (symbol)
1402 (when (= (symbol-tls-index symbol
) index
)
1403 (return-from symbol-from-tls-index symbol
)))
1404 ;; A specially bound uninterned symbol? how awesome
1405 (map-allocated-objects
1406 (lambda (obj type size
)
1407 (declare (ignore size
))
1408 (when (and (= type symbol-widetag
) (= (symbol-tls-index obj
) index
))
1409 (return-from symbol-from-tls-index obj
)))
1411 0) ; Return a non-symbol as the failure indicator
1415 (export 'find-arena-ptr
)
1416 (defun find-arena-ptr (this &optional all
&aux witness
(n 0))
1417 (flet ((visit (that)
1418 (when (find-containing-arena (get-lisp-obj-address that
))
1421 (unless all
(return-from find-arena-ptr
(values 1 witness
))))))
1422 (declare (inline visit
))
1423 (do-referenced-object (this visit
)
1426 (case (widetag-of this
)
1427 (#.value-cell-widetag
1428 (visit (value-cell-ref this
)))))))
1431 (defun show-heap->arena
(l)
1433 (cond ((typep x
'(cons sb-thread
:thread
))
1434 ;; It's tricky to figure out what a symbol in another thread pointed to,
1435 ;; so just show the symbol and hope the user knows what it's for.
1436 (format t
"~&Symbol ~/sb-ext:print-symbol-with-prefix/~%" (third x
)))
1438 (let ((pointee (nth-value 1 (find-arena-ptr x
))))
1439 (format t
"~x -> ~x ~s ~s~%"
1440 (get-lisp-obj-address x
)
1441 (get-lisp-obj-address pointee
)
1443 (type-of pointee
)))))))
1445 (macrolet ((aligned-base (blk)
1446 `(align-up (sap-int (sap+ ,blk
(* 4 n-word-bytes
))) 4096)))
1447 (defun dump-arena-objects (arena &aux
(tot-size 0))
1448 (do-arena-blocks (memblk arena
)
1449 (let ((from (aligned-base memblk
))
1450 (to (sap-int (arena-memblk-freeptr memblk
))))
1451 (format t
"~&Memory block ~X..~X~%" from to
)
1452 (map-objects-in-range
1453 (lambda (obj type size
)
1454 (declare (ignore type
))
1455 (incf tot-size size
)
1456 (format t
"~x ~s~%" (get-lisp-obj-address obj
) (type-of obj
)))
1457 (%make-lisp-obj from
)
1458 (%make-lisp-obj to
))))
1460 (defun arena-contents (arena)
1462 (do-arena-blocks (memblk arena
)
1463 (let ((base (aligned-base memblk
))
1464 (limit (sap-int (arena-memblk-freeptr memblk
))))
1465 (map-objects-in-range
1466 (lambda (obj widetag size
)
1467 (declare (ignore obj widetag size
))
1469 (%make-lisp-obj base
)
1470 (%make-lisp-obj limit
))))
1471 (let ((result (make-array count
))
1473 (do-arena-blocks (memblk arena
)
1474 (let ((base (aligned-base memblk
))
1475 (limit (sap-int (arena-memblk-freeptr memblk
))))
1476 (map-objects-in-range
1477 (lambda (obj widetag size
)
1478 (declare (ignore widetag size
))
1479 (setf (aref result index
) obj
)
1481 (%make-lisp-obj base
)
1482 (%make-lisp-obj limit
))))
1485 (defun show-hashed-instances ()
1486 (flet ((foo (legend pred
)
1487 (format t
"~&Instances in ~a state:~%" legend
)
1488 (map-allocated-objects pred
:all
)))
1490 (lambda (obj type size
)
1491 (declare (ignore size
))
1492 (when (and (= type instance-widetag
)
1493 (logbitp 9 (instance-header-word obj
)))
1494 (format t
"~x ~s~%" (get-lisp-obj-address obj
) obj
))))
1495 (foo "HASHED (unmoved)"
1496 (lambda (obj type size
)
1497 (declare (ignore size
))
1498 (when (and (= type instance-widetag
)
1499 (= (ldb (byte 2 8) (instance-header-word obj
)) 1))
1500 (format t
"~x ~s~%" (get-lisp-obj-address obj
) obj
))))))
1503 ;;; As soon as practical in warm build it makes sense to add
1504 ;;; cold-allocation-patch-points into the weak hash-table.
1505 ;;; FIXME: I suspect that this wants to be just a weak vector
1506 ;;; (all code objects that have any allocation profiling compiled in),
1507 ;;; and not a hash-table, and that the list of fixups in the component
1508 ;;; can be attached to the debug info (in the manner of debug funs).
1509 ;;; When this was first implemented, weak-vectors weren't a thing. Maybe?
1510 (defvar *!cold-allocation-patch-point
*)
1511 (loop for
(code . points
) in
*!cold-allocation-patch-point
*
1512 do
(setf (gethash code
*allocation-patch-points
*) points
))
1514 (defun gctablesize (heap-size-gb page-size cards-per-page
)
1515 (let* ((card-size (/ page-size cards-per-page
))
1516 (heap-size (* heap-size-gb
(expt 1024 3)))
1517 (npages (/ heap-size page-size
))
1518 (ncards (/ heap-size card-size
))
1519 (pte-nbytes (* 8 npages
)))
1520 (format t
" PTE bytes: ~8D~%" pte-nbytes
)
1521 (format t
"card bytes: ~8D~%" ncards
)
1522 (format t
" total: ~8D~%" (+ pte-nbytes ncards
))))