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         if ("callId" in message)
     65             this.dispatchEventToListeners("message", message);
     66         else {
     67             if (message.object !== "console") {
     68                 console.log(WebInspector.UIString("Worker asks to call a method '%s' on an unsupported object '%s'.", message.method, message.object));
     69                 return;
     70             }
     71             if (message.method !== "log" && message.method !== "info" && message.method !== "error") {
     72                 console.log(WebInspector.UIString("Worker asks to call an unsupported method '%s' on the console object.", message.method));
     73                 return;
     74             }
     75             console[message.method].apply(window[message.object], message.arguments);
     76         }
     77     },
     78 
     79     postMessage: function(message)
     80     {
     81         this._worker.postMessage(message);
     82     },
     83 
     84     terminate: function()
     85     {
     86         this._worker.terminate();
     87     },
     88 
     89     __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype
     90 }
     91 
     92 
     93 /**
     94  * @constructor
     95  */
     96 WebInspector.AsyncTaskQueue = function()
     97 {
     98     this._queue = [];
     99     this._isTimerSheduled = false;
    100 }
    101 
    102 WebInspector.AsyncTaskQueue.prototype = {
    103     /**
    104      * @param {function()} task
    105      */
    106     addTask: function(task)
    107     {
    108         this._queue.push(task);
    109         this._scheduleTimer();
    110     },
    111 
    112     _onTimeout: function()
    113     {
    114         this._isTimerSheduled = false;
    115         var queue = this._queue;
    116         this._queue = [];
    117         for (var i = 0; i < queue.length; i++) {
    118             try {
    119                 queue[i]();
    120             } catch (e) {
    121                 console.error("Exception while running task: " + e.stack);
    122             }
    123         }
    124         this._scheduleTimer();
    125     },
    126 
    127     _scheduleTimer: function()
    128     {
    129         if (this._queue.length && !this._isTimerSheduled) {
    130             setTimeout(this._onTimeout.bind(this), 0);
    131             this._isTimerSheduled = true;
    132         }
    133     }
    134 }
    135 
    136 /**
    137  * @constructor
    138  * @extends {WebInspector.HeapSnapshotWorkerWrapper}
    139  */
    140 WebInspector.HeapSnapshotFakeWorker = function()
    141 {
    142     this._dispatcher = new WebInspector.HeapSnapshotWorkerDispatcher(window, this._postMessageFromWorker.bind(this));
    143     this._asyncTaskQueue = new WebInspector.AsyncTaskQueue();
    144 }
    145 
    146 WebInspector.HeapSnapshotFakeWorker.prototype = {
    147     postMessage: function(message)
    148     {
    149         function dispatch()
    150         {
    151             if (this._dispatcher)
    152                 this._dispatcher.dispatchMessage({data: message});
    153         }
    154         this._asyncTaskQueue.addTask(dispatch.bind(this));
    155     },
    156 
    157     terminate: function()
    158     {
    159         this._dispatcher = null;
    160     },
    161 
    162     _postMessageFromWorker: function(message)
    163     {
    164         function send()
    165         {
    166             this.dispatchEventToListeners("message", message);
    167         }
    168         this._asyncTaskQueue.addTask(send.bind(this));
    169     },
    170 
    171     __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype
    172 }
    173 
    174 
    175 /**
    176  * @constructor
    177  * @extends {WebInspector.Object}
    178  */
    179 WebInspector.HeapSnapshotWorker = function()
    180 {
    181     this._nextObjectId = 1;
    182     this._nextCallId = 1;
    183     this._callbacks = [];
    184     this._previousCallbacks = [];
    185     // There is no support for workers in Chromium DRT.
    186     this._worker = typeof InspectorTest === "undefined" ? new WebInspector.HeapSnapshotRealWorker() : new WebInspector.HeapSnapshotFakeWorker();
    187     this._worker.addEventListener("message", this._messageReceived, this);
    188 }
    189 
    190 WebInspector.HeapSnapshotWorker.prototype = {
    191     createLoader: function(snapshotConstructorName, proxyConstructor)
    192     {
    193         var objectId = this._nextObjectId++;
    194         var proxy = new WebInspector.HeapSnapshotLoaderProxy(this, objectId, snapshotConstructorName, proxyConstructor);
    195         this._postMessage({callId: this._nextCallId++, disposition: "create", objectId: objectId, methodName: "WebInspector.HeapSnapshotLoader"});
    196         return proxy;
    197     },
    198 
    199     dispose: function()
    200     {
    201         this._worker.terminate();
    202         if (this._interval)
    203             clearInterval(this._interval);
    204     },
    205 
    206     disposeObject: function(objectId)
    207     {
    208         this._postMessage({callId: this._nextCallId++, disposition: "dispose", objectId: objectId});
    209     },
    210 
    211     callGetter: function(callback, objectId, getterName)
    212     {
    213         var callId = this._nextCallId++;
    214         this._callbacks[callId] = callback;
    215         this._postMessage({callId: callId, disposition: "getter", objectId: objectId, methodName: getterName});
    216     },
    217 
    218     callFactoryMethod: function(callback, objectId, methodName, proxyConstructor)
    219     {
    220         var callId = this._nextCallId++;
    221         var methodArguments = Array.prototype.slice.call(arguments, 4);
    222         var newObjectId = this._nextObjectId++;
    223         if (callback) {
    224             function wrapCallback(remoteResult)
    225             {
    226                 callback(remoteResult ? new proxyConstructor(this, newObjectId) : null);
    227             }
    228             this._callbacks[callId] = wrapCallback.bind(this);
    229             this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
    230             return null;
    231         } else {
    232             this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId});
    233             return new proxyConstructor(this, newObjectId);
    234         }
    235     },
    236 
    237     callMethod: function(callback, objectId, methodName)
    238     {
    239         var callId = this._nextCallId++;
    240         var methodArguments = Array.prototype.slice.call(arguments, 3);
    241         if (callback)
    242             this._callbacks[callId] = callback;
    243         this._postMessage({callId: callId, disposition: "method", objectId: objectId, methodName: methodName, methodArguments: methodArguments});
    244     },
    245 
    246     startCheckingForLongRunningCalls: function()
    247     {
    248         if (this._interval)
    249             return;
    250         this._checkLongRunningCalls();
    251         this._interval = setInterval(this._checkLongRunningCalls.bind(this), 300);
    252     },
    253 
    254     _checkLongRunningCalls: function()
    255     {
    256         for (var callId in this._previousCallbacks)
    257             if (!(callId in this._callbacks))
    258                 delete this._previousCallbacks[callId];
    259         var hasLongRunningCalls = false;
    260         for (callId in this._previousCallbacks) {
    261             hasLongRunningCalls = true;
    262             break;
    263         }
    264         this.dispatchEventToListeners("wait", hasLongRunningCalls);
    265         for (callId in this._callbacks)
    266             this._previousCallbacks[callId] = true;
    267     },
    268 
    269     _findFunction: function(name)
    270     {
    271         var path = name.split(".");
    272         var result = window;
    273         for (var i = 0; i < path.length; ++i)
    274             result = result[path[i]];
    275         return result;
    276     },
    277 
    278     _messageReceived: function(event)
    279     {
    280         var data = event.data;
    281         if (event.data.error) {
    282             if (event.data.errorMethodName)
    283                 WebInspector.log(WebInspector.UIString("An error happened when a call for method '%s' was requested", event.data.errorMethodName));
    284             WebInspector.log(event.data.errorCallStack);
    285             delete this._callbacks[data.callId];
    286             return;
    287         }
    288         if (!this._callbacks[data.callId])
    289             return;
    290         var callback = this._callbacks[data.callId];
    291         delete this._callbacks[data.callId];
    292         callback(data.result);
    293     },
    294 
    295     _postMessage: function(message)
    296     {
    297         this._worker.postMessage(message);
    298     },
    299 
    300     __proto__: WebInspector.Object.prototype
    301 }
    302 
    303 
    304 /**
    305  * @constructor
    306  */
    307 WebInspector.HeapSnapshotProxyObject = function(worker, objectId)
    308 {
    309     this._worker = worker;
    310     this._objectId = objectId;
    311 }
    312 
    313 WebInspector.HeapSnapshotProxyObject.prototype = {
    314     _callWorker: function(workerMethodName, args)
    315     {
    316         args.splice(1, 0, this._objectId);
    317         return this._worker[workerMethodName].apply(this._worker, args);
    318     },
    319 
    320     dispose: function()
    321     {
    322         this._worker.disposeObject(this._objectId);
    323     },
    324 
    325     disposeWorker: function()
    326     {
    327         this._worker.dispose();
    328     },
    329 
    330     /**
    331      * @param {...*} var_args
    332      */
    333     callFactoryMethod: function(callback, methodName, proxyConstructor, var_args)
    334     {
    335         return this._callWorker("callFactoryMethod", Array.prototype.slice.call(arguments, 0));
    336     },
    337 
    338     callGetter: function(callback, getterName)
    339     {
    340         return this._callWorker("callGetter", Array.prototype.slice.call(arguments, 0));
    341     },
    342 
    343     /**
    344      * @param {...*} var_args
    345      */
    346     callMethod: function(callback, methodName, var_args)
    347     {
    348         return this._callWorker("callMethod", Array.prototype.slice.call(arguments, 0));
    349     },
    350 
    351     get worker() {
    352         return this._worker;
    353     }
    354 };
    355 
    356 /**
    357  * @constructor
    358  * @extends {WebInspector.HeapSnapshotProxyObject}
    359  * @implements {WebInspector.OutputStream}
    360  */
    361 WebInspector.HeapSnapshotLoaderProxy = function(worker, objectId, snapshotConstructorName, proxyConstructor)
    362 {
    363     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    364     this._snapshotConstructorName = snapshotConstructorName;
    365     this._proxyConstructor = proxyConstructor;
    366     this._pendingSnapshotConsumers = [];
    367 }
    368 
    369 WebInspector.HeapSnapshotLoaderProxy.prototype = {
    370     /**
    371      * @param {function(WebInspector.HeapSnapshotProxy)} callback
    372      */
    373     addConsumer: function(callback)
    374     {
    375         this._pendingSnapshotConsumers.push(callback);
    376     },
    377 
    378     /**
    379      * @param {string} chunk
    380      * @param {function(WebInspector.OutputStream)=} callback
    381      */
    382     write: function(chunk, callback)
    383     {
    384         this.callMethod(callback, "write", chunk);
    385     },
    386 
    387     close: function()
    388     {
    389         function buildSnapshot()
    390         {
    391             this.callFactoryMethod(updateStaticData.bind(this), "buildSnapshot", this._proxyConstructor, this._snapshotConstructorName);
    392         }
    393         function updateStaticData(snapshotProxy)
    394         {
    395             this.dispose();
    396             snapshotProxy.updateStaticData(notifyPendingConsumers.bind(this));
    397         }
    398         function notifyPendingConsumers(snapshotProxy)
    399         {
    400             for (var i = 0; i < this._pendingSnapshotConsumers.length; ++i)
    401                 this._pendingSnapshotConsumers[i](snapshotProxy);
    402             this._pendingSnapshotConsumers = [];
    403         }
    404         this.callMethod(buildSnapshot.bind(this), "close");
    405     },
    406 
    407     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    408 }
    409 
    410 
    411 /**
    412  * @constructor
    413  * @extends {WebInspector.HeapSnapshotProxyObject}
    414  */
    415 WebInspector.HeapSnapshotProxy = function(worker, objectId)
    416 {
    417     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    418 }
    419 
    420 WebInspector.HeapSnapshotProxy.prototype = {
    421     aggregates: function(sortedIndexes, key, filter, callback)
    422     {
    423         this.callMethod(callback, "aggregates", sortedIndexes, key, filter);
    424     },
    425 
    426     aggregatesForDiff: function(callback)
    427     {
    428         this.callMethod(callback, "aggregatesForDiff");
    429     },
    430 
    431     calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates, callback)
    432     {
    433         this.callMethod(callback, "calculateSnapshotDiff", baseSnapshotId, baseSnapshotAggregates);
    434     },
    435 
    436     nodeClassName: function(snapshotObjectId, callback)
    437     {
    438         this.callMethod(callback, "nodeClassName", snapshotObjectId);
    439     },
    440 
    441     dominatorIdsForNode: function(nodeIndex, callback)
    442     {
    443         this.callMethod(callback, "dominatorIdsForNode", nodeIndex);
    444     },
    445 
    446     createEdgesProvider: function(nodeIndex, showHiddenData)
    447     {
    448         return this.callFactoryMethod(null, "createEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData);
    449     },
    450 
    451     createRetainingEdgesProvider: function(nodeIndex, showHiddenData)
    452     {
    453         return this.callFactoryMethod(null, "createRetainingEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData);
    454     },
    455 
    456     createAddedNodesProvider: function(baseSnapshotId, className)
    457     {
    458         return this.callFactoryMethod(null, "createAddedNodesProvider", WebInspector.HeapSnapshotProviderProxy, baseSnapshotId, className);
    459     },
    460 
    461     createDeletedNodesProvider: function(nodeIndexes)
    462     {
    463         return this.callFactoryMethod(null, "createDeletedNodesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndexes);
    464     },
    465 
    466     createNodesProvider: function(filter)
    467     {
    468         return this.callFactoryMethod(null, "createNodesProvider", WebInspector.HeapSnapshotProviderProxy, filter);
    469     },
    470 
    471     createNodesProviderForClass: function(className, aggregatesKey)
    472     {
    473         return this.callFactoryMethod(null, "createNodesProviderForClass", WebInspector.HeapSnapshotProviderProxy, className, aggregatesKey);
    474     },
    475 
    476     createNodesProviderForDominator: function(nodeIndex)
    477     {
    478         return this.callFactoryMethod(null, "createNodesProviderForDominator", WebInspector.HeapSnapshotProviderProxy, nodeIndex);
    479     },
    480 
    481     dispose: function()
    482     {
    483         this.disposeWorker();
    484     },
    485 
    486     get nodeCount()
    487     {
    488         return this._staticData.nodeCount;
    489     },
    490 
    491     get rootNodeIndex()
    492     {
    493         return this._staticData.rootNodeIndex;
    494     },
    495 
    496     updateStaticData: function(callback)
    497     {
    498         function dataReceived(staticData)
    499         {
    500             this._staticData = staticData;
    501             callback(this);
    502         }
    503         this.callMethod(dataReceived.bind(this), "updateStaticData");
    504     },
    505 
    506     get totalSize()
    507     {
    508         return this._staticData.totalSize;
    509     },
    510 
    511     get uid()
    512     {
    513         return this._staticData.uid;
    514     },
    515 
    516     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    517 }
    518 
    519 
    520 /**
    521  * @constructor
    522  * @extends {WebInspector.HeapSnapshotProxy}
    523  */
    524 WebInspector.NativeHeapSnapshotProxy = function(worker, objectId)
    525 {
    526     WebInspector.HeapSnapshotProxy.call(this, worker, objectId);
    527 }
    528 
    529 WebInspector.NativeHeapSnapshotProxy.prototype = {
    530     images: function(callback)
    531     {
    532         this.callMethod(callback, "images");
    533     },
    534 
    535     __proto__: WebInspector.HeapSnapshotProxy.prototype
    536 }
    537 
    538 /**
    539  * @constructor
    540  * @extends {WebInspector.HeapSnapshotProxyObject}
    541  */
    542 WebInspector.HeapSnapshotProviderProxy = function(worker, objectId)
    543 {
    544     WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId);
    545 }
    546 
    547 WebInspector.HeapSnapshotProviderProxy.prototype = {
    548     nodePosition: function(snapshotObjectId, callback)
    549     {
    550         this.callMethod(callback, "nodePosition", snapshotObjectId);
    551     },
    552 
    553     isEmpty: function(callback)
    554     {
    555         this.callMethod(callback, "isEmpty");
    556     },
    557 
    558     serializeItemsRange: function(startPosition, endPosition, callback)
    559     {
    560         this.callMethod(callback, "serializeItemsRange", startPosition, endPosition);
    561     },
    562 
    563     sortAndRewind: function(comparator, callback)
    564     {
    565         this.callMethod(callback, "sortAndRewind", comparator);
    566     },
    567 
    568     __proto__: WebInspector.HeapSnapshotProxyObject.prototype
    569 }
    570 
    571