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 */ 35WebInspector.HeapSnapshotWorkerWrapper = function() 36{ 37} 38 39WebInspector.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 */ 54WebInspector.HeapSnapshotRealWorker = function() 55{ 56 this._worker = new Worker("HeapSnapshotWorker.js"); 57 this._worker.addEventListener("message", this._messageReceived.bind(this), false); 58} 59 60WebInspector.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 */ 96WebInspector.AsyncTaskQueue = function() 97{ 98 this._queue = []; 99 this._isTimerSheduled = false; 100} 101 102WebInspector.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 */ 140WebInspector.HeapSnapshotFakeWorker = function() 141{ 142 this._dispatcher = new WebInspector.HeapSnapshotWorkerDispatcher(window, this._postMessageFromWorker.bind(this)); 143 this._asyncTaskQueue = new WebInspector.AsyncTaskQueue(); 144} 145 146WebInspector.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 */ 179WebInspector.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 190WebInspector.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 */ 307WebInspector.HeapSnapshotProxyObject = function(worker, objectId) 308{ 309 this._worker = worker; 310 this._objectId = objectId; 311} 312 313WebInspector.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 */ 361WebInspector.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 369WebInspector.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 */ 415WebInspector.HeapSnapshotProxy = function(worker, objectId) 416{ 417 WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId); 418} 419 420WebInspector.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 */ 524WebInspector.NativeHeapSnapshotProxy = function(worker, objectId) 525{ 526 WebInspector.HeapSnapshotProxy.call(this, worker, objectId); 527} 528 529WebInspector.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 */ 542WebInspector.HeapSnapshotProviderProxy = function(worker, objectId) 543{ 544 WebInspector.HeapSnapshotProxyObject.call(this, worker, objectId); 545} 546 547WebInspector.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