1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is leaksoup3, a memory graph analysis tool that
15 * finds roots based on strongly connected components (SCCs).
17 * The Initial Developer of the Original Code is L. David Baron.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
22 * L. David Baron <dbaron@dbaron.org> (original author)
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
43 #include "nsVoidArray.h"
44 #include "nsQuickSort.h"
47 * Read in an allocation dump, presumably one taken at shutdown (using
48 * the --shutdown-leaks=file option, which must be used along with
49 * --trace-malloc=tmlog), and treat the memory in the dump as leaks.
50 * Find the leak roots, including cycles that are roots, by finding the
51 * strongly connected components in the graph. Print output to stdout
55 struct AllocationNode
{
56 const ADLog::Entry
*entry
;
58 // Other |AllocationNode| objects whose memory has a pointer to
60 nsAutoVoidArray pointers_to
;
63 nsAutoVoidArray pointers_from
;
65 // Early on in the algorithm, the pre-order index from a DFS.
66 // Later on, set to the index of the strongly connected component to
67 // which this node belongs.
74 static PLHashNumber
hash_pointer(const void *key
)
76 return (PLHashNumber
) NS_PTR_TO_INT32(key
);
79 static int sort_by_index(const void* e1
, const void* e2
, void*)
81 const AllocationNode
*n1
= *static_cast<const AllocationNode
*const*>(e1
);
82 const AllocationNode
*n2
= *static_cast<const AllocationNode
*const*>(e2
);
83 return n1
->index
- n2
->index
;
86 static int sort_by_reverse_index(const void* e1
, const void* e2
, void*)
88 const AllocationNode
*n1
= *static_cast<const AllocationNode
*const*>(e1
);
89 const AllocationNode
*n2
= *static_cast<const AllocationNode
*const*>(e2
);
90 return n2
->index
- n1
->index
;
93 static void print_escaped(FILE *aStream
, const char* aData
)
98 while ((c
= *aData
++)) {
100 #define CH(char) *p++ = char
102 CH('&'); CH('l'); CH('t'); CH(';');
105 CH('&'); CH('g'); CH('t'); CH(';');
108 CH('&'); CH('a'); CH('m'); CH('p'); CH(';');
115 if (p
+ 10 > buf
+ sizeof(buf
)) {
125 int main(int argc
, char **argv
)
129 "Expected usage: %s <sd-leak-file>\n"
130 " sd-leak-file: Output of --shutdown-leaks=<file> option.\n",
136 if (!log
.Read(argv
[1])) {
138 "%s: Error reading input file %s.\n", argv
[0], argv
[1]);
141 const size_t count
= log
.count();
143 PLHashTable
*memory_map
=
144 PL_NewHashTable(count
* 8, hash_pointer
, PL_CompareValues
,
145 PL_CompareValues
, 0, 0);
147 fprintf(stderr
, "%s: Out of memory.\n", argv
[0]);
151 // Create one |AllocationNode| object for each log entry, and create
152 // entries in the hashtable pointing to it for each byte it occupies.
153 AllocationNode
*nodes
= new AllocationNode
[count
];
155 fprintf(stderr
, "%s: Out of memory.\n", argv
[0]);
160 AllocationNode
*cur_node
= nodes
;
161 for (ADLog::const_iterator entry
= log
.begin(), entry_end
= log
.end();
162 entry
!= entry_end
; ++entry
, ++cur_node
) {
163 const ADLog::Entry
*e
= cur_node
->entry
= *entry
;
164 cur_node
->reached
= PR_FALSE
;
166 for (ADLog::Pointer p
= e
->address
,
167 p_end
= e
->address
+ e
->datasize
;
169 PLHashEntry
*e
= PL_HashTableAdd(memory_map
, p
, cur_node
);
171 fprintf(stderr
, "%s: Out of memory.\n", argv
[0]);
178 // Construct graph based on pointers.
179 for (AllocationNode
*node
= nodes
, *node_end
= nodes
+ count
;
180 node
!= node_end
; ++node
) {
181 const ADLog::Entry
*e
= node
->entry
;
182 for (const char *d
= e
->data
, *d_end
= e
->data
+ e
->datasize
-
183 e
->datasize
% sizeof(ADLog::Pointer
);
184 d
!= d_end
; d
+= sizeof(ADLog::Pointer
)) {
185 AllocationNode
*target
= (AllocationNode
*)
186 PL_HashTableLookup(memory_map
, *(void**)d
);
188 target
->pointers_from
.AppendElement(node
);
189 node
->pointers_to
.AppendElement(target
);
194 // Do a depth-first search on the graph (i.e., by following
195 // |pointers_to|) and assign the post-order index to |index|.
197 PRUint32 dfs_index
= 0;
200 for (AllocationNode
*n
= nodes
, *n_end
= nodes
+count
; n
!= n_end
; ++n
) {
204 stack
.AppendElement(n
);
207 PRUint32 pos
= stack
.Count() - 1;
209 static_cast<AllocationNode
*>(stack
[pos
]);
211 n
->index
= dfs_index
++;
212 stack
.RemoveElementAt(pos
);
214 n
->reached
= PR_TRUE
;
216 // When doing post-order processing, we have to be
217 // careful not to put reached nodes into the stack.
218 nsVoidArray
&pt
= n
->pointers_to
;
219 for (PRInt32 i
= pt
.Count() - 1; i
>= 0; --i
) {
220 if (!static_cast<AllocationNode
*>(pt
[i
])->reached
) {
221 stack
.AppendElement(pt
[i
]);
225 } while (stack
.Count() > 0);
229 // Sort the nodes by their DFS index, in reverse, so that the first
230 // node is guaranteed to be in a root SCC.
231 AllocationNode
**sorted_nodes
= new AllocationNode
*[count
];
233 fprintf(stderr
, "%s: Out of memory.\n", argv
[0]);
238 for (size_t i
= 0; i
< count
; ++i
) {
239 sorted_nodes
[i
] = nodes
+ i
;
241 NS_QuickSort(sorted_nodes
, count
, sizeof(AllocationNode
*),
242 sort_by_reverse_index
, 0);
245 // Put the nodes into their strongly-connected components.
246 PRUint32 num_sccs
= 0;
248 for (size_t i
= 0; i
< count
; ++i
) {
249 nodes
[i
].reached
= PR_FALSE
;
252 for (AllocationNode
**sn
= sorted_nodes
,
253 **sn_end
= sorted_nodes
+ count
; sn
!= sn_end
; ++sn
) {
254 if ((*sn
)->reached
) {
258 // We found a new strongly connected index.
259 stack
.AppendElement(*sn
);
261 PRUint32 pos
= stack
.Count() - 1;
263 static_cast<AllocationNode
*>(stack
[pos
]);
264 stack
.RemoveElementAt(pos
);
267 n
->reached
= PR_TRUE
;
269 stack
.AppendElements(n
->pointers_from
);
271 } while (stack
.Count() > 0);
276 // Identify which nodes are leak roots by using DFS, and watching
277 // for component transitions.
278 PRUint32 num_root_nodes
= count
;
280 for (size_t i
= 0; i
< count
; ++i
) {
281 nodes
[i
].is_root
= PR_TRUE
;
285 for (AllocationNode
*n
= nodes
, *n_end
= nodes
+count
; n
!= n_end
; ++n
) {
290 // Loop through pointers_to, and add any that are in a
291 // different SCC to stack:
292 for (int i
= n
->pointers_to
.Count() - 1; i
>= 0; --i
) {
293 AllocationNode
*target
=
294 static_cast<AllocationNode
*>(n
->pointers_to
[i
]);
295 if (n
->index
!= target
->index
) {
296 stack
.AppendElement(target
);
300 while (stack
.Count() > 0) {
301 PRUint32 pos
= stack
.Count() - 1;
303 static_cast<AllocationNode
*>(stack
[pos
]);
304 stack
.RemoveElementAt(pos
);
307 n
->is_root
= PR_FALSE
;
309 stack
.AppendElements(n
->pointers_to
);
315 // Sort the nodes by their SCC index.
316 NS_QuickSort(sorted_nodes
, count
, sizeof(AllocationNode
*),
321 printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
324 "<title>Leak analysis</title>\n"
325 "<style type=\"text/css\">\n"
326 " .root { background: white; color: black; }\n"
327 " .nonroot { background: #ccc; color: black; }\n"
331 "<p>Generated %d entries (%d in root SCCs) and %d SCCs.</p>\n\n",
332 count
, num_root_nodes
, num_sccs
);
334 for (size_t i
= 0; i
< count
; ++i
) {
335 nodes
[i
].reached
= PR_FALSE
;
338 // Loop over the sorted nodes twice, first printing the roots
339 // and then the non-roots.
340 for (PRBool root_type
= PR_TRUE
;
341 root_type
== PR_TRUE
|| root_type
== PR_FALSE
; --root_type
) {
344 "<div class=\"root\">\n"
345 "<h1 id=\"root\">Root components</h1>\n");
348 "<div class=\"nonroot\">\n"
349 "<h1 id=\"nonroot\">Non-root components</h1>\n");
351 PRUint32 component
= (PRUint32
)-1;
352 PRBool one_object_component
;
353 for (const AllocationNode
*const* sn
= sorted_nodes
,
354 *const* sn_end
= sorted_nodes
+ count
;
355 sn
!= sn_end
; ++sn
) {
356 const AllocationNode
*n
= *sn
;
357 if (n
->is_root
!= root_type
)
359 const ADLog::Entry
*e
= n
->entry
;
361 if (n
->index
!= component
) {
362 component
= n
->index
;
363 one_object_component
=
364 sn
+ 1 == sn_end
|| (*(sn
+1))->index
!= component
;
365 if (!one_object_component
)
366 printf("\n\n<h2 id=\"c%d\">Component %d</h2>\n",
367 component
, component
);
370 if (one_object_component
) {
371 printf("\n\n<div id=\"c%d\">\n", component
);
372 printf("<h2 id=\"o%d\">Object %d "
373 "(single-object component %d)</h2>\n",
374 n
-nodes
, n
-nodes
, component
);
376 printf("\n\n<h3 id=\"o%d\">Object %d</h3>\n",
380 printf("%p <%s> (%d)\n",
381 e
->address
, e
->type
, e
->datasize
);
382 for (size_t d
= 0; d
< e
->datasize
;
383 d
+= sizeof(ADLog::Pointer
)) {
384 AllocationNode
*target
= (AllocationNode
*)
385 PL_HashTableLookup(memory_map
, *(void**)(e
->data
+ d
));
387 printf(" <a href=\"#o%d\">0x%08X</a> <%s>",
389 *(unsigned int*)(e
->data
+ d
),
390 target
->entry
->type
);
391 if (target
->index
!= n
->index
) {
392 printf(", component %d", target
->index
);
397 *(unsigned int*)(e
->data
+ d
));
401 if (n
->pointers_from
.Count()) {
402 printf("\nPointers from:\n");
403 for (PRUint32 i
= 0, i_end
= n
->pointers_from
.Count();
405 AllocationNode
*t
= static_cast<AllocationNode
*>
406 (n
->pointers_from
[i
]);
407 const ADLog::Entry
*te
= t
->entry
;
408 printf(" <a href=\"#o%d\">%s</a> (Object %d, ",
409 t
- nodes
, te
->type
, t
- nodes
);
410 if (t
->index
!= n
->index
) {
411 printf("component %d, ", t
->index
);
416 printf("%p)\n", te
->address
);
421 print_escaped(stdout
, e
->allocation_stack
);
424 if (one_object_component
) {
434 delete [] sorted_nodes
;