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 id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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);