Bug 597811: Make mozJSComponentLoader use JSVERSION_LATEST. (r=sayrer)
[mozilla-central.git] / tools / trace-malloc / leaksoup.cpp
blob4a3ea6d2cab28db4fc9baaa7d77b3f5e09d825fb
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
12 * License.
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.
21 * Contributor(s):
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 ***** */
38 #include "adreader.h"
40 #include <stdio.h>
41 #include "plhash.h"
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
52 * as HTML.
55 struct AllocationNode {
56 const ADLog::Entry *entry;
58 // Other |AllocationNode| objects whose memory has a pointer to
59 // this object.
60 nsAutoVoidArray pointers_to;
62 // The reverse.
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.
68 PRUint32 index;
70 PRPackedBool reached;
71 PRPackedBool is_root;
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)
95 char c;
96 char buf[1000];
97 char *p = buf;
98 while ((c = *aData++)) {
99 switch (c) {
100 #define CH(char) *p++ = char
101 case '<':
102 CH('&'); CH('l'); CH('t'); CH(';');
103 break;
104 case '>':
105 CH('&'); CH('g'); CH('t'); CH(';');
106 break;
107 case '&':
108 CH('&'); CH('a'); CH('m'); CH('p'); CH(';');
109 break;
110 default:
111 CH(c);
112 break;
113 #undef CH
115 if (p + 10 > buf + sizeof(buf)) {
116 *p = '\0';
117 fputs(buf, aStream);
118 p = buf;
121 *p = '\0';
122 fputs(buf, aStream);
125 int main(int argc, char **argv)
127 if (argc != 2) {
128 fprintf(stderr,
129 "Expected usage: %s <sd-leak-file>\n"
130 " sd-leak-file: Output of --shutdown-leaks=<file> option.\n",
131 argv[0]);
132 return 1;
135 ADLog log;
136 if (!log.Read(argv[1])) {
137 fprintf(stderr,
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);
146 if (!memory_map) {
147 fprintf(stderr, "%s: Out of memory.\n", argv[0]);
148 return 1;
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];
154 if (!nodes) {
155 fprintf(stderr, "%s: Out of memory.\n", argv[0]);
156 return 1;
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;
168 p != p_end; ++p) {
169 PLHashEntry *e = PL_HashTableAdd(memory_map, p, cur_node);
170 if (!e) {
171 fprintf(stderr, "%s: Out of memory.\n", argv[0]);
172 return 1;
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);
187 if (target) {
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;
198 nsVoidArray stack;
200 for (AllocationNode *n = nodes, *n_end = nodes+count; n != n_end; ++n) {
201 if (n->reached) {
202 continue;
204 stack.AppendElement(n);
206 do {
207 PRUint32 pos = stack.Count() - 1;
208 AllocationNode *n =
209 static_cast<AllocationNode*>(stack[pos]);
210 if (n->reached) {
211 n->index = dfs_index++;
212 stack.RemoveElementAt(pos);
213 } else {
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];
232 if (!sorted_nodes) {
233 fprintf(stderr, "%s: Out of memory.\n", argv[0]);
234 return 1;
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;
251 nsVoidArray stack;
252 for (AllocationNode **sn = sorted_nodes,
253 **sn_end = sorted_nodes + count; sn != sn_end; ++sn) {
254 if ((*sn)->reached) {
255 continue;
258 // We found a new strongly connected index.
259 stack.AppendElement(*sn);
260 do {
261 PRUint32 pos = stack.Count() - 1;
262 AllocationNode *n =
263 static_cast<AllocationNode*>(stack[pos]);
264 stack.RemoveElementAt(pos);
266 if (!n->reached) {
267 n->reached = PR_TRUE;
268 n->index = num_sccs;
269 stack.AppendElements(n->pointers_from);
271 } while (stack.Count() > 0);
272 ++num_sccs;
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;
284 nsVoidArray stack;
285 for (AllocationNode *n = nodes, *n_end = nodes+count; n != n_end; ++n) {
286 if (!n->is_root) {
287 continue;
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;
302 AllocationNode *n =
303 static_cast<AllocationNode*>(stack[pos]);
304 stack.RemoveElementAt(pos);
306 if (n->is_root) {
307 n->is_root = PR_FALSE;
308 --num_root_nodes;
309 stack.AppendElements(n->pointers_to);
315 // Sort the nodes by their SCC index.
316 NS_QuickSort(sorted_nodes, count, sizeof(AllocationNode*),
317 sort_by_index, 0);
319 // Print output.
321 printf("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
322 "<html>\n"
323 "<head>\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"
328 "</style>\n"
329 "</head>\n");
330 printf("<body>\n\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) {
342 if (root_type) {
343 printf("\n\n"
344 "<div class=\"root\">\n"
345 "<h1 id=\"root\">Root components</h1>\n");
346 } else {
347 printf("\n\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)
358 continue;
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);
375 } else {
376 printf("\n\n<h3 id=\"o%d\">Object %d</h3>\n",
377 n-nodes, n-nodes);
379 printf("<pre>\n");
380 printf("%p &lt;%s&gt; (%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));
386 if (target) {
387 printf(" <a href=\"#o%d\">0x%08X</a> &lt;%s&gt;",
388 target - nodes,
389 *(unsigned int*)(e->data + d),
390 target->entry->type);
391 if (target->index != n->index) {
392 printf(", component %d", target->index);
394 printf("\n");
395 } else {
396 printf(" 0x%08X\n",
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();
404 i != i_end; ++i) {
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);
413 if (t == n) {
414 printf("self)\n");
415 } else {
416 printf("%p)\n", te->address);
421 print_escaped(stdout, e->allocation_stack);
423 printf("</pre>\n");
424 if (one_object_component) {
425 printf("</div>\n");
428 printf("</div>\n");
430 printf("</body>\n"
431 "</html>\n");
434 delete [] sorted_nodes;
435 delete [] nodes;
437 return 0;