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