~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/gc/boehm/leaksoup/leaksoup.java

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 
2
 *
 
3
 * The contents of this file are subject to the Netscape Public
 
4
 * License Version 1.1 (the "License"); you may not use this file
 
5
 * except in compliance with the License. You may obtain a copy of
 
6
 * the License at http://www.mozilla.org/NPL/
 
7
 *
 
8
 * Software distributed under the License is distributed on an "AS
 
9
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 
10
 * implied. See the License for the specific language governing
 
11
 * rights and limitations under the License.
 
12
 *
 
13
 * The Original Code is Mozilla Communicator client code, released
 
14
 * March 31, 1998.
 
15
 *
 
16
 * The Initial Developer of the Original Code is Netscape
 
17
 * Communications Corporation.  Portions created by Netscape are
 
18
 * Copyright (C) 1998 Netscape Communications Corporation. All
 
19
 * Rights Reserved.
 
20
 *
 
21
 * Contributor(s):
 
22
 *
 
23
 * Patrick C. Beard <beard@netscape.com>
 
24
 *
 
25
 * Alternatively, the contents of this file may be used under the
 
26
 * terms of the GNU Public License (the "GPL"), in which case the
 
27
 * provisions of the GPL are applicable instead of those above.
 
28
 * If you wish to allow use of your version of this file only
 
29
 * under the terms of the GPL and not to allow others to use your
 
30
 * version of this file under the NPL, indicate your decision by
 
31
 * deleting the provisions above and replace them with the notice
 
32
 * and other provisions required by the GPL.  If you do not delete
 
33
 * the provisions above, a recipient may use your version of this
 
34
 * file under either the NPL or the GPL.
 
35
 */
 
36
 
 
37
import java.io.*;
 
38
import java.util.*;
 
