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