| /* |
| * Copyright (C) 2011 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyrightdd |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.Object} |
| */ |
| WebInspector.HeapSnapshotWorkerWrapper = function() |
| { |
| } |
| |
| WebInspector.HeapSnapshotWorkerWrapper.prototype = { |
| postMessage: function(message) |
| { |
| }, |
| terminate: function() |
| { |
| }, |
| |
| __proto__: WebInspector.Object.prototype |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotWorkerWrapper} |
| */ |
| WebInspector.HeapSnapshotRealWorker = function() |
| { |
| this._worker = new Worker("HeapSnapshotWorker.js"); |
| this._worker.addEventListener("message", this._messageReceived.bind(this), false); |
| } |
| |
| WebInspector.HeapSnapshotRealWorker.prototype = { |
| _messageReceived: function(event) |
| { |
| var message = event.data; |
| this.dispatchEventToListeners("message", message); |
| }, |
| |
| postMessage: function(message) |
| { |
| this._worker.postMessage(message); |
| }, |
| |
| terminate: function() |
| { |
| this._worker.terminate(); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| */ |
| WebInspector.AsyncTaskQueue = function() |
| { |
| this._queue = []; |
| this._isTimerSheduled = false; |
| } |
| |
| WebInspector.AsyncTaskQueue.prototype = { |
| /** |
| * @param {function()} task |
| */ |
| addTask: function(task) |
| { |
| this._queue.push(task); |
| this._scheduleTimer(); |
| }, |
| |
| _onTimeout: function() |
| { |
| this._isTimerSheduled = false; |
| var queue = this._queue; |
| this._queue = []; |
| for (var i = 0; i < queue.length; i++) { |
| try { |
| queue[i](); |
| } catch (e) { |
| console.error("Exception while running task: " + e.stack); |
| } |
| } |
| this._scheduleTimer(); |
| }, |
| |
| _scheduleTimer: function() |
| { |
| if (this._queue.length && !this._isTimerSheduled) { |
| setTimeout(this._onTimeout.bind(this), 0); |
| this._isTimerSheduled = true; |
| } |
| } |
| } |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotWorkerWrapper} |
| */ |
| WebInspector.HeapSnapshotFakeWorker = function() |
| { |
| this._dispatcher = new WebInspector.HeapSnapshotWorkerDispatcher(window, this._postMessageFromWorker.bind(this)); |
| this._asyncTaskQueue = new WebInspector.AsyncTaskQueue(); |
| } |
| |
| WebInspector.HeapSnapshotFakeWorker.prototype = { |
| postMessage: function(message) |
| { |
| /** |
| * @this {WebInspector.HeapSnapshotFakeWorker} |
| */ |
| function dispatch() |
| { |
| if (this._dispatcher) |
| this._dispatcher.dispatchMessage({data: message}); |
| } |
| this._asyncTaskQueue.addTask(dispatch.bind(this)); |
| }, |
| |
| terminate: function() |
| { |
| this._dispatcher = null; |
| }, |
| |
| _postMessageFromWorker: function(message) |
| { |
| /** |
| * @this {WebInspector.HeapSnapshotFakeWorker} |
| */ |
| function send() |
| { |
| this.dispatchEventToListeners("message", message); |
| } |
| this._asyncTaskQueue.addTask(send.bind(this)); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotWorkerWrapper.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @param {function(string, *)} eventHandler |
| * @extends {WebInspector.Object} |
| */ |
| WebInspector.HeapSnapshotWorkerProxy = function(eventHandler) |
| { |
| this._eventHandler = eventHandler; |
| this._nextObjectId = 1; |
| this._nextCallId = 1; |
| this._callbacks = []; |
| this._previousCallbacks = []; |
| // There is no support for workers in Chromium DRT. |
| this._worker = typeof InspectorTest === "undefined" ? new WebInspector.HeapSnapshotRealWorker() : new WebInspector.HeapSnapshotFakeWorker(); |
| this._worker.addEventListener("message", this._messageReceived, this); |
| } |
| |
| WebInspector.HeapSnapshotWorkerProxy.prototype = { |
| /** |
| * @return {!WebInspector.HeapSnapshotLoaderProxy} |
| */ |
| createLoader: function(snapshotConstructorName, proxyConstructor) |
| { |
| var objectId = this._nextObjectId++; |
| var proxy = new WebInspector.HeapSnapshotLoaderProxy(this, objectId, snapshotConstructorName, proxyConstructor); |
| this._postMessage({callId: this._nextCallId++, disposition: "create", objectId: objectId, methodName: "WebInspector.HeapSnapshotLoader"}); |
| return proxy; |
| }, |
| |
| dispose: function() |
| { |
| this._worker.terminate(); |
| if (this._interval) |
| clearInterval(this._interval); |
| }, |
| |
| disposeObject: function(objectId) |
| { |
| this._postMessage({callId: this._nextCallId++, disposition: "dispose", objectId: objectId}); |
| }, |
| |
| callGetter: function(callback, objectId, getterName) |
| { |
| var callId = this._nextCallId++; |
| this._callbacks[callId] = callback; |
| this._postMessage({callId: callId, disposition: "getter", objectId: objectId, methodName: getterName}); |
| }, |
| |
| callFactoryMethod: function(callback, objectId, methodName, proxyConstructor) |
| { |
| var callId = this._nextCallId++; |
| var methodArguments = Array.prototype.slice.call(arguments, 4); |
| var newObjectId = this._nextObjectId++; |
| |
| /** |
| * @this {WebInspector.HeapSnapshotWorkerProxy} |
| */ |
| function wrapCallback(remoteResult) |
| { |
| callback(remoteResult ? new proxyConstructor(this, newObjectId) : null); |
| } |
| |
| if (callback) { |
| this._callbacks[callId] = wrapCallback.bind(this); |
| this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId}); |
| return null; |
| } else { |
| this._postMessage({callId: callId, disposition: "factory", objectId: objectId, methodName: methodName, methodArguments: methodArguments, newObjectId: newObjectId}); |
| return new proxyConstructor(this, newObjectId); |
| } |
| }, |
| |
| callMethod: function(callback, objectId, methodName) |
| { |
| var callId = this._nextCallId++; |
| var methodArguments = Array.prototype.slice.call(arguments, 3); |
| if (callback) |
| this._callbacks[callId] = callback; |
| this._postMessage({callId: callId, disposition: "method", objectId: objectId, methodName: methodName, methodArguments: methodArguments}); |
| }, |
| |
| startCheckingForLongRunningCalls: function() |
| { |
| if (this._interval) |
| return; |
| this._checkLongRunningCalls(); |
| this._interval = setInterval(this._checkLongRunningCalls.bind(this), 300); |
| }, |
| |
| _checkLongRunningCalls: function() |
| { |
| for (var callId in this._previousCallbacks) |
| if (!(callId in this._callbacks)) |
| delete this._previousCallbacks[callId]; |
| var hasLongRunningCalls = false; |
| for (callId in this._previousCallbacks) { |
| hasLongRunningCalls = true; |
| break; |
| } |
| this.dispatchEventToListeners("wait", hasLongRunningCalls); |
| for (callId in this._callbacks) |
| this._previousCallbacks[callId] = true; |
| }, |
| |
| _findFunction: function(name) |
| { |
| var path = name.split("."); |
| var result = window; |
| for (var i = 0; i < path.length; ++i) |
| result = result[path[i]]; |
| return result; |
| }, |
| |
| _messageReceived: function(event) |
| { |
| var data = event.data; |
| if (data.eventName) { |
| if (this._eventHandler) |
| this._eventHandler(data.eventName, data.data); |
| return; |
| } |
| if (data.error) { |
| if (data.errorMethodName) |
| WebInspector.log(WebInspector.UIString("An error happened when a call for method '%s' was requested", data.errorMethodName)); |
| WebInspector.log(data.errorCallStack); |
| delete this._callbacks[data.callId]; |
| return; |
| } |
| if (!this._callbacks[data.callId]) |
| return; |
| var callback = this._callbacks[data.callId]; |
| delete this._callbacks[data.callId]; |
| callback(data.result); |
| }, |
| |
| _postMessage: function(message) |
| { |
| this._worker.postMessage(message); |
| }, |
| |
| __proto__: WebInspector.Object.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| */ |
| WebInspector.HeapSnapshotProxyObject = function(worker, objectId) |
| { |
| this._worker = worker; |
| this._objectId = objectId; |
| } |
| |
| WebInspector.HeapSnapshotProxyObject.prototype = { |
| _callWorker: function(workerMethodName, args) |
| { |
| args.splice(1, 0, this._objectId); |
| return this._worker[workerMethodName].apply(this._worker, args); |
| }, |
| |
| dispose: function() |
| { |
| this._worker.disposeObject(this._objectId); |
| }, |
| |
| disposeWorker: function() |
| { |
| this._worker.dispose(); |
| }, |
| |
| /** |
| * @param {...*} var_args |
| */ |
| callFactoryMethod: function(callback, methodName, proxyConstructor, var_args) |
| { |
| return this._callWorker("callFactoryMethod", Array.prototype.slice.call(arguments, 0)); |
| }, |
| |
| callGetter: function(callback, getterName) |
| { |
| return this._callWorker("callGetter", Array.prototype.slice.call(arguments, 0)); |
| }, |
| |
| /** |
| * @param {...*} var_args |
| */ |
| callMethod: function(callback, methodName, var_args) |
| { |
| return this._callWorker("callMethod", Array.prototype.slice.call(arguments, 0)); |
| }, |
| |
| get worker() { |
| return this._worker; |
| } |
| }; |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotProxyObject} |
| * @implements {WebInspector.OutputStream} |
| */ |
| WebInspector.HeapSnapshotLoaderProxy = function(worker, objectId, snapshotConstructorName, proxyConstructor) |
| { |
| WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId); |
| this._snapshotConstructorName = snapshotConstructorName; |
| this._proxyConstructor = proxyConstructor; |
| this._pendingSnapshotConsumers = []; |
| } |
| |
| WebInspector.HeapSnapshotLoaderProxy.prototype = { |
| /** |
| * @param {function(!WebInspector.HeapSnapshotProxy)} callback |
| */ |
| addConsumer: function(callback) |
| { |
| this._pendingSnapshotConsumers.push(callback); |
| }, |
| |
| /** |
| * @param {string} chunk |
| * @param {function(!WebInspector.OutputStream)=} callback |
| */ |
| write: function(chunk, callback) |
| { |
| this.callMethod(callback, "write", chunk); |
| }, |
| |
| /** |
| * @param {function()=} callback |
| */ |
| close: function(callback) |
| { |
| /** |
| * @this {WebInspector.HeapSnapshotLoaderProxy} |
| */ |
| function buildSnapshot() |
| { |
| if (callback) |
| callback(); |
| this.callFactoryMethod(updateStaticData.bind(this), "buildSnapshot", this._proxyConstructor, this._snapshotConstructorName); |
| } |
| |
| /** |
| * @this {WebInspector.HeapSnapshotLoaderProxy} |
| */ |
| function updateStaticData(snapshotProxy) |
| { |
| this.dispose(); |
| snapshotProxy.updateStaticData(notifyPendingConsumers.bind(this)); |
| } |
| |
| /** |
| * @this {WebInspector.HeapSnapshotLoaderProxy} |
| */ |
| function notifyPendingConsumers(snapshotProxy) |
| { |
| for (var i = 0; i < this._pendingSnapshotConsumers.length; ++i) |
| this._pendingSnapshotConsumers[i](snapshotProxy); |
| this._pendingSnapshotConsumers = []; |
| } |
| |
| this.callMethod(buildSnapshot.bind(this), "close"); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotProxyObject.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotProxyObject} |
| */ |
| WebInspector.HeapSnapshotProxy = function(worker, objectId) |
| { |
| WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId); |
| } |
| |
| WebInspector.HeapSnapshotProxy.prototype = { |
| aggregates: function(sortedIndexes, key, filter, callback) |
| { |
| this.callMethod(callback, "aggregates", sortedIndexes, key, filter); |
| }, |
| |
| aggregatesForDiff: function(callback) |
| { |
| this.callMethod(callback, "aggregatesForDiff"); |
| }, |
| |
| calculateSnapshotDiff: function(baseSnapshotId, baseSnapshotAggregates, callback) |
| { |
| this.callMethod(callback, "calculateSnapshotDiff", baseSnapshotId, baseSnapshotAggregates); |
| }, |
| |
| nodeClassName: function(snapshotObjectId, callback) |
| { |
| this.callMethod(callback, "nodeClassName", snapshotObjectId); |
| }, |
| |
| dominatorIdsForNode: function(nodeIndex, callback) |
| { |
| this.callMethod(callback, "dominatorIdsForNode", nodeIndex); |
| }, |
| |
| createEdgesProvider: function(nodeIndex, showHiddenData) |
| { |
| return this.callFactoryMethod(null, "createEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData); |
| }, |
| |
| createRetainingEdgesProvider: function(nodeIndex, showHiddenData) |
| { |
| return this.callFactoryMethod(null, "createRetainingEdgesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndex, showHiddenData); |
| }, |
| |
| createAddedNodesProvider: function(baseSnapshotId, className) |
| { |
| return this.callFactoryMethod(null, "createAddedNodesProvider", WebInspector.HeapSnapshotProviderProxy, baseSnapshotId, className); |
| }, |
| |
| createDeletedNodesProvider: function(nodeIndexes) |
| { |
| return this.callFactoryMethod(null, "createDeletedNodesProvider", WebInspector.HeapSnapshotProviderProxy, nodeIndexes); |
| }, |
| |
| createNodesProvider: function(filter) |
| { |
| return this.callFactoryMethod(null, "createNodesProvider", WebInspector.HeapSnapshotProviderProxy, filter); |
| }, |
| |
| createNodesProviderForClass: function(className, aggregatesKey) |
| { |
| return this.callFactoryMethod(null, "createNodesProviderForClass", WebInspector.HeapSnapshotProviderProxy, className, aggregatesKey); |
| }, |
| |
| createNodesProviderForDominator: function(nodeIndex) |
| { |
| return this.callFactoryMethod(null, "createNodesProviderForDominator", WebInspector.HeapSnapshotProviderProxy, nodeIndex); |
| }, |
| |
| maxJsNodeId: function(callback) |
| { |
| this.callMethod(callback, "maxJsNodeId"); |
| }, |
| |
| allocationTracesTops: function(callback) |
| { |
| this.callMethod(callback, "allocationTracesTops"); |
| }, |
| |
| allocationNodeCallers: function(nodeId, callback) |
| { |
| this.callMethod(callback, "allocationNodeCallers", nodeId); |
| }, |
| |
| dispose: function() |
| { |
| this.disposeWorker(); |
| }, |
| |
| get nodeCount() |
| { |
| return this._staticData.nodeCount; |
| }, |
| |
| get rootNodeIndex() |
| { |
| return this._staticData.rootNodeIndex; |
| }, |
| |
| updateStaticData: function(callback) |
| { |
| /** |
| * @this {WebInspector.HeapSnapshotProxy} |
| */ |
| function dataReceived(staticData) |
| { |
| this._staticData = staticData; |
| callback(this); |
| } |
| this.callMethod(dataReceived.bind(this), "updateStaticData"); |
| }, |
| |
| get totalSize() |
| { |
| return this._staticData.totalSize; |
| }, |
| |
| get uid() |
| { |
| return this._staticData.uid; |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotProxyObject.prototype |
| } |
| |
| |
| /** |
| * @constructor |
| * @extends {WebInspector.HeapSnapshotProxyObject} |
| */ |
| WebInspector.HeapSnapshotProviderProxy = function(worker, objectId) |
| { |
| WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId); |
| } |
| |
| WebInspector.HeapSnapshotProviderProxy.prototype = { |
| nodePosition: function(snapshotObjectId, callback) |
| { |
| this.callMethod(callback, "nodePosition", snapshotObjectId); |
| }, |
| |
| isEmpty: function(callback) |
| { |
| this.callMethod(callback, "isEmpty"); |
| }, |
| |
| serializeItemsRange: function(startPosition, endPosition, callback) |
| { |
| this.callMethod(callback, "serializeItemsRange", startPosition, endPosition); |
| }, |
| |
| sortAndRewind: function(comparator, callback) |
| { |
| this.callMethod(callback, "sortAndRewind", comparator); |
| }, |
| |
| __proto__: WebInspector.HeapSnapshotProxyObject.prototype |
| } |
| |