Add leak logging for node info managers that can be added to leak-gauge. b=414704 r+sr=sicking a=schrep
authordbaron@dbaron.org
Fri, 08 Feb 2008 11:55:03 -0800
changeset 11410 99161526f261d7ddd4d052b180fac7fc2370bc43
parent 11409 57945916f0e58228738d48d9636ac35565538afd
child 11411 38b06c09f9ad17898e854d7ed68e0cfd9552087a
push idunknown
push userunknown
push dateunknown
reviewersschrep
bugs414704
milestone1.9b4pre
Add leak logging for node info managers that can be added to leak-gauge. b=414704 r+sr=sicking a=schrep
content/base/src/nsNodeInfoManager.cpp
tools/footprint/leak-gauge.html
tools/footprint/leak-gauge.pl
--- a/content/base/src/nsNodeInfoManager.cpp
+++ b/content/base/src/nsNodeInfoManager.cpp
@@ -48,16 +48,26 @@
 #include "nsIPrincipal.h"
 #include "nsIURI.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsGkAtoms.h"
 #include "nsComponentManagerUtils.h"
 #include "nsLayoutStatics.h"
 
+#ifdef MOZ_LOGGING
+// so we can get logging even in release builds
+#define FORCE_PR_LOG 1
+#endif
+#include "prlog.h"
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gNodeInfoManagerLeakPRLog;
+#endif
+
 PLHashNumber
 nsNodeInfoManager::GetNodeInfoInnerHashValue(const void *key)
 {
   NS_ASSERTION(key, "Null key passed to nsNodeInfo::GetHashValue!");
 
   const nsINodeInfo::nsNodeInfoInner *node =
     reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key);
 
@@ -86,30 +96,45 @@ nsNodeInfoManager::nsNodeInfoManager()
   : mDocument(nsnull),
     mPrincipal(nsnull),
     mTextNodeInfo(nsnull),
     mCommentNodeInfo(nsnull),
     mDocumentNodeInfo(nsnull)
 {
   nsLayoutStatics::AddRef();
 
+#ifdef PR_LOGGING
+  if (!gNodeInfoManagerLeakPRLog)
+    gNodeInfoManagerLeakPRLog = PR_NewLogModule("NodeInfoManagerLeak");
+
+  if (gNodeInfoManagerLeakPRLog)
+    PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
+           ("NODEINFOMANAGER %p created", this));
+#endif
+
   mNodeInfoHash = PL_NewHashTable(32, GetNodeInfoInnerHashValue,
                                   NodeInfoInnerKeyCompare,
                                   PL_CompareValues, nsnull, nsnull);
 }
 
 
 nsNodeInfoManager::~nsNodeInfoManager()
 {
   if (mNodeInfoHash)
     PL_HashTableDestroy(mNodeInfoHash);
 
   // Note: mPrincipal may be null here if we never got inited correctly
   NS_IF_RELEASE(mPrincipal);
 
+#ifdef PR_LOGGING
+  if (gNodeInfoManagerLeakPRLog)
+    PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
+           ("NODEINFOMANAGER %p destroyed", this));
+#endif
+
   nsLayoutStatics::Release();
 }
 
 
 nsrefcnt
 nsNodeInfoManager::AddRef()
 {
   NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
@@ -145,16 +170,22 @@ nsNodeInfoManager::Init(nsIDocument *aDo
   nsresult rv = CallCreateInstance("@mozilla.org/nullprincipal;1",
                                    &mPrincipal);
   NS_ENSURE_TRUE(mPrincipal, rv);
 
   mDefaultPrincipal = mPrincipal;
 
   mDocument = aDocument;
 
+#ifdef PR_LOGGING
+  if (gNodeInfoManagerLeakPRLog)
+    PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
+           ("NODEINFOMANAGER %p Init document=%p", this, aDocument));
+#endif
+
   return NS_OK;
 }
 
 void
 nsNodeInfoManager::DropDocumentReference()
 {
   mDocument = nsnull;
 }
