Back to index

lightning-sunbird  0.9+nobinonly
bloatsoup.java
Go to the documentation of this file.
00001 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * The contents of this file are subject to the Netscape Public
00004  * License Version 1.1 (the "License"); you may not use this file
00005  * except in compliance with the License. You may obtain a copy of
00006  * the License at http://www.mozilla.org/NPL/
00007  *
00008  * Software distributed under the License is distributed on an "AS
00009  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
00010  * implied. See the License for the specific language governing
00011  * rights and limitations under the License.
00012  *
00013  * The Original Code is Mozilla Communicator client code, released
00014  * March 31, 1998.
00015  *
00016  * The Initial Developer of the Original Code is Netscape
00017  * Communications Corporation.  Portions created by Netscape are
00018  * Copyright (C) 1998 Netscape Communications Corporation. All
00019  * Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Patrick C. Beard <beard@netscape.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the
00026  * terms of the GNU Public License (the "GPL"), in which case the
00027  * provisions of the GPL are applicable instead of those above.
00028  * If you wish to allow use of your version of this file only
00029  * under the terms of the GPL and not to allow others to use your
00030  * version of this file under the NPL, indicate your decision by
00031  * deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL.  If you do not delete
00033  * the provisions above, a recipient may use your version of this
00034  * file under either the NPL or the GPL.
00035  */
00036 
00037 import java.io.*;
00038 import java.util.*;
00039 
00040 public class bloatsoup {
00041     public static void main(String[] args) {
00042         if (args.length == 0) {
00043             System.out.println("usage:  bloatsoup trace");
00044             System.exit(1);
00045         }
00046 
00047         FileLocator.USE_BLAME = true;
00048         
00049         for (int i = 0; i < args.length; i++) {
00050             cook(args[i]);
00051         }
00052         
00053         // quit the application.
00054         System.exit(0);
00055     }
00056     
00060     static class Entry {
00061         String mAddress;
00062         Type mType;
00063         Object[] mReferences;
00064         CallTree.Node mCrawl;
00065         int mCrawlID;
00066         int mRefCount;
00067         Entry[] mParents;
00068         int mTotalSize;
00069 
00070         Entry(String addr, Type type, Object[] refs, CallTree.Node crawl) {
00071             mAddress = addr;
00072             mReferences = refs;
00073             mCrawl = crawl;
00074             mCrawlID = crawl.id;
00075             mRefCount = 0;
00076             mType = type;
00077             mTotalSize = 0;
00078         }
00079 
00080         void setParents(Vector parents) {
00081             mParents = new Entry[parents.size()];
00082             parents.copyInto(mParents);
00083         }
00084 
00085         public String toString() {
00086             String typeName = mType.mName;
00087             int typeSize = mType.mSize;
00088             String typeLink = "<A HREF=\"" + typeName + "_" + typeSize + ".html\">&LT;" + typeName + "&GT;</A> (" + typeSize + ")";
00089             return ("<A HREF=\"" + typeName + "_" + typeSize + ".html#" + mAddress + "\" onMouseDown='return showCrawl(event,\"" + mCrawlID + "\");'>" + mAddress + "</A> [" + mRefCount + "] " + typeLink + " {" + mTotalSize + "}");
00090         }
00091     }
00092 
00093     static class ByTypeBloat extends QuickSort.Comparator {
00094         Histogram hist;
00095         
00096         ByTypeBloat(Histogram hist) {
00097             this.hist = hist;
00098         }
00099     
00100         public int compare(Object obj1, Object obj2) {
00101             Entry e1 = (Entry) obj1, e2 = (Entry) obj2;
00102             Type t1 = e1.mType, t2 = e2.mType;
00103             return (hist.count(t2) * t2.mSize - hist.count(t1) * t1.mSize);
00104         }
00105     }
00106 
00111     static class HistComparator extends QuickSort.Comparator {
00112         Histogram hist;
00113         
00114         HistComparator(Histogram hist) {
00115             this.hist = hist;
00116         }
00117     
00118         public int compare(Object obj1, Object obj2) {
00119             Type t1 = (Type) obj1, t2 = (Type) obj2;
00120             return (hist.count(t1) * t1.mSize - hist.count(t2) * t2.mSize);
00121         }
00122     }
00123 
00124     static void printHistogram(PrintWriter out, Histogram hist, String dir) throws IOException {
00125         // sort the types by histogram count.
00126         Object[] types = hist.objects();
00127         QuickSort sorter = new QuickSort(new HistComparator(hist));
00128         sorter.sort(types);
00129         
00130         out.println("<PRE>");
00131         int index = types.length;
00132         while (index > 0) {
00133             Type type = (Type) types[--index];
00134             int count = hist.count(type);
00135             String name = type.mName;
00136             int size = type.mSize;
00137             out.print("<A HREF=\"" + dir + name + "_" + size + ".html\">&LT;" + name + "&GT;</A> (" + size + ")");
00138             out.println(" : " + count + " {" + (count * type.mSize) + "}");
00139         }
00140         out.println("</PRE>");
00141     }
00142     
00143     static String kStackCrawlScript =
00144         "var currentURL = null;\n" +
00145         "function showCrawl(event, crawlID) {\n" +
00146         "   if (!(event.modifiers & Event.SHIFT_MASK)) return true;\n" +
00147         "   var l = document.layers['popup'];\n" +
00148         "   var docURL = document.URL;\n" +
00149         "   var crawlURL = docURL.substring(0, docURL.lastIndexOf('/') + 1) + crawlID + '.html';\n" +
00150         "   // alert(crawlURL);\n" +
00151         "   if (l.visibility == 'hide' || currentURL != crawlURL) {\n" +
00152         "      if (currentURL != crawlURL) {\n" +
00153         "         l.load(crawlURL, 800);\n" +
00154         "         currentURL = crawlURL;\n" +
00155         "      }\n" +
00156         "      l.top = event.target.y + 15;\n" +
00157         "      l.left = event.target.x + 30;\n" +
00158         "      l.visibility='show';\n" +
00159         "   } else {\n" +
00160         "      l.visibility = 'hide';\n" +
00161         "   }\n" +
00162         "   return false;\n" +
00163         "}\n" +
00164         "";
00165 
00166     static String kStackCrawlPrefix =
00167         "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=3><TR><TD BGCOLOR=#00A000>\n" +
00168         "<TABLE BORDER=0 CELLSPACING=0 CELLPADDING=6><TR><TD BGCOLOR=#FFFFFF>\n" +
00169         "<PRE>\n" +
00170         "";
00171     static String kStackCrawlSuffix =
00172         "</PRE>\n" +
00173         "</TD></TR></TABLE>\n" +
00174         "</TD></TR></TABLE>\n" +
00175         "";
00176 
00180     static void cook(String inputName) {
00181         String outputName = inputName + ".html";
00182         String contentsDir = inputName + ".contents/"; 
00183 
00184         try {
00185             int objectCount = 0;
00186             long totalSize = 0;
00187             
00188             StringTable strings = new StringTable();
00189             strings.internAs("void*", "void");
00190             Hashtable types = new Hashtable();
00191             Histogram hist = new Histogram();
00192             CallTree calls = new CallTree(strings);
00193             Hashtable entriesTable = new Hashtable();
00194             Vector vec = new Vector();
00195             BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(inputName)));
00196             String line = reader.readLine();
00197             while (line != null) {
00198                 if (line.startsWith("0x")) {
00199                     String addr = strings.intern(line.substring(0, 10));
00200                     String name = strings.intern(line.substring(line.indexOf('<') + 1, line.indexOf('>')));
00201                     int size;
00202                     try {
00203                         String str = line.substring(line.indexOf('(') + 1, line.indexOf(')'));
00204                         size = Integer.parseInt(str);
00205                     } catch (NumberFormatException nfe) {
00206                         size = 0;
00207                     }
00208                     String key = strings.intern(name + "_" + size);
00209                     Type type = (Type) types.get(key);
00210                     if (type == null) {
00211                         type = new Type(name, size);
00212                         types.put(key, type);
00213                     }
00214                     hist.record(type);
00215                     
00216                     ++objectCount;
00217                     totalSize += size;
00218 
00219                     line = reader.readLine();
00220 
00221                     // process references (optional).
00222                     Object[] refs = null;
00223                     if (line != null && line.charAt(0) == '\t') {
00224                         vec.setSize(0);
00225                         do {
00226                             vec.addElement(strings.intern(line.substring(1, 11)));
00227                             line = reader.readLine();
00228                         } while (line != null && line.charAt(0) == '\t');
00229                         refs = new Object[vec.size()];
00230                         vec.copyInto(refs);
00231                     }
00232                     
00233                     // process stack crawl (optional).
00234                     CallTree.Node crawl = null;
00235                     if (line != null && line.charAt(0) == '<') {
00236                         do {
00237                             crawl = calls.parseNode(line);
00238                             line = reader.readLine();
00239                         } while (line != null && line.charAt(0) == '<');
00240                     }
00241                     
00242                     entriesTable.put(addr, new Entry(addr, type, refs, crawl));
00243                 } else {
00244                     line = reader.readLine();
00245                 }
00246             }
00247             reader.close();
00248             
00249             // build the entries array & graph.
00250             Entry[] entries = new Entry[objectCount];
00251 
00252             // now, we have a table full of leaked objects, lets derive reference counts, and build the graph.
00253             {
00254                 Hashtable parentTable = new Hashtable();
00255                 int entryIndex = 0;
00256                 Enumeration e = entriesTable.elements();
00257                 while (e.hasMoreElements()) {
00258                     Entry entry = (Entry) e.nextElement();
00259                     Object[] refs = entry.mReferences;
00260                     if (refs != null) {
00261                         int count = refs.length;
00262                         for (int i = 0; i < count; ++i) {
00263                             String addr = (String) refs[i];
00264                             Entry ref = (Entry) entriesTable.get(addr);
00265                             if (ref != null) {
00266                                 // increase the ref count.
00267                                 ref.mRefCount++;
00268                                 // change string to ref itself.
00269                                 refs[i] = ref;
00270                                 // add entry to ref's parents vector.
00271                                 Vector parents = (Vector) parentTable.get(ref);
00272                                 if (parents == null) {
00273                                     parents = new Vector();
00274                                     parentTable.put(ref, parents);
00275                                 }
00276                                 parents.addElement(entry);
00277                             }
00278                         }
00279                     }
00280                     entries[entryIndex++] = entry;
00281                 }
00282 
00283                 // be nice to the GC.
00284                 entriesTable.clear();
00285                 entriesTable = null;
00286                 
00287                 // set the parents of each entry.
00288                 e = parentTable.keys();
00289                 while (e.hasMoreElements()) {
00290                     Entry entry = (Entry) e.nextElement();
00291                     Vector parents = (Vector) parentTable.get(entry);
00292                     if (parents != null)
00293                         entry.setParents(parents);
00294                 }
00295                 
00296                 // be nice to the GC.
00297                 parentTable.clear();
00298                 parentTable = null;
00299             }
00300             
00301             // Sort the entries by type bloat.
00302             {
00303                 QuickSort sorter = new QuickSort(new ByTypeBloat(hist));
00304                 sorter.sort(entries);
00305             }
00306 
00307             // Create the bloat summary report.
00308             PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputName))));
00309             Date now = new Date();
00310             writer.println("<HTML><HEAD><TITLE>Bloat Summary as of: " + now + "</TITLE></HEAD>");
00311             
00312             // print bloat summary.
00313             writer.println("<H2>Bloat Summary</H2>");
00314             writer.println("total object count = " + objectCount + "<BR>");
00315             writer.println("total memory bloat = " + totalSize + " bytes.<BR>");
00316             
00317             // print histogram sorted by count * size.
00318             writer.println("<H2>Bloat Histogram</H2>");
00319             printHistogram(writer, hist, contentsDir);
00320             writer.println("</BODY></HTML>");
00321             writer.close();
00322             writer = null;
00323             
00324             // ensure the contents directory has been created.
00325             File contentsFile = new File(inputName + ".contents");
00326             if (!contentsFile.exists())
00327                 contentsFile.mkdir();
00328             
00329             // create the stack crawl script.
00330             outputName = contentsDir + "showCrawl.js";
00331             writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputName))));
00332             writer.print(kStackCrawlScript);
00333             writer.close();
00334             writer = null;
00335 
00336             // print the Entries graph.
00337             int length = entries.length;
00338             Type anchorType = null;
00339             for (int i = 0; i < length; ++i) {
00340                 Entry entry = entries[i];
00341                 if (anchorType != entry.mType) {
00342                     if (writer != null) {
00343                         writer.println("</PRE>");
00344                         writer.close();
00345                     }
00346                     anchorType = entry.mType;
00347                     outputName = contentsDir + anchorType.mName + "_" + anchorType.mSize + ".html";
00348                     writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputName))));
00349                     // set up the stack crawl script. use a <LAYER> on Communicator 4.X, a separate
00350                     // window on other browsers.
00351                     writer.println("<TITLE>&LT;" + anchorType.mName + "&GT; (" + anchorType.mSize + ")</TITLE>");
00352                     writer.println("<SCRIPT src='showCrawl.js'></SCRIPT>");
00353                     writer.println("<LAYER NAME='popup' LEFT=0 TOP=0 BGCOLOR='#FFFFFF' VISIBILITY='hide'></LAYER>");
00354                     writer.println("<A NAME=\"" + anchorType.mName + "_" + anchorType.mSize + "\"></A>");
00355                     writer.println("<H3>" + anchorType + " Bloat</H3>");
00356                     writer.println("<PRE>");
00357                 }
00358                 writer.println("<A NAME=\"" + entry.mAddress + "\"></A>");
00359                 if (entry.mParents != null) {
00360                     writer.print(entry);
00361                     writer.println(" <A HREF=\"#" + entry.mAddress + "_parents\">parents</A>");
00362                 } else {
00363                     writer.println(entry);
00364                 }
00365                 // print object's fields:
00366                 Object[] refs = entry.mReferences;
00367                 if (refs != null) {
00368                     int count = refs.length;
00369                     for (int j = 0; j < count; ++j)
00370                         leaksoup.printField(writer, refs[j]);
00371                 }
00372                 // print object's stack crawl, if it hasn't been printed already.
00373                 CallTree.Node crawl = entry.mCrawl;
00374                 int crawlID = crawl.id;
00375                 if (crawlID > 0) {
00376                     // encode already printed by negating the crawl id.
00377                     crawl.id = -crawlID;
00378                     File crawlFile = new File(contentsDir + crawlID + ".html");
00379                     if (! crawlFile.exists()) {
00380                         PrintWriter crawlWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(crawlFile))));
00381                         crawlWriter.print(kStackCrawlPrefix);
00382                         while (crawl != null) {
00383                             String location = FileLocator.getFileLocation(crawl.data);
00384                             crawlWriter.println(location);
00385                             crawl = crawl.parent;
00386                         }
00387                         crawlWriter.print(kStackCrawlSuffix);
00388                         crawlWriter.close();
00389                     }
00390                 }
00391                 // print object's parents.
00392                 if (entry.mParents != null) {
00393                     writer.println("<A NAME=\"" + entry.mAddress + "_parents\"></A>");
00394                     writer.println("\nObject Parents:");
00395                     Entry[] parents = entry.mParents;
00396                     int count = parents.length;
00397                     for (int j = 0; j < count; ++j)
00398                         writer.println("\t" + parents[j]);
00399                 }
00400             }
00401             if (writer != null) {
00402                 writer.println("</PRE>");
00403                 writer.close();
00404             }
00405         } catch (Exception e) {
00406             e.printStackTrace(System.err);
00407         }
00408     }
00409 }