39
 
 
40
class Leak extends Reference {
 
41
        String mName;
 
42
        long mCrawlOffset;
 
43
        short mCrawlCount;
 
44
        short mRefCount;
 
45
        short mChildCount;
 
46
        Leak[] mParents;
 
47
        int mTotalSize;
 
48
        boolean mMarked;
 
49
 
 
50
        Leak(String addr, Type type, Object[] refs, long crawlOffset, short crawlCount) {
 
51
            super(addr, type, refs);
 
52
            mName = addr;
 
53
                mCrawlOffset = crawlOffset;
 
54
                mCrawlCount = crawlCount;
 
55
                mRefCount = 0;
 
56
                mChildCount = 0;
 
57
                mParents = null;
 
58
                mTotalSize = 0;
 
59
                mMarked = false;
 
60
        }
 
61
        
 
62
        void setParents(Vector parents) {
 
63
                mParents = new Leak[parents.size()];
 
64
                parents.copyInto(mParents);
 
65
        }
 
66
        
 
67
        void computeTotalSize() {
 
68
                // first, mark this node as having been visited.
 
69
                // we only want to include nodes that haven't been
 
70
                // visited in our total size.
 
71
                mTotalSize = mType.mSize;
 
72
                
 
73
                // then, visit all nodes that haven't been visited,
 
74
                // and include their total size in ours.
 
75
                int count = mReferences.length;
 
76
                for (int i = 0; i < count; ++i) {
 
77
                        Object ref = mReferences[i];
 
78
                        if (ref instanceof Leak) {
 
79
                                Leak leak = (Leak) ref;
 
80
                                if (leak.mTotalSize == 0) {
 
81
                                        leak.computeTotalSize();
 
82
                                        mTotalSize += leak.mTotalSize;
 
83
                                }
 
84
                        }
 
85
                }
 
86
        }
 
87
 
 
88
        void clearTotalSize() {
 
89
                // first, clear our total size.
 
90
                mTotalSize = 0;
 
91
                
 
92
                // then, visit all nodes that haven't been visited,
 
93
                // and clear each one's total size.
 
94
                int count = mReferences.length;
 
95
                for (int i = 0; i < count; ++i) {
 
96
                        Object ref = mReferences[i];
 
97
                        if (ref instanceof Leak) {
 
98
                                Leak leak = (Leak) ref;
 
99
                                if (leak.mTotalSize != 0)
 
100
                                        leak.clearTotalSize();
 
101
                        }
 
102
                }
 
103
        }
 
104
 
 
105
        void clearMarks() {
 
106
                // first, clear mark.
 
107
                mMarked = false;
 
108
                
 
109
                // then, visit all nodes that haven't been visited,
 
110
                // and clear each one's mark.
 
111
                int count = mReferences.length;
 
112
                for (int i = 0; i < count; ++i) {
 
113
                        Object ref = mReferences[i];
 
114
                        if (ref instanceof Leak) {
 
115
                                Leak leak = (Leak) ref;
 
116
                                if (leak.mMarked)
 
117
                                        leak.clearMarks();
 
118
                        }
 
119
                }
 
120
        }
 
121
        
 
122
        static final char INDENT = '\t';
 
123
        
 
124
    void printGraph(PrintWriter out) {
 
125
            printGraph(out, 0);
 
126
            clearMarks();
 
127
    }
 
128
 
 
129
        private void printGraph(PrintWriter out, int indent) {
 
130
                // first, mark this node as having been visited.
 
131
                // we only want to include nodes that haven't been
 
132
                // visited in our total size.
 
133
                mMarked = true;
 
134
                for (int i = 0; i < indent; ++i)
 
135
                    out.print(INDENT);
 
136
                out.println(toString());
 
137
                
 
138
                // then, visit all nodes that haven't been visited,
 
139
                // and include their total size in ours.
 
140
                int count = mReferences.length;
 
141
                if (count > 0) {
 
142
                    int subIndent = indent + 1;
 
143
                for (int i = 0; i < count; ++i) {
 
144
                        Object ref = mReferences[i];
 
145
                        if (ref instanceof Leak) {
 
146
                                Leak leak = (Leak) ref;
 
147
                                if (!leak.mMarked)
 
148
                                        leak.printGraph(out, subIndent);
 
149
                        }
 
150
                }
 
151
        }
 
152
        }
 
153
        
 
154
        void printCycle(PrintWriter out) {
 
155
            printCycle(out, 0);
 
156
            clearMarks();
 
157
        }
 
158
        
 
159
        private void printCycle(PrintWriter out, int indent) {
 
160
                // first, mark this node as having been visited.
 
161
                // we only want to include nodes that haven't been
 
162
                // visited in our total size.
 
163
                mMarked = true;
 
164
                
 
165
                // then, visit all nodes that haven't been visited,
 
166
                // and include their total size in ours.
 
167
                if (mChildCount > 0) {
 
168
                    // don't print leaf nodes in a cycle. they aren't interesting.
 
169
                for (int i = 0; i < indent; ++i)
 
170
                    out.print(INDENT);
 
171
                out.println(toString());
 
172
                    int subIndent = indent + 1;
 
173
                int count = mReferences.length;
 
174
                for (int i = 0; i < count; ++i) {
 
175
                        Object ref = mReferences[i];
 
176
                        if (ref instanceof Leak) {
 
177
                                Leak leak = (Leak) ref;
 
178
                                if (!leak.mMarked)
 
179
                                        leak.printCycle(out, subIndent);
 
180
                        }
 
181
                }
 
182
        }
 
183
        }
 
184
 
 
185
        public String toString() {
 
186
                return ("<A HREF=\"#" + mName + "\">" + mName + "</A> [" + mRefCount + "] " + mType + "{" + mTotalSize + "}");
 
187
        }
 
188
        
 
189
        /**
 
190
         * Sorts in order of increasing reference count.
 
191
         */
 
192
        static class ByRefCount extends QuickSort.Comparator {
 
193
                public int compare(Object obj1, Object obj2) {
 
194
                        Leak l1 = (Leak) obj1, l2 = (Leak) obj2;
 
195
                        return (l1.mRefCount - l2.mRefCount);
 
196
                }
 
197
        }
 
198
 
 
199
        /**
 
200
         * Sorts in order of decreasing number of children.
 
201
         */
 
202
        public static class ByChildCount extends QuickSort.Comparator {
 
203
                public int compare(Object obj1, Object obj2) {
 
204
                        Leak l1 = (Leak) obj1, l2 = (Leak) obj2;
 
205
                        return (l2.mChildCount - l1.mChildCount);
 
206
                }
 
207
        }
 
208
 
 
209
        /**
 
210
         * Sorts in order of decreasing total size.
 
211
         */
 
212
        static class ByTotalSize extends QuickSort.Comparator {
 
213
                public int compare(Object obj1, Object obj2) {
 
214
                        Leak l1 = (Leak) obj1, l2 = (Leak) obj2;
 
215
                        return (l2.mTotalSize - l1.mTotalSize);
 
216
                }
 
217
        }
 
218
}
 
