browser/devtools/shared/test/leakhunt.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /**
     6  * Memory leak hunter. Walks a tree of objects looking for DOM nodes.
     7  * Usage:
     8  * leakHunt({
     9  *   thing: thing,
    10  *   otherthing: otherthing
    11  * });
    12  */
    13 function leakHunt(root) {
    14   var path = [];
    15   var seen = [];
    17   try {
    18     var output = leakHunt.inner(root, path, seen);
    19     output.forEach(function(line) {
    20       dump(line + '\n');
    21     });
    22   }
    23   catch (ex) {
    24     dump(ex + '\n');
    25   }
    26 }
    28 leakHunt.inner = function LH_inner(root, path, seen) {
    29   var prefix = new Array(path.length).join('  ');
    31   var reply = [];
    32   function log(msg) {
    33     reply.push(msg);
    34   }
    36   var direct
    37   try {
    38     direct = Object.keys(root);
    39   }
    40   catch (ex) {
    41     log(prefix + '  Error enumerating: ' + ex);
    42     return reply;
    43   }
    45   try {
    46     var index = 0;
    47     for (var data of root) {
    48       var prop = '' + index;
    49       leakHunt.digProperty(prop, data, path, seen, direct, log);
    50       index++;
    51     }
    52   }
    53   catch (ex) { /* Ignore things that are not enumerable */ }
    55   for (var prop in root) {
    56     var data;
    57     try {
    58       data = root[prop];
    59     }
    60     catch (ex) {
    61       log(prefix + '  ' + prop + ' = Error: ' + ex.toString().substring(0, 30));
    62       continue;
    63     }
    65     leakHunt.digProperty(prop, data, path, seen, direct, log);
    66   }
    68   return reply;
    69 }
    71 leakHunt.hide = [ /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/ ];
    73 leakHunt.noRecurse = [
    74   /^string$/, /^number$/, /^boolean$/, /^null/, /^undefined/,
    75   /^Window$/, /^Document$/,
    76   /^XULDocument$/, /^XULElement$/,
    77   /^DOMWindow$/, /^HTMLDocument$/, /^HTML.*Element$/, /^ChromeWindow$/
    78 ];
    80 leakHunt.digProperty = function LH_digProperty(prop, data, path, seen, direct, log) {
    81   var newPath = path.slice();
    82   newPath.push(prop);
    83   var prefix = new Array(newPath.length).join('  ');
    85   var recurse = true;
    86   var message = leakHunt.getType(data);
    88   if (leakHunt.matchesAnyPattern(message, leakHunt.hide)) {
    89     return;
    90   }
    92   if (message === 'function' && direct.indexOf(prop) == -1) {
    93     return;
    94   }
    96   if (message === 'string') {
    97     var extra = data.length > 10 ? data.substring(0, 9) + '_' : data;
    98     message += ' "' + extra.replace(/\n/g, "|") + '"';
    99     recurse = false;
   100   }
   101   else if (leakHunt.matchesAnyPattern(message, leakHunt.noRecurse)) {
   102     message += ' (no recurse)'
   103     recurse = false;
   104   }
   105   else if (seen.indexOf(data) !== -1) {
   106     message += ' (already seen)';
   107     recurse = false;
   108   }
   110   if (recurse) {
   111     seen.push(data);
   112     var lines = leakHunt.inner(data, newPath, seen);
   113     if (lines.length == 0) {
   114       if (message !== 'function') {
   115         log(prefix + prop + ' = ' + message + ' { }');
   116       }
   117     }
   118     else {
   119       log(prefix + prop + ' = ' + message + ' {');
   120       lines.forEach(function(line) {
   121         log(line);
   122       });
   123       log(prefix + '}');
   124     }
   125   }
   126   else {
   127     log(prefix + prop + ' = ' + message);
   128   }
   129 };
   131 leakHunt.matchesAnyPattern = function LH_matchesAnyPattern(str, patterns) {
   132   var match = false;
   133   patterns.forEach(function(pattern) {
   134     if (str.match(pattern)) {
   135       match = true;
   136     }
   137   });
   138   return match;
   139 };
   141 leakHunt.getType = function LH_getType(data) {
   142   if (data === null) {
   143     return 'null';
   144   }
   145   if (data === undefined) {
   146     return 'undefined';
   147   }
   149   var type = typeof data;
   150   if (type === 'object' || type === 'Object') {
   151     type = leakHunt.getCtorName(data);
   152   }
   154   return type;
   155 };
   157 leakHunt.getCtorName = function LH_getCtorName(aObj) {
   158   try {
   159     if (aObj.constructor && aObj.constructor.name) {
   160       return aObj.constructor.name;
   161     }
   162   }
   163   catch (ex) {
   164     return 'UnknownObject';
   165   }
   167   // If that fails, use Objects toString which sometimes gives something
   168   // better than 'Object', and at least defaults to Object if nothing better
   169   return Object.prototype.toString.call(aObj).slice(8, -1);
   170 };

mercurial