|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 Array.prototype.clone = function() { return this.slice(0); } |
|
8 |
|
9 function makeSample(frames, extraInfo, lines) { |
|
10 return { |
|
11 frames: frames, |
|
12 extraInfo: extraInfo, |
|
13 lines: lines |
|
14 }; |
|
15 } |
|
16 |
|
17 function cloneSample(sample) { |
|
18 return makeSample(sample.frames.clone(), sample.extraInfo, sample.lines.clone()); |
|
19 } |
|
20 |
|
21 function bucketsBySplittingArray(array, maxItemsPerBucket) { |
|
22 var buckets = []; |
|
23 while (buckets.length * maxItemsPerBucket < array.length) { |
|
24 buckets.push(array.slice(buckets.length * maxItemsPerBucket, |
|
25 (buckets.length + 1) * maxItemsPerBucket)); |
|
26 } |
|
27 return buckets; |
|
28 } |
|
29 |
|
30 var gParserWorker = new Worker("profiler/cleopatra/js/parserWorker.js"); |
|
31 gParserWorker.nextRequestID = 0; |
|
32 |
|
33 function WorkerRequest(worker) { |
|
34 var self = this; |
|
35 this._eventListeners = {}; |
|
36 var requestID = worker.nextRequestID++; |
|
37 this._requestID = requestID; |
|
38 this._worker = worker; |
|
39 this._totalReporter = new ProgressReporter(); |
|
40 this._totalReporter.addListener(function (reporter) { |
|
41 self._fireEvent("progress", reporter.getProgress(), reporter.getAction()); |
|
42 }) |
|
43 this._sendChunkReporter = this._totalReporter.addSubreporter(500); |
|
44 this._executeReporter = this._totalReporter.addSubreporter(3000); |
|
45 this._receiveChunkReporter = this._totalReporter.addSubreporter(100); |
|
46 this._totalReporter.begin("Processing task in worker..."); |
|
47 var partialResult = null; |
|
48 function onMessageFromWorker(msg) { |
|
49 pendingMessages.push(msg); |
|
50 scheduleMessageProcessing(); |
|
51 } |
|
52 function processMessage(msg) { |
|
53 var startTime = Date.now(); |
|
54 var data = msg.data; |
|
55 var readTime = Date.now() - startTime; |
|
56 |
|
57 if (data.requestID == requestID || !data.requestID) { |
|
58 switch(data.type) { |
|
59 case "error": |
|
60 self._sendChunkReporter.setAction("Error in worker: " + data.error); |
|
61 self._executeReporter.setAction("Error in worker: " + data.error); |
|
62 self._receiveChunkReporter.setAction("Error in worker: " + data.error); |
|
63 self._totalReporter.setAction("Error in worker: " + data.error); |
|
64 PROFILERERROR("Error in worker: " + data.error); |
|
65 self._fireEvent("error", data.error); |
|
66 break; |
|
67 case "progress": |
|
68 self._executeReporter.setProgress(data.progress); |
|
69 break; |
|
70 case "finished": |
|
71 self._executeReporter.finish(); |
|
72 self._receiveChunkReporter.begin("Receiving data from worker..."); |
|
73 self._receiveChunkReporter.finish(); |
|
74 self._fireEvent("finished", data.result); |
|
75 worker.removeEventListener("message", onMessageFromWorker); |
|
76 break; |
|
77 case "finishedStart": |
|
78 partialResult = null; |
|
79 self._totalReceiveChunks = data.numChunks; |
|
80 self._gotReceiveChunks = 0; |
|
81 self._executeReporter.finish(); |
|
82 self._receiveChunkReporter.begin("Receiving data from worker..."); |
|
83 break; |
|
84 case "finishedChunk": |
|
85 partialResult = partialResult ? partialResult.concat(data.chunk) : data.chunk; |
|
86 var chunkIndex = self._gotReceiveChunks++; |
|
87 self._receiveChunkReporter.setProgress((chunkIndex + 1) / self._totalReceiveChunks); |
|
88 break; |
|
89 case "finishedEnd": |
|
90 self._receiveChunkReporter.finish(); |
|
91 self._fireEvent("finished", partialResult); |
|
92 worker.removeEventListener("message", onMessageFromWorker); |
|
93 break; |
|
94 } |
|
95 // dump log if present |
|
96 if (data.log) { |
|
97 for (var line in data.log) { |
|
98 PROFILERLOG(line); |
|
99 } |
|
100 } |
|
101 } |
|
102 } |
|
103 var pendingMessages = []; |
|
104 var messageProcessingTimer = 0; |
|
105 function processMessages() { |
|
106 messageProcessingTimer = 0; |
|
107 processMessage(pendingMessages.shift()); |
|
108 if (pendingMessages.length) |
|
109 scheduleMessageProcessing(); |
|
110 } |
|
111 function scheduleMessageProcessing() { |
|
112 if (messageProcessingTimer) |
|
113 return; |
|
114 messageProcessingTimer = setTimeout(processMessages, 10); |
|
115 } |
|
116 worker.addEventListener("message", onMessageFromWorker); |
|
117 } |
|
118 |
|
119 WorkerRequest.prototype = { |
|
120 send: function WorkerRequest_send(task, taskData) { |
|
121 this._sendChunkReporter.begin("Sending data to worker..."); |
|
122 var startTime = Date.now(); |
|
123 this._worker.postMessage({ |
|
124 requestID: this._requestID, |
|
125 task: task, |
|
126 taskData: taskData |
|
127 }); |
|
128 var postTime = Date.now() - startTime; |
|
129 this._sendChunkReporter.finish(); |
|
130 this._executeReporter.begin("Processing worker request..."); |
|
131 }, |
|
132 sendInChunks: function WorkerRequest_sendInChunks(task, taskData, params, maxChunkSize) { |
|
133 this._sendChunkReporter.begin("Sending data to worker..."); |
|
134 var self = this; |
|
135 var chunks = bucketsBySplittingArray(taskData, maxChunkSize); |
|
136 var pendingMessages = [ |
|
137 { |
|
138 requestID: this._requestID, |
|
139 task: "chunkedStart", |
|
140 numChunks: chunks.length |
|
141 } |
|
142 ].concat(chunks.map(function (chunk) { |
|
143 return { |
|
144 requestID: self._requestID, |
|
145 task: "chunkedChunk", |
|
146 chunk: chunk |
|
147 }; |
|
148 })).concat([ |
|
149 { |
|
150 requestID: this._requestID, |
|
151 task: "chunkedEnd" |
|
152 }, |
|
153 { |
|
154 requestID: this._requestID, |
|
155 params: params, |
|
156 task: task |
|
157 }, |
|
158 ]); |
|
159 var totalMessages = pendingMessages.length; |
|
160 var numSentMessages = 0; |
|
161 function postMessage(msg) { |
|
162 var msgIndex = numSentMessages++; |
|
163 var startTime = Date.now(); |
|
164 self._worker.postMessage(msg); |
|
165 var postTime = Date.now() - startTime; |
|
166 self._sendChunkReporter.setProgress((msgIndex + 1) / totalMessages); |
|
167 } |
|
168 var messagePostingTimer = 0; |
|
169 function postMessages() { |
|
170 messagePostingTimer = 0; |
|
171 postMessage(pendingMessages.shift()); |
|
172 if (pendingMessages.length) { |
|
173 scheduleMessagePosting(); |
|
174 } else { |
|
175 self._sendChunkReporter.finish(); |
|
176 self._executeReporter.begin("Processing worker request..."); |
|
177 } |
|
178 } |
|
179 function scheduleMessagePosting() { |
|
180 if (messagePostingTimer) |
|
181 return; |
|
182 messagePostingTimer = setTimeout(postMessages, 10); |
|
183 } |
|
184 scheduleMessagePosting(); |
|
185 }, |
|
186 |
|
187 // TODO: share code with TreeView |
|
188 addEventListener: function WorkerRequest_addEventListener(eventName, callbackFunction) { |
|
189 if (!(eventName in this._eventListeners)) |
|
190 this._eventListeners[eventName] = []; |
|
191 if (this._eventListeners[eventName].indexOf(callbackFunction) != -1) |
|
192 return; |
|
193 this._eventListeners[eventName].push(callbackFunction); |
|
194 }, |
|
195 removeEventListener: function WorkerRequest_removeEventListener(eventName, callbackFunction) { |
|
196 if (!(eventName in this._eventListeners)) |
|
197 return; |
|
198 var index = this._eventListeners[eventName].indexOf(callbackFunction); |
|
199 if (index == -1) |
|
200 return; |
|
201 this._eventListeners[eventName].splice(index, 1); |
|
202 }, |
|
203 _fireEvent: function WorkerRequest__fireEvent(eventName, eventObject, p1) { |
|
204 if (!(eventName in this._eventListeners)) |
|
205 return; |
|
206 this._eventListeners[eventName].forEach(function (callbackFunction) { |
|
207 callbackFunction(eventObject, p1); |
|
208 }); |
|
209 }, |
|
210 } |
|
211 |
|
212 var Parser = { |
|
213 parse: function Parser_parse(data, params) { |
|
214 var request = new WorkerRequest(gParserWorker); |
|
215 request.sendInChunks("parseRawProfile", data, params, 3000000); |
|
216 return request; |
|
217 }, |
|
218 |
|
219 updateFilters: function Parser_updateFilters(filters) { |
|
220 var request = new WorkerRequest(gParserWorker); |
|
221 request.send("updateFilters", { |
|
222 filters: filters, |
|
223 profileID: 0 |
|
224 }); |
|
225 return request; |
|
226 }, |
|
227 |
|
228 updateViewOptions: function Parser_updateViewOptions(options) { |
|
229 var request = new WorkerRequest(gParserWorker); |
|
230 request.send("updateViewOptions", { |
|
231 options: options, |
|
232 profileID: 0 |
|
233 }); |
|
234 return request; |
|
235 }, |
|
236 |
|
237 getSerializedProfile: function Parser_getSerializedProfile(complete, callback) { |
|
238 var request = new WorkerRequest(gParserWorker); |
|
239 request.send("getSerializedProfile", { |
|
240 profileID: 0, |
|
241 complete: complete |
|
242 }); |
|
243 request.addEventListener("finished", callback); |
|
244 }, |
|
245 |
|
246 calculateHistogramData: function Parser_calculateHistogramData() { |
|
247 var request = new WorkerRequest(gParserWorker); |
|
248 request.send("calculateHistogramData", { |
|
249 profileID: 0 |
|
250 }); |
|
251 return request; |
|
252 }, |
|
253 |
|
254 calculateDiagnosticItems: function Parser_calculateDiagnosticItems(meta) { |
|
255 var request = new WorkerRequest(gParserWorker); |
|
256 request.send("calculateDiagnosticItems", { |
|
257 profileID: 0, |
|
258 meta: meta |
|
259 }); |
|
260 return request; |
|
261 }, |
|
262 |
|
263 updateLogSetting: function Parser_updateLogSetting() { |
|
264 var request = new WorkerRequest(gParserWorker); |
|
265 request.send("initWorker", { |
|
266 debugLog: gDebugLog, |
|
267 debugTrace: gDebugTrace, |
|
268 }); |
|
269 return request; |
|
270 }, |
|
271 }; |