Home | History | Annotate | Download | only in front_end
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyrightdd
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /**
     32  * @constructor
     33  * @extends {WebInspector.Object}
     34  */
     35 WebInspector.HeapSnapshotWorkerWrapper = function()
     36 {
     37 }
     38 
     39 WebInspector.HeapSnapshotWorkerWrapper.prototype =  {
     40     postMessage: function(message)
     41     {
     42     },
     43     terminate: function()
     44     {
     45     },
     46 
     47     __proto__: WebInspector.Object.prototype
     48 }
     49 
     50 /**
     51  * @constructor
     52  * @extends {WebInspector.HeapSnapshotWorkerWrapper}
     53  */
     54 WebInspector.HeapSnapshotRealWorker = function()
     55 {
     56     this._worker = new Worker("HeapSnapshotWorker.js");
     57     this._worker.addEventListener("message", this._messageReceived.bind(this), false);
     58 }
     59 
     60 WebInspector.HeapSnapshotRealWorker.prototype = {
     61     _messageReceived: function(event)
     62     {
     63         var message = event.data;
     64         this.dispatchEventToListeners("message", message);
     65     },
     66 
     67     postMessage: function(message)
     68     {
     69         this._worker.postMessage(message);
     70     },
     71 
     72     terminate: function()
     73     {
     74         this._worker.terminate();
     75     },
     76 
     77     __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype
     78 }
     79 
     80 
     81 /**
     82  * @constructor
     83  */
     84 WebInspector.AsyncTaskQueue = function()
     85 {
     86     this._queue = [];
     87     this._isTimerSheduled = false;
     88 }
     89 
     90 WebInspector.AsyncTaskQueue.prototype = {
     91     /**
     92      * @param {function()} task
     93      */
     94     addTask: function(task)
     95     {
     96         this._queue.push(task);
     97         this._scheduleTimer();
     98     },
     99 
    100     _onTimeout: function()
    101     {
    102         this._isTimerSheduled = false;
    103         var queue = this._queue;
    104         this._queue = [];
    105         for (var i = 0; i < queue.length; i++) {
    106             try {
    107                 queue[i]();
    108             } catch (e) {
    109                 console.error("Exception while running task: " + e.stack);
    110             }
    111         }
    112         this._scheduleTimer();
    113     },
    114 
    115     _scheduleTimer: function()
    116     {
    117         if (this._queue.length && !this._isTimerSheduled) {
    118             setTimeout(this._onTimeout.bind(this), 0);
    119             this._isTimerSheduled = true;
    120         }
    121     }
    122 }
    123 
    124 /**
    125  * @constructor
    126  * @extends {WebInspector.HeapSnapshotWorkerWrapper}
    127  */
    128 WebInspector.HeapSnapshotFakeWorker = function()
    129 {
    130     this._dispatcher = new WebInspector.HeapSnapshotWorkerDispatcher(window, this._postMessageFromWorker.bind(this));
    131     this._asyncTaskQueue = new WebInspector.AsyncTaskQueue();
    132 }
    133 
    134 WebInspector.HeapSnapshotFakeWorker.prototype = {
    135     postMessage: function(message)
    136     {
    137         /**
    138          * @this {WebInspector.HeapSnapshotFakeWorker}
    139          */
    140         function dispatch()
    141         {
    142             if (this._dispatcher)
    143                 this._dispatcher.dispatchMessage({data: message});
    144         }
    145         this._asyncTaskQueue.addTask(dispatch.bind(this));
    146     },
    147 
    148     terminate: function()
    149     {
    150         this._dispatcher = null;
    151     },
    152 
    153     _postMessageFromWorker: function(message)
    154     {
    155         /**
    156          * @this {WebInspector.HeapSnapshotFakeWorker}
    157          */
    158         function send()
    159         {
    160             this.dispatchEventToListeners("message", message);
    161         }
    162         this._asyncTaskQueue.addTask(send.bind(this));
    163     },
    164 
    165     __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype
    166 }
    167 
    168 
    169 /**
    170  * @constructor
    171  * @param {function(string, *)} eventHandler
    172  * @extends {WebInspector.Object}
    173  */
    174 WebInspector.HeapSnapshotWorkerProxy = function(eventHandler)
    175 {
    176     this._eventHandler = eventHandler;
    177     this._nextObjectId = 1;
    178     this._nextCallId = 1;
    179     this._callbacks = [];
    180     this._previousCallbacks = [];
    181     // There is no support for workers in Chromium DRT.
    182     this._worker = typeof InspectorTest === "undefined" ? new WebInspector.HeapSnapshotRealWorker() : new WebInspector.HeapSnapshotFakeWorker();
    183     this._worker.addEventListener("message", this._messageReceived, this);
    184 }
    185 
    186 WebInspector.HeapSnapshotWorkerProxy.prototype = {
    187     /**
    188      * @return {!WebInspector.HeapSnapshotLoaderProxy}
    189      */
    190     createLoader: function(snapshotConstructorName, proxyConstructor)
    191     {
    192         var objectId = this._nextObjectId++;
    193         var proxy = new WebInspector.HeapSnapshotLoaderProxy(this, objectId, snapshotConstructorName, proxyConstructor);
    194         this._postMessage({callId: this._nextCallId++, disposition: "create", objectId: objectId, methodName: "WebInspector.HeapSnapshotLoader"});
    195         return proxy;
    196     },
    197 
    198     dispose: function()
    199     {
    200         this._worker.terminate();
    201         if (this._interval)
    202             clearInterval(this._interval);
    203     },
    204 
    205     disposeObject: function(objectId)
    206     {
    207         this._postMessage({callId: this._nextCallId++, disposition: "dispose", objectId: objectId});
    208     },
    209 
    210     callGetter: function(callback, objectId, getterName)
    211     {
    212         var callId = this._nextCallId++;
    213         this._callbacks[callId] = callback;
    214         this._postMessage({callId: callId, disposition: "getter", objectId: objectId, methodName: getterName});
    215     },
    216 
    217     callFactoryMethod: function(callback, objectId, methodName, proxyConstructor)
    218     {
    219         var callId = this._nextCallId++;
    220         var methodArguments = Array.prototype.slice.call(arguments, 4);
    221         var newObjectId = this._nextObjectId++;
    222 
    223         /**
    224          * @this {WebInspector.HeapSnapshotWorkerProxy}
    225          */
    226         function wrapCallback(remoteResult)
    227         {
    228             callback(remoteResult ? new proxyConstructor(this, newObjectId) : null);
    229         }
    230 
    231         if (callback) {
    232             this._callbacks[callId] = wrapCallback.bind(this);
    233             this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
    234             return null;
    235         } else {
    236             this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
    237             return new proxyConstructor(this, newObjectId);
    238         }
    239     },
    240 
    241     callMethod: function(callback, objectId, methodName)
    242     {
    243         var callId = this._nextCallId++;
    244         var methodArguments = Array.prototype.slice.call(arguments, 3);
    245         if (callback)
    246             this._callbacks[callId] = callback;
    247         this._postMessage({callId: callId, disposition: "method", objectId: objectId, methodName: methodName, methodArguments: methodArguments});
    248     },
    249 
    250     startCheckingForLongRunningCalls: function()
    251     {
    252         if (this._interval)
    253             return;
    254         this._checkLongRunningCalls();
    255         this._interval = setInterval(this._checkLongRunningCalls.bind(this), 300);
    256     },
    257 
    258     _checkLongRunningCalls: function()
    259     {
    260         for (var callId in this._previousCallbacks)
    261             if (!(callId in this._callbacks))
    262                 delete this._previousCallbacks[callId];
    263         var hasLongRunningCalls = false;
    264         for (callId in this._previousCallbacks) {
    265             hasLongRunningCalls = true;
    266             break;
    267         }
    268         this.dispatchEventToListeners("wait", hasLongRunningCalls);
    269         for (callId in this._callbacks)
    270             this._previousCallbacks[callId] = true;
    271     },
    272 
    273     _findFunction: function(name)
    274     {
    275         var path = name.split(".");
    276         var result = window;
    277         for (var i = 0; i < path.length; ++i)
    278             result = result[path[i]];
    279         return result;
    280     },
    281 
    282     _messageReceived: function(event)
    283     {
    284         var data = event.data;
    285         if (data.eventName) {
    286             if (this._eventHandler)
    287                 this._eventHandler(data.eventName, data.data);
    288             return;
    289         }
    290         if (data.error) {
    291             if (data.errorMethodName)
    292                 WebInspector.log(WebInspector.UIString("An error happened when a call for method '%s' was requested", data.errorMethodName));
    293             WebInspector.log(data.errorCallStack);
    294             delete this._callbacks[data.callId];
    295             return;
    296         }
    297         if (!this._callbacks[data.callId])
    298             return;
    299         var callback = this._callbacks[data.callId];
    300         delete this._callbacks[data.callId];
    301         callback(data.result);
    302     },
    303 
    304     _postMessage: function(message)
    305     {
    306         this._worker.postMessage(message);
    307     },
    308 
    309     __proto__: WebInspector.Object.prototype
    310 }
    311 
    312 
    313 /**
    314  * @constructor
    315  */
    316 WebInspector.HeapSnapshotProxyObject = function(worker, objectId)
    317 {
    318     this._worker = worker;
    319     this._objectId = objectId;
    320 }
    321 
    322 WebInspector.HeapSnapshotProxyObject.prototype = {
    323     _callWorker: function(workerMethodName, args)
    324     {
    325         args.splice(1, 0, this._objectId);
    326         return this._worker[workerMethodName].apply(this._worker, args);
    327     },
    328 
    329     dispose: function()
    330     {
    331         this._worker.disposeObject(this._objectId);
    332     },
    333 
    334     disposeWorker: function()
    335     {
    336         this._worker.dispose();
    337     },
    338 
    339     /**
    340      * @param {...*} var_args
    341      */
    342     callFactoryMethod: function(callback, methodName, proxyConstructor, var_args)
    343     {
    344         return this._callWorker("callFactoryMethod", Array.prototype.slice.call(arguments, 0));
    345     },
    346 
    347     callGetter: function(callback, getterName)
    348     {
    349         return this._callWorker("callGetter", Array.prototype.slice.call(arguments, 0));
    350     },
    351 
    352     /**
    353      * @param {...*} var_args
    354      */
    355     callMethod: function(callback, methodName, var_args)
    356     {
    357         return this._callWorker("callMethod", Array.prototype.slice.call(arguments, 0));
    358     },
    359 
    360     get worker() {
    361         return this._worker;
    362     }
    363 };
    364 
    365 /**
    366  * @constructor
    367  * @extends {WebInspector.HeapSnapshotProxyObject}
    368  * @implements {WebInspector.OutputStream}
    369  */
    370 WebInspector.HeapSnapshotLoaderProxy = function(worker, objectId, snapshotConstructorName, proxyConstructor)
    371 {
    372     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    373     this._snapshotConstructorName = snapshotConstructorName;
    374     this._proxyConstructor = proxyConstructor;
    375     this._pendingSnapshotConsumers = [];
    376 }
    377 
    378 WebInspector.HeapSnapshotLoaderProxy.prototype = {
    379     /**
    380      * @param {function(!WebInspector.HeapSnapshotProxy)} callback
    381      */
    382     addConsumer: function(callback)
    383     {
    384         this._pendingSnapshotConsumers.push(callback);
    385     },
    386 
    387     /**
    388      * @param {string} chunk
    389      * @param {function(!WebInspector.OutputStream)=} callback
    390      */
    391     write: function(chunk, callback)
    392     {
    393         this.callMethod(callback, "write", chunk);
    394     },
    395 
    396     /**
    397      * @param {function()=} callback
    398      */
    399     close: function(callback)
    400     {
    401         /**
    402          * @this {WebInspector.HeapSnapshotLoaderProxy}
    403          */
    404         function buildSnapshot()
    405         {
    406             if (callback)
    407                 callback();
    408             this.callFactoryMethod(updateStaticData.bind(this), "buildSnapshot", this._proxyConstructor, this._snapshotConstructorName);
    409         }
    410 
    411         /**
    412          * @this {WebInspector.HeapSnapshotLoaderProxy}
    413          */
    414         function updateStaticData(snapshotProxy)
    415         {
    416             this.dispose();
    417             snapshotProxy.updateStaticData(notifyPendingConsumers.bind(this));
    418         }
    419 
    420         /**
    421          * @this {WebInspector.HeapSnapshotLoaderProxy}
    422          */
    423         function notifyPendingConsumers(snapshotProxy)
    424         {
    425             for (var i = 0; i < this._pendingSnapshotConsumers.length; ++i)
    426                 this._pendingSnapshotConsumers[i](snapshotProxy);
    427             this._pendingSnapshotConsumers = [];
    428         }
    429 
    430         this.callMethod(buildSnapshot.bind(this), "close");
    431     },
    432 
    433     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    434 }
    435 
    436 
    437 /**
    438  * @constructor
    439  * @extends {WebInspector.HeapSnapshotProxyObject}
    440  */
    441 WebInspector.HeapSnapshotProxy = function(worker, objectId)
    442 {
    443     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    444 }
    445 
    446 WebInspector.HeapSnapshotProxy.prototype = {
    447     aggregates: function(sortedIndexes, key, filter, callback)
    448     {
    449         this.callMethod(callback, "aggregates", sortedIndexes, key, filter);
    450     },
    451 
    452     aggregatesForDiff: function(callback)
    453     {
    454         this.callMethod(callback, "aggregatesForDiff");
    455     },
    456 
    457     calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates, callback)
    458     {
    459         this.callMethod(callback, "calculateSnapshotDiff", baseSnapshotId, baseSnapshotAggregates);
    460     },
    461 
    462     nodeClassName: function(snapshotObjectId, callback)
    463     {
    464         this.callMethod(callback, "nodeClassName", snapshotObjectId);
    465     },
    466 
    467     dominatorIdsForNode: function(nodeIndex, callback)
    468     {
    469         this.callMethod(callback, "dominatorIdsForNode", nodeIndex);
    470     },
    471 
    472     createEdgesProvider: function(nodeIndex, showHiddenData)
    473     {
    474         return this.callFactoryMethod(null, "createEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData);
    475     },
    476 
    477     createRetainingEdgesProvider: function(nodeIndex, showHiddenData)
    478     {
    479         return this.callFactoryMethod(null, "createRetainingEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData);
    480     },
    481 
    482     createAddedNodesProvider: function(baseSnapshotId, className)
    483     {
    484         return this.callFactoryMethod(null, "createAddedNodesProvider", WebInspector.HeapSnapshotProviderProxy, baseSnapshotId, className);
    485     },
    486 
    487     createDeletedNodesProvider: function(nodeIndexes)
    488     {
    489         return this.callFactoryMethod(null, "createDeletedNodesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndexes);
    490     },
    491 
    492     createNodesProvider: function(filter)
    493     {
    494         return this.callFactoryMethod(null, "createNodesProvider", WebInspector.HeapSnapshotProviderProxy, filter);
    495     },
    496 
    497     createNodesProviderForClass: function(className, aggregatesKey)
    498     {
    499         return this.callFactoryMethod(null, "createNodesProviderForClass", WebInspector.HeapSnapshotProviderProxy, className, aggregatesKey);
    500     },
    501 
    502     createNodesProviderForDominator: function(nodeIndex)
    503     {
    504         return this.callFactoryMethod(null, "createNodesProviderForDominator", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
    505     },
    506 
    507     maxJsNodeId: function(callback)
    508     {
    509         this.callMethod(callback, "maxJsNodeId");
    510     },
    511 
    512     allocationTracesTops: function(callback)
    513     {
    514         this.callMethod(callback, "allocationTracesTops");
    515     },
    516 
    517     allocationNodeCallers: function(nodeId, callback)
    518     {
    519         this.callMethod(callback, "allocationNodeCallers", nodeId);
    520     },
    521 
    522     dispose: function()
    523     {
    524         this.disposeWorker();
    525     },
    526 
    527     get nodeCount()
    528     {
    529         return this._staticData.nodeCount;
    530     },
    531 
    532     get rootNodeIndex()
    533     {
    534         return this._staticData.rootNodeIndex;
    535     },
    536 
    537     updateStaticData: function(callback)
    538     {
    539         /**
    540          * @this {WebInspector.HeapSnapshotProxy}
    541          */
    542         function dataReceived(staticData)
    543         {
    544             this._staticData = staticData;
    545             callback(this);
    546         }
    547         this.callMethod(dataReceived.bind(this), "updateStaticData");
    548     },
    549 
    550     get totalSize()
    551     {
    552         return this._staticData.totalSize;
    553     },
    554 
    555     get uid()
    556     {
    557         return this._staticData.uid;
    558     },
    559 
    560     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    561 }
    562 
    563 
    564 /**
    565  * @constructor
    566  * @extends {WebInspector.HeapSnapshotProxyObject}
    567  */
    568 WebInspector.HeapSnapshotProviderProxy = function(worker, objectId)
    569 {
    570     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    571 }
    572 
    573 WebInspector.HeapSnapshotProviderProxy.prototype = {
    574     nodePosition: function(snapshotObjectId, callback)
    575     {
    576         this.callMethod(callback, "nodePosition", snapshotObjectId);
    577     },
    578 
    579     isEmpty: function(callback)
    580     {
    581         this.callMethod(callback, "isEmpty");
    582     },
    583 
    584     serializeItemsRange: function(startPosition, endPosition, callback)
    585     {
    586         this.callMethod(callback, "serializeItemsRange", startPosition, endPosition);
    587     },
    588 
    589     sortAndRewind: function(comparator, callback)
    590     {
    591         this.callMethod(callback, "sortAndRewind", comparator);
    592     },
    593 
    594     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    595 }
    596 
    597