219
 
 
220
final class LineReader {
 
221
    BufferedReader reader;
 
222
    long offset;
 
223
    
 
224
    LineReader(BufferedReader reader) {
 
225
        this.reader = reader;
 
226
        this.offset = 0;
 
227
    }
 
228
    
 
229
    String readLine() throws IOException {
 
230
        String line = reader.readLine();
 
231
        if (line != null)
 
232
            offset += 1 + line.length();
 
233
        return line;
 
234
    }
 
235
    
 
236
    void close() throws IOException {
 
237
        reader.close();
 
238
    }
 
239
}
 
240
 
 
241
public class leaksoup {
 
242
        private static boolean ROOTS_ONLY = false;
 
243
 
 
244
        public static void main(String[] args) {
 
245
                if (args.length == 0) {
 
246
                        System.out.println("usage:  leaksoup [-blame] [-lxr] [-assign] [-roots] leaks");
 
247
                        System.exit(1);
 
248
                }
 
249
                
 
250
                // assume user want's blame URLs.
 
251
                FileLocator.USE_BLAME = true;
 
252
                FileLocator.ASSIGN_BLAME = false;
 
253
                ROOTS_ONLY = false;
 
254
                
 
255
                for (int i = 0; i < args.length; i++) {
 
256
                        String arg = args[i];
 
257
                        if (arg.charAt(0) == '-') {
 
258
                                if (arg.equals("-blame"))
 
259
                                        FileLocator.USE_BLAME = true;
 
260
                                else if (arg.equals("-lxr"))
 
261
                                        FileLocator.USE_BLAME = false;
 
262
                                else if (arg.equals("-assign"))
 
263
                                        FileLocator.ASSIGN_BLAME = true;
 
264
                                else if (arg.equals("-roots"))
 
265
                                        ROOTS_ONLY = true;
 
266
                                else
 
267
                                        System.out.println("unrecognized option: " + arg);
 
268
                        } else {
 
269
                                cook(arg);
 
270
                        }
 
271
                }
 
272
                
 
273
                // quit the application.
 
274
                System.exit(0);
 
275
        }
 
276
        
 
277
        static void cook(String inputName) {
 
278
                try {
 
279
                        Vector vec = new Vector();
 
280
                        Hashtable leakTable = new Hashtable();
 
281
                        Hashtable types = new Hashtable();
 
282
                        Histogram hist = new Histogram();
 
283
                        LineReader reader = new LineReader(new BufferedReader(new InputStreamReader(new FileInputStream(inputName))));
 
284
                        StringTable strings = new StringTable();
 
285
                        String line = reader.readLine();
 
286
                        while (line != null) {
 
287
                                if (line.startsWith("0x")) {
 
288
                                        String addr = strings.intern(line.substring(0, 10));
 
289
                                        String name = strings.intern(line.substring(line.indexOf('<') + 1, line.indexOf('>')));
 
290
                                        int size;
 
291
                                        try {
 
292
                                                String str = line.substring(line.indexOf('(') + 1, line.indexOf(')')).trim();
 
293
                                                size = Integer.parseInt(str);
 
294
                                        } catch (NumberFormatException nfe) {
 
295
                                                size = 0;
 
296
                                        }
 
297
 
 
298
                                        // generate a unique type for this object.
 
299
                                        String key = strings.intern(name + "_" + size);
 
300
                                        Type type = (Type) types.get(key);
 
301
                                        if (type == null) {
 
302
                                                type = new Type(name, size);
 
303
                                                types.put(key, type);
 
304
                                        }
 
305
                                        
 
306
                                        // read in fields. could compress these by converting to Integer objects.
 
307
                                        vec.setSize(0);
 
308
                                        for (line = reader.readLine(); line != null && line.charAt(0) == '\t'; line = reader.readLine())
 
309
                                                vec.addElement(strings.intern(line.substring(1, 11)));
 
310
                                        Object[] refs = new Object[vec.size()];
 
311
                                        vec.copyInto(refs);
 
312
                                        vec.setSize(0);
 
313
                                        
 
314
                                    // record the offset of the stack crawl, which will be read in and formatted at the end, to save memory.
 
315
                                        long crawlOffset = reader.offset;
 
316
                                        short crawlCount = 0;
 
317
                                        for (line = reader.readLine(); line != null && !line.startsWith("Leaked "); line = reader.readLine())
 
318
                                                ++crawlCount;
 
319
 
 
320
                                        // record the leak.
 
321
                                        leakTable.put(addr, new Leak(addr, type, refs, crawlOffset, crawlCount));
 
322
 
 
323
                                        // count the leak types in a histogram.
 
324
                                        hist.record(type);
 
325
                                } else {
 
326
                                        line = reader.readLine();
 
327
                                }
 
328
                        }
 
329
                        reader.close();
 
330
                        
 
331
                        // don't need the interned strings table anymore.
 
332
                        strings = null;
 
333
                        
 
334
                        Leak[] leaks = new Leak[leakTable.size()];
 
335
                        int leakCount = 0;
 
336
                        long totalSize = 0;
 
337
 
 
338
                        Hashtable parentTable = new Hashtable();
 
339
 
 
340
                        // now, we have a table full of leaked objects, lets derive reference counts, and build the graph.
 
341
                        Enumeration e = leakTable.elements();
 
342
                        while (e.hasMoreElements()) {
 
343
                                Leak leak = (Leak) e.nextElement();
 
344
                                Object[] refs = leak.mReferences;
 
345
                                int count = refs.length;
 
346
                                for (int r = 0; r < count; ++r) {
 
347
                                        String addr = (String) refs[r];
 
348
                                        Leak ref = (Leak) leakTable.get(addr);
 
349
                                        if (ref != null) {
 
350
                                                // increase the ref count.
 
351
                                                ref.mRefCount++;
 
352
                                                // change string to ref itself.
 
353
                                                refs[r] = ref;
 
354
                                                // add leak to ref's parents vector.
 
355
                                                Vector parents = (Vector) parentTable.get(ref);
 
356
                                                if (parents == null) {
 
357
                                                        parents = new Vector();
 
358
                                                        parentTable.put(ref, parents);
 
359
                                                }
 
360
                                                parents.addElement(leak);
 
361
                                        }
 
362
                                }
 
363
                                leaks[leakCount++] = leak;
 
364
                                totalSize += leak.mType.mSize;
 
365
                        }
 
366
 
 
367
                        // be nice to the GC.
 
368
                        leakTable.clear();
 
369
                        leakTable = null;
 
370
 
 
371
            // sort the leaks by address, and find interior pointers.
 
372
            {
 
373
                QuickSort byAddress = new QuickSort(new Reference.ByAddress());
 
374
                byAddress.sort(leaks);
 
375
            }
 
376
            
 
377
            for (int i = 0; i < leakCount; ++i) {
 
378
                Leak leak = leaks[i];
 
379
                Object[] refs = leak.mReferences;
 
380
                int count = refs.length;
 
381
                short childCount = 0;
 
382
                for (int r = 0; r < count; ++r) {
 
383
                    if (refs[r] instanceof String) {
 
384
                        String addr = (String) refs[r];
 
385
                        if (addr.equals("0x00000000")) continue;
 
386
                        int address = (int) Long.parseLong(addr.substring(2), 16);
 
387
                        Leak ref = (Leak) Reference.findNearest(leaks, address);
 
388
                        if (ref != null) {
 
389
                                                // increase the ref count.
 
390
                                                ref.mRefCount++;
 
391
                                                // change string to ref itself.
 
392
                                                refs[r] = ref;
 
393
                                                    // add leak to ref's parents vector.
 
394
                                                Vector parents = (Vector) parentTable.get(ref);
 
395
                                                if (parents == null) {
 
396
                                                        parents = new Vector();
 
397
                                                        parentTable.put(ref, parents);
 
398
                                                }
 
399
                                                parents.addElement(leak);
 
400
                                                ++childCount;
 
401
                        }
 
402
                    } else {
 
403
                        ++childCount;
 
404
                    }
 
405
                }
 
406
                leak.mChildCount = childCount;
 
407
            }
 
408
                        
 
409
                        // set the parents of each leak.
 
410
                        e = parentTable.keys();
 
411
                        while (e.hasMoreElements()) {
 
412
                                Leak leak = (Leak) e.nextElement();
 
413
                                Vector parents = (Vector) parentTable.get(leak);
 
414
                                if (parents != null)
 
415
                                        leak.setParents(parents);
 
416
                        }
 
417
                        
 
418
                        // be nice to the GC.
 
419
                        parentTable.clear();
 
420
                        parentTable = null;
 
421
                        
 
422
                        // store the leak report in inputName + ".html"
 
423
                        PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(inputName + ".html"))));
 
