Revision: 2780 http://vexi.svn.sourceforge.net/vexi/?rev=2780&view=rev Author: mkpg2 Date: 2008-01-31 10:26:49 -0800 (Thu, 31 Jan 2008)
Log Message: ----------- Feature. Memory leak hunting - dump the stack traces of created objects in descending order of their frequencies - show the reference chain from a static root to the leak Modified Paths: -------------- trunk/core/org.vexi.devl/.classpath trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.core.prefs trunk/core/org.vexi.devl/src/org/vexi/devl/Main.java Added Paths: ----------- trunk/core/org.vexi.devl/lib/insanelib.jar trunk/core/org.vexi.devl/src/org/vexi/devl/Devl.java trunk/core/org.vexi.devl/src/org/vexi/devl/GCUtil.java Removed Paths: ------------- trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.ui.prefs Property Changed: ---------------- trunk/core/org.vexi.devl/ Property changes on: trunk/core/org.vexi.devl ___________________________________________________________________ Name: svn:ignore - src_gen _vexi_test _temp_ + src_gen _vexi_test _temp_ bin Modified: trunk/core/org.vexi.devl/.classpath =================================================================== --- trunk/core/org.vexi.devl/.classpath 2008-01-31 18:18:33 UTC (rev 2779) +++ trunk/core/org.vexi.devl/.classpath 2008-01-31 18:26:49 UTC (rev 2780) @@ -1,13 +1,14 @@ -<?xml version="1.0" encoding="UTF-8"?> -<classpath> - <classpathentry kind="src" path="src"/> - <classpathentry kind="src" path="src_gen"/> - <classpathentry excluding="testold/debug/|testold/debug/simple/|testold/debug/thread/|testold/debug/vars/" kind="src" path="src_junit"/> - <classpathentry combineaccessrules="false" kind="src" path="/org.ibex.js"/> - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="lib" path="lib/commons-codec-1.3.jar"/> - <classpathentry kind="lib" path="lib/xmlrpc-2.0.jar"/> - <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3.8.1"/> - <classpathentry combineaccessrules="false" kind="src" path="/org.vexi.core"/> - <classpathentry kind="output" path="bin"/> -</classpath> +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="src_gen"/> + <classpathentry excluding="testold/debug/|testold/debug/simple/|testold/debug/thread/|testold/debug/vars/" kind="src" path="src_junit"/> + <classpathentry combineaccessrules="false" kind="src" path="/org.ibex.js"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> + <classpathentry kind="lib" path="lib/commons-codec-1.3.jar"/> + <classpathentry kind="lib" path="lib/xmlrpc-2.0.jar"/> + <classpathentry kind="lib" path="lib/insanelib.jar"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3.8.1"/> + <classpathentry combineaccessrules="false" kind="src" path="/org.vexi.core"/> + <classpathentry kind="output" path="bin"/> +</classpath> Modified: trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.core.prefs =================================================================== --- trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.core.prefs 2008-01-31 18:18:33 UTC (rev 2779) +++ trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.core.prefs 2008-01-31 18:26:49 UTC (rev 2780) @@ -1,12 +1,12 @@ -#Sat Sep 09 17:32:01 CEST 2006 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.4 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning -org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning -org.eclipse.jdt.core.compiler.source=1.3 +#Thu Jan 31 16:20:34 GMT 2008 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.4 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning +org.eclipse.jdt.core.compiler.source=1.4 Deleted: trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.ui.prefs =================================================================== --- trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.ui.prefs 2008-01-31 18:18:33 UTC (rev 2779) +++ trunk/core/org.vexi.devl/.settings/org.eclipse.jdt.ui.prefs 2008-01-31 18:26:49 UTC (rev 2780) @@ -1,3 +0,0 @@ -#Sat Sep 09 17:32:01 CEST 2006 -eclipse.preferences.version=1 -internal.default.compliance=default Added: trunk/core/org.vexi.devl/lib/insanelib.jar =================================================================== (Binary files differ) Property changes on: trunk/core/org.vexi.devl/lib/insanelib.jar ___________________________________________________________________ Name: svn:mime-type + application/octet-stream Added: trunk/core/org.vexi.devl/src/org/vexi/devl/Devl.java =================================================================== --- trunk/core/org.vexi.devl/src/org/vexi/devl/Devl.java (rev 0) +++ trunk/core/org.vexi.devl/src/org/vexi/devl/Devl.java 2008-01-31 18:26:49 UTC (rev 2780) @@ -0,0 +1,153 @@ +package org.vexi.devl; + +import java.awt.event.KeyEvent; +import java.lang.ref.WeakReference; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.WeakHashMap; + +import org.ibex.js.JS; +import org.ibex.js.JSExn; +import org.ibex.js.JSU; +import org.ibex.util.Basket; +import org.ibex.util.Log; +import org.vexi.plat.Platform.Scheduler; + +public class Devl extends JS.Immutable implements JSU.Devl { + + static JS MARKER = new JS.Obj(); + static WeakReference MARKER_REF; + + public JS get(JS key_) throws JSExn { + String key = JSU.toString(key_); + if("MARKER".equals(key)){ + if(MARKER == null) + return null; + JS r = MARKER; + MARKER_REF = new WeakReference(MARKER); + MARKER = null; + return r; + } + return null; + } + + public void interceptKeyPress(KeyEvent k){ + if(k.getKeyCode() == KeyEvent.VK_F12) + dumpStacks(); + else if(k.getKeyCode() == KeyEvent.VK_F11){ + if(MARKER != null) Log.warn(Devl.class, "Marker has not been set yet"); + else{ + GCUtil.assertGC("MARKER not collected", MARKER_REF); + + } + } + } + + + static private Integer ZERO = new Integer(0); + public void dumpStacks(){ + + System.err.println("Object creation dump"); + + //while(JSU.stackToCount.size()>0){ + for(int i=0;i<10 && stackToCount.size()>0; i++){ + Integer highest = ZERO; + Iterator Vs = stackToCount.values().iterator(); + while(Vs.hasNext()){ + Integer next = (Integer)Vs.next(); + if(next.intValue()>highest.intValue()) + highest = next; + } + + Iterator Ks = stackToCount.keySet().iterator(); + while(Ks.hasNext()){ + String k = (String)Ks.next(); + Integer v = (Integer)stackToCount.get(k); + if(v.intValue()==highest.intValue()){ + System.err.println(v); + System.err.println(k); + stackToCount.remove(k); + break; + } + } + + + } + + } + + + //////// + // MEMORY LEAK TRACKING + ///// + // FEATURE - make this an option in the devl version that can be switched on + static private Map hashToObjInfo = new HashMap(); + static private Map stackToCount = new HashMap(); + + + static class ObjInfo{ + final String bt; // the backtrace when it is created + ObjInfo(String bt){ this.bt = bt; } + String firstLine(){ return bt.split("\\n")[0]; } + } + + static private boolean doesOverrideHashCode(Object o){ + try { + java.lang.reflect.Method m = o.getClass().getMethod("hashCode", new Class[]{}); + Class declaring = m.getDeclaringClass(); + if(Object.class!=declaring) + return false; + return true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + static public ObjInfo get(Object o){ + if(!doesOverrideHashCode(o)) + return null; + + Integer hash = new Integer(o.hashCode()); + return (ObjInfo) hashToObjInfo.get(hash); + } + + public void register(Object o){ + if(!(o instanceof JSExn.ExnJSObj)){ + if(!doesOverrideHashCode(o)) + throw new RuntimeException("Cannot register objects with overridden hashcodes " + o.getClass().getName()); + + Integer hash = new Integer(o.hashCode()); + ObjInfo oi = objInfo(); + hashToObjInfo.put(hash, oi); + + Integer c = (Integer)stackToCount.get(oi.bt); + if(c==null) stackToCount.put(oi.bt,new Integer(0)); + else stackToCount.put(oi.bt,new Integer(c.intValue()+1)); + } + } + + public void unregister(Object o){ + if(!(o instanceof JSExn.ExnJSObj)){ + Integer hash = new Integer(o.hashCode()); + ObjInfo oi = (ObjInfo) hashToObjInfo.remove(hash); + + Integer c = (Integer)stackToCount.get(oi.bt); + if(c.intValue()==1) stackToCount.remove(oi.bt); + else stackToCount.put(oi.bt,new Integer(c.intValue()-1)); + } + } + + + static public ObjInfo objInfo(){ + Basket.List bt = JSU.backtrace(); + StringBuffer b = new StringBuffer(512); + for(int i=0; i<bt.size(); i++){ + if(i!=0) b.append("\n"); + b.append(bt.get(i)); + } + return new ObjInfo(b.toString().intern()); + } + +} Property changes on: trunk/core/org.vexi.devl/src/org/vexi/devl/Devl.java ___________________________________________________________________ Name: svn:mime-type + text/plain Added: trunk/core/org.vexi.devl/src/org/vexi/devl/GCUtil.java =================================================================== --- trunk/core/org.vexi.devl/src/org/vexi/devl/GCUtil.java (rev 0) +++ trunk/core/org.vexi.devl/src/org/vexi/devl/GCUtil.java 2008-01-31 18:26:49 UTC (rev 2780) @@ -0,0 +1,238 @@ +/* + * Sun Public License Notice + * + * The contents of this file are subject to the Sun Public License + * Version 1.0 (the "License"). You may not use this file except in + * compliance with the License. A copy of the License is available at + * http://www.sun.com/ + * + * The Original Code is NetBeans. The Initial Developer of the Original + * Code is Sun Microsystems, Inc. Portions Copyright 1997-2005 Sun + * Microsystems, Inc. All Rights Reserved. + */ +// Code derived from NbTestCase + + +package org.vexi.devl; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; + +import org.ibex.js.JS; +import org.ibex.js.JSExn; +import org.ibex.js.JSU; +import org.ibex.util.Log; +import org.netbeans.insane.scanner.ObjectMap; +import org.netbeans.insane.scanner.ScannerUtils; +import org.netbeans.insane.scanner.Visitor; +/** + * + */ +public abstract class GCUtil { + + + /** Assert GC. Tries to GC ref's referent. + * @param text the text to show when test fails. + * @param ref the referent to object that + * should be GCed + */ + public static void assertGC(String text, java.lang.ref.Reference ref) { + ArrayList alloc = new ArrayList (); + int size = 100000; + for (int i = 0; i < 50; i++) { + if (ref.get() == null) { + return; + } + System.gc(); + System.runFinalization(); + try { + alloc.add (new byte[size]); + size = (int)(((double)size) * 1.3); + } catch (OutOfMemoryError error) { + size = size / 2; + } + try { + if (i % 3 == 0) Thread.sleep(321); + } catch (InterruptedException t) { + // ignore + } + } + alloc = null; + Log.uWarn("GCUtil", "Reference not GC'd. Processing heap now, this may take a while ..."); + throw new AssertionError(text + ":\n" + findRefsFromRoot(ref.get())); + } + + private static String findRefsFromRoot(final Object target) { + final Map objects = new IdentityHashMap(); + boolean found = false; + + Visitor vis = new Visitor() { + public void visitClass(Class cls) {} + + public void visitObject(ObjectMap map, Object object) { + objects.put(object, new Entry(object)); + } + + public void visitArrayReference(ObjectMap map, Object from, Object to, int index) { + visitRef(from, to); + } + + public void visitObjectReference(ObjectMap map, Object from, Object to, java.lang.reflect.Field ref) { + visitRef(from, to); + } + + private void visitRef(Object from, Object to) { + ((Entry)objects.get(from)).addOut(to); + ((Entry)objects.get(to)).addIn(from); + if (to == target) throw new RuntimeException("Done"); + } + + + public void visitStaticReference(ObjectMap map, Object to, java.lang.reflect.Field ref) { + ((Entry)objects.get(to)).addStatic(ref); + if (to == target) throw new RuntimeException("Done"); + } + }; + + try { + ScannerUtils.scanExclusivelyInAWT(ScannerUtils.skipNonStrongReferencesFilter(), vis, ScannerUtils.interestingRoots()); + } catch (Exception ex) { + // found object + found = true; + } + + if (found) { + return findRoots(objects, target); + } else { + return "Not found!!!"; + } + } + /** BFS scan of incomming refs*/ + private static String findRoots(Map objects, Object obj) { + class PathElement { + private Entry item; + private PathElement next; + public PathElement(Entry item, PathElement next) { + this.item = item; + this.next = next; + } + + public Entry getItem() { + return item; + } + public String toString() { + if (next == null) { + return item.toString(); + } else { + return item.toString() + "->\n" + next.toString(); + } + } + } + + Set visited = new HashSet(); + Entry fin = (Entry)objects.get(obj); + assert(fin!=null); + + visited.add(fin); + LinkedList queue = new LinkedList(); + queue.add(new PathElement(fin, null)); + + while (!queue.isEmpty()) { + PathElement act = (PathElement)queue.remove(0); + // any static ref? + Iterator it = act.getItem().staticRefs(); + if (it.hasNext()) { + Field fld = (Field)it.next(); + return fld + "->\n" + act; + } + + // follow incomming + it = act.getItem().incommingRefs(); + while(it.hasNext()) { + Entry ref = (Entry)objects.get(it.next()); + assert(ref!=null); + + // add to the queue if not new + if (visited.add(ref)) queue.add(new PathElement(ref, act)); + } + } + return "Error"; + } + + + static Object[] EMPTY = new Object[0]; + + /** Entry represents one object and its incomming/outgoing refs */ + private static class Entry { + private Object obj; + private Object[] in; + private Object[] out; + private Object[] stat; + + public Entry(Object o) { + obj = o; + in = EMPTY; + out = EMPTY; + stat = EMPTY; + } + + void addOut(Object o) { + out = append(out, o); + } + + void addStatic(Field ref) { + stat = append(stat, ref); + } + + void addIn(Object o) { + in = append(in, o); + } + + public Iterator incommingRefs() { + return Arrays.asList(in).iterator(); + } + + public Iterator staticRefs() { + return Arrays.asList(stat).iterator(); + } + + public Iterator outgoingRefs() { + return Arrays.asList(stat).iterator(); + } + + private Object[] append(Object[] orig, Object add) { + int origLen = orig.length; + Object[] ret = new Object[origLen + 1]; + System.arraycopy(orig, 0, ret, 0, origLen); + ret[origLen] = add; + return ret; + } + + public String toString() { + String r = ""; + if(Devl.get(obj)!=null) + r+= Devl.get(obj).firstLine() + ":"; + else + r+= " "; + + if(obj instanceof JS){ + try { + return r+=JSU.json_marshal((JS)obj).coerceToString(); + } catch (JSExn e) { + return r+=obj.toString() + " (Could not stringify: "+e.getMessage()+")"; + } + }else{ + r+=obj.toString(); + } + return r; + //return obj.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(obj)); + } + } +} Property changes on: trunk/core/org.vexi.devl/src/org/vexi/devl/GCUtil.java ___________________________________________________________________ Name: svn:mime-type + text/plain Modified: trunk/core/org.vexi.devl/src/org/vexi/devl/Main.java =================================================================== --- trunk/core/org.vexi.devl/src/org/vexi/devl/Main.java 2008-01-31 18:18:33 UTC (rev 2779) +++ trunk/core/org.vexi.devl/src/org/vexi/devl/Main.java 2008-01-31 18:26:49 UTC (rev 2780) @@ -5,6 +5,9 @@ import org.ibex.js.DebugHandler; import org.ibex.js.Fountain; +import org.ibex.js.JS; +import org.ibex.js.JSExn; +import org.ibex.js.JSU; import org.ibex.js.Scheduler; import org.ibex.util.Log; import org.vexi.core.DevlTemplateBuilder; @@ -78,6 +81,8 @@ Fountain rr = org.vexi.core.Main.init(args); + JSU.devl = new Devl(); + if(debugmode) DebugHandler.instance.init(rr); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------- This SF.net email is sponsored by: Microsoft Defy all challenges. Microsoft(R) Visual Studio 2008. http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/ _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn