Better handling of mixed arguments within LOG
[sbcl.git] / src / code / room.lisp
blobbd0cfb1f787c7563f0b818d27056a52e7c99726d
1 ;;;; heap-grovelling memory usage stuff
3 ;;;; This software is part of the SBCL system. See the README file for
4 ;;;; more information.
5 ;;;;
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.
12 (in-package "SB-VM")
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))
20 (:copier nil))
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))
62 cons-info)
63 (setf (svref infos (logior (ash i n-lowtag-bits) list-pointer-lowtag))
64 cons-info)
65 (setf (svref infos (logior (ash i n-lowtag-bits) fun-pointer-lowtag))
66 cons-info)
67 (setf (svref infos (logior (ash i n-lowtag-bits) other-pointer-lowtag))
68 cons-info))
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))
77 infos))
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)
84 #+immobile-space
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))
88 #'equal)
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))
93 #'equal)
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))))
113 (ecase space
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*)))
119 (:read-only
120 (bounds read-only-space-start
121 (sap-int *read-only-space-free-pointer*)))
122 #+immobile-space
123 (:fixed
124 (bounds fixedobj-space-start
125 (sap-int *fixedobj-space-free-pointer*)))
126 #+immobile-space
127 (:variable
128 (bounds text-space-start
129 (sap-int *text-space-free-pointer*)))
130 (:dynamic
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))
156 1))))
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)))
169 (loop
170 (if (sap>= start end) (return))
171 (let ((word (sap-ref-word start 0)))
172 (cond
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))))
181 (binding*
182 ((widetag (widetag@baseptr start))
183 (obj (lispobj@baseptr start widetag))
184 ((typecode size)
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.
187 (if (listp obj)
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)))))))
196 (when strict-bound
197 ;; If START is not eq to END, then we have blown past our endpoint.
198 #+sb-devel
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~%")))
203 (ldb-monitor))
204 #-sb-devel
205 (aver (sap= start end)))))
207 #+mark-region-gc
208 (define-alien-variable "allocation_bitmap" (* unsigned-char))
210 #+mark-region-gc
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
223 do (dotimes (bit 8)
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.
229 (binding*
230 ((widetag (widetag@baseptr (int-sap position)))
231 (obj (lispobj@baseptr (int-sap position) widetag))
232 ((typecode size)
233 (if (listp obj)
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)
252 #+immobile-space
253 (progn
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
314 :read-only :static
315 #+immobile-space :immobile
316 :dynamic)))
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)
320 (ecase space
321 (:static
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)))
328 ((:read-only)
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)))
333 #+immobile-space
334 (:immobile
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
376 ;; NEXT-FREE-PAGE).
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))
386 (start-page 0)
387 (end-page 0)
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)
397 word-shift))
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))
401 (return))
402 (incf end-page))
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)
408 end-page-bytes-used)
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.
416 #-mark-region-gc
417 (when (and (logbitp (logand (slot (deref page-table start-page) 'gen) 7)
418 generation-mask)
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.
429 #+mark-region-gc
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))
435 generation-mask)))))
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)))
446 nil)
448 ;;;; MEMORY-USAGE
450 #+immobile-space
451 (progn
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))
457 (holes '())
458 (hole-bytes 0))
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)))
464 subspace)
465 (let ((sum-sizes 0))
466 (map-immobile-objects
467 (lambda (obj type size)
468 (declare (ignore obj type))
469 (incf sum-sizes size))
470 subspace)
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 ~
483 bytes used)~%"
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)))
495 ) ; end PROGN
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)))
508 space)
510 (let ((totals (make-hash-table :test 'eq)))
511 (dotimes (i 256)
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)
522 (declare (ignore k))
523 (totals-list v))
524 totals)
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)
540 (declare (ignore k))
541 (let ((sum 0))
542 (declare (unsigned-byte sum))
543 (dolist (space-total v)
544 (incf sum (first (cdr space-total))))
545 (summary-totals (cons sum v))))
546 summary)
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)
555 (total-bytes 0)
556 name)
557 (declare (unsigned-byte total-objects total-bytes))
558 (collect ((spaces))
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))))
571 (format t ".~%")
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))
595 (reported-bytes 0)
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~*~
601 ~:[~; ~:*(~A)~]~%"
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)
607 "other")
608 (return))
609 (incf reported-bytes bytes)
610 (incf reported-objects objects)
611 (type-usage bytes objects name))
612 (terpri)
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)))
629 spaces)))
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)))
638 (values))
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))
647 (total-objects 0)
648 (total-bytes 0))
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))
655 (incf total-objects)
656 (block nil
657 (let* ((layout (if (eql type funcallable-instance-widetag)
658 (%fun-layout obj)
659 (%instance-layout obj)))
660 (classoid (if (zerop (get-lisp-obj-address layout))
661 ;; Don't crash on partially allocated instances
662 (return)
663 (layout-classoid layout)))
664 (found (ensure-gethash classoid totals (cons 0 0)))
665 (size size))
666 (declare (fixnum size))
667 (incf total-bytes size)
668 (incf (the fixnum (car found)))
669 (incf (the fixnum (cdr found)) size)))))
670 space)
671 (let* ((sorted (sort (%hash-table-alist totals) #'> :key #'cddr))
672 (interesting (if top-n
673 (subseq sorted 0 (min (length sorted) top-n))
674 sorted))
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
679 :key (lambda (info)
680 (let ((type (first info)))
681 (length
682 (typecase type
683 (string
684 type)
685 (classoid
686 (with-output-to-string (stream)
687 (sb-ext:print-symbol-with-prefix
688 stream (classoid-name type))))))))
689 :initial-value (length totals-label)))
690 (printed-bytes 0)
691 (printed-objects 0))
692 (declare (unsigned-byte printed-bytes printed-objects))
693 (flet ((type-usage (type objects bytes)
694 (etypecase type
695 (string
696 (format t " ~V@<~A~> ~V:D bytes, ~V:D object~:P.~%"
697 (1+ types-width) type bytes-width bytes
698 objects-width objects))
699 (classoid
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))
708 (terpri)
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))))
714 (values))
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))
736 (pages-so-far 0)
737 (count-so-far 0)
738 (last-page 0))
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)
750 (when (if count
751 (> count-so-far count)
752 (> pages-so-far pages))
753 (return-from print-allocated-objects (values)))
755 (unless count
756 (let ((this-page (* (the (values word t)
757 (truncate addr pagesize))
758 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:~%"
766 pages-so-far addr))
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)))
773 (incf count-so-far)
774 (case type
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)
780 "No debug info."))))
781 (#.symbol-widetag
782 (format stream "~&~S~%" obj))
783 (#.list-pointer-lowtag
784 (unless (gethash obj printed-conses)
785 (note-conses obj)
786 (let ((*print-circle* t)
787 (*print-level* 5)
788 (*print-length* 10))
789 (format stream "~&~S~%" obj))))
791 (fresh-line stream)
792 (let ((str (write-to-string obj :level 5 :length 10
793 :pretty nil)))
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))))))))))
798 space))))
799 (values))
801 ;;;; LIST-ALLOCATED-OBJECTS, LIST-REFERENCING-OBJECTS
803 (defun list-allocated-objects (space &key type larger smaller count
804 test)
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))
810 (when (eql count 0)
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))
834 count
835 (let ((n 0))
836 (map-allocated-objects
837 (lambda (obj widetag size)
838 (when (wantp obj widetag size) (incf n)))
839 space)
840 n))))
841 (index 0))
842 (block done
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))))
849 space))
850 (let ((list
851 (cond ((= index (length output)) ; easy case
852 (coerce output 'list))
853 (t ; didn't fill the array
854 (collect ((res))
855 (dotimes (i index (res))
856 (res (svref output i))))))))
857 (fill output 0) ; assist GC a bit
858 list))))
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))
874 (seen nil))
875 ((,test sp 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)
883 (push 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
893 ;;; for more detail.
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))
900 actions)))
901 (make-case* (type &rest actions)
902 (let* ((found (assoc type alterations :test 'equal))
903 (alteration (or (cdr found) '(:extend))))
904 (when found
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
910 (prog1
911 `(typecase ,obj
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))))
925 (function
926 (typecase ,obj
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
935 `(progn
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.
943 (loop for .i. from 0
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.
950 (typecase ,obj
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)
958 `(%array-data ,obj)
959 `(%array-displaced-p ,obj)
960 `(%array-displaced-from ,obj))
961 ,.(make-case 'array)
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))
967 ,.(make-case 'fdefn
968 `(fdefn-name ,obj)
969 `(fdefn-fun ,obj)
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)
982 `(%make-lisp-obj
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.
1004 ,.(make-case 't))))
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))))
1014 (tagbody
1015 (do-referenced-object (this test)
1016 (code-component
1017 :extend
1018 (dotimes (i (code-n-entries this))
1019 (let ((f (%code-entry-point this i)))
1020 (when (eq f that)
1021 (go win)))))
1023 :extend
1024 (case (widetag-of this)
1025 (#.value-cell-widetag
1026 (test (value-cell-ref this)))
1028 (bug "Unknown object type #x~x addr=~x"
1029 (widetag-of this)
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
1038 #+system-tlabs
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)))))
1051 (loop
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))
1062 (let (list)
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)))
1070 space)
1071 (mapc (%coerce-callable-to-fun fun) list)))
1073 (defun list-referencing-objects (space object)
1074 (collect ((res))
1075 (map-referencing-objects
1076 (lambda (obj) (res obj)) space object)
1077 (res)))
1079 ;;;; ROOM
1081 (defun room-minimal-info ()
1082 (multiple-value-bind (names name-width
1083 used-bytes used-bytes-width
1084 overhead-bytes)
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
1094 names name-maximum
1095 used-bytes (decimal-with-grouped-digits-width
1096 used-bytes-maximum)
1097 overhead-bytes)))
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 ~
1102 overhead)~].~%"
1103 (+ name-width 10) name used-bytes-width space-used-bytes
1104 space-overhead-bytes)))
1105 #+sb-thread
1106 (format t "Control and binding stack usage is for the current thread ~
1107 only.~%")
1108 (format t "Garbage collection is currently ~:[enabled~;DISABLED~].~%"
1109 *gc-inhibit*))
1111 (defun room-intermediate-info ()
1112 (room-minimal-info)
1113 (memory-usage :count-spaces '(:dynamic #+immobile-space :immobile)
1114 :print-spaces t
1115 :cutoff 0.05f0
1116 :print-summary nil))
1118 (defun room-maximal-info ()
1119 (let ((spaces '(:dynamic #+immobile-space :immobile :static)))
1120 (room-minimal-info)
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."
1132 (fresh-line)
1133 (ecase verbosity
1134 ((t)
1135 (room-maximal-info))
1136 ((nil)
1137 (room-minimal-info))
1138 (:default
1139 (room-intermediate-info)))
1140 (values))
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)
1146 (pages
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)))
1152 (seen-filler nil))
1153 (loop
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))
1160 (if seen-filler
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)))
1166 (incf where size)
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 --~%")
1172 (return next-page))
1173 ((eq next-page page-num))
1175 (setq page-num next-page seen-filler nil))))))))
1176 (let ((i 0))
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))
1181 (incf 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)))
1216 space)))
1218 ;;; Unfortunately this is a near total copy of the test in gc.impure.lisp
1219 (defun !ensure-genesis-code/data-separation ()
1220 (let* ((n-bits
1221 (progn
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)
1235 code-bits)
1237 data-bits)))
1238 (other-array (cond ((= type code-header-widetag)
1239 data-bits)
1241 code-bits))))
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.
1246 #+use-cons-region
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)
1249 (ok (if (consp obj)
1250 (or (= type #b101) ; PAGE_TYPE_CONS
1251 (and (eq (car obj) 0) (eq (cdr obj) 0)))
1252 (/= type #b101))))
1253 (unless ok
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)
1262 (1- size))))
1263 do (cond ((= (sbit other-array index) 1)
1264 (format t "~&broken on page index ~d base ~x~%"
1265 index
1266 (+ dynamic-space-start (* index gencgc-page-bytes)))
1267 (alien-funcall (extern-alien "ldb_monitor" (function void))))
1269 (setf (sbit array index) 1)))))))
1270 :dynamic)))
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*))
1279 (list
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))))
1284 #+sb-thread
1285 (defun show-tls-map ()
1286 (let ((list
1287 (sort (list-allocated-objects
1288 :all
1289 :type symbol-widetag
1290 :test (lambda (x) (plusp (sb-kernel:symbol-tls-index x))))
1292 :key #'sb-kernel:symbol-tls-index))
1293 (prev 0))
1294 (dolist (x list)
1295 (let ((n (ash (sb-kernel:symbol-tls-index x) (- word-shift))))
1296 (when (and (> n primitive-thread-object-length)
1297 (> n (1+ prev)))
1298 (format t "(unused)~%"))
1299 (format t "~5d = ~s~%" n x)
1300 (setq prev n)))))
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
1314 ;;; to fail.
1315 (defun print-page-contents (page)
1316 (let* ((start
1317 (+ dynamic-space-start (* gencgc-page-bytes page)))
1318 (end
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))))
1328 (without-gcing
1329 #+immobile-code
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)
1349 (dolist (x l)
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 #\-)))))
1359 (when zero
1360 (legend nil "Zero bitmap (~d):" zero)
1361 (dolist (x zero) (format t "~a~%" (classoid-name (layout-classoid x)))))
1362 (when trailing-raw
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))
1369 m))))
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))))
1380 #+ubsan
1381 (defun find-poisoned-vectors (&aux result)
1382 (dolist (v (list-allocated-objects :all :type simple-vector-widetag)
1383 result)
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)
1388 (return t))))
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)))))
1395 #+sb-thread
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)))
1410 :all))
1411 0) ; Return a non-symbol as the failure indicator
1413 #+system-tlabs
1414 (progn
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))
1419 (incf n)
1420 (setq witness that)
1421 (unless all (return-from find-arena-ptr (values 1 witness))))))
1422 (declare (inline visit))
1423 (do-referenced-object (this visit)
1425 :extend
1426 (case (widetag-of this)
1427 (#.value-cell-widetag
1428 (visit (value-cell-ref this)))))))
1429 (values n witness))
1431 (defun show-heap->arena (l)
1432 (dolist (x 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)
1442 (type-of x)
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))))
1459 tot-size)
1460 (defun arena-contents (arena)
1461 (let ((count 0))
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))
1468 (incf count))
1469 (%make-lisp-obj base)
1470 (%make-lisp-obj limit))))
1471 (let ((result (make-array count))
1472 (index 0))
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)
1480 (incf count))
1481 (%make-lisp-obj base)
1482 (%make-lisp-obj limit))))
1483 result)))))
1485 (defun show-hashed-instances ()
1486 (flet ((foo (legend pred)
1487 (format t "~&Instances in ~a state:~%" legend)
1488 (map-allocated-objects pred :all)))
1489 (foo "HASHED+MOVED"
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))))))
1502 (in-package "SB-C")
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))))