424
 
 
425
                        Date now = new Date();
 
426
                        out.println("<TITLE>Leaks as of " + now + "</TITLE>");
 
427
 
 
428
            // print leak summary.
 
429
            out.println("<H2>Leak Summary</H2>");
 
430
            out.println("total objects leaked = " + leakCount + "<BR>");
 
431
            out.println("total memory leaked  = " + totalSize + " bytes.<BR>");
 
432
 
 
433
                printLeakHistogram(out, hist);
 
434
                printLeakStructure(out, leaks);
 
435
 
 
436
                        // open original file again, as a RandomAccessFile, to read in stack crawl information.
 
437
                        
 
438
                        // print the leak report.
 
439
                        if (!ROOTS_ONLY) {
 
440
                        RandomAccessFile in = new RandomAccessFile(inputName, "r");
 
441
                                printLeaks(in, out, leaks);
 
442
                                in.close();
 
443
                        }
 
444
                        
 
445
                        out.close();
 
446
                } catch (Exception e) {
 
447
                        e.printStackTrace(System.err);
 
448
                }
 
449
        }
 
450
        
 
451
        /**
 
452
         * Sorts the bins of a histogram by (count * typeSize) to show the
 
453
         * most pressing leaks.
 
454
         */
 
455
        static class ByTypeBinSize extends QuickSort.Comparator {
 
456
                Histogram hist;
 
457
                
 
458
                ByTypeBinSize(Histogram hist) {
 
459
                        this.hist = hist;
 
460
                }
 
461
        
 
462
                public int compare(Object obj1, Object obj2) {
 
463
                        Type t1 = (Type) obj1, t2 = (Type) obj2;
 
464
                        return (hist.count(t1) * t1.mSize - hist.count(t2) * t2.mSize);
 
465
                }
 
466
        }
 
