Home | History | Annotate | Download | only in profiler
      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  * @param {function(string, *)} eventHandler
     34  * @extends {WebInspector.Object}
     35  */
     36 WebInspector.HeapSnapshotWorkerProxy = function(eventHandler)
     37 {
     38     this._eventHandler = eventHandler;
     39     this._nextObjectId = 1;
     40     this._nextCallId = 1;
     41     this._callbacks = [];
     42     this._previousCallbacks = [];
     43     this._worker = new Worker("profiler/heap_snapshot_worker/HeapSnapshotWorker.js");
     44     this._worker.onmessage = this._messageReceived.bind(this);
     45 }
     46 
     47 WebInspector.HeapSnapshotWorkerProxy.prototype = {
     48     /**
     49      * @param {number} profileUid
     50      * @param {function(!WebInspector.HeapSnapshotProxy)} snapshotReceivedCallback
     51      * @return {!WebInspector.HeapSnapshotLoaderProxy}
     52      */
     53     createLoader: function(profileUid, snapshotReceivedCallback)
     54     {
     55         var objectId = this._nextObjectId++;
     56         var proxy = new WebInspector.HeapSnapshotLoaderProxy(this, objectId, profileUid, snapshotReceivedCallback);
     57         this._postMessage({callId: this._nextCallId++, disposition: "create", objectId: objectId, methodName: "WebInspector.HeapSnapshotLoader"});
     58         return proxy;
     59     },
     60 
     61     dispose: function()
     62     {
     63         this._worker.terminate();
     64         if (this._interval)
     65             clearInterval(this._interval);
     66     },
     67 
     68     disposeObject: function(objectId)
     69     {
     70         this._postMessage({callId: this._nextCallId++, disposition: "dispose", objectId: objectId});
     71     },
     72 
     73     evaluateForTest: function(script, callback)
     74     {
     75         var callId = this._nextCallId++;
     76         this._callbacks[callId] = callback;
     77         this._postMessage({callId: callId, disposition: "evaluateForTest", source: script});
     78     },
     79 
     80     /**
     81      * @param {?function(...[?])} callback
     82      * @param {string} objectId
     83      * @param {string} methodName
     84      * @param {function(new:T, ...[?])} proxyConstructor
     85      * @return {?Object}
     86      * @template T
     87      */
     88     callFactoryMethod: function(callback, objectId, methodName, proxyConstructor)
     89     {
     90         var callId = this._nextCallId++;
     91         var methodArguments = Array.prototype.slice.call(arguments, 4);
     92         var newObjectId = this._nextObjectId++;
     93 
     94         /**
     95          * @this {WebInspector.HeapSnapshotWorkerProxy}
     96          */
     97         function wrapCallback(remoteResult)
     98         {
     99             callback(remoteResult ? new proxyConstructor(this, newObjectId) : null);
    100         }
    101 
    102         if (callback) {
    103             this._callbacks[callId] = wrapCallback.bind(this);
    104             this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
    105             return null;
    106         } else {
    107             this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
    108             return new proxyConstructor(this, newObjectId);
    109         }
    110     },
    111 
    112     /**
    113      * @param {function(*)} callback
    114      * @param {string} objectId
    115      * @param {string} methodName
    116      */
    117     callMethod: function(callback, objectId, methodName)
    118     {
    119         var callId = this._nextCallId++;
    120         var methodArguments = Array.prototype.slice.call(arguments, 3);
    121         if (callback)
    122             this._callbacks[callId] = callback;
    123         this._postMessage({callId: callId, disposition: "method", objectId: objectId, methodName: methodName, methodArguments: methodArguments});
    124     },
    125 
    126     startCheckingForLongRunningCalls: function()
    127     {
    128         if (this._interval)
    129             return;
    130         this._checkLongRunningCalls();
    131         this._interval = setInterval(this._checkLongRunningCalls.bind(this), 300);
    132     },
    133 
    134     _checkLongRunningCalls: function()
    135     {
    136         for (var callId in this._previousCallbacks)
    137             if (!(callId in this._callbacks))
    138                 delete this._previousCallbacks[callId];
    139         var hasLongRunningCalls = false;
    140         for (callId in this._previousCallbacks) {
    141             hasLongRunningCalls = true;
    142             break;
    143         }
    144         this.dispatchEventToListeners("wait", hasLongRunningCalls);
    145         for (callId in this._callbacks)
    146             this._previousCallbacks[callId] = true;
    147     },
    148 
    149     /**
    150      * @param {!MessageEvent} event
    151      */
    152     _messageReceived: function(event)
    153     {
    154         var data = event.data;
    155         if (data.eventName) {
    156             if (this._eventHandler)
    157                 this._eventHandler(data.eventName, data.data);
    158             return;
    159         }
    160         if (data.error) {
    161             if (data.errorMethodName)
    162                 WebInspector.messageSink.addMessage(WebInspector.UIString("An error occurred when a call to method '%s' was requested", data.errorMethodName));
    163             WebInspector.messageSink.addMessage(data["errorCallStack"]);
    164             delete this._callbacks[data.callId];
    165             return;
    166         }
    167         if (!this._callbacks[data.callId])
    168             return;
    169         var callback = this._callbacks[data.callId];
    170         delete this._callbacks[data.callId];
    171         callback(data.result);
    172     },
    173 
    174     _postMessage: function(message)
    175     {
    176         this._worker.postMessage(message);
    177     },
    178 
    179     __proto__: WebInspector.Object.prototype
    180 }
    181 
    182 
    183 /**
    184  * @constructor
    185  * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
    186  * @param {number} objectId
    187  */
    188 WebInspector.HeapSnapshotProxyObject = function(worker, objectId)
    189 {
    190     this._worker = worker;
    191     this._objectId = objectId;
    192 }
    193 
    194 WebInspector.HeapSnapshotProxyObject.prototype = {
    195     /**
    196      * @param {string} workerMethodName
    197      * @param {!Array.<*>} args
    198      */
    199     _callWorker: function(workerMethodName, args)
    200     {
    201         args.splice(1, 0, this._objectId);
    202         return this._worker[workerMethodName].apply(this._worker, args);
    203     },
    204 
    205     dispose: function()
    206     {
    207         this._worker.disposeObject(this._objectId);
    208     },
    209 
    210     disposeWorker: function()
    211     {
    212         this._worker.dispose();
    213     },
    214 
    215     /**
    216      * @param {?function(...[?])} callback
    217      * @param {string} methodName
    218      * @param {function (new:T, ...[?])} proxyConstructor
    219      * @param {...*} var_args
    220      * @return {!T}
    221      * @template T
    222      */
    223     callFactoryMethod: function(callback, methodName, proxyConstructor, var_args)
    224     {
    225         return this._callWorker("callFactoryMethod", Array.prototype.slice.call(arguments, 0));
    226     },
    227 
    228     /**
    229      * @param {function(T)|undefined} callback
    230      * @param {string} methodName
    231      * @param {...*} var_args
    232      * @return {*}
    233      * @template T
    234      */
    235     callMethod: function(callback, methodName, var_args)
    236     {
    237         return this._callWorker("callMethod", Array.prototype.slice.call(arguments, 0));
    238     }
    239 };
    240 
    241 /**
    242  * @constructor
    243  * @extends {WebInspector.HeapSnapshotProxyObject}
    244  * @implements {WebInspector.OutputStream}
    245  * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
    246  * @param {number} objectId
    247  * @param {number} profileUid
    248  * @param {function(!WebInspector.HeapSnapshotProxy)} snapshotReceivedCallback
    249  */
    250 WebInspector.HeapSnapshotLoaderProxy = function(worker, objectId, profileUid, snapshotReceivedCallback)
    251 {
    252     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    253     this._profileUid = profileUid;
    254     this._snapshotReceivedCallback = snapshotReceivedCallback;
    255 }
    256 
    257 WebInspector.HeapSnapshotLoaderProxy.prototype = {
    258     /**
    259      * @param {string} chunk
    260      * @param {function(!WebInspector.OutputStream)=} callback
    261      */
    262     write: function(chunk, callback)
    263     {
    264         this.callMethod(callback, "write", chunk);
    265     },
    266 
    267     /**
    268      * @param {function()=} callback
    269      */
    270     close: function(callback)
    271     {
    272         /**
    273          * @this {WebInspector.HeapSnapshotLoaderProxy}
    274          */
    275         function buildSnapshot()
    276         {
    277             if (callback)
    278                 callback();
    279             var showHiddenData = WebInspector.settings.showAdvancedHeapSnapshotProperties.get();
    280             this.callFactoryMethod(updateStaticData.bind(this), "buildSnapshot", WebInspector.HeapSnapshotProxy, showHiddenData);
    281         }
    282 
    283         /**
    284          * @param {!WebInspector.HeapSnapshotProxy} snapshotProxy
    285          * @this {WebInspector.HeapSnapshotLoaderProxy}
    286          */
    287         function updateStaticData(snapshotProxy)
    288         {
    289             this.dispose();
    290             snapshotProxy.setProfileUid(this._profileUid);
    291             snapshotProxy.updateStaticData(this._snapshotReceivedCallback.bind(this));
    292         }
    293 
    294         this.callMethod(buildSnapshot.bind(this), "close");
    295     },
    296 
    297     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    298 }
    299 
    300 
    301 /**
    302  * @constructor
    303  * @extends {WebInspector.HeapSnapshotProxyObject}
    304  * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
    305  * @param {number} objectId
    306  */
    307 WebInspector.HeapSnapshotProxy = function(worker, objectId)
    308 {
    309     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    310     /** @type {?WebInspector.HeapSnapshotCommon.StaticData} */
    311     this._staticData = null;
    312 }
    313 
    314 WebInspector.HeapSnapshotProxy.prototype = {
    315     /**
    316      * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} filter
    317      * @param {function(!Object.<string, !WebInspector.HeapSnapshotCommon.Aggregate>)} callback
    318      */
    319     aggregatesWithFilter: function(filter, callback)
    320     {
    321         this.callMethod(callback, "aggregatesWithFilter", filter);
    322     },
    323 
    324     aggregatesForDiff: function(callback)
    325     {
    326         this.callMethod(callback, "aggregatesForDiff");
    327     },
    328 
    329     calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates, callback)
    330     {
    331         this.callMethod(callback, "calculateSnapshotDiff", baseSnapshotId, baseSnapshotAggregates);
    332     },
    333 
    334     nodeClassName: function(snapshotObjectId, callback)
    335     {
    336         this.callMethod(callback, "nodeClassName", snapshotObjectId);
    337     },
    338 
    339     dominatorIdsForNode: function(nodeIndex, callback)
    340     {
    341         this.callMethod(callback, "dominatorIdsForNode", nodeIndex);
    342     },
    343 
    344     /**
    345      * @param {number} nodeIndex
    346      * @return {!WebInspector.HeapSnapshotProviderProxy}
    347      */
    348     createEdgesProvider: function(nodeIndex)
    349     {
    350         return this.callFactoryMethod(null, "createEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
    351     },
    352 
    353     /**
    354      * @param {number} nodeIndex
    355      * @return {!WebInspector.HeapSnapshotProviderProxy}
    356      */
    357     createRetainingEdgesProvider: function(nodeIndex)
    358     {
    359         return this.callFactoryMethod(null, "createRetainingEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
    360     },
    361 
    362     /**
    363      * @param {string} baseSnapshotId
    364      * @param {string} className
    365      * @return {?WebInspector.HeapSnapshotProviderProxy}
    366      */
    367     createAddedNodesProvider: function(baseSnapshotId, className)
    368     {
    369         return this.callFactoryMethod(null, "createAddedNodesProvider", WebInspector.HeapSnapshotProviderProxy, baseSnapshotId, className);
    370     },
    371 
    372     /**
    373      * @param {!Array.<number>} nodeIndexes
    374      * @return {?WebInspector.HeapSnapshotProviderProxy}
    375      */
    376     createDeletedNodesProvider: function(nodeIndexes)
    377     {
    378         return this.callFactoryMethod(null, "createDeletedNodesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndexes);
    379     },
    380 
    381     /**
    382      * @param {function(*):boolean} filter
    383      * @return {?WebInspector.HeapSnapshotProviderProxy}
    384      */
    385     createNodesProvider: function(filter)
    386     {
    387         return this.callFactoryMethod(null, "createNodesProvider", WebInspector.HeapSnapshotProviderProxy, filter);
    388     },
    389 
    390     /**
    391      * @param {string} className
    392      * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
    393      * @return {?WebInspector.HeapSnapshotProviderProxy}
    394      */
    395     createNodesProviderForClass: function(className, nodeFilter)
    396     {
    397         return this.callFactoryMethod(null, "createNodesProviderForClass", WebInspector.HeapSnapshotProviderProxy, className, nodeFilter);
    398     },
    399 
    400     /**
    401      * @param {number} nodeIndex
    402      * @return {?WebInspector.HeapSnapshotProviderProxy}
    403      */
    404     createNodesProviderForDominator: function(nodeIndex)
    405     {
    406         return this.callFactoryMethod(null, "createNodesProviderForDominator", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
    407     },
    408 
    409     allocationTracesTops: function(callback)
    410     {
    411         this.callMethod(callback, "allocationTracesTops");
    412     },
    413 
    414     /**
    415      * @param {number} nodeId
    416      * @param {function(!WebInspector.HeapSnapshotCommon.AllocationNodeCallers)} callback
    417      */
    418     allocationNodeCallers: function(nodeId, callback)
    419     {
    420         this.callMethod(callback, "allocationNodeCallers", nodeId);
    421     },
    422 
    423     /**
    424      * @param {number} nodeIndex
    425      * @param {function(?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>)} callback
    426      */
    427     allocationStack: function(nodeIndex, callback)
    428     {
    429         this.callMethod(callback, "allocationStack", nodeIndex);
    430     },
    431 
    432     dispose: function()
    433     {
    434         throw new Error("Should never be called");
    435     },
    436 
    437     get nodeCount()
    438     {
    439         return this._staticData.nodeCount;
    440     },
    441 
    442     get rootNodeIndex()
    443     {
    444         return this._staticData.rootNodeIndex;
    445     },
    446 
    447     updateStaticData: function(callback)
    448     {
    449         /**
    450          * @param {!WebInspector.HeapSnapshotCommon.StaticData} staticData
    451          * @this {WebInspector.HeapSnapshotProxy}
    452          */
    453         function dataReceived(staticData)
    454         {
    455             this._staticData = staticData;
    456             callback(this);
    457         }
    458         this.callMethod(dataReceived.bind(this), "updateStaticData");
    459     },
    460 
    461     /**
    462      * @param {!function(!WebInspector.HeapSnapshotCommon.Statistics):void} callback
    463      */
    464     getStatistics: function(callback)
    465     {
    466         this.callMethod(callback, "getStatistics");
    467     },
    468 
    469     get totalSize()
    470     {
    471         return this._staticData.totalSize;
    472     },
    473 
    474     get uid()
    475     {
    476         return this._profileUid;
    477     },
    478 
    479     setProfileUid: function(profileUid)
    480     {
    481         this._profileUid = profileUid;
    482     },
    483 
    484     /**
    485      * @return {number}
    486      */
    487     maxJSObjectId: function()
    488     {
    489         return this._staticData.maxJSObjectId;
    490     },
    491 
    492     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    493 }
    494 
    495 
    496 /**
    497  * @constructor
    498  * @extends {WebInspector.HeapSnapshotProxyObject}
    499  * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider}
    500  * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
    501  * @param {number} objectId
    502  */
    503 WebInspector.HeapSnapshotProviderProxy = function(worker, objectId)
    504 {
    505     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    506 }
    507 
    508 WebInspector.HeapSnapshotProviderProxy.prototype = {
    509     /**
    510      * @override
    511      * @param {number} snapshotObjectId
    512      * @param {function(number)} callback
    513      */
    514     nodePosition: function(snapshotObjectId, callback)
    515     {
    516         this.callMethod(callback, "nodePosition", snapshotObjectId);
    517     },
    518 
    519     /**
    520      * @override
    521      * @param {function(boolean)} callback
    522      */
    523     isEmpty: function(callback)
    524     {
    525         this.callMethod(callback, "isEmpty");
    526     },
    527 
    528     /**
    529      * @override
    530      * @param {number} startPosition
    531      * @param {number} endPosition
    532      * @param {function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
    533      */
    534     serializeItemsRange: function(startPosition, endPosition, callback)
    535     {
    536         this.callMethod(callback, "serializeItemsRange", startPosition, endPosition);
    537     },
    538 
    539     /**
    540      * @override
    541      * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
    542      * @param {function()} callback
    543      */
    544     sortAndRewind: function(comparator, callback)
    545     {
    546         this.callMethod(callback, "sortAndRewind", comparator);
    547     },
    548 
    549     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    550 }
    551