>> I wrote a small class for finding the missing leaks. Actually it just 
scans the qx-namespace and 
>> prints all living object (and the first found  reference-path to this 
>> object).

Here it is for your usage (see below), simply called by: 

----
var leakSearcher=new JSLeakSearcher();
leakSearcher.leakSearch();
---

>> Maybe I can incorporate your approach into this test or create an 
>> additional one.

You can access statistics through the members 'referencedObjects' and 
'referencedDisposedObjects'.
Each object-instance is just counted once, so this approach can be simply 
incorporated into your testcases, with the same pattern you already use 
(i.e. taking snapshots and then compare object-count).

------
/*
 * This class traverses the whole Object-Graph in the qx-namespace, 
finding any JSObject that won't be 
 * garbage collected due to other JS-references.
 * 
 * Note that (currently) an instance of the class can only be used for ONE 
call to the leakSearch function.
 * Duplicate calls will result in undefined behaviour so just create a new 
instance for every leak-search. 
 * 
 * Currently every found Object is counted and just printed to the debug 
log in two categories:
 * - normal qx-Objects which haven't been disposed yet  (see member 
'referencedObjects')
 * - disposed objects, which still have references on them. (see member 
'referencedDisposedObjects')
 * 
 * The first category might be pointing to programming errors, if the 
count constantly increases. Some object
 * disposals might be missing.
 * The second category is always pointing to programming errors - disposed 
objects should never be referenced
 * by any other objects any more.
 * 
 * As a third category also all links into the Browser-DOM are printed. In 
a normal application this shouldn't
 * increase as well over time.
 * 
 * 
 * Further ideas for this class: Just counting and writing to the log is 
good enough for Unit-Testing where you
 * make a 'before' and 'after' snapshot of a fixture that creates and 
destroys some objects.
 * Also finding leaks in the log is not too difficult, as leaking 
references tend to accumulate at one spot which
 * the eye can spot quite fast.
 * 
 * This class could easily be extended to create a histogram of active 
classes for debugging larger applications
 * and spotting potential missing disposals.
 * 
 */

qx.Class.define("JSLeakSearcher",
{
  extend : qx.core.Object,

  members :
  {
    referencedObjects: 0, 
    referencedDisposedObjects: 0, 
 
    /* current call-stack */
    __recursionInfo: new Array(), 
 
    /* Creates a string-representation of the recursion path.  */
    __recursionInfoToString : function() {
      var recursionInfoString="";
      for (var i=0;i<this.__recursionInfo.length; i++) 
      {
        recursionInfoString+="->"+this.__recursionInfo[i];
      }
      return recursionInfoString; 
    },

    /* starts a leak-search in the qx-namespace */
    leakSearch : function() {
      this.__leakSearch(window.qx, "qx");
    },

    /* internal leak-search-function. Will be called recursively */
    __leakSearch : function(obj, currentPath)
    {
      // avoid traversing cycles, so don't follow any js-object, that is 
alreadyMarked.
      // Instead just increase reference-count
      var marker='$$$leakSearcher_refCount_'+this.$$hash;
      if (obj[marker]) 
      {
        obj[marker]++; 
      } else {
        // mark object to avoid traversing in cycles
        obj[marker]=1;
 
        this.__recursionInfo.push(currentPath);
 
        this.__updateStatistics(obj);
 
        // Arrays are just traversed for each entry
        if (obj instanceof Array)
        {
          this.__leakSearchArrayElements(obj);
        }
        // Objects are traversed for each of their keys
        else if (obj instanceof Object)
        {
          this.__leakSearchObjectReferences(obj);
        }
 
        this.__recursionInfo.pop();
      }
    }, 
 
    __updateStatistics: function(obj) {
      // found a reference to a qooxdoo object ...
      // Depending on the dispose-state issue a simple log or an error 
log.
      if (obj instanceof qx.core.Object)
      {
        if (obj.isDisposed()) 
        {
          this.error("Found disposed but still referenced Obj '" + 
obj.classname + "' , hash '" + obj.$$hash+"', path: '"+this
__recursionInfoToString()+"'");
          this.referencedDisposedObjects++;
        } else {
          this.info("Found living Obj '" + obj.classname + "' , hash '" + 
obj.$$hash+"', path: '"+this.__recursionInfoToString()+"'");
          this.referencedObjects++; 
        }
      }
    },
 
    __leakSearchArrayElements: function(array) {
      for (var i=0, l=array.length; i<l; i++)
      {
        var entry = array[i];

        if (entry == null || !(typeof entry== "object")) 
        {
          continue;
        }

        this.__leakSearch(entry, "["+i+"]");
      }
    }, 
 
    __leakSearchObjectReferences: function(obj) {
      // don't follow references into the DOM
      if (qx.dom.Node.isElement(obj) || qx.dom.Node.isWindow(obj) || 
qx.dom.Node.isDocument(obj)) 
      {
        this.info("Stopped at link to DOM, path: '" + this
__recursionInfoToString() + "'");
      } else {
        for (var key in obj)
        {
          if (obj[key] == null || !obj.hasOwnProperty(key) || key==
'prototype') 
          {
            continue;
          }
 
          this.__leakSearch(obj[key], key);
        }
      }

    } 
  }
});
------
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
qooxdoo-devel mailing list
qooxdoo-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel

Reply via email to