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 = Runtime.startWorker("heap_snapshot_worker");
     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.console.error(WebInspector.UIString("An error occurred when a call to method '%s' was requested", data.errorMethodName));
    163             WebInspector.console.error(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     /**
    340      * @param {number} nodeIndex
    341      * @return {!WebInspector.HeapSnapshotProviderProxy}
    342      */
    343     createEdgesProvider: function(nodeIndex)
    344     {
    345         return this.callFactoryMethod(null, "createEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
    346     },
    347 
    348     /**
    349      * @param {number} nodeIndex
    350      * @return {!WebInspector.HeapSnapshotProviderProxy}
    351      */
    352     createRetainingEdgesProvider: function(nodeIndex)
    353     {
    354         return this.callFactoryMethod(null, "createRetainingEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
    355     },
    356 
    357     /**
    358      * @param {string} baseSnapshotId
    359      * @param {string} className
    360      * @return {?WebInspector.HeapSnapshotProviderProxy}
    361      */
    362     createAddedNodesProvider: function(baseSnapshotId, className)
    363     {
    364         return this.callFactoryMethod(null, "createAddedNodesProvider", WebInspector.HeapSnapshotProviderProxy, baseSnapshotId, className);
    365     },
    366 
    367     /**
    368      * @param {!Array.<number>} nodeIndexes
    369      * @return {?WebInspector.HeapSnapshotProviderProxy}
    370      */
    371     createDeletedNodesProvider: function(nodeIndexes)
    372     {
    373         return this.callFactoryMethod(null, "createDeletedNodesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndexes);
    374     },
    375 
    376     /**
    377      * @param {function(*):boolean} filter
    378      * @return {?WebInspector.HeapSnapshotProviderProxy}
    379      */
    380     createNodesProvider: function(filter)
    381     {
    382         return this.callFactoryMethod(null, "createNodesProvider", WebInspector.HeapSnapshotProviderProxy, filter);
    383     },
    384 
    385     /**
    386      * @param {string} className
    387      * @param {!WebInspector.HeapSnapshotCommon.NodeFilter} nodeFilter
    388      * @return {?WebInspector.HeapSnapshotProviderProxy}
    389      */
    390     createNodesProviderForClass: function(className, nodeFilter)
    391     {
    392         return this.callFactoryMethod(null, "createNodesProviderForClass", WebInspector.HeapSnapshotProviderProxy, className, nodeFilter);
    393     },
    394 
    395     allocationTracesTops: function(callback)
    396     {
    397         this.callMethod(callback, "allocationTracesTops");
    398     },
    399 
    400     /**
    401      * @param {number} nodeId
    402      * @param {function(!WebInspector.HeapSnapshotCommon.AllocationNodeCallers)} callback
    403      */
    404     allocationNodeCallers: function(nodeId, callback)
    405     {
    406         this.callMethod(callback, "allocationNodeCallers", nodeId);
    407     },
    408 
    409     /**
    410      * @param {number} nodeIndex
    411      * @param {function(?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>)} callback
    412      */
    413     allocationStack: function(nodeIndex, callback)
    414     {
    415         this.callMethod(callback, "allocationStack", nodeIndex);
    416     },
    417 
    418     dispose: function()
    419     {
    420         throw new Error("Should never be called");
    421     },
    422 
    423     get nodeCount()
    424     {
    425         return this._staticData.nodeCount;
    426     },
    427 
    428     get rootNodeIndex()
    429     {
    430         return this._staticData.rootNodeIndex;
    431     },
    432 
    433     updateStaticData: function(callback)
    434     {
    435         /**
    436          * @param {!WebInspector.HeapSnapshotCommon.StaticData} staticData
    437          * @this {WebInspector.HeapSnapshotProxy}
    438          */
    439         function dataReceived(staticData)
    440         {
    441             this._staticData = staticData;
    442             callback(this);
    443         }
    444         this.callMethod(dataReceived.bind(this), "updateStaticData");
    445     },
    446 
    447     /**
    448      * @param {!function(!WebInspector.HeapSnapshotCommon.Statistics):void} callback
    449      */
    450     getStatistics: function(callback)
    451     {
    452         this.callMethod(callback, "getStatistics");
    453     },
    454 
    455     get totalSize()
    456     {
    457         return this._staticData.totalSize;
    458     },
    459 
    460     get uid()
    461     {
    462         return this._profileUid;
    463     },
    464 
    465     setProfileUid: function(profileUid)
    466     {
    467         this._profileUid = profileUid;
    468     },
    469 
    470     /**
    471      * @return {number}
    472      */
    473     maxJSObjectId: function()
    474     {
    475         return this._staticData.maxJSObjectId;
    476     },
    477 
    478     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    479 }
    480 
    481 
    482 /**
    483  * @constructor
    484  * @extends {WebInspector.HeapSnapshotProxyObject}
    485  * @implements {WebInspector.HeapSnapshotGridNode.ChildrenProvider}
    486  * @param {!WebInspector.HeapSnapshotWorkerProxy} worker
    487  * @param {number} objectId
    488  */
    489 WebInspector.HeapSnapshotProviderProxy = function(worker, objectId)
    490 {
    491     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    492 }
    493 
    494 WebInspector.HeapSnapshotProviderProxy.prototype = {
    495     /**
    496      * @override
    497      * @param {number} snapshotObjectId
    498      * @param {function(number)} callback
    499      */
    500     nodePosition: function(snapshotObjectId, callback)
    501     {
    502         this.callMethod(callback, "nodePosition", snapshotObjectId);
    503     },
    504 
    505     /**
    506      * @override
    507      * @param {function(boolean)} callback
    508      */
    509     isEmpty: function(callback)
    510     {
    511         this.callMethod(callback, "isEmpty");
    512     },
    513 
    514     /**
    515      * @override
    516      * @param {number} startPosition
    517      * @param {number} endPosition
    518      * @param {function(!WebInspector.HeapSnapshotCommon.ItemsRange)} callback
    519      */
    520     serializeItemsRange: function(startPosition, endPosition, callback)
    521     {
    522         this.callMethod(callback, "serializeItemsRange", startPosition, endPosition);
    523     },
    524 
    525     /**
    526      * @override
    527      * @param {!WebInspector.HeapSnapshotCommon.ComparatorConfig} comparator
    528      * @param {function()} callback
    529      */
    530     sortAndRewind: function(comparator, callback)
    531     {
    532         this.callMethod(callback, "sortAndRewind", comparator);
    533     },
    534 
    535     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    536 }
    537