--- a/tools/footprint/leak-gauge.html
+++ b/tools/footprint/leak-gauge.html
@@ -116,26 +116,33 @@ function run() {
                         ++this.count;
                     } else if (verb == "destroyed") {
                         delete this.docs[addr];
                     } else if (verb == "ResetToURI" ||
                                verb == "StartDocumentLoad") {
                         var m = rest.match(/^ (.*)$/);
                         if (!m)
                             throw "URI expected";
-                        this.docs[addr][m[1]] = true;
+                        var uri = m[1];
+                        var doc_info = this.docs[addr];
+                        doc_info[uri] = true;
+                        if ("nim" in doc_info) {
+                            doc_info["nim"][uri] = true;
+                        }
                     }
                 }
             },
             dump: function() {
                 for (var addr in this.docs) {
                     var doc = this.docs[addr];
                     result += "Leaked document at address " + addr + ".\n";
                     for (var uri in doc) {
-                        result += " ... with URI \"" + uri + "\".\n";
+                        if (uri != "nim") {
+                            result += " ... with URI \"" + uri + "\".\n";
+                        }
                     }
                 }
             },
             summary: function() {
                 var len = 0;
                 for (var w in this.docs)
                     ++len;
                 result += 'Leaked ' + len + ' out of ' +
@@ -176,16 +183,63 @@ function run() {
             },
             summary: function() {
                 var len = 0;
                 for (var w in this.shells)
                     ++len;
                 result += 'Leaked ' + len + ' out of ' +
                           this.count + " docshells\n";
             }
+        },
+        "NODEINFOMANAGER": {
+            count: 0,
+            nims: {},
+            handle_line: function(line) {
+                var match = line.match(/^([0-9a-f]*) (\S*)(.*)/);
+                if (match) {
+                    var addr = match[1];
+                    var verb = match[2];
+                    var rest = match[3];
+                    if (verb == "created") {
+                        this.nims[addr] = {};
+                        ++this.count;
+                    } else if (verb == "destroyed") {
+                        delete this.nims[addr];
+                    } else if (verb == "Init") {
+                        var m = rest.match(/^ document=(.*)$/);
+                        if (!m)
+                            throw "document pointer expected";
+                        var nim_info = this.nims[addr];
+                        var doc = m[1];
+                        if (doc != "0") {
+                            var doc_info = handlers["DOCUMENT"].docs[doc];
+                            for (var uri in doc_info) {
+                                nim_info[uri] = true;
+                            }
+                            doc_info["nim"] = nim_info;
+                        }
+                    }
+                }
+            },
+            dump: function() {
+                for (var addr in this.nims) {
+                    var nim = this.nims[addr];
+                    result += "Leaked content nodes associated with node info manager at address " + addr + ".\n";
+                    for (var uri in nim) {
+                        result += " ... with document URI \"" + uri + "\".\n";
+                    }
+                }
+            },
+            summary: function() {
+                var len = 0;
+                for (var w in this.nims)
+                    ++len;
+                result += 'Leaked content nodes in ' + len + ' out of ' +
+                          this.count + " documents\n";
+            }
         }
     };
 
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
     const cs = Components.classes;
     const ifs = Components.interfaces;
 
@@ -232,29 +286,29 @@ function run() {
 }
 
 </script>
 </head>
 <body>
 
 <h1>Leak Gauge</h1>
 
-<pre>$Id: leak-gauge.html,v 1.6 2006/01/14 00:27:41 dbaron%dbaron.org Exp $</pre>
+<pre>$Id: leak-gauge.html,v 1.7 2008/02/08 19:55:03 dbaron%dbaron.org Exp $</pre>
 
 <p>This script is designed to help testers isolate and simplify testcases
 for many classes of leaks (those that involve large graphs of core
 data structures) in Mozilla-based browsers.  It is designed to print
 information about what has leaked by processing a log taken while
 running the browser.  Such a log can be taken over a long session of
 normal browsing and then the log can be processed to find sites that
 leak.  Once a site is known to leak, the logging can then be repeated
 to figure out under what conditions the leak occurs.</p>
 
 <p>The way to create this log is to set the environment variables:</p>
-<pre>  NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5
+<pre>  NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5,NodeInfoManagerLeak:5
   NSPR_LOG_FILE=nspr.log     <i>(or any other filename of your choice)</i></pre>
 <p>in your shell and then run the program.</p>
 <ul>
 <li>In a Windows command prompt, set environment variables with
 <pre>    set VAR=value</pre></li>
 <li> In an sh-based shell such as bash, set environment variables with
 <pre>    export VAR=value</pre></li>
 <li>In a csh-based shell such as tcsh, set environment variables with
--- a/tools/footprint/leak-gauge.pl
+++ b/tools/footprint/leak-gauge.pl
@@ -31,28 +31,28 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
-# $Id: leak-gauge.pl,v 1.7 2006/01/14 00:27:41 dbaron%dbaron.org Exp $
+# $Id: leak-gauge.pl,v 1.8 2008/02/08 19:55:03 dbaron%dbaron.org Exp $
 # This script is designed to help testers isolate and simplify testcases
 # for many classes of leaks (those that involve large graphs of core
 # data structures) in Mozilla-based browsers.  It is designed to print
 # information about what has leaked by processing a log taken while
 # running the browser.  Such a log can be taken over a long session of
 # normal browsing and then the log can be processed to find sites that
 # leak.  Once a site is known to leak, the logging can then be repeated
 # to figure out under what conditions the leak occurs.
 #
 # The way to create this log is to set the environment variables:
