803e1e7eb2cf0a3d633cb6894304a5dca275c7ed
[sbcl.git] / src / code / room.lisp
1 ;;;; heap-grovelling memory usage stuff
2
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.
11
12 (in-package "SB!VM")
13 \f
14 ;;;; type format database
15
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)
21     (kind (missing-arg)
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))))
26
27 (eval-when (:compile-toplevel :execute)
28
29 (defvar *meta-room-info* (make-array 256 :initial-element nil))
30
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)))
37     (cond
38      ((not lowtag))
39      (;; KLUDGE described in dan_b message "Another one for the
40       ;; collection [bug 108]" (sbcl-devel 2004-01-22)
41       ;;
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.]
47       ;;
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.
53       ;;
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.
60       (eql name 'thread))
61      ((not widetag)
62       (let ((info (make-room-info :name name
63                                   :kind :lowtag))
64             (lowtag (symbol-value lowtag)))
65         (declare (fixnum lowtag))
66         (dotimes (i 32)
67           (setf (svref *meta-room-info* (logior lowtag (ash i 3))) info))))
68      (variable)
69      (t
70       (setf (svref *meta-room-info* (symbol-value widetag))
71             (make-room-info :name name
72                             :kind :fixed
73                             :length size))))))
74
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
81                         :kind :header)))
82
83 (setf (svref *meta-room-info* bignum-widetag)
84       (make-room-info :name 'bignum
85                       :kind :header))
86
87 (setf (svref *meta-room-info* closure-header-widetag)
88       (make-room-info :name 'closure
89                       :kind :closure))
90
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))
118          (size (cdr stuff))
119          (sname (string name)))
120     (when (boundp name)
121       (setf (svref *meta-room-info* (symbol-value name))
122             (make-room-info :name (intern (subseq sname
123                                                   0
124                                                   (mismatch sname "-WIDETAG"
125                                                             :from-end t)))
126                             :kind :vector
127                             :length size)))))
128
129 (setf (svref *meta-room-info* simple-base-string-widetag)
130       (make-room-info :name 'simple-base-string
131                       :kind :string
132                       :length 0))
133
134 #!+sb-unicode
135 (setf (svref *meta-room-info* simple-character-string-widetag)
136       (make-room-info :name 'simple-character-string
137                       :kind :string
138                       :length 2))
139
140 (setf (svref *meta-room-info* simple-array-nil-widetag)
141       (make-room-info :name 'simple-array-nil
142                       :kind :fixed
143                       :length 2))
144
145 (setf (svref *meta-room-info* code-header-widetag)
146       (make-room-info :name 'code
147                       :kind :code))
148
149 (setf (svref *meta-room-info* instance-header-widetag)
150       (make-room-info :name 'instance
151                       :kind :instance))
152
153 ) ; EVAL-WHEN
154
155 (defparameter *room-info* '#.*meta-room-info*)
156 (deftype spaces () '(member :static :dynamic :read-only))
157 \f
158 ;;;; MAP-ALLOCATED-OBJECTS
159
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*))
165
166 (defun space-bounds (space)
167   (declare (type spaces space))
168   (ecase space
169     (:static
170      (values (int-sap static-space-start)
171              (int-sap (* *static-space-free-pointer* n-word-bytes))))
172     (:read-only
173      (values (int-sap read-only-space-start)
174              (int-sap (* *read-only-space-free-pointer* n-word-bytes))))
175     (:dynamic
176      (values (int-sap (current-dynamic-space-start))
177              (dynamic-space-free-pointer)))))
178
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))))
183
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   (declare (unsigned-byte size))
190   (ldb (byte n-word-bits 0)
191        (logand (+ size lowtag-mask) (lognot lowtag-mask))))
192
193 ;;; Return the total size of a vector in bytes, including any pad.
194 #!-sb-fluid (declaim (inline vector-total-size))
195 (defun vector-total-size (obj info)
196   (declare (type (simple-array * (*)) obj))
197   (let ((shift (room-info-length info))
198         (len (+ (length obj)
199                 (ecase (room-info-kind info)
200                   (:vector 0)
201                   (:string 1)))))
202     (round-to-dualword
203      (+ (* vector-data-offset n-word-bytes)
204         (if (minusp shift)
205             (ash (+ len (1- (ash 1 (- shift))))
206                  shift)
207             (ash len shift))))))
208
209 ;;; Access to the GENCGC page table for better precision in
210 ;;; MAP-ALLOCATED-OBJECTS
211 #!+gencgc
212 (progn
213   (define-alien-type (struct page)
214       (struct page
215               (start long)
216               ;; On platforms with small enough GC pages, this field
217               ;; will be a short. On platforms with larger ones, it'll
218               ;; be an int.
219               (bytes-used (unsigned
220                            #.(if (typep sb!vm:gencgc-page-size
221                                         '(unsigned-byte 16))
222                                  16
223                                  32)))
224               (flags (unsigned 8))
225               (gen (signed 8))))
226   (declaim (inline find-page-index))
227   (define-alien-routine "find_page_index" long (index long))
228   (define-alien-variable "page_table" (* (struct page))))
229
230 ;;; Iterate over all the objects allocated in SPACE, calling FUN with
231 ;;; the object, the object's type code, and the object's total size in
232 ;;; bytes, including any header and padding.
233 #!-sb-fluid (declaim (maybe-inline map-allocated-objects))
234 (defun map-allocated-objects (fun space)
235   (declare (type function fun) (type spaces space))
236   (without-gcing
237    (multiple-value-bind (start end) (space-bounds space)
238      (declare (type system-area-pointer start end))
239      (declare (optimize (speed 3)))
240      (let ((current start)
241            #!+gencgc (skip-tests-until-addr 0))
242        (labels ((maybe-finish-mapping ()
243                   (unless (sap< current end)
244                     (aver (sap= current end))
245                     (return-from map-allocated-objects)))
246                 ;; GENCGC doesn't allocate linearly, which means that the
247                 ;; dynamic space can contain large blocks zeros that get
248                 ;; accounted as conses in ROOM (and slow down other
249                 ;; applications of MAP-ALLOCATED-OBJECTS). To fix this
250                 ;; check the GC page structure for the current address.
251                 ;; If the page is free or the address is beyond the page-
252                 ;; internal allocation offset (bytes-used) skip to the
253                 ;; next page immediately.
254                 (maybe-skip-page ()
255                   #!+gencgc
256                   (when (eq space :dynamic)
257                     (loop with page-mask = #.(1- sb!vm:gencgc-page-size)
258                           for addr of-type sb!vm:word = (sap-int current)
259                           while (>= addr skip-tests-until-addr)
260                           do
261                           ;; For some reason binding PAGE with LET
262                           ;; conses like mad (but gives no compiler notes...)
263                           ;; Work around the problem with SYMBOL-MACROLET
264                           ;; instead of trying to figure out the real
265                           ;; issue. -- JES, 2005-05-17
266                           (symbol-macrolet
267                               ((page (deref page-table
268                                             (find-page-index addr))))
269                             ;; Don't we have any nicer way to access C struct
270                             ;; bitfields?
271                             (let ((alloc-flag (ldb (byte 3 2)
272                                                    (slot page 'flags)))
273                                   (bytes-used (slot page 'bytes-used)))
274                               ;; If the page is not free and the current
275                               ;; pointer is still below the allocation offset
276                               ;; of the page
277                               (when (and (not (zerop alloc-flag))
278                                          (<= (logand page-mask addr)
279                                              bytes-used))
280                                 ;; Don't bother testing again until we
281                                 ;; get past that allocation offset
282                                 (setf skip-tests-until-addr
283                                       (+ (logandc2 addr page-mask) bytes-used))
284                                 ;; And then continue with the scheduled
285                                 ;; mapping
286                                 (return-from maybe-skip-page))
287                               ;; Move CURRENT to start of next page
288                               (setf current (int-sap (+ (logandc2 addr page-mask)
289                                                         sb!vm:gencgc-page-size)))
290                               (maybe-finish-mapping)))))))
291          (declare (inline maybe-finish-mapping maybe-skip-page))
292          (loop
293              (maybe-finish-mapping)
294              (maybe-skip-page)
295            (let* ((header (sap-ref-word current 0))
296                   (header-widetag (logand header #xFF))
297                   (info (svref *room-info* header-widetag)))
298              (cond
299                ((or (not info)
300                     (eq (room-info-kind info) :lowtag))
301                 (let ((size (* cons-size n-word-bytes)))
302                   (funcall fun
303                            (make-lisp-obj (logior (sap-int current)
304                                                   list-pointer-lowtag))
305                            list-pointer-lowtag
306                            size)
307                   (setq current (sap+ current size))))
308                ((eql header-widetag closure-header-widetag)
309                 (let* ((obj (make-lisp-obj (logior (sap-int current)
310                                                    fun-pointer-lowtag)))
311                        (size (round-to-dualword
312                               (* (1+ (get-closure-length obj)) n-word-bytes))))
313                   (funcall fun obj header-widetag size)
314                   (setq current (sap+ current size))))
315                ((eq (room-info-kind info) :instance)
316                 (let* ((obj (make-lisp-obj
317                              (logior (sap-int current) instance-pointer-lowtag)))
318                        (size (round-to-dualword
319                               (* (+ (%instance-length obj) 1) n-word-bytes))))
320                   (declare (fixnum size))
321                   (funcall fun obj header-widetag size)
322                   (aver (zerop (logand size lowtag-mask)))
323                   (setq current (sap+ current size))))
324                (t
325                 (let* ((obj (make-lisp-obj
326                              (logior (sap-int current) other-pointer-lowtag)))
327                        (size (ecase (room-info-kind info)
328                                (:fixed
329                                 (aver (or (eql (room-info-length info)
330                                                (1+ (get-header-data obj)))
331                                           (floatp obj)
332                                           (simple-array-nil-p obj)))
333                                 (round-to-dualword
334                                  (* (room-info-length info) n-word-bytes)))
335                                ((:vector :string)
336                                 (vector-total-size obj info))
337                                (:header
338                                 (round-to-dualword
339                                  (* (1+ (get-header-data obj)) n-word-bytes)))
340                                (:code
341                                 (+ (* (get-header-data obj) n-word-bytes)
342                                    (round-to-dualword
343                                     (* (%code-code-size obj) n-word-bytes)))))))
344                   (funcall fun obj header-widetag size)
345                   (aver (zerop (logand size lowtag-mask)))
346                   (setq current (sap+ current size))))))))))))
347
348 \f
349 ;;;; MEMORY-USAGE
350
351 ;;; Return a list of 3-lists (bytes object type-name) for the objects
352 ;;; allocated in Space.
353 (defun type-breakdown (space)
354   (let ((sizes (make-array 256 :initial-element 0))
355         (counts (make-array 256 :initial-element 0 :element-type 'fixnum)))
356     (map-allocated-objects
357      (lambda (obj type size)
358        (declare (index type) (optimize (speed 3)) (ignore obj))
359        (incf (aref sizes type) size)
360        (incf (aref counts type)))
361      space)
362
363     (let ((totals (make-hash-table :test 'eq)))
364       (dotimes (i 256)
365         (let ((total-count (aref counts i)))
366           (unless (zerop total-count)
367             (let* ((total-size (aref sizes i))
368                    (name (room-info-name (aref *room-info* i)))
369                    (found (gethash name totals)))
370               (cond (found
371                      (incf (first found) total-size)
372                      (incf (second found) total-count))
373                     (t
374                      (setf (gethash name totals)
375                            (list total-size total-count name))))))))
376
377       (collect ((totals-list))
378         (maphash (lambda (k v)
379                    (declare (ignore k))
380                    (totals-list v))
381                  totals)
382         (sort (totals-list) #'> :key #'first)))))
383
384 ;;; Handle the summary printing for MEMORY-USAGE. Totals is a list of lists
385 ;;; (space-name . totals-for-space), where totals-for-space is the list
386 ;;; returned by TYPE-BREAKDOWN.
387 (defun print-summary (spaces totals)
388   (let ((summary (make-hash-table :test 'eq)))
389     (dolist (space-total totals)
390       (dolist (total (cdr space-total))
391         (push (cons (car space-total) total)
392               (gethash (third total) summary))))
393
394     (collect ((summary-totals))
395       (maphash (lambda (k v)
396                  (declare (ignore k))
397                  (let ((sum 0))
398                    (dolist (space-total v)
399                      (incf sum (first (cdr space-total))))
400                    (summary-totals (cons sum v))))
401                summary)
402
403       (format t "~2&Summary of spaces: ~(~{~A ~}~)~%" spaces)
404       (let ((summary-total-bytes 0)
405             (summary-total-objects 0))
406         (declare (fixnum summary-total-objects))
407         (dolist (space-totals
408                  (mapcar #'cdr (sort (summary-totals) #'> :key #'car)))
409           (let ((total-objects 0)
410                 (total-bytes 0)
411                 name)
412             (declare (fixnum total-objects))
413             (collect ((spaces))
414               (dolist (space-total space-totals)
415                 (let ((total (cdr space-total)))
416                   (setq name (third total))
417                   (incf total-bytes (first total))
418                   (incf total-objects (second total))
419                   (spaces (cons (car space-total) (first total)))))
420               (format t "~%~A:~%    ~:D bytes, ~:D object~:P"
421                       name total-bytes total-objects)
422               (dolist (space (spaces))
423                 (format t ", ~W% ~(~A~)"
424                         (round (* (cdr space) 100) total-bytes)
425                         (car space)))
426               (format t ".~%")
427               (incf summary-total-bytes total-bytes)
428               (incf summary-total-objects total-objects))))
429         (format t "~%Summary total:~%    ~:D bytes, ~:D objects.~%"
430                 summary-total-bytes summary-total-objects)))))
431
432 ;;; Report object usage for a single space.
433 (defun report-space-total (space-total cutoff)
434   (declare (list space-total) (type (or single-float null) cutoff))
435   (format t "~2&Breakdown for ~(~A~) space:~%" (car space-total))
436   (let* ((types (cdr space-total))
437          (total-bytes (reduce #'+ (mapcar #'first types)))
438          (total-objects (reduce #'+ (mapcar #'second types)))
439          (cutoff-point (if cutoff
440                            (truncate (* (float total-bytes) cutoff))
441                            0))
442          (reported-bytes 0)
443          (reported-objects 0))
444     (declare (fixnum total-objects cutoff-point reported-objects))
445     (loop for (bytes objects name) in types do
446       (when (<= bytes cutoff-point)
447         (format t "  ~10:D bytes for ~9:D other object~2:*~P.~%"
448                 (- total-bytes reported-bytes)
449                 (- total-objects reported-objects))
450         (return))
451       (incf reported-bytes bytes)
452       (incf reported-objects objects)
453       (format t "  ~10:D bytes for ~9:D ~(~A~) object~2:*~P.~%"
454               bytes objects name))
455     (format t "  ~10:D bytes for ~9:D ~(~A~) object~2:*~P (space total.)~%"
456             total-bytes total-objects (car space-total))))
457
458 ;;; Print information about the heap memory in use. PRINT-SPACES is a
459 ;;; list of the spaces to print detailed information for.
460 ;;; COUNT-SPACES is a list of the spaces to scan. For either one, T
461 ;;; means all spaces (i.e. :STATIC, :DYNAMIC and :READ-ONLY.) If
462 ;;; PRINT-SUMMARY is true, then summary information will be printed.
463 ;;; The defaults print only summary information for dynamic space. If
464 ;;; true, CUTOFF is a fraction of the usage in a report below which
465 ;;; types will be combined as OTHER.
466 (defun memory-usage (&key print-spaces (count-spaces '(:dynamic))
467                           (print-summary t) cutoff)
468   (declare (type (or single-float null) cutoff))
469   (let* ((spaces (if (eq count-spaces t)
470                      '(:static :dynamic :read-only)
471                      count-spaces))
472          (totals (mapcar (lambda (space)
473                            (cons space (type-breakdown space)))
474                          spaces)))
475
476     (dolist (space-total totals)
477       (when (or (eq print-spaces t)
478                 (member (car space-total) print-spaces))
479         (report-space-total space-total cutoff)))
480
481     (when print-summary (print-summary spaces totals)))
482
483   (values))
484 \f
485 ;;; Print info about how much code and no-ops there are in SPACE.
486 (defun count-no-ops (space)
487   (declare (type spaces space))
488   (let ((code-words 0)
489         (no-ops 0)
490         (total-bytes 0))
491     (declare (fixnum code-words no-ops)
492              (type unsigned-byte total-bytes))
493     (map-allocated-objects
494      (lambda (obj type size)
495        (declare (fixnum size))
496        (when (eql type code-header-widetag)
497          (incf total-bytes size)
498          (let ((words (%code-code-size obj))
499                (sap (code-instructions obj)))
500            (incf code-words words)
501            (dotimes (i words)
502              (when (zerop (sap-ref-word sap (* i n-word-bytes)))
503                (incf no-ops))))))
504      space)
505
506     (format t
507             "~:D code-object bytes, ~:D code words, with ~:D no-ops (~D%).~%"
508             total-bytes code-words no-ops
509             (round (* no-ops 100) code-words)))
510
511   (values))
512 \f
513 (defun descriptor-vs-non-descriptor-storage (&rest spaces)
514   (let ((descriptor-words 0)
515         (non-descriptor-headers 0)
516         (non-descriptor-bytes 0))
517     (declare (type unsigned-byte descriptor-words non-descriptor-headers
518                    non-descriptor-bytes))
519     (dolist (space (or spaces '(:read-only :static :dynamic)))
520       (declare (inline map-allocated-objects))
521       (map-allocated-objects
522        (lambda (obj type size)
523          (declare (fixnum size))
524          (case type
525            (#.code-header-widetag
526             (let ((inst-words (%code-code-size obj)))
527               (declare (type fixnum inst-words))
528               (incf non-descriptor-bytes (* inst-words n-word-bytes))
529               (incf descriptor-words
530                     (- (truncate size n-word-bytes) inst-words))))
531            ((#.bignum-widetag
532              #.single-float-widetag
533              #.double-float-widetag
534              #.simple-base-string-widetag
535              #!+sb-unicode #.simple-character-string-widetag
536              #.simple-array-nil-widetag
537              #.simple-bit-vector-widetag
538              #.simple-array-unsigned-byte-2-widetag
539              #.simple-array-unsigned-byte-4-widetag
540              #.simple-array-unsigned-byte-8-widetag
541              #.simple-array-unsigned-byte-16-widetag
542              #.simple-array-unsigned-byte-32-widetag
543              #.simple-array-signed-byte-8-widetag
544              #.simple-array-signed-byte-16-widetag
545              ; #.simple-array-signed-byte-30-widetag
546              #.simple-array-signed-byte-32-widetag
547              #.simple-array-single-float-widetag
548              #.simple-array-double-float-widetag
549              #.simple-array-complex-single-float-widetag
550              #.simple-array-complex-double-float-widetag)
551             (incf non-descriptor-headers)
552             (incf non-descriptor-bytes (- size n-word-bytes)))
553            ((#.list-pointer-lowtag
554              #.instance-pointer-lowtag
555              #.ratio-widetag
556              #.complex-widetag
557              #.simple-array-widetag
558              #.simple-vector-widetag
559              #.complex-base-string-widetag
560              #.complex-vector-nil-widetag
561              #.complex-bit-vector-widetag
562              #.complex-vector-widetag
563              #.complex-array-widetag
564              #.closure-header-widetag
565              #.funcallable-instance-header-widetag
566              #.value-cell-header-widetag
567              #.symbol-header-widetag
568              #.sap-widetag
569              #.weak-pointer-widetag
570              #.instance-header-widetag)
571             (incf descriptor-words (truncate size n-word-bytes)))
572            (t
573             (error "bogus widetag: ~W" type))))
574        space))
575     (format t "~:D words allocated for descriptor objects.~%"
576             descriptor-words)
577     (format t "~:D bytes data/~:D words header for non-descriptor objects.~%"
578             non-descriptor-bytes non-descriptor-headers)
579     (values)))
580 \f
581 ;;; Print a breakdown by instance type of all the instances allocated
582 ;;; in SPACE. If TOP-N is true, print only information for the
583 ;;; TOP-N types with largest usage.
584 (defun instance-usage (space &key (top-n 15))
585   (declare (type spaces space) (type (or fixnum null) top-n))
586   (format t "~2&~@[Top ~W ~]~(~A~) instance types:~%" top-n space)
587   (let ((totals (make-hash-table :test 'eq))
588         (total-objects 0)
589         (total-bytes 0))
590     (declare (fixnum total-objects))
591     (map-allocated-objects
592      (lambda (obj type size)
593        (declare (fixnum size) (optimize (speed 3)))
594        (when (eql type instance-header-widetag)
595          (incf total-objects)
596          (incf total-bytes size)
597          (let* ((classoid (layout-classoid (%instance-ref obj 0)))
598                 (found (gethash classoid totals)))
599            (cond (found
600                   (incf (car found))
601                   (incf (cdr found) size))
602                  (t
603                   (setf (gethash classoid totals) (cons 1 size)))))))
604      space)
605
606     (collect ((totals-list))
607       (maphash (lambda (classoid what)
608                  (totals-list (cons (prin1-to-string
609                                      (classoid-proper-name classoid))
610                                     what)))
611                totals)
612       (let ((sorted (sort (totals-list) #'> :key #'cddr))
613             (printed-bytes 0)
614             (printed-objects 0))
615         (declare (fixnum printed-objects))
616         (dolist (what (if top-n
617                           (subseq sorted 0 (min (length sorted) top-n))
618                           sorted))
619           (let ((bytes (cddr what))
620                 (objects (cadr what)))
621             (incf printed-bytes bytes)
622             (incf printed-objects objects)
623             (format t "  ~A: ~:D bytes, ~:D object~:P.~%" (car what)
624                     bytes objects)))
625
626         (let ((residual-objects (- total-objects printed-objects))
627               (residual-bytes (- total-bytes printed-bytes)))
628           (unless (zerop residual-objects)
629             (format t "  Other types: ~:D bytes, ~:D object~:P.~%"
630                     residual-bytes residual-objects))))
631
632       (format t "  ~:(~A~) instance total: ~:D bytes, ~:D object~:P.~%"
633               space total-bytes total-objects)))
634
635   (values))
636 \f
637 ;;;; PRINT-ALLOCATED-OBJECTS
638
639 (defun print-allocated-objects (space &key (percent 0) (pages 5)
640                                       type larger smaller count
641                                       (stream *standard-output*))
642   (declare (type (integer 0 99) percent) (type index pages)
643            (type stream stream) (type spaces space)
644            (type (or index null) type larger smaller count))
645   (multiple-value-bind (start-sap end-sap) (space-bounds space)
646     (let* ((space-start (sap-int start-sap))
647            (space-end (sap-int end-sap))
648            (space-size (- space-end space-start))
649            (pagesize (sb!sys:get-page-size))
650            (start (+ space-start (round (* space-size percent) 100)))
651            (printed-conses (make-hash-table :test 'eq))
652            (pages-so-far 0)
653            (count-so-far 0)
654            (last-page 0))
655       (declare (type (unsigned-byte 32) last-page start)
656                (fixnum pages-so-far count-so-far pagesize))
657       (labels ((note-conses (x)
658                  (unless (or (atom x) (gethash x printed-conses))
659                    (setf (gethash x printed-conses) t)
660                    (note-conses (car x))
661                    (note-conses (cdr x)))))
662         (map-allocated-objects
663          (lambda (obj obj-type size)
664            (let ((addr (get-lisp-obj-address obj)))
665              (when (>= addr start)
666                (when (if count
667                          (> count-so-far count)
668                          (> pages-so-far pages))
669                  (return-from print-allocated-objects (values)))
670
671                (unless count
672                  (let ((this-page (* (truncate addr pagesize) pagesize)))
673                    (declare (type (unsigned-byte 32) this-page))
674                    (when (/= this-page last-page)
675                      (when (< pages-so-far pages)
676                        ;; FIXME: What is this? (ERROR "Argh..")? or
677                        ;; a warning? or code that can be removed
678                        ;; once the system is stable? or what?
679                        (format stream "~2&**** Page ~W, address ~X:~%"
680                                pages-so-far addr))
681                      (setq last-page this-page)
682                      (incf pages-so-far))))
683
684                (when (and (or (not type) (eql obj-type type))
685                           (or (not smaller) (<= size smaller))
686                           (or (not larger) (>= size larger)))
687                  (incf count-so-far)
688                  (case type
689                    (#.code-header-widetag
690                     (let ((dinfo (%code-debug-info obj)))
691                       (format stream "~&Code object: ~S~%"
692                               (if dinfo
693                                   (sb!c::compiled-debug-info-name dinfo)
694                                   "No debug info."))))
695                    (#.symbol-header-widetag
696                     (format stream "~&~S~%" obj))
697                    (#.list-pointer-lowtag
698                     (unless (gethash obj printed-conses)
699                       (note-conses obj)
700                       (let ((*print-circle* t)
701                             (*print-level* 5)
702                             (*print-length* 10))
703                         (format stream "~&~S~%" obj))))
704                    (t
705                     (fresh-line stream)
706                     (let ((str (write-to-string obj :level 5 :length 10
707                                                 :pretty nil)))
708                       (unless (eql type instance-header-widetag)
709                         (format stream "~S: " (type-of obj)))
710                       (format stream "~A~%"
711                               (subseq str 0 (min (length str) 60))))))))))
712          space))))
713   (values))
714 \f
715 ;;;; LIST-ALLOCATED-OBJECTS, LIST-REFERENCING-OBJECTS
716
717 (defvar *ignore-after* nil)
718
719 (defun valid-obj (space x)
720   (or (not (eq space :dynamic))
721       ;; this test looks bogus if the allocator doesn't work linearly,
722       ;; which I suspect is the case for GENCGC.  -- CSR, 2004-06-29
723       (< (get-lisp-obj-address x) (get-lisp-obj-address *ignore-after*))))
724
725 (defun maybe-cons (space x stuff)
726   (if (valid-obj space x)
727       (cons x stuff)
728       stuff))
729
730 (defun list-allocated-objects (space &key type larger smaller count
731                                      test)
732   (declare (type spaces space)
733            (type (or index null) larger smaller type count)
734            (type (or function null) test)
735            (inline map-allocated-objects))
736   (unless *ignore-after*
737     (setq *ignore-after* (cons 1 2)))
738   (collect ((counted 0 1+))
739     (let ((res ()))
740       (map-allocated-objects
741        (lambda (obj obj-type size)
742          (when (and (or (not type) (eql obj-type type))
743                     (or (not smaller) (<= size smaller))
744                     (or (not larger) (>= size larger))
745                     (or (not test) (funcall test obj)))
746            (setq res (maybe-cons space obj res))
747            (when (and count (>= (counted) count))
748              (return-from list-allocated-objects res))))
749        space)
750       res)))
751
752 (defun map-referencing-objects (fun space object)
753   (declare (type spaces space) (inline map-allocated-objects))
754   (unless *ignore-after*
755     (setq *ignore-after* (cons 1 2)))
756   (flet ((maybe-call (fun obj)
757            (when (valid-obj space obj)
758              (funcall fun obj))))
759     (map-allocated-objects
760      (lambda (obj obj-type size)
761        (declare (ignore obj-type size))
762        (typecase obj
763          (cons
764           (when (or (eq (car obj) object)
765                     (eq (cdr obj) object))
766             (maybe-call fun obj)))
767          (instance
768           (dotimes (i (%instance-length obj))
769             (when (eq (%instance-ref obj i) object)
770               (maybe-call fun obj)
771               (return))))
772          (code-component
773           (let ((length (get-header-data obj)))
774             (do ((i code-constants-offset (1+ i)))
775                 ((= i length))
776               (when (eq (code-header-ref obj i) object)
777                 (maybe-call fun obj)
778                 (return)))))
779          (simple-vector
780           (dotimes (i (length obj))
781             (when (eq (svref obj i) object)
782               (maybe-call fun obj)
783               (return))))
784          (symbol
785           (when (or (eq (symbol-name obj) object)
786                     (eq (symbol-package obj) object)
787                     (eq (symbol-plist obj) object)
788                     (eq (symbol-value obj) object))
789             (maybe-call fun obj)))))
790      space)))
791
792 (defun list-referencing-objects (space object)
793   (collect ((res))
794     (map-referencing-objects
795      (lambda (obj) (res obj)) space object)
796     (res)))