1 ;;;; heap-grovelling memory usage stuff
3 ;;;; This software is part of the SBCL system. See the README file for
6 ;;;; This software is derived from the CMU CL system, which was
7 ;;;; written at Carnegie Mellon University and released into the
8 ;;;; public domain. The software is in the public domain and is
9 ;;;; provided with absolutely no warranty. See the COPYING and CREDITS
10 ;;;; files for more information.
14 ;;;; type format database
16 (eval-when (:compile-toplevel
:load-toplevel
:execute
)
17 (def!struct
(room-info (:make-load-form-fun just-dump-it-normally
))
18 ;; the name of this type
19 (name nil
:type symbol
)
20 ;; kind of type (how we determine length)
22 :type
(member :lowtag
:fixed
:header
:vector
23 :string
:code
:closure
:instance
))
24 ;; length if fixed-length, shift amount for element size if :VECTOR
25 (length nil
:type
(or fixnum null
))))
27 (eval-when (:compile-toplevel
:execute
)
29 (defvar *meta-room-info
* (make-array 256 :initial-element nil
))
31 (dolist (obj *primitive-objects
*)
32 (let ((widetag (primitive-object-widetag obj
))
33 (lowtag (primitive-object-lowtag obj
))
34 (name (primitive-object-name obj
))
35 (variable (primitive-object-variable-length-p obj
))
36 (size (primitive-object-size obj
)))
39 (;; KLUDGE described in dan_b message "Another one for the
40 ;; collection [bug 108]" (sbcl-devel 2004-01-22)
42 ;; In a freshly started SBCL 0.8.7.20ish, (TIME (ROOM T)) causes
43 ;; debugger invoked on a SB-INT:BUG in thread 5911:
44 ;; failed AVER: "(SAP= CURRENT END)"
45 ;; [WHN: Similar things happened on one but not the other of my
46 ;; machines when I just run ROOM a lot in a loop.]
48 ;; This appears to be due to my [DB] abuse of the primitive
49 ;; object macros to define a thread object that shares a lowtag
50 ;; with fixnums and has no widetag: it looks like the code that
51 ;; generates *META-ROOM-INFO* infers from this that even fixnums
52 ;; are thread-sized - probably undesirable.
54 ;; This [the fix; the EQL NAME 'THREAD clause here] is more in the
55 ;; nature of a workaround than a really good fix. I'm not sure
56 ;; what a really good fix is: I /think/ it's probably to remove
57 ;; the :LOWTAG option in DEFINE-PRIMITIVE-OBJECT THREAD, then teach
58 ;; genesis to generate the necessary OBJECT_SLOT_OFFSET macros
59 ;; for assembly source in the runtime/genesis/*.h files.
62 (let ((info (make-room-info :name name
64 (lowtag (symbol-value lowtag
)))
65 (declare (fixnum lowtag
))
67 (setf (svref *meta-room-info
* (logior lowtag
(ash i
3))) info
))))
70 (setf (svref *meta-room-info
* (symbol-value widetag
))
71 (make-room-info :name name
75 (dolist (code (list #!+sb-unicode complex-character-string-widetag
76 complex-base-string-widetag simple-array-widetag
77 complex-bit-vector-widetag complex-vector-widetag
78 complex-array-widetag complex-vector-nil-widetag
))
79 (setf (svref *meta-room-info
* code
)
80 (make-room-info :name
'array-header
83 (setf (svref *meta-room-info
* bignum-widetag
)
84 (make-room-info :name
'bignum
87 (setf (svref *meta-room-info
* closure-header-widetag
)
88 (make-room-info :name
'closure
91 ;; FIXME: This looks rather brittle. Can we get more of these numbers
92 ;; from somewhere sensible?
93 (dolist (stuff '((simple-bit-vector-widetag . -
3)
94 (simple-vector-widetag .
#.sb
!vm
:word-shift
)
95 (simple-array-unsigned-byte-2-widetag . -
2)
96 (simple-array-unsigned-byte-4-widetag . -
1)
97 (simple-array-unsigned-byte-7-widetag .
0)
98 (simple-array-unsigned-byte-8-widetag .
0)
99 (simple-array-unsigned-byte-15-widetag .
1)
100 (simple-array-unsigned-byte-16-widetag .
1)
101 (simple-array-unsigned-byte-31-widetag .
2)
102 (simple-array-unsigned-byte-32-widetag .
2)
103 (simple-array-unsigned-byte-60-widetag .
3)
104 (simple-array-unsigned-byte-63-widetag .
3)
105 (simple-array-unsigned-byte-64-widetag .
3)
106 (simple-array-signed-byte-8-widetag .
0)
107 (simple-array-signed-byte-16-widetag .
1)
108 (simple-array-unsigned-byte-29-widetag .
2)
109 (simple-array-signed-byte-30-widetag .
2)
110 (simple-array-signed-byte-32-widetag .
2)
111 (simple-array-signed-byte-61-widetag .
3)
112 (simple-array-signed-byte-64-widetag .
3)
113 (simple-array-single-float-widetag .
2)
114 (simple-array-double-float-widetag .
3)
115 (simple-array-complex-single-float-widetag .
3)
116 (simple-array-complex-double-float-widetag .
4)))
117 (let* ((name (car stuff
))
119 (sname (string name
)))
121 (setf (svref *meta-room-info
* (symbol-value name
))
122 (make-room-info :name
(intern (subseq sname
124 (mismatch sname
"-WIDETAG"
129 (setf (svref *meta-room-info
* simple-base-string-widetag
)
130 (make-room-info :name
'simple-base-string
135 (setf (svref *meta-room-info
* simple-character-string-widetag
)
136 (make-room-info :name
'simple-character-string
140 (setf (svref *meta-room-info
* simple-array-nil-widetag
)
141 (make-room-info :name
'simple-array-nil
145 (setf (svref *meta-room-info
* code-header-widetag
)
146 (make-room-info :name
'code
149 (setf (svref *meta-room-info
* instance-header-widetag
)
150 (make-room-info :name
'instance
155 (defparameter *room-info
* '#.
*meta-room-info
*)
156 (deftype spaces
() '(member :static
:dynamic
:read-only
))
158 ;;;; MAP-ALLOCATED-OBJECTS
160 ;;; Since they're represented as counts of words, we should never
161 ;;; need bignums to represent these:
162 (declaim (type fixnum
163 *static-space-free-pointer
*
164 *read-only-space-free-pointer
*))
166 (defun space-bounds (space)
167 (declare (type spaces space
))
170 (values (int-sap static-space-start
)
171 (int-sap (* *static-space-free-pointer
* n-word-bytes
))))
173 (values (int-sap read-only-space-start
)
174 (int-sap (* *read-only-space-free-pointer
* n-word-bytes
))))
176 (values (int-sap (current-dynamic-space-start))
177 (dynamic-space-free-pointer)))))
179 ;;; Return the total number of bytes used in SPACE.
180 (defun space-bytes (space)
181 (multiple-value-bind (start end
) (space-bounds space
)
182 (- (sap-int end
) (sap-int start
))))
184 ;;; Round SIZE (in bytes) up to the next dualword boundary. A dualword
185 ;;; is eight bytes on platforms with 32-bit word size and 16 bytes on
186 ;;; platforms with 64-bit word size.
187 #!-sb-fluid
(declaim (inline round-to-dualword
))
188 (defun round-to-dualword (size)
189 (logand (the word
(+ size lowtag-mask
)) (lognot lowtag-mask
)))
191 ;;; Return the total size of a vector in bytes, including any pad.
192 #!-sb-fluid
(declaim (inline vector-total-size
))
193 (defun vector-total-size (obj info
)
194 (let ((shift (room-info-length info
))
195 (len (+ (length (the (simple-array * (*)) obj
))
196 (ecase (room-info-kind info
)
200 (+ (* vector-data-offset n-word-bytes
)
202 (ash (+ len
(1- (ash 1 (- shift
))))
206 ;;; Access to the GENCGC page table for better precision in
207 ;;; MAP-ALLOCATED-OBJECTS
210 (define-alien-type (struct page
)
213 ;; On platforms with small enough GC pages, this field
214 ;; will be a short. On platforms with larger ones, it'll
216 (bytes-used (unsigned
217 #.
(if (typep sb
!vm
:gencgc-page-bytes
223 (declaim (inline find-page-index
))
224 (define-alien-routine "find_page_index" long
(index long
))
225 (define-alien-variable "page_table" (* (struct page
))))
227 ;;; Iterate over all the objects allocated in SPACE, calling FUN with
228 ;;; the object, the object's type code, and the object's total size in
229 ;;; bytes, including any header and padding. CAREFUL makes
230 ;;; MAP-ALLOCATED-OBJECTS slightly more accurate, but a lot slower: it
231 ;;; is intended for slightly more demanding uses of heap groveling
233 #!-sb-fluid
(declaim (maybe-inline map-allocated-objects
))
234 (defun map-allocated-objects (fun space
&optional careful
)
235 (declare (type function fun
) (type spaces space
))
236 (flet ((make-obj (tagged-address)
238 (make-lisp-obj tagged-address nil
)
239 (values (%make-lisp-obj tagged-address
) t
))))
240 ;; Inlining MAKE-OBJ reduces consing on platforms where dynamic
241 ;; space extends past fixnum range.
242 (declare (inline make-obj
))
244 (multiple-value-bind (start end
) (space-bounds space
)
245 (declare (type system-area-pointer start end
))
246 (declare (optimize (speed 3)))
247 (let ((current start
)
249 (skip-tests-until-addr 0))
250 (labels ((maybe-finish-mapping ()
251 (unless (sap< current end
)
252 (aver (sap= current end
))
253 (return-from map-allocated-objects
)))
254 ;; GENCGC doesn't allocate linearly, which means that the
255 ;; dynamic space can contain large blocks zeros that get
256 ;; accounted as conses in ROOM (and slow down other
257 ;; applications of MAP-ALLOCATED-OBJECTS). To fix this
258 ;; check the GC page structure for the current address.
259 ;; If the page is free or the address is beyond the page-
260 ;; internal allocation offset (bytes-used) skip to the
261 ;; next page immediately.
264 (when (eq space
:dynamic
)
265 (loop with page-mask
= #.
(1- sb
!vm
:gencgc-page-bytes
)
266 for addr of-type sb
!vm
:word
= (sap-int current
)
267 while
(>= addr skip-tests-until-addr
)
269 ;; For some reason binding PAGE with LET
270 ;; conses like mad (but gives no compiler notes...)
271 ;; Work around the problem with SYMBOL-MACROLET
272 ;; instead of trying to figure out the real
273 ;; issue. -- JES, 2005-05-17
275 ((page (deref page-table
276 (find-page-index addr
))))
277 ;; Don't we have any nicer way to access C struct
279 (let ((alloc-flag (ldb (byte 3 2)
281 (bytes-used (slot page
'bytes-used
)))
282 ;; If the page is not free and the current
283 ;; pointer is still below the allocation offset
285 (when (and (not (zerop alloc-flag
))
286 (<= (logand page-mask addr
)
288 ;; Don't bother testing again until we
289 ;; get past that allocation offset
290 (setf skip-tests-until-addr
291 (+ (logandc2 addr page-mask
) bytes-used
))
292 ;; And then continue with the
294 (return-from maybe-skip-page
))
295 ;; Move CURRENT to start of next page.
296 (setf current
(int-sap (+ (logandc2 addr page-mask
)
297 sb
!vm
:gencgc-page-bytes
)))
298 (maybe-finish-mapping))))))
299 (maybe-map (obj obj-tag n-obj-bytes
&optional
(ok t
))
300 (let ((next (typecase n-obj-bytes
301 (fixnum (sap+ current n-obj-bytes
))
302 (integer (sap+ current n-obj-bytes
)))))
303 ;; If this object would take us past END, it must
304 ;; be either bogus, or it has been allocated after
305 ;; the call to M-A-O.
306 (cond ((and ok next
(sap<= next end
))
307 (funcall fun obj obj-tag n-obj-bytes
)
310 (setf current
(sap+ current n-word-bytes
)))))))
311 (declare (inline maybe-finish-mapping maybe-skip-page maybe-map
))
313 (maybe-finish-mapping)
315 (let* ((header (sap-ref-word current
0))
316 (header-widetag (logand header
#xFF
))
317 (info (svref *room-info
* header-widetag
)))
320 (eq (room-info-kind info
) :lowtag
))
321 (multiple-value-bind (obj ok
)
322 (make-obj (logior (sap-int current
) list-pointer-lowtag
))
325 (* cons-size n-word-bytes
)
327 ((eql header-widetag closure-header-widetag
)
328 (let* ((obj (%make-lisp-obj
(logior (sap-int current
)
329 fun-pointer-lowtag
)))
330 (size (round-to-dualword
331 (* (the fixnum
(1+ (get-closure-length obj
)))
333 (maybe-map obj header-widetag size
)))
334 ((eq (room-info-kind info
) :instance
)
335 (let* ((obj (%make-lisp-obj
336 (logior (sap-int current
) instance-pointer-lowtag
)))
337 (size (round-to-dualword
338 (* (+ (%instance-length obj
) 1) n-word-bytes
))))
339 (aver (zerop (logand size lowtag-mask
)))
340 (maybe-map obj header-widetag size
)))
342 (multiple-value-bind (obj ok
)
343 (make-obj (logior (sap-int current
) other-pointer-lowtag
))
345 (ecase (room-info-kind info
)
347 (aver (or (eql (room-info-length info
)
348 (1+ (get-header-data obj
)))
350 (simple-array-nil-p obj
)))
352 (* (room-info-length info
) n-word-bytes
)))
354 (vector-total-size obj info
))
357 (* (1+ (get-header-data obj
)) n-word-bytes
)))
360 (* (get-header-data obj
) n-word-bytes
))
362 (* (the fixnum
(%code-code-size obj
))
366 (when size
(aver (zerop (logand size lowtag-mask
))))
367 (maybe-map obj header-widetag size
))))
371 (null (frob))))))))))))))))
376 ;;; Return a list of 3-lists (bytes object type-name) for the objects
377 ;;; allocated in Space.
378 (defun type-breakdown (space)
379 (let ((sizes (make-array 256 :initial-element
0 :element-type
'(unsigned-byte #.sb
!vm
:n-word-bits
)))
380 (counts (make-array 256 :initial-element
0 :element-type
'(unsigned-byte #.sb
!vm
:n-word-bits
))))
381 (map-allocated-objects
382 (lambda (obj type size
)
383 (declare (word size
) (optimize (speed 3)) (ignore obj
))
384 (incf (aref sizes type
) size
)
385 (incf (aref counts type
)))
388 (let ((totals (make-hash-table :test
'eq
)))
390 (let ((total-count (aref counts i
)))
391 (unless (zerop total-count
)
392 (let* ((total-size (aref sizes i
))
393 (name (room-info-name (aref *room-info
* i
)))
394 (found (gethash name totals
)))
396 (incf (first found
) total-size
)
397 (incf (second found
) total-count
))
399 (setf (gethash name totals
)
400 (list total-size total-count name
))))))))
402 (collect ((totals-list))
403 (maphash (lambda (k v
)
407 (sort (totals-list) #'> :key
#'first
)))))
409 ;;; Handle the summary printing for MEMORY-USAGE. Totals is a list of lists
410 ;;; (space-name . totals-for-space), where totals-for-space is the list
411 ;;; returned by TYPE-BREAKDOWN.
412 (defun print-summary (spaces totals
)
413 (let ((summary (make-hash-table :test
'eq
)))
414 (dolist (space-total totals
)
415 (dolist (total (cdr space-total
))
416 (push (cons (car space-total
) total
)
417 (gethash (third total
) summary
))))
419 (collect ((summary-totals))
420 (maphash (lambda (k v
)
423 (declare (unsigned-byte sum
))
424 (dolist (space-total v
)
425 (incf sum
(first (cdr space-total
))))
426 (summary-totals (cons sum v
))))
429 (format t
"~2&Summary of spaces: ~(~{~A ~}~)~%" spaces
)
430 (let ((summary-total-bytes 0)
431 (summary-total-objects 0))
432 (declare (unsigned-byte summary-total-bytes summary-total-objects
))
433 (dolist (space-totals
434 (mapcar #'cdr
(sort (summary-totals) #'> :key
#'car
)))
435 (let ((total-objects 0)
438 (declare (unsigned-byte total-objects total-bytes
))
440 (dolist (space-total space-totals
)
441 (let ((total (cdr space-total
)))
442 (setq name
(third total
))
443 (incf total-bytes
(first total
))
444 (incf total-objects
(second total
))
445 (spaces (cons (car space-total
) (first total
)))))
446 (format t
"~%~A:~% ~:D bytes, ~:D object~:P"
447 name total-bytes total-objects
)
448 (dolist (space (spaces))
449 (format t
", ~W% ~(~A~)"
450 (round (* (cdr space
) 100) total-bytes
)
453 (incf summary-total-bytes total-bytes
)
454 (incf summary-total-objects total-objects
))))
455 (format t
"~%Summary total:~% ~:D bytes, ~:D objects.~%"
456 summary-total-bytes summary-total-objects
)))))
458 ;;; Report object usage for a single space.
459 (defun report-space-total (space-total cutoff
)
460 (declare (list space-total
) (type (or single-float null
) cutoff
))
461 (format t
"~2&Breakdown for ~(~A~) space:~%" (car space-total
))
462 (let* ((types (cdr space-total
))
463 (total-bytes (reduce #'+ (mapcar #'first types
)))
464 (total-objects (reduce #'+ (mapcar #'second types
)))
465 (cutoff-point (if cutoff
466 (truncate (* (float total-bytes
) cutoff
))
469 (reported-objects 0))
470 (declare (unsigned-byte total-objects total-bytes cutoff-point reported-objects
472 (loop for
(bytes objects name
) in types do
473 (when (<= bytes cutoff-point
)
474 (format t
" ~10:D bytes for ~9:D other object~2:*~P.~%"
475 (- total-bytes reported-bytes
)
476 (- total-objects reported-objects
))
478 (incf reported-bytes bytes
)
479 (incf reported-objects objects
)
480 (format t
" ~10:D bytes for ~9:D ~(~A~) object~2:*~P.~%"
482 (format t
" ~10:D bytes for ~9:D ~(~A~) object~2:*~P (space total.)~%"
483 total-bytes total-objects
(car space-total
))))
485 ;;; Print information about the heap memory in use. PRINT-SPACES is a
486 ;;; list of the spaces to print detailed information for.
487 ;;; COUNT-SPACES is a list of the spaces to scan. For either one, T
488 ;;; means all spaces (i.e. :STATIC, :DYNAMIC and :READ-ONLY.) If
489 ;;; PRINT-SUMMARY is true, then summary information will be printed.
490 ;;; The defaults print only summary information for dynamic space. If
491 ;;; true, CUTOFF is a fraction of the usage in a report below which
492 ;;; types will be combined as OTHER.
493 (defun memory-usage (&key print-spaces
(count-spaces '(:dynamic
))
494 (print-summary t
) cutoff
)
495 (declare (type (or single-float null
) cutoff
))
496 (let* ((spaces (if (eq count-spaces t
)
497 '(:static
:dynamic
:read-only
)
499 (totals (mapcar (lambda (space)
500 (cons space
(type-breakdown space
)))
503 (dolist (space-total totals
)
504 (when (or (eq print-spaces t
)
505 (member (car space-total
) print-spaces
))
506 (report-space-total space-total cutoff
)))
508 (when print-summary
(print-summary spaces totals
)))
512 ;;; Print info about how much code and no-ops there are in SPACE.
513 (defun count-no-ops (space)
514 (declare (type spaces space
))
518 (declare (fixnum code-words no-ops
)
519 (type unsigned-byte total-bytes
))
520 (map-allocated-objects
521 (lambda (obj type size
)
522 (when (eql type code-header-widetag
)
523 (let ((words (truly-the fixnum
(%code-code-size obj
)))
524 (sap (%primitive code-instructions obj
))
526 (declare (fixnum size
))
527 (incf total-bytes size
)
528 (incf code-words words
)
530 (when (zerop (sap-ref-word sap
(* i n-word-bytes
)))
535 "~:D code-object bytes, ~:D code words, with ~:D no-ops (~D%).~%"
536 total-bytes code-words no-ops
537 (round (* no-ops
100) code-words
)))
541 (defun descriptor-vs-non-descriptor-storage (&rest spaces
)
542 (let ((descriptor-words 0)
543 (non-descriptor-headers 0)
544 (non-descriptor-bytes 0))
545 (declare (type unsigned-byte descriptor-words non-descriptor-headers
546 non-descriptor-bytes
))
547 (dolist (space (or spaces
'(:read-only
:static
:dynamic
)))
548 (declare (inline map-allocated-objects
))
549 (map-allocated-objects
550 (lambda (obj type size
)
552 (#.code-header-widetag
553 (let ((inst-words (truly-the fixnum
(%code-code-size obj
)))
555 (declare (type fixnum size inst-words
))
556 (incf non-descriptor-bytes
(* inst-words n-word-bytes
))
557 (incf descriptor-words
558 (- (truncate size n-word-bytes
) inst-words
))))
560 #.single-float-widetag
561 #.double-float-widetag
562 #.simple-base-string-widetag
563 #!+sb-unicode
#.simple-character-string-widetag
564 #.simple-array-nil-widetag
565 #.simple-bit-vector-widetag
566 #.simple-array-unsigned-byte-2-widetag
567 #.simple-array-unsigned-byte-4-widetag
568 #.simple-array-unsigned-byte-8-widetag
569 #.simple-array-unsigned-byte-16-widetag
570 #.simple-array-unsigned-byte-32-widetag
571 #.simple-array-signed-byte-8-widetag
572 #.simple-array-signed-byte-16-widetag
573 ;; #.simple-array-signed-byte-30-widetag
574 #.simple-array-signed-byte-32-widetag
575 #.simple-array-single-float-widetag
576 #.simple-array-double-float-widetag
577 #.simple-array-complex-single-float-widetag
578 #.simple-array-complex-double-float-widetag
)
579 (incf non-descriptor-headers
)
580 (incf non-descriptor-bytes
(- size n-word-bytes
)))
581 ((#.list-pointer-lowtag
582 #.instance-pointer-lowtag
585 #.simple-array-widetag
586 #.simple-vector-widetag
587 #.complex-base-string-widetag
588 #.complex-vector-nil-widetag
589 #.complex-bit-vector-widetag
590 #.complex-vector-widetag
591 #.complex-array-widetag
592 #.closure-header-widetag
593 #.funcallable-instance-header-widetag
594 #.value-cell-header-widetag
595 #.symbol-header-widetag
597 #.weak-pointer-widetag
598 #.instance-header-widetag
)
599 (incf descriptor-words
(truncate (the fixnum size
) n-word-bytes
)))
601 (error "bogus widetag: ~W" type
))))
603 (format t
"~:D words allocated for descriptor objects.~%"
605 (format t
"~:D bytes data/~:D words header for non-descriptor objects.~%"
606 non-descriptor-bytes non-descriptor-headers
)
609 ;;; Print a breakdown by instance type of all the instances allocated
610 ;;; in SPACE. If TOP-N is true, print only information for the
611 ;;; TOP-N types with largest usage.
612 (defun instance-usage (space &key
(top-n 15))
613 (declare (type spaces space
) (type (or fixnum null
) top-n
))
614 (format t
"~2&~@[Top ~W ~]~(~A~) instance types:~%" top-n space
)
615 (let ((totals (make-hash-table :test
'eq
))
618 (declare (unsigned-byte total-objects total-bytes
))
619 (map-allocated-objects
620 (lambda (obj type size
)
621 (declare (optimize (speed 3)))
622 (when (eql type instance-header-widetag
)
624 (let* ((classoid (layout-classoid (%instance-ref obj
0)))
625 (found (gethash classoid totals
))
627 (declare (fixnum size
))
628 (incf total-bytes size
)
630 (incf (the fixnum
(car found
)))
631 (incf (the fixnum
(cdr found
)) size
))
633 (setf (gethash classoid totals
) (cons 1 size
)))))))
636 (collect ((totals-list))
637 (maphash (lambda (classoid what
)
638 (totals-list (cons (prin1-to-string
639 (classoid-proper-name classoid
))
642 (let ((sorted (sort (totals-list) #'> :key
#'cddr
))
645 (declare (unsigned-byte printed-bytes printed-objects
))
646 (dolist (what (if top-n
647 (subseq sorted
0 (min (length sorted
) top-n
))
649 (let ((bytes (cddr what
))
650 (objects (cadr what
)))
651 (incf printed-bytes bytes
)
652 (incf printed-objects objects
)
653 (format t
" ~A: ~:D bytes, ~:D object~:P.~%" (car what
)
656 (let ((residual-objects (- total-objects printed-objects
))
657 (residual-bytes (- total-bytes printed-bytes
)))
658 (unless (zerop residual-objects
)
659 (format t
" Other types: ~:D bytes, ~:D object~:P.~%"
660 residual-bytes residual-objects
))))
662 (format t
" ~:(~A~) instance total: ~:D bytes, ~:D object~:P.~%"
663 space total-bytes total-objects
)))
667 ;;;; PRINT-ALLOCATED-OBJECTS
669 (defun print-allocated-objects (space &key
(percent 0) (pages 5)
670 type larger smaller count
671 (stream *standard-output
*))
672 (declare (type (integer 0 99) percent
) (type index pages
)
673 (type stream stream
) (type spaces space
)
674 (type (or index null
) type larger smaller count
))
675 (multiple-value-bind (start-sap end-sap
) (space-bounds space
)
676 (let* ((space-start (sap-int start-sap
))
677 (space-end (sap-int end-sap
))
678 (space-size (- space-end space-start
))
679 (pagesize (sb!sys
:get-page-size
))
680 (start (+ space-start
(round (* space-size percent
) 100)))
681 (printed-conses (make-hash-table :test
'eq
))
685 (declare (type (unsigned-byte 32) last-page start
)
686 (fixnum pages-so-far count-so-far pagesize
))
687 (labels ((note-conses (x)
688 (unless (or (atom x
) (gethash x printed-conses
))
689 (setf (gethash x printed-conses
) t
)
690 (note-conses (car x
))
691 (note-conses (cdr x
)))))
692 (map-allocated-objects
693 (lambda (obj obj-type size
)
694 (let ((addr (get-lisp-obj-address obj
)))
695 (when (>= addr start
)
697 (> count-so-far count
)
698 (> pages-so-far pages
))
699 (return-from print-allocated-objects
(values)))
702 (let ((this-page (* (the (values (unsigned-byte 32) t
)
703 (truncate addr pagesize
))
705 (declare (type (unsigned-byte 32) this-page
))
706 (when (/= this-page last-page
)
707 (when (< pages-so-far pages
)
708 ;; FIXME: What is this? (ERROR "Argh..")? or
709 ;; a warning? or code that can be removed
710 ;; once the system is stable? or what?
711 (format stream
"~2&**** Page ~W, address ~X:~%"
713 (setq last-page this-page
)
714 (incf pages-so-far
))))
716 (when (and (or (not type
) (eql obj-type type
))
717 (or (not smaller
) (<= size smaller
))
718 (or (not larger
) (>= size larger
)))
721 (#.code-header-widetag
722 (let ((dinfo (%code-debug-info obj
)))
723 (format stream
"~&Code object: ~S~%"
725 (sb!c
::compiled-debug-info-name dinfo
)
727 (#.symbol-header-widetag
728 (format stream
"~&~S~%" obj
))
729 (#.list-pointer-lowtag
730 (unless (gethash obj printed-conses
)
732 (let ((*print-circle
* t
)
735 (format stream
"~&~S~%" obj
))))
738 (let ((str (write-to-string obj
:level
5 :length
10
740 (unless (eql type instance-header-widetag
)
741 (format stream
"~S: " (type-of obj
)))
742 (format stream
"~A~%"
743 (subseq str
0 (min (length str
) 60))))))))))
747 ;;;; LIST-ALLOCATED-OBJECTS, LIST-REFERENCING-OBJECTS
749 (defvar *ignore-after
* nil
)
751 (defun valid-obj (space x
)
752 (or (not (eq space
:dynamic
))
753 ;; this test looks bogus if the allocator doesn't work linearly,
754 ;; which I suspect is the case for GENCGC. -- CSR, 2004-06-29
755 (< (get-lisp-obj-address x
) (get-lisp-obj-address *ignore-after
*))))
757 (defun maybe-cons (space x stuff
)
758 (if (valid-obj space x
)
762 (defun list-allocated-objects (space &key type larger smaller count
764 (declare (type spaces space
)
765 (type (or index null
) larger smaller type count
)
766 (type (or function null
) test
)
767 (inline map-allocated-objects
))
768 (unless *ignore-after
*
769 (setq *ignore-after
* (cons 1 2)))
770 (collect ((counted 0 1+))
772 (map-allocated-objects
773 (lambda (obj obj-type size
)
774 (when (and (or (not type
) (eql obj-type type
))
775 (or (not smaller
) (<= size smaller
))
776 (or (not larger
) (>= size larger
))
777 (or (not test
) (funcall test obj
)))
778 (setq res
(maybe-cons space obj res
))
779 (when (and count
(>= (counted) count
))
780 (return-from list-allocated-objects res
))))
784 (defun map-referencing-objects (fun space object
)
785 (declare (type spaces space
) (inline map-allocated-objects
))
786 (unless *ignore-after
*
787 (setq *ignore-after
* (cons 1 2)))
788 (flet ((maybe-call (fun obj
)
789 (when (valid-obj space obj
)
791 (map-allocated-objects
792 (lambda (obj obj-type size
)
793 (declare (ignore obj-type size
))
796 (when (or (eq (car obj
) object
)
797 (eq (cdr obj
) object
))
798 (maybe-call fun obj
)))
800 (dotimes (i (%instance-length obj
))
801 (when (eq (%instance-ref obj i
) object
)
805 (let ((length (get-header-data obj
)))
806 (do ((i code-constants-offset
(1+ i
)))
808 (when (eq (code-header-ref obj i
) object
)
812 (dotimes (i (length obj
))
813 (when (eq (svref obj i
) object
)
817 (when (or (eq (symbol-name obj
) object
)
818 (eq (symbol-package obj
) object
)
819 (eq (symbol-plist obj
) object
)
821 (eq (symbol-value obj
) object
)))
822 (maybe-call fun obj
)))))
825 (defun list-referencing-objects (space object
)
827 (map-referencing-objects
828 (lambda (obj) (res obj
)) space object
)