467
 
 
468
        static void printLeakHistogram(PrintWriter out, Histogram hist) throws IOException {
 
469
                // sort the types by histogram count.
 
470
                Object[] types = hist.objects();
 
471
                QuickSort byTypeBinSize = new QuickSort(new ByTypeBinSize(hist));
 
472
                byTypeBinSize.sort(types);
 
473
                
 
474
                out.println("<H2>Leak Histogram</H2>");
 
475
                out.println("<PRE>");
 
476
                int index = types.length;
 
477
                while (index > 0) {
 
478
                        Type type = (Type) types[--index];
 
479
                        int count = hist.count(type);
 
480
                        out.println(type.toString() + " : " + count + " {" + (count * type.mSize) + "}");
 
481
                }
 
482
                out.println("</PRE>");
 
483
        }
 
484
        
 
485
        static void printLeakStructure(PrintWriter out, Leak[] leaks) {
 
486
        // print root leaks. consider only leaks with a reference
 
487
        // count of 0, which when fixed, will hopefully reclaim
 
488
        // all of the objects below them in the graph.
 
489
        {
 
490
            QuickSort byRefCount = new QuickSort(new Leak.ByRefCount());
 
491
            byRefCount.sort(leaks);
 
492
        }
 
493
        
 
494
        int rootCount = 0;
 
495
        int leakCount = leaks.length;
 
496
        for (int i = 0; i < leakCount; ++i) {
 
497
            Leak leak = leaks[i];
 
498
            if (leak.mRefCount > 0)
 
499
                break;
 
500
            ++rootCount;
 
501
            leak.computeTotalSize();
 
502
        }
 
503
 
 
504
        {
 
505
            QuickSort byTotalSize = new QuickSort(new Leak.ByTotalSize());
 
506
            byTotalSize.sort(leaks, rootCount);
 
507
        }
 
508
 
 
509
        out.println("<H2>Leak Roots</H2>");
 
510
        out.println("<PRE>");
 
511
        for (int i = 0; i < rootCount; ++i) {
 
512
            Leak leak = leaks[i];
 
513
            leak.printGraph(out);
 
514
        }
 
515
        out.println("</PRE>");
 
516
 
 
517
        // print leak cycles. traverse the leaks from objects with most number
 
518
        // of children to least, so that leaf objects will be printed after
 
519
        // their parents.
 
520
        {
 
521
            QuickSort byChildCount = new QuickSort(new Leak.ByChildCount());
 
522
            byChildCount.sort(leaks);
 
523
        }
 
524
        
 
525
        out.println("<H2>Leak Cycles</H2>");
 
526
        out.println("<PRE>");
 
527
        for (int i = 0; i < leakCount; ++i) {
 
528
            Leak leak = leaks[i];
 
529
            // if an object's total size isn't known yet, then it must
 
530
            // be a member of a cycle, since it wasn't reached when traversing roots.
 
531
            if (leak.mTotalSize == 0) {
 
532
                leak.computeTotalSize();
 
533
                leak.printCycle(out);
 
534
            }
 
535
        }
 
536
        out.println("</PRE>");
 
537
        }
 