-#   NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5
+#   NSPR_LOG_MODULES=DOMLeak:5,DocumentLeak:5,nsDocShellLeak:5,NodeInfoManagerLeak:5
 #   NSPR_LOG_FILE=nspr.log     (or any other filename of your choice)
 # in your shell and then run the program.
 # * In a Windows command prompt, set environment variables with
 #     set VAR=value
 # * In an sh-based shell such as bash, set environment variables with
 #     export VAR=value
 # * In a csh-based shell such as tcsh, set environment variables with
 #     setenv VAR value
@@ -122,39 +122,45 @@ my $handlers = {
                   ${$self}{count} . " DOM Windows\n";
         }
     },
     "DOCUMENT" => {
         count => 0,
         docs => {},
         handle_line => sub($$) {
             my ($self, $line) = @_;
-            my $docs = ${$self}{docs};
+            # This doesn't work; I don't have time to figure out why not.
+            # my $docs = ${$self}{docs};
+            my $docs = ${$handlers}{"DOCUMENT"}{docs};
             if ($line =~ /^([0-9a-f]*) (\S*)/) {
                 my ($addr, $verb, $rest) = ($1, $2, $');
                 if ($verb eq "created") {
                     ${$docs}{$addr} = {};
                     ++${$self}{count};
                 } elsif ($verb eq "destroyed") {
                     delete ${$docs}{$addr};
                 } elsif ($verb eq "ResetToURI" ||
                          $verb eq "StartDocumentLoad") {
                     $rest =~ /^ (.*)$/ || die "URI expected";
                     my $uri = $1;
-                    ${${$docs}{$addr}}{$uri} = 1;
+                    my $doc_info = ${$docs}{$addr};
+                    ${$doc_info}{$uri} = 1;
+                    if (exists(${$doc_info}{"nim"})) {
+                        ${$doc_info}{"nim"}{$uri} = 1;
+                    }
                 }
             }
         },
         dump => sub ($) {
             my ($self) = @_;
             my $docs = ${$self}{docs};
             foreach my $addr (keys(%{$docs})) {
                 print "Leaked document at address $addr.\n";
                 foreach my $uri (keys(%{${$docs}{$addr}})) {
-                    print " ... with URI \"$uri\".\n";
+                    print " ... with URI \"$uri\".\n" unless $uri eq "nim";
                 }
             }
         },
         summary => sub($) {
             my ($self) = @_;
             my $docs = ${$self}{docs};
             print 'Leaked ' . keys(%{$docs}) . ' out of ' .
                   ${$self}{count} . " documents\n";
@@ -192,16 +198,61 @@ my $handlers = {
             }
         },
         summary => sub($) {
             my ($self) = @_;
             my $shells = ${$self}{shells};
             print 'Leaked ' . keys(%{$shells}) . ' out of ' .
                   ${$self}{count} . " docshells\n";
         }
+    },
+    "NODEINFOMANAGER" => {
+        count => 0,
+        nims => {},
+        handle_line => sub($$) {
+            my ($self, $line) = @_;
+            my $nims = ${$self}{nims};
+            if ($line =~ /^([0-9a-f]*) (\S*)/) {
+                my ($addr, $verb, $rest) = ($1, $2, $');
+                if ($verb eq "created") {
+                    ${$nims}{$addr} = {};
+                    ++${$self}{count};
+                } elsif ($verb eq "destroyed") {
+                    delete ${$nims}{$addr};
+                } elsif ($verb eq "Init") {
+                    $rest =~ /^ document=(.*)$/ ||
+                        die "document pointer expected";
+                    my $doc = $1;
+                    if ($doc ne "0") {
+                        my $nim_info = ${$nims}{$addr};
+                        my $doc_info = ${$handlers}{"DOCUMENT"}{docs}{$doc};
+                        foreach my $uri (keys(%{$doc_info})) {
+                            ${$nim_info}{$uri} = 1;
+                        }
+                        ${$doc_info}{"nim"} = $nim_info;
+                    }
+                }
+            }
+        },
+        dump => sub ($) {
+            my ($self) = @_;
+            my $nims = ${$self}{nims};
+            foreach my $addr (keys(%{$nims})) {
+                print "Leaked content nodes associated with node info manager at address $addr.\n";
+                foreach my $uri (keys(%{${$nims}{$addr}})) {
+                    print " ... with document URI \"$uri\".\n";
+                }
+            }
+        },
+        summary => sub($) {
+            my ($self) = @_;
+            my $nims = ${$self}{nims};
+            print 'Leaked content nodes within ' . keys(%{$nims}) . ' out of ' .
+                  ${$self}{count} . " documents\n";
+        }
     }
 };
 
 while (<>) {
     # strip off initial "-", thread id, and thread pointer; separate
     # first word and rest
     if (/^\-?[0-9]*\[[0-9a-f]*\]: (\S*) ([^\n\r]*)[\n\r]*$/) {
         my ($handler, $data) = ($1, $2);