Fix SB-VM::SPACE-BYTES to avoid consing SAPs
[sbcl.git] / src / code / room.lisp
blob44d52285b24affc60d44c3af9092e713b1be5ce0
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 (eval-when (:compile-toplevel :load-toplevel :execute)
19 (def!struct (room-info (:constructor make-room-info (name kind))
20 (:copier nil))
21 ;; the name of this type
22 (name nil :type symbol :read-only t)
23 ;; kind of type (how to reconstitute an object)
24 (kind (missing-arg)
25 :type (member :other :tiny-other :closure :instance :list
26 :code :vector-nil :weak-pointer)
27 :read-only t))
28 (!set-load-form-method room-info (:xc))
30 (defun room-info-type-name (info)
31 (if (specialized-array-element-type-properties-p info)
32 (saetp-primitive-type-name info)
33 (room-info-name info))))
35 (eval-when (:compile-toplevel :execute)
37 (defvar *meta-room-info* (make-array 256 :initial-element nil))
39 (dolist (obj *primitive-objects*)
40 (let ((widetag (primitive-object-widetag obj))
41 (lowtag (primitive-object-lowtag obj))
42 (name (primitive-object-name obj)))
43 (when (and (eq lowtag 'other-pointer-lowtag)
44 (not (member widetag '(t nil)))
45 (not (eq name 'weak-pointer)))
46 (setf (svref *meta-room-info* (symbol-value widetag))
47 (make-room-info name (if (member name '(fdefn symbol))
48 :tiny-other
49 :other))))))
51 (dolist (code (list #!+sb-unicode complex-character-string-widetag
52 complex-base-string-widetag simple-array-widetag
53 complex-bit-vector-widetag complex-vector-widetag
54 complex-array-widetag complex-vector-nil-widetag))
55 (setf (svref *meta-room-info* code)
56 (make-room-info 'array-header :other)))
58 (setf (svref *meta-room-info* bignum-widetag)
59 (make-room-info 'bignum :other))
61 (setf (svref *meta-room-info* closure-widetag)
62 (make-room-info 'closure :closure))
64 (dotimes (i (length *specialized-array-element-type-properties*))
65 (let ((saetp (aref *specialized-array-element-type-properties* i)))
66 (when (saetp-specifier saetp) ;; SIMPLE-ARRAY-NIL is a special case.
67 (setf (svref *meta-room-info* (saetp-typecode saetp)) saetp))))
69 (setf (svref *meta-room-info* simple-array-nil-widetag)
70 (make-room-info 'simple-array-nil :vector-nil))
72 (setf (svref *meta-room-info* code-header-widetag)
73 (make-room-info 'code :code))
75 (setf (svref *meta-room-info* instance-widetag)
76 (make-room-info 'instance :instance))
78 (setf (svref *meta-room-info* funcallable-instance-widetag)
79 (make-room-info 'funcallable-instance :closure))
81 (setf (svref *meta-room-info* weak-pointer-widetag)
82 (make-room-info 'weak-pointer :weak-pointer))
84 (let ((cons-info (make-room-info 'cons :list)))
85 ;; A cons consists of two words, both of which may be either a
86 ;; pointer or immediate data. According to the runtime this means
87 ;; either a fixnum, a character, an unbound-marker, a single-float
88 ;; on a 64-bit system, or a pointer.
89 (dotimes (i (ash 1 (- n-widetag-bits n-fixnum-tag-bits)))
90 (setf (svref *meta-room-info* (ash i n-fixnum-tag-bits)) cons-info))
92 (dotimes (i (ash 1 (- n-widetag-bits n-lowtag-bits)))
93 (setf (svref *meta-room-info* (logior (ash i n-lowtag-bits)
94 instance-pointer-lowtag))
95 cons-info)
96 (setf (svref *meta-room-info* (logior (ash i n-lowtag-bits)
97 list-pointer-lowtag))
98 cons-info)
99 (setf (svref *meta-room-info* (logior (ash i n-lowtag-bits)
100 fun-pointer-lowtag))
101 cons-info)
102 (setf (svref *meta-room-info* (logior (ash i n-lowtag-bits)
103 other-pointer-lowtag))
104 cons-info))
106 (setf (svref *meta-room-info* character-widetag) cons-info)
108 (setf (svref *meta-room-info* unbound-marker-widetag) cons-info)
110 ;; Single-floats are immediate data on 64-bit systems.
111 #!+64-bit
112 (setf (svref *meta-room-info* single-float-widetag) cons-info))
114 ) ; EVAL-WHEN
116 (define-load-time-global *room-info*
117 ;; SAETP instances don't dump properly from XC (or possibly
118 ;; normally), and we'd rather share structure with the master copy
119 ;; if we can anyway, so...
120 (make-array 256
121 :initial-contents
122 #.`(list
123 ,@(map 'list
124 (lambda (info)
125 (if (specialized-array-element-type-properties-p info)
126 `(aref *specialized-array-element-type-properties*
127 ,(position info *specialized-array-element-type-properties*))
128 info))
129 *meta-room-info*))))
131 (eval-when (:compile-toplevel :load-toplevel :execute)
133 (defglobal **heap-spaces**
134 #1='((:dynamic "Dynamic space" sb!kernel:dynamic-usage)
135 #!+immobile-space
136 (:immobile "Immobile space" sb!kernel::immobile-space-usage)
137 (:read-only "Read-only space" sb!kernel::read-only-space-usage)
138 (:static "Static space" sb!kernel::static-space-usage)))
140 (defglobal **stack-spaces**
141 #2='((:control-stack "Control stack" sb!kernel::control-stack-usage)
142 (:binding-stack "Binding stack" sb!kernel::binding-stack-usage)))
144 (defglobal **spaces**
145 (append #1# #2#)))
147 (deftype spaces ()
148 `(member ,@(mapcar #'first **heap-spaces**)))
151 ;;;; MAP-ALLOCATED-OBJECTS
153 #!-sb-fluid
154 (declaim (inline current-dynamic-space-start))
155 (defun current-dynamic-space-start ()
156 #!+(and gencgc relocatable-heap)
157 (extern-alien "DYNAMIC_SPACE_START" unsigned-long)
158 #!+(and gencgc (not relocatable-heap))
159 sb!vm:dynamic-space-start
160 #!-gencgc (extern-alien "current_dynamic_space" unsigned-long))
162 ;;; Return the lower limit and current free-pointer of SPACE as fixnums
163 ;;; whose raw bits (at the register level) represent a pointer.
164 ;;; This makes it "off" by a factor of (EXPT 2 N-FIXNUM-TAG-BITS) - and/or
165 ;;; possibly negative - if you look at the value in Lisp,
166 ;;; but avoids potentially needing a bignum on 32-bit machines.
167 ;;; 64-bit machines have no problem since most current generation CPUs
168 ;;; use an address width that is narrower than 64 bits.
169 ;;; This function is private because of the wacky representation.
170 (defun %space-bounds (space)
171 (declare (type spaces space))
172 (ecase space
173 (:static
174 (values (%make-lisp-obj static-space-start)
175 (%make-lisp-obj (sap-int *static-space-free-pointer*))))
176 (:read-only
177 (values (%make-lisp-obj read-only-space-start)
178 (%make-lisp-obj (sap-int *read-only-space-free-pointer*))))
179 #!+immobile-space
180 (:immobile
181 (values (%make-lisp-obj immobile-space-start)
182 (%make-lisp-obj (sap-int *immobile-space-free-pointer*))))
183 (:dynamic
184 (values (%make-lisp-obj (current-dynamic-space-start))
185 (%make-lisp-obj (sap-int (dynamic-space-free-pointer)))))))
187 ;;; Return the total number of bytes used in SPACE.
188 (defun space-bytes (space)
189 (multiple-value-bind (start end) (%space-bounds space)
190 (ash (- end start) n-fixnum-tag-bits)))
192 ;;; Round SIZE (in bytes) up to the next dualword boundary. A dualword
193 ;;; is eight bytes on platforms with 32-bit word size and 16 bytes on
194 ;;; platforms with 64-bit word size.
195 #!-sb-fluid (declaim (inline round-to-dualword))
196 (defun round-to-dualword (size)
197 (logand (the word (+ size lowtag-mask)) (lognot lowtag-mask)))
199 ;;; Return the vector OBJ, its WIDETAG, and the number of octets
200 ;;; required for its storage (including padding and alignment).
201 (defun reconstitute-vector (obj saetp)
202 (declare (type (simple-array * (*)) obj)
203 (type specialized-array-element-type-properties saetp))
204 (let* ((length (+ (length obj)
205 (saetp-n-pad-elements saetp)))
206 (n-bits (saetp-n-bits saetp))
207 (alignment-pad (floor 7 n-bits))
208 (n-data-octets (if (>= n-bits 8)
209 (* length (ash n-bits -3))
210 (ash (* (+ length alignment-pad)
211 n-bits)
212 -3))))
213 (values obj
214 (saetp-typecode saetp)
215 (round-to-dualword (+ (* vector-data-offset n-word-bytes)
216 n-data-octets)))))
218 ;;; Given the address (untagged, aligned, and interpreted as a FIXNUM)
219 ;;; of a lisp object, return the object, its "type code" (either
220 ;;; LIST-POINTER-LOWTAG or a header widetag), and the number of octets
221 ;;; required for its storage (including padding and alignment). Note
222 ;;; that this function is designed to NOT CONS, even if called
223 ;;; out-of-line.
224 (defun reconstitute-object (address)
225 (let* ((object-sap (int-sap (get-lisp-obj-address address)))
226 (header (sap-ref-word object-sap 0))
227 (widetag (logand header widetag-mask))
228 (header-value (ash header (- n-widetag-bits)))
229 (info (svref *room-info* widetag)))
230 (macrolet
231 ((boxed-size (header-value)
232 `(round-to-dualword (ash (1+ ,header-value) word-shift)))
233 (tagged-object (tag)
234 `(%make-lisp-obj (logior ,tag (get-lisp-obj-address address)))))
235 (cond
236 ;; Pick off arrays, as they're the only plausible cause for
237 ;; a non-nil, non-ROOM-INFO object as INFO.
238 ((specialized-array-element-type-properties-p info)
239 (reconstitute-vector (tagged-object other-pointer-lowtag) info))
241 ((null info)
242 (error "Unrecognized widetag #x~2,'0X in reconstitute-object"
243 widetag))
246 (case (room-info-kind info)
247 (:list
248 (values (tagged-object list-pointer-lowtag)
249 list-pointer-lowtag
250 (* 2 n-word-bytes)))
252 (:closure ; also funcallable-instance
253 (values (tagged-object fun-pointer-lowtag)
254 widetag
255 (boxed-size (logand header-value short-header-max-words))))
257 (:instance
258 (values (tagged-object instance-pointer-lowtag)
259 widetag
260 (boxed-size (logand header-value short-header-max-words))))
262 (:other
263 (values (tagged-object other-pointer-lowtag)
264 widetag
265 (boxed-size header-value)))
267 (:tiny-other
268 (values (tagged-object other-pointer-lowtag)
269 widetag
270 (boxed-size (logand header-value #xFF))))
272 (:vector-nil
273 (values (tagged-object other-pointer-lowtag)
274 simple-array-nil-widetag
275 (* 2 n-word-bytes)))
277 (:weak-pointer
278 (values (tagged-object other-pointer-lowtag)
279 weak-pointer-widetag
280 (round-to-dualword
281 (* weak-pointer-size
282 n-word-bytes))))
284 (:code
285 (let ((c (tagged-object other-pointer-lowtag)))
286 (values c
287 code-header-widetag
288 (round-to-dualword
289 (+ (* (logand header-value short-header-max-words)
290 n-word-bytes)
291 (%code-code-size (truly-the code-component c)))))))
293 (error "Unrecognized room-info-kind ~S in reconstitute-object"
294 (room-info-kind info)))))))))
296 ;;; Iterate over all the objects in the contiguous block of memory
297 ;;; with the low address at START and the high address just before
298 ;;; END, calling FUN with the object, the object's type code, and the
299 ;;; object's total size in bytes, including any header and padding.
300 ;;; START and END are untagged, aligned memory addresses interpreted
301 ;;; as FIXNUMs (unlike SAPs or tagged addresses, these will not cons).
302 (defun map-objects-in-range (fun start end)
303 (declare (type function fun))
304 ;; If START is (unsigned) greater than END, then we have somehow
305 ;; blown past our endpoint.
306 (aver (<= (get-lisp-obj-address start)
307 (get-lisp-obj-address end)))
308 (unless (= start end)
309 (multiple-value-bind
310 (obj typecode size)
311 (reconstitute-object start)
312 (aver (zerop (logand n-lowtag-bits size)))
313 (let ((next-start
314 ;; This special little dance is to add a number of octets
315 ;; (and it had best be a number evenly divisible by our
316 ;; allocation granularity) to an unboxed, aligned address
317 ;; masquerading as a fixnum. Without consing.
318 (%make-lisp-obj
319 (mask-field (byte #.n-word-bits 0)
320 (+ (get-lisp-obj-address start)
321 size)))))
322 (funcall fun obj typecode size)
323 (map-objects-in-range fun next-start end)))))
325 ;;; Access to the GENCGC page table for better precision in
326 ;;; MAP-ALLOCATED-OBJECTS
327 #!+gencgc
328 (progn
329 (define-alien-type (struct page)
330 (struct page
331 ;; To cut down the size of the page table, the scan_start_offset
332 ;; - a/k/a "start" - is measured in 4-byte integers regardless
333 ;; of word size. This is fine for 32-bit address space,
334 ;; but if 64-bit then we have to scale the value. Additionally
335 ;; there is a fallback for when even the scaled value is too big.
336 ;; (None of this matters to Lisp code for the most part)
337 (start #!+64-bit (unsigned 32) #!-64-bit signed)
338 ;; On platforms with small enough GC pages, this field
339 ;; will be a short. On platforms with larger ones, it'll
340 ;; be an int.
341 ;; Measured in bytes; the low bit has to be masked off.
342 (bytes-used (unsigned
343 #.(if (typep gencgc-card-bytes '(unsigned-byte 16))
345 32)))
346 (flags (unsigned 8))
347 (gen (signed 8))))
348 #!+immobile-space
349 (progn
350 (define-alien-type (struct immobile-page)
351 ;; ... and yet another place for Lisp to become out-of-sync with C.
352 (struct immobile-page
353 (flags (unsigned 8))
354 (obj-spacing (unsigned 8))
355 (obj-size (unsigned 8))
356 (generations (unsigned 8))
357 (free-index (unsigned 32))
358 (page-link (unsigned 16))
359 (prior-free-index (unsigned 16))))
360 (define-alien-variable "fixedobj_pages" (* (struct immobile-page))))
361 (declaim (inline find-page-index))
362 (define-alien-routine ("ext_find_page_index" find-page-index)
363 long (index signed))
364 (define-alien-variable "last_free_page" sb!kernel::page-index-t)
365 (define-alien-variable "page_table" (* (struct page))))
367 (declaim (inline code-header-words))
368 (defun code-header-words (code)
369 (logand (get-header-data code) short-header-max-words))
371 ;;; Iterate over all the objects allocated in each of the SPACES, calling FUN
372 ;;; with the object, the object's type code, and the object's total size in
373 ;;; bytes, including any header and padding. As a special case, if exactly one
374 ;;; space named :ALL is requested, then map over the known spaces.
375 (defun map-allocated-objects (fun &rest spaces)
376 (declare (type function fun))
377 (when (and (= (length spaces) 1) (eq (first spaces) :all))
378 (return-from map-allocated-objects
379 (map-allocated-objects fun
380 :read-only :static
381 #!+immobile-space :immobile
382 :dynamic)))
383 ;; You can't specify :ALL and also a list of spaces. Check that up front.
384 (do-rest-arg ((space) spaces) (the spaces space))
385 (flet ((do-1-space (space)
386 (ecase space
387 (:static
388 ;; Static space starts with NIL, which requires special
389 ;; handling, as the header and alignment are slightly off.
390 (multiple-value-bind (start end) (%space-bounds space)
391 ;; This "8" is very magical. It happens to work for both
392 ;; word sizes, even though symbols differ in length
393 ;; (they can be either 6 or 7 words).
394 (funcall fun nil symbol-widetag (* 8 n-word-bytes))
395 (map-objects-in-range fun
396 (+ (ash (* 8 n-word-bytes) (- n-fixnum-tag-bits))
397 start)
398 end)))
400 ((:read-only #!-gencgc :dynamic)
401 ;; Read-only space (and dynamic space on cheneygc) is a block
402 ;; of contiguous allocations.
403 (multiple-value-bind (start end) (%space-bounds space)
404 (map-objects-in-range fun start end)))
405 #!+immobile-space
406 (:immobile
407 ;; Filter out filler objects. These either look like cons cells
408 ;; in fixedobj subspace, or code without enough header words
409 ;; in varyobj subspace. (cf 'immobile_filler_p' in gc-internal.h)
410 (dx-flet ((filter (obj type size)
411 (unless (consp obj)
412 (funcall fun obj type size))))
413 (map-immobile-objects #'filter :fixed))
414 (dx-flet ((filter (obj type size)
415 (unless (and (code-component-p obj)
416 (eql (code-header-words obj) 2))
417 (funcall fun obj type size))))
418 (map-immobile-objects #'filter :variable)))
420 #!+gencgc
421 (:dynamic
422 ;; Dynamic space on gencgc requires walking the GC page tables
423 ;; in order to determine what regions contain objects.
425 ;; We explicitly presume that any pages in an allocation region
426 ;; that are in-use have a BYTES-USED of GENCGC-CARD-BYTES
427 ;; (indicating a full page) or an otherwise-valid BYTES-USED.
428 ;; We also presume that the pages of an open allocation region
429 ;; after the first page, and any pages that are unallocated,
430 ;; have a BYTES-USED of zero. GENCGC seems to guarantee this.
432 ;; Our procedure is to scan forward through the page table,
433 ;; maintaining an "end pointer" until we reach a page where
434 ;; BYTES-USED is not GENCGC-CARD-BYTES or we reach
435 ;; LAST-FREE-PAGE. We then MAP-OBJECTS-IN-RANGE if the range
436 ;; is not empty, and proceed to the next page (unless we've hit
437 ;; LAST-FREE-PAGE). We happily take advantage of the fact that
438 ;; MAP-OBJECTS-IN-RANGE will simply return if passed two
439 ;; coincident pointers for the range.
441 ;; FIXME: WITHOUT-GCING prevents a GC flip, but doesn't prevent
442 ;; closing allocation regions and opening new ones. This may
443 ;; prove to be an issue with concurrent systems, or with
444 ;; spectacularly poor timing for closing an allocation region
445 ;; in a single-threaded system.
447 (loop
448 with page-size = (ash gencgc-card-bytes (- n-fixnum-tag-bits))
449 ;; This magic dance gets us an unboxed aligned pointer as a
450 ;; FIXNUM.
451 with start = (%make-lisp-obj (current-dynamic-space-start))
452 with end = start
454 ;; This is our page range. The type constraint is far too generous,
455 ;; but it does its job of producing efficient code.
456 for page-index
457 of-type (integer -1 (#.(/ (ash 1 n-machine-word-bits) gencgc-card-bytes)))
458 from 0 below last-free-page
459 for next-page-addr from (+ start page-size) by page-size
460 for page-bytes-used
461 ;; The low bits of bytes-used is the need-to-zero flag.
462 = (logandc1 1 (slot (deref page-table page-index) 'bytes-used))
464 when (< page-bytes-used gencgc-card-bytes)
465 do (progn
466 (incf end (ash page-bytes-used (- n-fixnum-tag-bits)))
467 (map-objects-in-range fun start end)
468 (setf start next-page-addr)
469 (setf end next-page-addr))
470 else do (incf end page-size)
472 finally (map-objects-in-range fun start end))))))
473 (do-rest-arg ((space) spaces)
474 (if (eq space :dynamic)
475 (without-gcing (do-1-space space))
476 (do-1-space space)))))
478 ;;;; MEMORY-USAGE
480 ;;; Return a list of 3-lists (bytes object type-name) for the objects
481 ;;; allocated in Space.
482 (defun type-breakdown (space)
483 (declare (muffle-conditions t))
484 (let ((sizes (make-array 256 :initial-element 0 :element-type '(unsigned-byte #.n-word-bits)))
485 (counts (make-array 256 :initial-element 0 :element-type '(unsigned-byte #.n-word-bits))))
486 (map-allocated-objects
487 (lambda (obj type size)
488 (declare (word size) (optimize (speed 3)) (ignore obj))
489 (incf (aref sizes type) size)
490 (incf (aref counts type)))
491 space)
493 (let ((totals (make-hash-table :test 'eq)))
494 (dotimes (i 256)
495 (let ((total-count (aref counts i)))
496 (unless (zerop total-count)
497 (let* ((total-size (aref sizes i))
498 (name (room-info-type-name (aref *room-info* i)))
499 (found (ensure-gethash name totals (list 0 0 name))))
500 (incf (first found) total-size)
501 (incf (second found) total-count)))))
503 (collect ((totals-list))
504 (maphash (lambda (k v)
505 (declare (ignore k))
506 (totals-list v))
507 totals)
508 (sort (totals-list) #'> :key #'first)))))
510 ;;; Handle the summary printing for MEMORY-USAGE. Totals is a list of lists
511 ;;; (space-name . totals-for-space), where totals-for-space is the list
512 ;;; returned by TYPE-BREAKDOWN.
513 (defun print-summary (spaces totals)
514 (let ((summary (make-hash-table :test 'eq))
515 (space-count (length spaces)))
516 (dolist (space-total totals)
517 (dolist (total (cdr space-total))
518 (push (cons (car space-total) total)
519 (gethash (third total) summary))))
521 (collect ((summary-totals))
522 (maphash (lambda (k v)
523 (declare (ignore k))
524 (let ((sum 0))
525 (declare (unsigned-byte sum))
526 (dolist (space-total v)
527 (incf sum (first (cdr space-total))))
528 (summary-totals (cons sum v))))
529 summary)
531 (format t "~2&Summary of space~P: ~(~{~A ~}~)~%" space-count spaces)
532 (let ((summary-total-bytes 0)
533 (summary-total-objects 0))
534 (declare (unsigned-byte summary-total-bytes summary-total-objects))
535 (dolist (space-totals
536 (mapcar #'cdr (sort (summary-totals) #'> :key #'car)))
537 (let ((total-objects 0)
538 (total-bytes 0)
539 name)
540 (declare (unsigned-byte total-objects total-bytes))
541 (collect ((spaces))
542 (dolist (space-total space-totals)
543 (let ((total (cdr space-total)))
544 (setq name (third total))
545 (incf total-bytes (first total))
546 (incf total-objects (second total))
547 (spaces (cons (car space-total) (first total)))))
548 (format t "~%~A:~% ~:D bytes, ~:D object~:P"
549 name total-bytes total-objects)
550 (unless (= 1 space-count)
551 (dolist (space (spaces))
552 (format t ", ~D% ~(~A~)"
553 (round (* (cdr space) 100) total-bytes) (car space))))
554 (format t ".~%")
555 (incf summary-total-bytes total-bytes)
556 (incf summary-total-objects total-objects))))
557 (format t "~%Summary total:~% ~:D bytes, ~:D objects.~%"
558 summary-total-bytes summary-total-objects)))))
560 ;;; Report object usage for a single space.
561 (defun report-space-total (space-info cutoff)
562 (declare (list space-info) (type (or single-float null) cutoff))
563 (destructuring-bind (space . types) space-info
564 (format t "~2&Breakdown for ~(~A~) space:~%" space)
565 (let* ((total-bytes (reduce #'+ (mapcar #'first types)))
566 (bytes-width (decimal-with-grouped-digits-width total-bytes))
567 (total-objects (reduce #'+ (mapcar #'second types)))
568 (objects-width (decimal-with-grouped-digits-width total-objects))
569 (cutoff-point (if cutoff
570 (truncate (* (float total-bytes) cutoff))
572 (reported-bytes 0)
573 (reported-objects 0))
574 (declare (unsigned-byte total-objects total-bytes cutoff-point
575 reported-objects reported-bytes))
576 (flet ((type-usage (bytes objects name &optional note)
577 (format t " ~V:D bytes for ~V:D ~(~A~) object~2:*~P~*~
578 ~:[~; ~:*(~A)~]~%"
579 bytes-width bytes objects-width objects name note)))
580 (loop for (bytes objects name) in types do
581 (when (<= bytes cutoff-point)
582 (type-usage (- total-bytes reported-bytes)
583 (- total-objects reported-objects)
584 "other")
585 (return))
586 (incf reported-bytes bytes)
587 (incf reported-objects objects)
588 (type-usage bytes objects name))
589 (terpri)
590 (type-usage total-bytes total-objects space "space total")))))
592 ;;; Print information about the heap memory in use. PRINT-SPACES is a
593 ;;; list of the spaces to print detailed information for.
594 ;;; COUNT-SPACES is a list of the spaces to scan. For either one, T
595 ;;; means all spaces (i.e. :STATIC, :DYNAMIC and :READ-ONLY.) If
596 ;;; PRINT-SUMMARY is true, then summary information will be printed.
597 ;;; The defaults print only summary information for dynamic space. If
598 ;;; true, CUTOFF is a fraction of the usage in a report below which
599 ;;; types will be combined as OTHER.
600 (defun memory-usage (&key print-spaces (count-spaces '(:dynamic #!+immobile-space :immobile))
601 (print-summary t) cutoff)
602 (declare (type (or single-float null) cutoff))
603 (let* ((spaces (if (eq count-spaces t)
604 (mapcar #'first **heap-spaces**)
605 count-spaces))
606 (totals (mapcar (lambda (space)
607 (cons space (type-breakdown space)))
608 spaces)))
610 (dolist (space-total totals)
611 (when (or (eq print-spaces t)
612 (member (car space-total) print-spaces))
613 (report-space-total space-total cutoff)))
615 (when print-summary (print-summary spaces totals)))
617 (values))
619 ;;; Print a breakdown by instance type of all the instances allocated
620 ;;; in SPACE. If TOP-N is true, print only information for the
621 ;;; TOP-N types with largest usage.
622 (defun instance-usage (space &key (top-n 15))
623 (declare (type spaces space) (type (or fixnum null) top-n))
624 (format t "~2&~@[Top ~W ~]~(~A~) instance types:~%" top-n space)
625 (let ((totals (make-hash-table :test 'eq))
626 (total-objects 0)
627 (total-bytes 0))
628 (declare (unsigned-byte total-objects total-bytes))
629 (map-allocated-objects
630 (lambda (obj type size)
631 (declare (optimize (speed 3)))
632 (when (eql type instance-widetag)
633 (incf total-objects)
634 (let* ((classoid (layout-classoid (%instance-layout obj)))
635 (found (ensure-gethash classoid totals (cons 0 0)))
636 (size size))
637 (declare (fixnum size))
638 (incf total-bytes size)
639 (incf (the fixnum (car found)))
640 (incf (the fixnum (cdr found)) size))))
641 space)
642 (let* ((sorted (sort (%hash-table-alist totals) #'> :key #'cddr))
643 (interesting (if top-n
644 (subseq sorted 0 (min (length sorted) top-n))
645 sorted))
646 (bytes-width (decimal-with-grouped-digits-width total-bytes))
647 (objects-width (decimal-with-grouped-digits-width total-objects))
648 (types-width (reduce #'max interesting
649 :key (lambda (x) (length (symbol-name (classoid-name (first x)))))
650 :initial-value 0))
651 (printed-bytes 0)
652 (printed-objects 0))
653 (declare (unsigned-byte printed-bytes printed-objects))
654 (flet ((type-usage (type objects bytes)
655 (let ((name (etypecase type
656 (string type)
657 (classoid (symbol-name (classoid-name type))))))
658 (format t " ~V@<~A~> ~V:D bytes, ~V:D object~:P.~%"
659 (1+ types-width) name bytes-width bytes
660 objects-width objects))))
661 (loop for (type . (objects . bytes)) in interesting do
662 (incf printed-bytes bytes)
663 (incf printed-objects objects)
664 (type-usage type objects bytes))
665 (let ((residual-objects (- total-objects printed-objects))
666 (residual-bytes (- total-bytes printed-bytes)))
667 (unless (zerop residual-objects)
668 (type-usage "Other types" residual-bytes residual-objects)))
669 (type-usage (format nil "~:(~A~) instance total" space)
670 total-bytes total-objects))))
671 (values))
673 ;;;; PRINT-ALLOCATED-OBJECTS
675 (defun print-allocated-objects (space &key (percent 0) (pages 5)
676 type larger smaller count
677 (stream *standard-output*))
678 (declare (type (integer 0 99) percent) (type index pages)
679 (type stream stream) (type spaces space)
680 (type (or index null) type larger smaller count))
681 (multiple-value-bind (start end) (%space-bounds space)
682 (let* ((space-start (ash start n-fixnum-tag-bits))
683 (space-end (ash end n-fixnum-tag-bits))
684 (space-size (- space-end space-start))
685 (pagesize (get-page-size))
686 (start (+ space-start (round (* space-size percent) 100)))
687 (printed-conses (make-hash-table :test 'eq))
688 (pages-so-far 0)
689 (count-so-far 0)
690 (last-page 0))
691 (declare (type word last-page start)
692 (fixnum pages-so-far count-so-far pagesize))
693 (labels ((note-conses (x)
694 (unless (or (atom x) (gethash x printed-conses))
695 (setf (gethash x printed-conses) t)
696 (note-conses (car x))
697 (note-conses (cdr x)))))
698 (map-allocated-objects
699 (lambda (obj obj-type size)
700 (let ((addr (get-lisp-obj-address obj)))
701 (when (>= addr start)
702 (when (if count
703 (> count-so-far count)
704 (> pages-so-far pages))
705 (return-from print-allocated-objects (values)))
707 (unless count
708 (let ((this-page (* (the (values word t)
709 (truncate addr pagesize))
710 pagesize)))
711 (declare (type word this-page))
712 (when (/= this-page last-page)
713 (when (< pages-so-far pages)
714 ;; FIXME: What is this? (ERROR "Argh..")? or
715 ;; a warning? or code that can be removed
716 ;; once the system is stable? or what?
717 (format stream "~2&**** Page ~W, address ~X:~%"
718 pages-so-far addr))
719 (setq last-page this-page)
720 (incf pages-so-far))))
722 (when (and (or (not type) (eql obj-type type))
723 (or (not smaller) (<= size smaller))
724 (or (not larger) (>= size larger)))
725 (incf count-so-far)
726 (case type
727 (#.code-header-widetag
728 (let ((dinfo (%code-debug-info obj)))
729 (format stream "~&Code object: ~S~%"
730 (if dinfo
731 (sb!c::compiled-debug-info-name dinfo)
732 "No debug info."))))
733 (#.symbol-widetag
734 (format stream "~&~S~%" obj))
735 (#.list-pointer-lowtag
736 (unless (gethash obj printed-conses)
737 (note-conses obj)
738 (let ((*print-circle* t)
739 (*print-level* 5)
740 (*print-length* 10))
741 (format stream "~&~S~%" obj))))
743 (fresh-line stream)
744 (let ((str (write-to-string obj :level 5 :length 10
745 :pretty nil)))
746 (unless (eql type instance-widetag)
747 (format stream "~S: " (type-of obj)))
748 (format stream "~A~%"
749 (subseq str 0 (min (length str) 60))))))))))
750 space))))
751 (values))
753 ;;;; LIST-ALLOCATED-OBJECTS, LIST-REFERENCING-OBJECTS
755 (defvar *ignore-after* nil)
757 (defun valid-obj (space x)
758 (or (not (eq space :dynamic))
759 ;; this test looks bogus if the allocator doesn't work linearly,
760 ;; which I suspect is the case for GENCGC. -- CSR, 2004-06-29
761 (< (get-lisp-obj-address x) (get-lisp-obj-address *ignore-after*))))
763 (defun maybe-cons (space x stuff)
764 (if (valid-obj space x)
765 (cons x stuff)
766 stuff))
768 (defun list-allocated-objects (space &key type larger smaller count
769 test)
770 (declare (type spaces space)
771 (type (or index null) larger smaller type count)
772 (type (or function null) test))
773 (unless *ignore-after*
774 (setq *ignore-after* (cons 1 2)))
775 (collect ((counted 0 1+))
776 (let ((res ()))
777 (map-allocated-objects
778 (lambda (obj obj-type size)
779 (when (and (or (not type) (eql obj-type type))
780 (or (not smaller) (<= size smaller))
781 (or (not larger) (>= size larger))
782 (or (not test) (funcall test obj)))
783 (setq res (maybe-cons space obj res))
784 (when (and count (>= (counted) count))
785 (return-from list-allocated-objects res))))
786 space)
787 res)))
789 ;;; Convert the descriptor into a SAP. The bits all stay the same, we just
790 ;;; change our notion of what we think they are.
792 ;;; Defining this here (as opposed to in 'debug-int' where it belongs)
793 ;;; is the path of least resistance to avoiding an inlining failure warning.
794 #!-sb-fluid (declaim (inline sb!di::descriptor-sap))
795 (defun sb!di::descriptor-sap (x)
796 (int-sap (get-lisp-obj-address x)))
798 ;;; Calls FUNCTION with all objects that have (possibly conservative)
799 ;;; references to them on current stack.
800 (defun map-stack-references (function)
801 (let ((end
802 (sb!di::descriptor-sap
803 #!+stack-grows-downward-not-upward *control-stack-end*
804 #!-stack-grows-downward-not-upward *control-stack-start*))
805 (sp (current-sp))
806 (seen nil))
807 (loop until #!+stack-grows-downward-not-upward (sap> sp end)
808 #!-stack-grows-downward-not-upward (sap< sp end)
809 do (multiple-value-bind (obj ok) (make-lisp-obj (sap-ref-word sp 0) nil)
810 (when (and ok (typep obj '(not (or fixnum character))))
811 (unless (member obj seen :test #'eq)
812 (funcall function obj)
813 (push obj seen))))
814 (setf sp
815 #!+stack-grows-downward-not-upward (sap+ sp n-word-bytes)
816 #!-stack-grows-downward-not-upward (sap+ sp (- n-word-bytes))))))
818 ;;; This interface allows one either to be agnostic of the referencing space,
819 ;;; or specify exactly one space, but not specify a list of spaces.
820 ;;; An upward-compatible change would be to assume a list, and call ENSURE-LIST.
821 (defun map-referencing-objects (fun space object)
822 (declare (type (or (eql :all) spaces) space))
823 (unless *ignore-after*
824 (setq *ignore-after* (cons 1 2)))
825 (flet ((ref-p (this widetag nwords) ; return T if 'this' references object
826 (when (listp this)
827 (return-from ref-p
828 (or (eq (car this) object) (eq (cdr this) object))))
829 (case widetag
830 ;; purely boxed objects
831 ((#.ratio-widetag #.complex-widetag #.value-cell-widetag
832 #.symbol-widetag #.weak-pointer-widetag
833 #.simple-array-widetag #.simple-vector-widetag
834 #.complex-array-widetag #.complex-vector-widetag
835 #.complex-bit-vector-widetag #.complex-vector-nil-widetag
836 #.complex-base-string-widetag
837 #!+sb-unicode #.complex-character-string-widetag))
838 ;; mixed boxed/unboxed objects
839 (#.code-header-widetag
840 (dotimes (i (code-n-entries this))
841 (let ((f (%code-entry-point this i)))
842 (when (or (eq f object)
843 (eq (%simple-fun-name f) object)
844 (eq (%simple-fun-arglist f) object)
845 (eq (%simple-fun-type f) object)
846 (eq (%simple-fun-info f) object))
847 (return-from ref-p t))))
848 (setq nwords (code-header-words this)))
849 (#.instance-widetag
850 (return-from ref-p
851 (or (eq (%instance-layout this) object)
852 (do-instance-tagged-slot (i this)
853 (when (eq (%instance-ref this i) object)
854 (return t))))))
855 (#.funcallable-instance-widetag
856 (let ((l (%funcallable-instance-layout this)))
857 (when (eq l object)
858 (return-from ref-p t))
859 (let ((bitmap (layout-bitmap l)))
860 (unless (eql bitmap -1)
861 ;; tagged slots precede untagged slots,
862 ;; so integer-length is the count of tagged slots.
863 (setq nwords (1+ (integer-length bitmap)))))))
864 (#.closure-widetag
865 (when (eq (%closure-fun this) object)
866 (return-from ref-p t)))
867 (#.fdefn-widetag
868 #!+immobile-code
869 (when (eq (make-lisp-obj
870 (alien-funcall
871 (extern-alien "fdefn_raw_referent" (function unsigned unsigned))
872 (logandc2 (get-lisp-obj-address this) lowtag-mask)))
873 object)
874 (return-from ref-p t))
875 ;; Without immobile-code the 'raw-addr' slot either holds the same thing
876 ;; as the 'fun' slot, or holds a trampoline address. We'll overlook the
877 ;; minor issue that due to concurrent writes, two representations of the
878 ;; allegedly same referent may diverge; thus the last slot is skipped
879 ;; even if it refers to a different simple-fun.
880 (decf nwords))
882 (return-from ref-p nil)))
883 ;; gencgc has WITHOUT-GCING in map-allocated-objects over dynamic space,
884 ;; so we don't have to pin each object inside REF-P.
885 (#!+cheneygc with-pinned-objects #!+cheneygc (this)
886 #!-cheneygc progn
887 (do ((sap (int-sap (logandc2 (get-lisp-obj-address this) lowtag-mask)))
888 (i (* (1- nwords) n-word-bytes) (- i n-word-bytes)))
889 ((<= i 0) nil)
890 (when (eq (sap-ref-lispobj sap i) object)
891 (return t))))))
892 (let ((fun (%coerce-callable-to-fun fun)))
893 (dx-flet ((mapfun (obj widetag size)
894 (when (and (ref-p obj widetag (/ size n-word-bytes))
895 (valid-obj space obj))
896 (funcall fun obj))))
897 (map-allocated-objects #'mapfun space)))))
899 (defun list-referencing-objects (space object)
900 (collect ((res))
901 (map-referencing-objects
902 (lambda (obj) (res obj)) space object)
903 (res)))
906 #+nil ; for debugging
907 (defun dump-dynamic-space-code (&optional (stream *standard-output*)
908 &aux (n-pages 0) (n-code-bytes 0))
909 (flet ((dump-page (page-num)
910 (incf n-pages)
911 (format stream "~&Page ~D~%" page-num)
912 (let ((where (+ dynamic-space-start (* page-num gencgc-card-bytes)))
913 (seen-filler nil))
914 (loop
915 (multiple-value-bind (obj type size)
916 (reconstitute-object (ash where (- n-fixnum-tag-bits)))
917 (when (= type code-header-widetag)
918 (incf n-code-bytes size))
919 (when (if (and (consp obj) (eq (car obj) 0) (eq (cdr obj) 0))
920 (if seen-filler
921 (progn (write-char #\. stream) nil)
922 (setq seen-filler t))
923 (progn (setq seen-filler nil) t))
924 (let ((*print-pretty* nil))
925 (format stream "~& ~X ~4X ~S " where size obj)))
926 (incf where size))
927 (let ((next-page (find-page-index where)))
928 (cond ((= (logand where (1- gencgc-card-bytes)) 0)
929 (format stream "~&-- END OF PAGE --~%")
930 (return next-page))
931 ((eq next-page page-num))
933 (incf n-pages)
934 (setq page-num next-page seen-filler nil))))))))
935 (let ((i 0))
936 (loop while (< i last-free-page)
937 do (let ((allocation (ldb (byte 2 0)
938 (slot (deref page-table i) 'flags))))
939 (if (= allocation 3)
940 (setq i (dump-page i))
941 (incf i)))))
942 (let* ((tot (* n-pages gencgc-card-bytes))
943 (waste (- tot n-code-bytes)))
944 (format t "~&Used=~D Waste=~D (~F%)~%" n-code-bytes waste
945 (* 100 (/ waste tot))))))