538
        
 
539
        static StringBuffer appendChar(StringBuffer buffer, int ch) {
 
540
                if (ch > 32 && ch < 0x7F) {
 
541
                        switch (ch) {
 
542
                        case '<':       buffer.append("&LT;"); break;
 
543
                        case '>':       buffer.append("&GT;"); break;
 
544
                        default:        buffer.append((char)ch); break;
 
545
                        }
 
546
                } else {
 
547
                        buffer.append("&#183;");
 
548
                }
 
549
                return buffer;
 
550
        }
 
551
        
 
552
        static void printField(PrintWriter out, Object field) {
 
553
                String value = field.toString();
 
554
                if (field instanceof String) {
 
555
                        // this is just a plain HEX value, print its contents as ASCII as well.
 
556
                        if (value.startsWith("0x")) {
 
557
                                try {
 
558
                                        int hexValue = Integer.parseInt(value.substring(2), 16);
 
559
                                        // don't interpret some common values, to save some space.
 
560
                                        if (hexValue != 0 && hexValue != -1) {
 
561
                                                StringBuffer buffer = new StringBuffer(value);
 
562
                                                buffer.append('\t');
 
563
                                                appendChar(buffer, ((hexValue >>> 24) & 0x00FF));
 
564
                                                appendChar(buffer, ((hexValue >>> 16) & 0x00FF));
 
565
                                                appendChar(buffer, ((hexValue >>>  8) & 0x00FF));
 
566
                                                appendChar(buffer, (hexValue & 0x00FF));
 
567
                                                value = buffer.toString();
 
568
                                        }
 
569
                                } catch (NumberFormatException nfe) {
 
570
                                }
 
571
                        }
 
572
                }
 
573
                out.println("\t" + value);
 
574
        }
 
575
 
 
576
        static void printLeaks(RandomAccessFile in, PrintWriter out, Leak[] leaks) throws IOException {
 
577
                // sort the leaks by total size.
 
578
                QuickSort bySize = new QuickSort(new Leak.ByTotalSize());
 
579
                bySize.sort(leaks);
 
580
 
 
581
                // now, print the report, sorted by type size.
 
582
                out.println("<PRE>");
 
583
                Type anchorType = null;
 
584
        int leakCount = leaks.length;
 
585
                for (int i = 0; i < leakCount; ++i) {
 
586
                        Leak leak = leaks[i];
 
587
                        if (anchorType != leak.mType) {
 
588
                                anchorType = leak.mType;
 
589
                                out.println("\n<HR>");
 
590
                                out.println("<A NAME=\"" + anchorType.mName + "_" + anchorType.mSize + "\"></A>");
 
591
                                out.println("<H3>" + anchorType + " Leaks</H3>");
 
592
                        }
 
593
                        out.println("<A NAME=\"" + leak.mName + "\"></A>");
 
594
                        if (leak.mParents != null) {
 
595
                                out.print(leak);
 
596
                                out.println(" <A HREF=\"#" + leak.mName + "_parents\">parents</A>");
 
597
                        } else {
 
598
                                out.println(leak);
 
599
                        }
 
600
                        // print object's fields:
 
601
                        Object[] refs = leak.mReferences;
 
602
                        int count = refs.length;
 
603
                        for (int j = 0; j < count; j++)
 
604
                                printField(out, refs[j]);
 
605
                        // print object's stack crawl:
 
606
                        in.seek(leak.mCrawlOffset);
 
607
                        short crawlCount = leak.mCrawlCount;
 
608
                        while (crawlCount-- > 0) {
 
609
                            String line = in.readLine();
 
610
                                String location = FileLocator.getFileLocation(line);
 
611
                                out.println(location);
 
612
                        }
 
613
                        // print object's parents.
 
614
                        if (leak.mParents != null) {
 
615
                                out.println("<A NAME=\"" + leak.mName + "_parents\"></A>");
 
616
                                out.println("\nLeak Parents:");
 
617
                                Leak[] parents = leak.mParents;
 
618
                                count = parents.length;
 
619
                                for (int j = 0; j < count; j++)
 
620
                                        out.println("\t" + parents[j]);
 
621
                        }
 
622
                }
 
623
                out.println("</PRE>");
 
624
        }
 
625
}