1 | ;;;-*-Mode: LISP; Package: ccl -*- |
---|
2 | ;;; |
---|
3 | ;;; Copyright (C) 2008, Clozure Associates and contributors |
---|
4 | ;;; This file is part of OpenMCL. |
---|
5 | ;;; |
---|
6 | ;;; OpenMCL is licensed under the terms of the Lisp Lesser GNU Public |
---|
7 | ;;; License , known as the LLGPL and distributed with OpenMCL as the |
---|
8 | ;;; file "LICENSE". The LLGPL consists of a preamble and the LGPL, |
---|
9 | ;;; which is distributed with OpenMCL as the file "LGPL". Where these |
---|
10 | ;;; conflict, the preamble takes precedence. |
---|
11 | ;;; |
---|
12 | ;;; OpenMCL is referenced in the preamble as the "LIBRARY." |
---|
13 | ;;; |
---|
14 | ;;; The LLGPL is also available online at |
---|
15 | ;;; http://opensource.franz.com/preamble.html |
---|
16 | |
---|
17 | ; leaks.lisp |
---|
18 | ; A few functions to help in finding memory leaks |
---|
19 | |
---|
20 | (in-package :ccl) |
---|
21 | |
---|
22 | ;; Returns all objects that satisfy predicate of one of the types in |
---|
23 | ;; ccl::*heap-utilization-vector-type-names* |
---|
24 | ;; Note that these can contain stack-consed objects that are dead. |
---|
25 | ;; Use pointer-in-some-dynamic-area-p to be sure to follow only real objects |
---|
26 | ;; (ccl::heap-utilization) prints a useful list of object counts and sizes |
---|
27 | ;; per type. |
---|
28 | (defun all-objects-of-type (type &optional predicate) |
---|
29 | (let ((typecode (position type ccl::*heap-utilization-vector-type-names*)) |
---|
30 | (res nil)) |
---|
31 | (when typecode |
---|
32 | (flet ((mapper (thing) |
---|
33 | (when (and (eq typecode (ccl::typecode thing)) |
---|
34 | (or (null predicate) (funcall predicate thing))) |
---|
35 | (push thing res)))) |
---|
36 | (declare (dynamic-extent #'mapper)) |
---|
37 | (ccl::%map-areas #'mapper)) |
---|
38 | res))) |
---|
39 | |
---|
40 | ;; Counts objects that satisfy predicate of one of the types in |
---|
41 | ;; ccl::*heap-utilization-vector-type-names* |
---|
42 | (defun count-objects-of-type (type &optional predicate) |
---|
43 | (let ((typecode (position type ccl::*heap-utilization-vector-type-names*)) |
---|
44 | (res 0)) |
---|
45 | (when typecode |
---|
46 | (flet ((mapper (thing) |
---|
47 | (when (and (eq typecode (ccl::typecode thing)) |
---|
48 | (or (null predicate) (funcall predicate thing))) |
---|
49 | (incf res)))) |
---|
50 | (declare (dynamic-extent #'mapper)) |
---|
51 | (ccl::%map-areas #'mapper)) |
---|
52 | res))) |
---|
53 | |
---|
54 | (defun count-conses () |
---|
55 | (let ((res 0)) |
---|
56 | (flet ((mapper (thing) |
---|
57 | (when (consp thing) (incf res)))) |
---|
58 | (declare (dynamic-extent #'mapper)) |
---|
59 | (ccl::%map-areas #'mapper)) |
---|
60 | res)) |
---|
61 | |
---|
62 | ;; Like set-difference, but uses a hash table to go faster. |
---|
63 | (defun fast-set-difference (list1 list2 &optional (test #'eq)) |
---|
64 | (let ((hash (make-hash-table :test test)) |
---|
65 | (res nil)) |
---|
66 | (dolist (e1 list1) (setf (gethash e1 hash) t)) |
---|
67 | (dolist (e2 list2) (remhash e2 hash)) |
---|
68 | (maphash (lambda (k v) |
---|
69 | (declare (ignore v)) |
---|
70 | (push k res)) |
---|
71 | hash) |
---|
72 | res)) |
---|
73 | |
---|
74 | ;; Returns all references to object. |
---|
75 | ;; Note that these can contain stack-consed objects that are dead. |
---|
76 | ;; Use pointer-in-some-dynamic-area-p to be sure to follow only real objects |
---|
77 | (defun find-references (object) |
---|
78 | (let ((res nil)) |
---|
79 | (ccl::%map-areas |
---|
80 | (lambda (thing) |
---|
81 | (cond ((and (not (eq thing object)) |
---|
82 | (ccl::uvectorp thing) |
---|
83 | (not (ccl::ivectorp thing))) |
---|
84 | (dotimes (i (ccl::uvsize thing)) |
---|
85 | (when (eq object (ccl::uvref thing i)) |
---|
86 | (push thing res) |
---|
87 | (return)))) |
---|
88 | ((consp thing) |
---|
89 | (when(or (eq object (car thing)) |
---|
90 | (eq object (cdr thing))) |
---|
91 | (push thing res)))))) |
---|
92 | res)) |
---|
93 | |
---|
94 | ;; Return true if P is heap-consed |
---|
95 | (defun pointer-in-some-dynamic-area-p (p) |
---|
96 | (block found |
---|
97 | (ccl::do-consing-areas (a) |
---|
98 | (when (eql (%fixnum-ref a target::area.code) ccl::area-dynamic) |
---|
99 | (when (ccl::%ptr-in-area-p p a) |
---|
100 | (return-from found t)))))) |
---|
101 | |
---|
102 | ;; Find all transitive referencers to object-or-list. If as-object is |
---|
103 | ;; true, just start with object-or-list. If as-object is false, then if |
---|
104 | ;; object-or-list is a list, start with its elements, and ignore its |
---|
105 | ;; cons cells. |
---|
106 | ;; Returns a hash table with the references as keys. |
---|
107 | (defun transitive-referencers (object-or-list &optional as-object) |
---|
108 | (let ((found (make-hash-table :test 'eq))) |
---|
109 | (cond ((or (atom object-or-list) as-object) |
---|
110 | (setf (gethash object-or-list found) t)) |
---|
111 | (t (loop for cons on object-or-list |
---|
112 | do |
---|
113 | (setf (gethash cons found) t |
---|
114 | (gethash (car cons) found) t)))) |
---|
115 | (ccl:gc) |
---|
116 | (format t "Searching") (finish-output) |
---|
117 | (loop |
---|
118 | (let ((added-one nil)) |
---|
119 | (format t " ~d" (hash-table-count found)) (finish-output) |
---|
120 | (ccl::%map-areas |
---|
121 | (lambda (thing) |
---|
122 | (unless (or (not (pointer-in-some-dynamic-area-p thing)) |
---|
123 | (gethash thing found)) |
---|
124 | (cond ((and (not (eq thing (ccl::nhash.vector found))) |
---|
125 | (ccl::uvectorp thing) |
---|
126 | (not (ccl::ivectorp thing)) |
---|
127 | (not (packagep thing))) |
---|
128 | (dotimes (i (ccl::uvsize thing)) |
---|
129 | (let ((object (ccl::uvref thing i))) |
---|
130 | (when (gethash object found) |
---|
131 | (setf (gethash thing found) t |
---|
132 | added-one t) |
---|
133 | (return))))) |
---|
134 | ((and (consp thing) |
---|
135 | (pointer-in-some-dynamic-area-p (car thing)) |
---|
136 | (pointer-in-some-dynamic-area-p (cdr thing))) |
---|
137 | (when (or (gethash (car thing) found) |
---|
138 | (gethash (cdr thing) found)) |
---|
139 | (setf (gethash thing found) t))))))) |
---|
140 | (unless added-one |
---|
141 | (return)))) |
---|
142 | (format t " done.~%") (finish-output) |
---|
143 | ;; Eliminate any cons that is referenced by another cons. |
---|
144 | ;; Also eliminate or replace objects that nobody will want to see. |
---|
145 | (let ((cons-refs (make-hash-table :test 'eq)) |
---|
146 | (additions nil)) |
---|
147 | (loop for cons being the hash-keys of found |
---|
148 | when (consp cons) |
---|
149 | do |
---|
150 | (when (consp (car cons)) |
---|
151 | (setf (gethash (car cons) cons-refs) t)) |
---|
152 | (when (consp (cdr cons)) |
---|
153 | (setf (gethash (cdr cons) cons-refs) t))) |
---|
154 | (loop for key being the hash-keys of found |
---|
155 | when (or (and (consp key) (gethash key cons-refs)) |
---|
156 | (and (consp key) (eq (car key) 'ccl::function-source-note)) |
---|
157 | (typep key 'ccl::hash-table-vector) |
---|
158 | (when (typep key 'ccl::symbol-vector) |
---|
159 | (push (ccl::%symvector->symptr key) additions) |
---|
160 | t) |
---|
161 | (when (typep key 'ccl::function-vector) |
---|
162 | (push (ccl::%function-vector-to-function key) additions) |
---|
163 | t)) |
---|
164 | do |
---|
165 | (remhash key found)) |
---|
166 | (dolist (addition additions) |
---|
167 | (setf (gethash addition found) t)) |
---|
168 | (remhash object-or-list found) |
---|
169 | (unless (or (atom object-or-list) as-object) |
---|
170 | (loop for cons on object-or-list |
---|
171 | do |
---|
172 | (remhash cons found) |
---|
173 | (remhash (car cons) found))) |
---|
174 | found))) |
---|
175 | |
---|
176 | ;; One convenient way to print the hash table returned by transitive-referencers |
---|
177 | (defun print-referencers (hash &key |
---|
178 | predicate |
---|
179 | (pause-period 20) |
---|
180 | (print-circle t) |
---|
181 | (print-length 20) |
---|
182 | (print-level 5)) |
---|
183 | (let ((cnt 0) |
---|
184 | (*print-circle* print-circle) |
---|
185 | (*print-length* print-length) |
---|
186 | (*print-level* print-level)) |
---|
187 | (maphash (lambda (key value) |
---|
188 | (declare (ignore value)) |
---|
189 | (when (or (null predicate) (funcall predicate key)) |
---|
190 | (format t "~s~%" key) |
---|
191 | (when (> (incf cnt) pause-period) |
---|
192 | (format t "Continue (Y/N)? ") |
---|
193 | (unless (equalp (read-line) "Y") |
---|
194 | (return-from print-referencers)) |
---|
195 | (setq cnt 0)))) |
---|
196 | hash))) |
---|
197 | |
---|
198 | ;; Returns all the obsolete CLOS instances, those whose class has been |
---|
199 | ;; changed since they were created. Each will be updated as soon as |
---|
200 | ;; method dispatch is done on it." |
---|
201 | (defun obsolete-instances (list) |
---|
202 | (let ((res nil)) |
---|
203 | (dolist (i list) |
---|
204 | (when (eq 0 (ccl::%wrapper-hash-index (ccl::instance-class-wrapper i))) |
---|
205 | (push i res))) |
---|
206 | res)) |
---|
207 | |
---|
208 | ;; Linux-only malloc leak finding |
---|
209 | #+linux-target |
---|
210 | (progn |
---|
211 | |
---|
212 | ;; (ccl::start-mtrace LOGFILE) |
---|
213 | ;; Do some work. |
---|
214 | ;; (ccl::stop-mtrace) |
---|
215 | ;; (ccl::parse-mtrace-log LOGFILE) |
---|
216 | (defun start-mtrace (log-file) |
---|
217 | (touch log-file) |
---|
218 | (setf log-file (probe-file log-file)) |
---|
219 | (setenv "MALLOC_TRACE" (namestring log-file)) |
---|
220 | (gc) |
---|
221 | (#_mtrace)) |
---|
222 | |
---|
223 | (defun stop-mtrace () |
---|
224 | (gc) |
---|
225 | (#_muntrace)) |
---|
226 | |
---|
227 | (defun parse-mtrace-log (log-file) |
---|
228 | (with-open-file (s log-file) |
---|
229 | (let ((hash (make-hash-table :test 'equal)) |
---|
230 | (eof (list :eof))) |
---|
231 | (loop for line = (read-line s nil eof) |
---|
232 | until (eq line eof) |
---|
233 | when (and (> (length line) 2) |
---|
234 | (equal "@ " (subseq line 0 2))) |
---|
235 | do |
---|
236 | (setf line (subseq line 2)) |
---|
237 | (let ((plus-pos (search " + " line)) |
---|
238 | (minus-pos (search " - " line))) |
---|
239 | (cond (plus-pos |
---|
240 | (let* ((where (subseq line 0 plus-pos)) |
---|
241 | (addr-and-size (subseq line (+ plus-pos 3))) |
---|
242 | (space-pos (position #\space addr-and-size)) |
---|
243 | (addr (subseq addr-and-size 0 space-pos)) |
---|
244 | (size (subseq addr-and-size (1+ space-pos)))) |
---|
245 | (setf (gethash addr hash) (list where size)))) |
---|
246 | (minus-pos |
---|
247 | (let ((addr (subseq line (+ minus-pos 3)))) |
---|
248 | (remhash addr hash)))))) |
---|
249 | (let ((res nil)) |
---|
250 | (maphash (lambda (key value) |
---|
251 | (push (append value (list key)) res)) |
---|
252 | hash) |
---|
253 | res)))) |
---|
254 | |
---|
255 | ;; Return the total number of bytes allocated by malloc() |
---|
256 | (defun mallinfo () |
---|
257 | (ccl:rlet ((mallinfo :mallinfo)) |
---|
258 | (#_mallinfo mallinfo) |
---|
259 | (ccl::rref mallinfo :mallinfo.uordblks))) |
---|
260 | |
---|
261 | ) ;; end of linux-only code |
---|