OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 15 matching lines...) Expand all Loading... |
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
27 | 27 |
28 var kV8BinarySuffixes = ["/d8", "/libv8.so"]; | 28 var kV8BinarySuffixes = ["/d8", "/libv8.so"]; |
29 var kStackFrames = 8; | 29 var kStackFrames = 8; |
30 | 30 |
31 var kTimerEventWidth = 0.33; | 31 var kTimerEventWidth = 0.33; |
32 var kExecutionFrameWidth = 0.2; | 32 var kExecutionFrameWidth = 0.2; |
33 var kStackFrameWidth = 0.1; | 33 var kStackFrameWidth = 0.1; |
34 var kGapWidth = 0.05; | 34 var kGapWidth = 0.05; |
35 | 35 |
36 var kPauseTolerance = 0.1; // Milliseconds. | |
37 var kY1Offset = 10; | 36 var kY1Offset = 10; |
38 | 37 |
39 var kResX = 1600; | 38 var kResX = 1600; |
40 var kResY = 600; | 39 var kResY = 600; |
41 var kPauseLabelPadding = 5; | 40 var kPauseLabelPadding = 5; |
42 var kNumPauseLabels = 7; | 41 var kNumPauseLabels = 7; |
43 var kTickHalfDuration = 0.5; // Milliseconds | 42 var kTickHalfDuration = 0.5; // Milliseconds |
44 var kCodeKindLabelPadding = 100; | 43 var kCodeKindLabelPadding = 100; |
| 44 var kMinRangeLength = 0.0005; // Milliseconds |
45 | 45 |
46 var num_timer_event = kY1Offset + 0.5; | 46 var num_timer_event = kY1Offset + 0.5; |
47 | 47 |
| 48 var kNumThreads = 2; |
| 49 var kExecutionThreadId = 0; |
48 | 50 |
49 function TimerEvent(color, pause, no_execution) { | 51 function assert(something, message) { |
| 52 if (!something) { |
| 53 print(new Error(message).stack); |
| 54 } |
| 55 } |
| 56 |
| 57 function TimerEvent(color, pause, thread_id) { |
| 58 assert(thread_id >= 0 && thread_id < kNumThreads, "invalid thread id"); |
50 this.color = color; | 59 this.color = color; |
51 this.pause = pause; | 60 this.pause = pause; |
52 this.ranges = []; | 61 this.ranges = []; |
53 this.no_execution = no_execution; | 62 this.thread_id = thread_id; |
54 this.index = ++num_timer_event; | 63 this.index = ++num_timer_event; |
55 } | 64 } |
56 | 65 |
57 | 66 |
58 var TimerEvents = { | 67 var TimerEvents = { |
59 'V8.Execute': new TimerEvent("#000000", false, false), | 68 'V8.Execute': new TimerEvent("#000000", false, 0), |
60 'V8.External': new TimerEvent("#3399FF", false, true), | 69 'V8.External': new TimerEvent("#3399FF", false, 0), |
61 'V8.CompileFullCode': new TimerEvent("#CC0000", true, true), | 70 'V8.CompileFullCode': new TimerEvent("#CC0000", true, 0), |
62 'V8.RecompileSynchronous': new TimerEvent("#CC0044", true, true), | 71 'V8.RecompileSynchronous': new TimerEvent("#CC0044", true, 0), |
63 'V8.RecompileParallel': new TimerEvent("#CC4499", false, false), | 72 'V8.RecompileParallel': new TimerEvent("#CC4499", false, 1), |
64 'V8.CompileEval': new TimerEvent("#CC4400", true, true), | 73 'V8.CompileEval': new TimerEvent("#CC4400", true, 0), |
65 'V8.Parse': new TimerEvent("#00CC00", true, true), | 74 'V8.Parse': new TimerEvent("#00CC00", true, 0), |
66 'V8.PreParse': new TimerEvent("#44CC00", true, true), | 75 'V8.PreParse': new TimerEvent("#44CC00", true, 0), |
67 'V8.ParseLazy': new TimerEvent("#00CC44", true, true), | 76 'V8.ParseLazy': new TimerEvent("#00CC44", true, 0), |
68 'V8.GCScavenger': new TimerEvent("#0044CC", true, true), | 77 'V8.GCScavenger': new TimerEvent("#0044CC", true, 0), |
69 'V8.GCCompactor': new TimerEvent("#4444CC", true, true), | 78 'V8.GCCompactor': new TimerEvent("#4444CC", true, 0), |
70 'V8.GCContext': new TimerEvent("#4400CC", true, true), | 79 'V8.GCContext': new TimerEvent("#4400CC", true, 0), |
71 } | 80 } |
72 | 81 |
73 var kExecutionEvent = TimerEvents['V8.Execute']; | 82 |
| 83 Array.prototype.top = function() { |
| 84 if (this.length == 0) return undefined; |
| 85 return this[this.length - 1]; |
| 86 } |
| 87 |
| 88 var event_stack = []; |
| 89 var last_time_stamp = []; |
| 90 |
| 91 for (var i = 0; i < kNumThreads; i++) { |
| 92 event_stack[i] = []; |
| 93 last_time_stamp[i] = -1; |
| 94 } |
74 | 95 |
75 | 96 |
76 function CodeKind(color, kinds) { | 97 function CodeKind(color, kinds) { |
77 this.color = color; | 98 this.color = color; |
78 this.in_execution = []; | 99 this.in_execution = []; |
79 this.stack_frames = []; | 100 this.stack_frames = []; |
80 for (var i = 0; i < kStackFrames; i++) this.stack_frames.push([]); | 101 for (var i = 0; i < kStackFrames; i++) this.stack_frames.push([]); |
81 this.kinds = kinds; | 102 this.kinds = kinds; |
82 } | 103 } |
83 | 104 |
(...skipping 12 matching lines...) Expand all Loading... |
96 | 117 |
97 var xrange_start; | 118 var xrange_start; |
98 var xrange_end; | 119 var xrange_end; |
99 var obj_index = 0; | 120 var obj_index = 0; |
100 var execution_pauses = []; | 121 var execution_pauses = []; |
101 var code_map = new CodeMap(); | 122 var code_map = new CodeMap(); |
102 | 123 |
103 var xrange_start_override = undefined; | 124 var xrange_start_override = undefined; |
104 var xrange_end_override = undefined; | 125 var xrange_end_override = undefined; |
105 var distortion_per_entry = 0.005; // Milliseconds | 126 var distortion_per_entry = 0.005; // Milliseconds |
| 127 var pause_tolerance = 0.005; // Milliseconds. |
106 | 128 |
107 var sort_by_start = []; | 129 var distortion = 0; |
108 var sort_by_end = []; | |
109 var sorted_ticks = []; | |
110 | 130 |
111 | 131 |
112 function Range(start, end) { | 132 function Range(start, end) { |
113 // Everthing from here are in milliseconds. | 133 // Everthing from here are in milliseconds. |
114 this.start = start; | 134 this.start = start; |
115 this.end = end; | 135 this.end = end; |
116 } | 136 } |
117 | 137 |
118 | 138 |
119 function Tick(tick) { | 139 function Tick(tick) { |
120 this.tick = tick; | 140 this.tick = tick; |
121 } | 141 } |
122 | 142 |
123 | 143 |
124 Range.prototype.duration = function() { return this.end - this.start; } | 144 Range.prototype.duration = function() { return this.end - this.start; } |
125 | 145 |
126 | 146 |
127 function ProcessTimerEvent(name, start, length) { | 147 function ProcessTimerEventStart(name, start) { |
128 var event = TimerEvents[name]; | 148 // Find out the thread id. |
129 if (event === undefined) return; | 149 var new_event = TimerEvents[name]; |
130 start /= 1000; // Convert to milliseconds. | 150 if (new_event === undefined) return; |
131 length /= 1000; | 151 var thread_id = new_event.thread_id; |
132 var end = start + length; | 152 |
133 var range = new Range(start, end); | 153 start = Math.max(last_time_stamp[thread_id] + kMinRangeLength, start); |
134 event.ranges.push(range); | 154 |
135 sort_by_start.push(range); | 155 // Last event on this thread is done with the start of this event. |
136 sort_by_end.push(range); | 156 var last_event = event_stack[thread_id].top(); |
| 157 if (last_event !== undefined) { |
| 158 var new_range = new Range(last_time_stamp[thread_id], start); |
| 159 last_event.ranges.push(new_range); |
| 160 } |
| 161 event_stack[thread_id].push(new_event); |
| 162 last_time_stamp[thread_id] = start; |
137 } | 163 } |
138 | 164 |
139 | 165 |
| 166 function ProcessTimerEventEnd(name, end) { |
| 167 // Find out about the thread_id. |
| 168 var finished_event = TimerEvents[name]; |
| 169 var thread_id = finished_event.thread_id; |
| 170 assert(finished_event === event_stack[thread_id].pop(), |
| 171 "inconsistent event stack"); |
| 172 |
| 173 end = Math.max(last_time_stamp[thread_id] + kMinRangeLength, end); |
| 174 |
| 175 var new_range = new Range(last_time_stamp[thread_id], end); |
| 176 finished_event.ranges.push(new_range); |
| 177 last_time_stamp[thread_id] = end; |
| 178 } |
| 179 |
| 180 |
140 function ProcessCodeCreateEvent(type, kind, address, size, name) { | 181 function ProcessCodeCreateEvent(type, kind, address, size, name) { |
141 var code_entry = new CodeMap.CodeEntry(size, name); | 182 var code_entry = new CodeMap.CodeEntry(size, name); |
142 code_entry.kind = kind; | 183 code_entry.kind = kind; |
143 code_map.addCode(address, code_entry); | 184 code_map.addCode(address, code_entry); |
144 } | 185 } |
145 | 186 |
146 | 187 |
147 function ProcessCodeMoveEvent(from, to) { | 188 function ProcessCodeMoveEvent(from, to) { |
148 code_map.moveCode(from, to); | 189 code_map.moveCode(from, to); |
149 } | 190 } |
(...skipping 21 matching lines...) Expand all Loading... |
171 function FindCodeKind(kind) { | 212 function FindCodeKind(kind) { |
172 for (name in CodeKinds) { | 213 for (name in CodeKinds) { |
173 if (CodeKinds[name].kinds.indexOf(kind) >= 0) { | 214 if (CodeKinds[name].kinds.indexOf(kind) >= 0) { |
174 return CodeKinds[name]; | 215 return CodeKinds[name]; |
175 } | 216 } |
176 } | 217 } |
177 } | 218 } |
178 | 219 |
179 | 220 |
180 function ProcessTickEvent(pc, sp, timer, unused_x, unused_y, vmstate, stack) { | 221 function ProcessTickEvent(pc, sp, timer, unused_x, unused_y, vmstate, stack) { |
181 timer /= 1000; | |
182 var tick = new Tick(timer); | 222 var tick = new Tick(timer); |
183 | 223 |
184 var entered = false; | |
185 var entry = code_map.findEntry(pc); | 224 var entry = code_map.findEntry(pc); |
186 if (entry) { | 225 if (entry) FindCodeKind(entry.kind).in_execution.push(tick); |
187 FindCodeKind(entry.kind).in_execution.push(tick); | |
188 entered = true; | |
189 } | |
190 | 226 |
191 for (var i = 0; i < kStackFrames; i++) { | 227 for (var i = 0; i < kStackFrames; i++) { |
192 if (!stack[i]) break; | 228 if (!stack[i]) break; |
193 var entry = code_map.findEntry(stack[i]); | 229 var entry = code_map.findEntry(stack[i]); |
194 if (entry) { | 230 if (entry) FindCodeKind(entry.kind).stack_frames[i].push(tick); |
195 FindCodeKind(entry.kind).stack_frames[i].push(tick); | |
196 entered = true; | |
197 } | |
198 } | 231 } |
199 | |
200 if (entered) sorted_ticks.push(tick); | |
201 } | 232 } |
202 | 233 |
203 | 234 |
204 function ProcessDistortion(distortion_in_picoseconds) { | 235 function ProcessDistortion(distortion_in_picoseconds) { |
205 distortion_per_entry = distortion_in_picoseconds / 1000000; | 236 distortion_per_entry = distortion_in_picoseconds / 1000000; |
206 } | 237 } |
207 | 238 |
208 | 239 |
209 function ProcessPlotRange(start, end) { | 240 function ProcessPlotRange(start, end) { |
210 xrange_start_override = start; | 241 xrange_start_override = start; |
211 xrange_end_override = end; | 242 xrange_end_override = end; |
212 } | 243 } |
213 | 244 |
214 | 245 |
215 function Undistort() { | |
216 // Undistort timers wrt instrumentation overhead. | |
217 sort_by_start.sort(function(a, b) { return b.start - a.start; }); | |
218 sort_by_end.sort(function(a, b) { return b.end - a.end; }); | |
219 sorted_ticks.sort(function(a, b) { return b.tick - a.tick; }); | |
220 var distortion = 0; | |
221 | |
222 var next_start = sort_by_start.pop(); | |
223 var next_end = sort_by_end.pop(); | |
224 var next_tick = sorted_ticks.pop(); | |
225 | |
226 function UndistortTicksUntil(tick) { | |
227 while (next_tick) { | |
228 if (next_tick.tick > tick) return; | |
229 next_tick.tick -= distortion; | |
230 next_tick = sorted_ticks.pop(); | |
231 } | |
232 } | |
233 | |
234 while (true) { | |
235 var next_start_start = next_start ? next_start.start : Infinity; | |
236 var next_end_end = next_end ? next_end.end : Infinity; | |
237 if (!next_start && !next_end) { | |
238 UndistortTicksUntil(Infinity); | |
239 break; | |
240 } | |
241 if (next_start_start <= next_end_end) { | |
242 UndistortTicksUntil(next_start_start); | |
243 // Undistort the start time stamp. | |
244 next_start.start -= distortion; | |
245 next_start = sort_by_start.pop(); | |
246 } else { | |
247 // Undistort the end time stamp. We completely attribute the overhead | |
248 // to the point when we stop and log the timer, so we increase the | |
249 // distortion only here. | |
250 UndistortTicksUntil(next_end_end); | |
251 next_end.end -= distortion; | |
252 distortion += distortion_per_entry; | |
253 next_end = sort_by_end.pop(); | |
254 } | |
255 } | |
256 | |
257 sort_by_start = undefined; | |
258 sort_by_end = undefined; | |
259 sorted_ticks = undefined; | |
260 | |
261 // Make sure that start <= end applies for every range. | |
262 for (name in TimerEvents) { | |
263 var ranges = TimerEvents[name].ranges; | |
264 for (var j = 0; j < ranges.length; j++) { | |
265 if (ranges[j].end < ranges[j].start) ranges[j].end = ranges[j].start; | |
266 } | |
267 } | |
268 } | |
269 | |
270 | |
271 function FindPlotRange() { | 246 function FindPlotRange() { |
272 var start_found = (xrange_start_override || xrange_start_override == 0); | 247 var start_found = (xrange_start_override || xrange_start_override == 0); |
273 var end_found = (xrange_end_override || xrange_end_override == 0); | 248 var end_found = (xrange_end_override || xrange_end_override == 0); |
274 xrange_start = start_found ? xrange_start_override : Infinity; | 249 xrange_start = start_found ? xrange_start_override : Infinity; |
275 xrange_end = end_found ? xrange_end_override : -Infinity; | 250 xrange_end = end_found ? xrange_end_override : -Infinity; |
276 | 251 |
277 if (start_found && end_found) return; | 252 if (start_found && end_found) return; |
278 | 253 |
279 for (name in TimerEvents) { | 254 for (name in TimerEvents) { |
280 var ranges = TimerEvents[name].ranges; | 255 var ranges = TimerEvents[name].ranges; |
(...skipping 11 matching lines...) Expand all Loading... |
292 var ticks = CodeKinds[codekind].in_execution; | 267 var ticks = CodeKinds[codekind].in_execution; |
293 for (var i = 0; i < ticks.length; i++) { | 268 for (var i = 0; i < ticks.length; i++) { |
294 if (ticks[i].tick < xrange_start && !start_found) { | 269 if (ticks[i].tick < xrange_start && !start_found) { |
295 xrange_start = ticks[i].tick; | 270 xrange_start = ticks[i].tick; |
296 } | 271 } |
297 if (ticks[i].tick > xrange_end && !end_found) { | 272 if (ticks[i].tick > xrange_end && !end_found) { |
298 xrange_end = ticks[i].tick; | 273 xrange_end = ticks[i].tick; |
299 } | 274 } |
300 } | 275 } |
301 } | 276 } |
| 277 |
| 278 // Set pause tolerance to something appropriate for the plot resolution |
| 279 // to make it easier for gnuplot. |
| 280 pause_tolerance = (xrange_end - xrange_start) / kResX / 10; |
| 281 } |
| 282 |
| 283 |
| 284 function parseTimeStamp(timestamp) { |
| 285 distortion += distortion_per_entry; |
| 286 return parseInt(timestamp) / 1000 - distortion; |
302 } | 287 } |
303 | 288 |
304 | 289 |
305 function CollectData() { | 290 function CollectData() { |
306 // Collect data from log. | 291 // Collect data from log. |
307 var logreader = new LogReader( | 292 var logreader = new LogReader( |
308 { 'timer-event' : { parsers: [null, parseInt, parseInt], | 293 { 'timer-event-start': { parsers: [null, parseTimeStamp], |
309 processor: ProcessTimerEvent }, | 294 processor: ProcessTimerEventStart }, |
| 295 'timer-event-end': { parsers: [null, parseTimeStamp], |
| 296 processor: ProcessTimerEventEnd }, |
310 'shared-library': { parsers: [null, parseInt, parseInt], | 297 'shared-library': { parsers: [null, parseInt, parseInt], |
311 processor: ProcessSharedLibrary }, | 298 processor: ProcessSharedLibrary }, |
312 'code-creation': { parsers: [null, parseInt, parseInt, parseInt, null], | 299 'code-creation': { parsers: [null, parseInt, parseInt, parseInt, null], |
313 processor: ProcessCodeCreateEvent }, | 300 processor: ProcessCodeCreateEvent }, |
314 'code-move': { parsers: [parseInt, parseInt], | 301 'code-move': { parsers: [parseInt, parseInt], |
315 processor: ProcessCodeMoveEvent }, | 302 processor: ProcessCodeMoveEvent }, |
316 'code-delete': { parsers: [parseInt], | 303 'code-delete': { parsers: [parseInt], |
317 processor: ProcessCodeDeleteEvent }, | 304 processor: ProcessCodeDeleteEvent }, |
318 'tick': { parsers: [parseInt, parseInt, parseInt, | 305 'tick': { parsers: [parseInt, parseInt, parseTimeStamp, |
319 null, null, parseInt, 'var-args'], | 306 null, null, parseInt, 'var-args'], |
320 processor: ProcessTickEvent }, | 307 processor: ProcessTickEvent }, |
321 'distortion': { parsers: [parseInt], | 308 'distortion': { parsers: [parseInt], |
322 processor: ProcessDistortion }, | 309 processor: ProcessDistortion }, |
323 'plot-range': { parsers: [parseInt, parseInt], | 310 'plot-range': { parsers: [parseInt, parseInt], |
324 processor: ProcessPlotRange }, | 311 processor: ProcessPlotRange }, |
325 }); | 312 }); |
326 | 313 |
327 var line; | 314 var line; |
328 while (line = readline()) { | 315 while (line = readline()) { |
329 logreader.processLogLine(line); | 316 logreader.processLogLine(line); |
330 } | 317 } |
331 | 318 |
332 Undistort(); | |
333 | |
334 // Collect execution pauses. | 319 // Collect execution pauses. |
335 for (name in TimerEvents) { | 320 for (name in TimerEvents) { |
336 var event = TimerEvents[name]; | 321 var event = TimerEvents[name]; |
337 if (!event.pause) continue; | 322 if (!event.pause) continue; |
338 var ranges = event.ranges; | 323 var ranges = event.ranges; |
339 for (var j = 0; j < ranges.length; j++) execution_pauses.push(ranges[j]); | 324 for (var j = 0; j < ranges.length; j++) execution_pauses.push(ranges[j]); |
340 } | 325 } |
341 execution_pauses = MergeRanges(execution_pauses); | 326 execution_pauses = MergeRanges(execution_pauses); |
342 | |
343 // Knock out time not spent in javascript execution. Note that this also | |
344 // includes time spent external code, which do not contribute to execution | |
345 // pauses. | |
346 var exclude_ranges = []; | |
347 for (name in TimerEvents) { | |
348 var event = TimerEvents[name]; | |
349 if (!event.no_execution) continue; | |
350 var ranges = event.ranges; | |
351 // Add ranges of this event to the pause list. | |
352 for (var j = 0; j < ranges.length; j++) { | |
353 exclude_ranges.push(ranges[j]); | |
354 } | |
355 } | |
356 | |
357 kExecutionEvent.ranges = MergeRanges(kExecutionEvent.ranges); | |
358 exclude_ranges = MergeRanges(exclude_ranges); | |
359 kExecutionEvent.ranges = ExcludeRanges(kExecutionEvent.ranges, | |
360 exclude_ranges); | |
361 } | 327 } |
362 | 328 |
363 | 329 |
364 function DrawBar(row, color, start, end, width) { | 330 function DrawBar(row, color, start, end, width) { |
365 obj_index++; | 331 obj_index++; |
366 command = "set object " + obj_index + " rect"; | 332 command = "set object " + obj_index + " rect"; |
367 command += " from " + start + ", " + (row - width); | 333 command += " from " + start + ", " + (row - width); |
368 command += " to " + end + ", " + (row + width); | 334 command += " to " + end + ", " + (row + width); |
369 command += " fc rgb \"" + color + "\""; | 335 command += " fc rgb \"" + color + "\""; |
370 print(command); | 336 print(command); |
(...skipping 14 matching lines...) Expand all Loading... |
385 ranges.sort(function(a, b) { return a.start - b.start; }); | 351 ranges.sort(function(a, b) { return a.start - b.start; }); |
386 var result = []; | 352 var result = []; |
387 var j = 0; | 353 var j = 0; |
388 for (var i = 0; i < ranges.length; i = j) { | 354 for (var i = 0; i < ranges.length; i = j) { |
389 var merge_start = ranges[i].start; | 355 var merge_start = ranges[i].start; |
390 if (merge_start > xrange_end) break; // Out of plot range. | 356 if (merge_start > xrange_end) break; // Out of plot range. |
391 var merge_end = ranges[i].end; | 357 var merge_end = ranges[i].end; |
392 for (j = i + 1; j < ranges.length; j++) { | 358 for (j = i + 1; j < ranges.length; j++) { |
393 var next_range = ranges[j]; | 359 var next_range = ranges[j]; |
394 // Don't merge ranges if there is no overlap (including merge tolerance). | 360 // Don't merge ranges if there is no overlap (including merge tolerance). |
395 if (next_range.start > merge_end + kPauseTolerance) break; | 361 if (next_range.start > merge_end + pause_tolerance) break; |
396 // Merge ranges. | 362 // Merge ranges. |
397 if (next_range.end > merge_end) { // Extend range end. | 363 if (next_range.end > merge_end) { // Extend range end. |
398 merge_end = next_range.end; | 364 merge_end = next_range.end; |
399 } | 365 } |
400 } | 366 } |
401 if (merge_end < xrange_start) continue; // Out of plot range. | 367 if (merge_end < xrange_start) continue; // Out of plot range. |
402 if (merge_end < merge_start) continue; // Not an actual range. | 368 if (merge_end < merge_start) continue; // Not an actual range. |
403 result.push(new Range(merge_start, merge_end)); | 369 result.push(new Range(merge_start, merge_end)); |
404 } | 370 } |
405 return result; | 371 return result; |
406 } | 372 } |
407 | 373 |
408 | 374 |
409 function ExcludeRanges(include, exclude) { | 375 function RestrictRangesTo(ranges, start, end) { |
410 // We assume that both input lists are sorted and merged with MergeRanges. | |
411 var result = []; | 376 var result = []; |
412 var exclude_index = 0; | 377 for (var i = 0; i < ranges.length; i++) { |
413 var include_index = 0; | 378 if (ranges[i].start <= end && ranges[i].end >= start) { |
414 var include_start, include_end, exclude_start, exclude_end; | 379 result.push(new Range(Math.max(ranges[i].start, start), |
415 | 380 Math.min(ranges[i].end, end))); |
416 function NextInclude() { | |
417 if (include_index >= include.length) return false; | |
418 include_start = include[include_index].start; | |
419 include_end = include[include_index].end; | |
420 include_index++; | |
421 return true; | |
422 } | |
423 | |
424 function NextExclude() { | |
425 if (exclude_index >= exclude.length) { | |
426 // No more exclude, finish by repeating case (2). | |
427 exclude_start = Infinity; | |
428 exclude_end = Infinity; | |
429 return false; | |
430 } | |
431 exclude_start = exclude[exclude_index].start; | |
432 exclude_end = exclude[exclude_index].end; | |
433 exclude_index++; | |
434 return true; | |
435 } | |
436 | |
437 if (!NextInclude() || !NextExclude()) return include; | |
438 | |
439 while (true) { | |
440 if (exclude_end <= include_start) { | |
441 // (1) Exclude and include do not overlap. | |
442 // Include ##### | |
443 // Exclude ## | |
444 NextExclude(); | |
445 } else if (include_end <= exclude_start) { | |
446 // (2) Exclude and include do not overlap. | |
447 // Include ##### | |
448 // Exclude ### | |
449 result.push(new Range(include_start, include_end)); | |
450 if (!NextInclude()) break; | |
451 } else if (exclude_start <= include_start && | |
452 exclude_end < include_end && | |
453 include_start < exclude_end) { | |
454 // (3) Exclude overlaps with begin of include. | |
455 // Include ####### | |
456 // Exclude ##### | |
457 // Result #### | |
458 include_start = exclude_end; | |
459 NextExclude(); | |
460 } else if (include_start < exclude_start && | |
461 include_end <= exclude_end && | |
462 exclude_start < include_end) { | |
463 // (4) Exclude overlaps with end of include. | |
464 // Include ####### | |
465 // Exclude ##### | |
466 // Result #### | |
467 result.push(new Range(include_start, exclude_start)); | |
468 if (!NextInclude()) break; | |
469 } else if (exclude_start > include_start && exclude_end < include_end) { | |
470 // (5) Exclude splits include into two parts. | |
471 // Include ####### | |
472 // Exclude ## | |
473 // Result ## ### | |
474 result.push(new Range(include_start, exclude_start)); | |
475 include_start = exclude_end; | |
476 NextExclude(); | |
477 } else if (exclude_start <= include_start && exclude_end >= include_end) { | |
478 // (6) Exclude entirely covers include. | |
479 // Include ###### | |
480 // Exclude ######### | |
481 if (!NextInclude()) break; | |
482 } else { | |
483 throw new Error("this should not happen!"); | |
484 } | 381 } |
485 } | 382 } |
486 | |
487 return result; | 383 return result; |
488 } | 384 } |
489 | 385 |
490 | 386 |
491 function GnuplotOutput() { | 387 function GnuplotOutput() { |
492 FindPlotRange(); | 388 FindPlotRange(); |
493 | 389 |
494 print("set terminal pngcairo size " + kResX + "," + kResY + | 390 print("set terminal pngcairo size " + kResX + "," + kResY + |
495 " enhanced font 'Helvetica,10'"); | 391 " enhanced font 'Helvetica,10'"); |
496 print("set yrange [0:" + (num_timer_event + 1) + "]"); | 392 print("set yrange [0:" + (num_timer_event + 1) + "]"); |
497 print("set xlabel \"execution time in ms\""); | 393 print("set xlabel \"execution time in ms\""); |
498 print("set xrange [" + xrange_start + ":" + xrange_end + "]"); | 394 print("set xrange [" + xrange_start + ":" + xrange_end + "]"); |
499 print("set style fill pattern 2 bo 1"); | 395 print("set style fill pattern 2 bo 1"); |
500 print("set style rect fs solid 1 noborder"); | 396 print("set style rect fs solid 1 noborder"); |
501 print("set style line 1 lt 1 lw 1 lc rgb \"#000000\""); | 397 print("set style line 1 lt 1 lw 1 lc rgb \"#000000\""); |
502 print("set xtics out nomirror"); | 398 print("set xtics out nomirror"); |
503 print("unset key"); | 399 print("unset key"); |
504 | 400 |
505 var percentages = {}; | 401 var percentages = {}; |
506 var total = 0; | 402 var total = 0; |
507 for (var name in TimerEvents) { | 403 for (var name in TimerEvents) { |
508 var event = TimerEvents[name]; | 404 var event = TimerEvents[name]; |
509 var ranges = MergeRanges(event.ranges); | 405 var ranges = RestrictRangesTo(event.ranges, xrange_start, xrange_end); |
510 var exclude_ranges = [new Range(-Infinity, xrange_start), | 406 ranges = MergeRanges(ranges); |
511 new Range(xrange_end, Infinity)]; | |
512 ranges = ExcludeRanges(ranges, exclude_ranges); | |
513 var sum = | 407 var sum = |
514 ranges.map(function(range) { return range.duration(); }) | 408 ranges.map(function(range) { return range.duration(); }) |
515 .reduce(function(a, b) { return a + b; }, 0); | 409 .reduce(function(a, b) { return a + b; }, 0); |
516 percentages[name] = (sum / (xrange_end - xrange_start) * 100).toFixed(1); | 410 percentages[name] = (sum / (xrange_end - xrange_start) * 100).toFixed(1); |
517 } | 411 } |
518 | 412 |
519 // Name Y-axis. | 413 // Name Y-axis. |
520 var ytics = []; | 414 var ytics = []; |
521 for (name in TimerEvents) { | 415 for (name in TimerEvents) { |
522 var index = TimerEvents[name].index; | 416 var index = TimerEvents[name].index; |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
602 for (var i = 0; i < execution_pauses.length; i++) { | 496 for (var i = 0; i < execution_pauses.length; i++) { |
603 var pause = execution_pauses[i]; | 497 var pause = execution_pauses[i]; |
604 print(pause.end + " " + pause.duration()); | 498 print(pause.end + " " + pause.duration()); |
605 } | 499 } |
606 print("e"); | 500 print("e"); |
607 } | 501 } |
608 | 502 |
609 | 503 |
610 CollectData(); | 504 CollectData(); |
611 GnuplotOutput(); | 505 GnuplotOutput(); |
OLD | NEW |