michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: Array.prototype.clone = function() { return this.slice(0); } michael@0: michael@0: function makeSample(frames, extraInfo, lines) { michael@0: return { michael@0: frames: frames, michael@0: extraInfo: extraInfo, michael@0: lines: lines michael@0: }; michael@0: } michael@0: michael@0: function cloneSample(sample) { michael@0: return makeSample(sample.frames.clone(), sample.extraInfo, sample.lines.clone()); michael@0: } michael@0: michael@0: function bucketsBySplittingArray(array, maxItemsPerBucket) { michael@0: var buckets = []; michael@0: while (buckets.length * maxItemsPerBucket < array.length) { michael@0: buckets.push(array.slice(buckets.length * maxItemsPerBucket, michael@0: (buckets.length + 1) * maxItemsPerBucket)); michael@0: } michael@0: return buckets; michael@0: } michael@0: michael@0: var gParserWorker = new Worker("profiler/cleopatra/js/parserWorker.js"); michael@0: gParserWorker.nextRequestID = 0; michael@0: michael@0: function WorkerRequest(worker) { michael@0: var self = this; michael@0: this._eventListeners = {}; michael@0: var requestID = worker.nextRequestID++; michael@0: this._requestID = requestID; michael@0: this._worker = worker; michael@0: this._totalReporter = new ProgressReporter(); michael@0: this._totalReporter.addListener(function (reporter) { michael@0: self._fireEvent("progress", reporter.getProgress(), reporter.getAction()); michael@0: }) michael@0: this._sendChunkReporter = this._totalReporter.addSubreporter(500); michael@0: this._executeReporter = this._totalReporter.addSubreporter(3000); michael@0: this._receiveChunkReporter = this._totalReporter.addSubreporter(100); michael@0: this._totalReporter.begin("Processing task in worker..."); michael@0: var partialResult = null; michael@0: function onMessageFromWorker(msg) { michael@0: pendingMessages.push(msg); michael@0: scheduleMessageProcessing(); michael@0: } michael@0: function processMessage(msg) { michael@0: var startTime = Date.now(); michael@0: var data = msg.data; michael@0: var readTime = Date.now() - startTime; michael@0: michael@0: if (data.requestID == requestID || !data.requestID) { michael@0: switch(data.type) { michael@0: case "error": michael@0: self._sendChunkReporter.setAction("Error in worker: " + data.error); michael@0: self._executeReporter.setAction("Error in worker: " + data.error); michael@0: self._receiveChunkReporter.setAction("Error in worker: " + data.error); michael@0: self._totalReporter.setAction("Error in worker: " + data.error); michael@0: PROFILERERROR("Error in worker: " + data.error); michael@0: self._fireEvent("error", data.error); michael@0: break; michael@0: case "progress": michael@0: self._executeReporter.setProgress(data.progress); michael@0: break; michael@0: case "finished": michael@0: self._executeReporter.finish(); michael@0: self._receiveChunkReporter.begin("Receiving data from worker..."); michael@0: self._receiveChunkReporter.finish(); michael@0: self._fireEvent("finished", data.result); michael@0: worker.removeEventListener("message", onMessageFromWorker); michael@0: break; michael@0: case "finishedStart": michael@0: partialResult = null; michael@0: self._totalReceiveChunks = data.numChunks; michael@0: self._gotReceiveChunks = 0; michael@0: self._executeReporter.finish(); michael@0: self._receiveChunkReporter.begin("Receiving data from worker..."); michael@0: break; michael@0: case "finishedChunk": michael@0: partialResult = partialResult ? partialResult.concat(data.chunk) : data.chunk; michael@0: var chunkIndex = self._gotReceiveChunks++; michael@0: self._receiveChunkReporter.setProgress((chunkIndex + 1) / self._totalReceiveChunks); michael@0: break; michael@0: case "finishedEnd": michael@0: self._receiveChunkReporter.finish(); michael@0: self._fireEvent("finished", partialResult); michael@0: worker.removeEventListener("message", onMessageFromWorker); michael@0: break; michael@0: } michael@0: // dump log if present michael@0: if (data.log) { michael@0: for (var line in data.log) { michael@0: PROFILERLOG(line); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: var pendingMessages = []; michael@0: var messageProcessingTimer = 0; michael@0: function processMessages() { michael@0: messageProcessingTimer = 0; michael@0: processMessage(pendingMessages.shift()); michael@0: if (pendingMessages.length) michael@0: scheduleMessageProcessing(); michael@0: } michael@0: function scheduleMessageProcessing() { michael@0: if (messageProcessingTimer) michael@0: return; michael@0: messageProcessingTimer = setTimeout(processMessages, 10); michael@0: } michael@0: worker.addEventListener("message", onMessageFromWorker); michael@0: } michael@0: michael@0: WorkerRequest.prototype = { michael@0: send: function WorkerRequest_send(task, taskData) { michael@0: this._sendChunkReporter.begin("Sending data to worker..."); michael@0: var startTime = Date.now(); michael@0: this._worker.postMessage({ michael@0: requestID: this._requestID, michael@0: task: task, michael@0: taskData: taskData michael@0: }); michael@0: var postTime = Date.now() - startTime; michael@0: this._sendChunkReporter.finish(); michael@0: this._executeReporter.begin("Processing worker request..."); michael@0: }, michael@0: sendInChunks: function WorkerRequest_sendInChunks(task, taskData, params, maxChunkSize) { michael@0: this._sendChunkReporter.begin("Sending data to worker..."); michael@0: var self = this; michael@0: var chunks = bucketsBySplittingArray(taskData, maxChunkSize); michael@0: var pendingMessages = [ michael@0: { michael@0: requestID: this._requestID, michael@0: task: "chunkedStart", michael@0: numChunks: chunks.length michael@0: } michael@0: ].concat(chunks.map(function (chunk) { michael@0: return { michael@0: requestID: self._requestID, michael@0: task: "chunkedChunk", michael@0: chunk: chunk michael@0: }; michael@0: })).concat([ michael@0: { michael@0: requestID: this._requestID, michael@0: task: "chunkedEnd" michael@0: }, michael@0: { michael@0: requestID: this._requestID, michael@0: params: params, michael@0: task: task michael@0: }, michael@0: ]); michael@0: var totalMessages = pendingMessages.length; michael@0: var numSentMessages = 0; michael@0: function postMessage(msg) { michael@0: var msgIndex = numSentMessages++; michael@0: var startTime = Date.now(); michael@0: self._worker.postMessage(msg); michael@0: var postTime = Date.now() - startTime; michael@0: self._sendChunkReporter.setProgress((msgIndex + 1) / totalMessages); michael@0: } michael@0: var messagePostingTimer = 0; michael@0: function postMessages() { michael@0: messagePostingTimer = 0; michael@0: postMessage(pendingMessages.shift()); michael@0: if (pendingMessages.length) { michael@0: scheduleMessagePosting(); michael@0: } else { michael@0: self._sendChunkReporter.finish(); michael@0: self._executeReporter.begin("Processing worker request..."); michael@0: } michael@0: } michael@0: function scheduleMessagePosting() { michael@0: if (messagePostingTimer) michael@0: return; michael@0: messagePostingTimer = setTimeout(postMessages, 10); michael@0: } michael@0: scheduleMessagePosting(); michael@0: }, michael@0: michael@0: // TODO: share code with TreeView michael@0: addEventListener: function WorkerRequest_addEventListener(eventName, callbackFunction) { michael@0: if (!(eventName in this._eventListeners)) michael@0: this._eventListeners[eventName] = []; michael@0: if (this._eventListeners[eventName].indexOf(callbackFunction) != -1) michael@0: return; michael@0: this._eventListeners[eventName].push(callbackFunction); michael@0: }, michael@0: removeEventListener: function WorkerRequest_removeEventListener(eventName, callbackFunction) { michael@0: if (!(eventName in this._eventListeners)) michael@0: return; michael@0: var index = this._eventListeners[eventName].indexOf(callbackFunction); michael@0: if (index == -1) michael@0: return; michael@0: this._eventListeners[eventName].splice(index, 1); michael@0: }, michael@0: _fireEvent: function WorkerRequest__fireEvent(eventName, eventObject, p1) { michael@0: if (!(eventName in this._eventListeners)) michael@0: return; michael@0: this._eventListeners[eventName].forEach(function (callbackFunction) { michael@0: callbackFunction(eventObject, p1); michael@0: }); michael@0: }, michael@0: } michael@0: michael@0: var Parser = { michael@0: parse: function Parser_parse(data, params) { michael@0: var request = new WorkerRequest(gParserWorker); michael@0: request.sendInChunks("parseRawProfile", data, params, 3000000); michael@0: return request; michael@0: }, michael@0: michael@0: updateFilters: function Parser_updateFilters(filters) { michael@0: var request = new WorkerRequest(gParserWorker); michael@0: request.send("updateFilters", { michael@0: filters: filters, michael@0: profileID: 0 michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: updateViewOptions: function Parser_updateViewOptions(options) { michael@0: var request = new WorkerRequest(gParserWorker); michael@0: request.send("updateViewOptions", { michael@0: options: options, michael@0: profileID: 0 michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: getSerializedProfile: function Parser_getSerializedProfile(complete, callback) { michael@0: var request = new WorkerRequest(gParserWorker); michael@0: request.send("getSerializedProfile", { michael@0: profileID: 0, michael@0: complete: complete michael@0: }); michael@0: request.addEventListener("finished", callback); michael@0: }, michael@0: michael@0: calculateHistogramData: function Parser_calculateHistogramData() { michael@0: var request = new WorkerRequest(gParserWorker); michael@0: request.send("calculateHistogramData", { michael@0: profileID: 0 michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: calculateDiagnosticItems: function Parser_calculateDiagnosticItems(meta) { michael@0: var request = new WorkerRequest(gParserWorker); michael@0: request.send("calculateDiagnosticItems", { michael@0: profileID: 0, michael@0: meta: meta michael@0: }); michael@0: return request; michael@0: }, michael@0: michael@0: updateLogSetting: function Parser_updateLogSetting() { michael@0: var request = new WorkerRequest(gParserWorker); michael@0: request.send("initWorker", { michael@0: debugLog: gDebugLog, michael@0: debugTrace: gDebugTrace, michael@0: }); michael@0: return request; michael@0: }, michael@0: };