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) { |
52 if (!something) { | |
53 print(new Error("assertion failure.").stack); | |
54 } | |
55 } | |
56 | |
57 function TimerEvent(color, pause, thread_id) { | |
58 assert(thread_id >= 0 && thread_id < kNumThreads); | |
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]; | |
haraken
2013/01/28 12:35:49
Nit: this.length - 1 (Spaces needed around '-')
Yang
2013/01/28 14:42:53
Done.
| |
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 = []; | 130 |
109 var sorted_ticks = []; | 131 |
132 function AccountForDistortionToMs(timestamp) { | |
haraken
2013/01/28 12:35:49
I don't fully understand what 'AccountForDistortio
Yang
2013/01/28 14:42:53
I changed that into parseTimeStamp used in Collect
| |
133 distortion += distortion_per_entry; | |
134 return timestamp / 1000 - distortion; | |
135 } | |
110 | 136 |
111 | 137 |
112 function Range(start, end) { | 138 function Range(start, end) { |
113 // Everthing from here are in milliseconds. | 139 // Everthing from here are in milliseconds. |
114 this.start = start; | 140 this.start = start; |
115 this.end = end; | 141 this.end = end; |
116 } | 142 } |
117 | 143 |
118 | 144 |
119 function Tick(tick) { | 145 function Tick(tick) { |
120 this.tick = tick; | 146 this.tick = tick; |
121 } | 147 } |
122 | 148 |
123 | 149 |
124 Range.prototype.duration = function() { return this.end - this.start; } | 150 Range.prototype.duration = function() { return this.end - this.start; } |
125 | 151 |
126 | 152 |
127 function ProcessTimerEvent(name, start, length) { | 153 function ProcessTimerEventStart(name, start) { |
128 var event = TimerEvents[name]; | 154 // Find out the thread id. |
129 if (event === undefined) return; | 155 var new_event = TimerEvents[name]; |
130 start /= 1000; // Convert to milliseconds. | 156 if (new_event === undefined) return; |
131 length /= 1000; | 157 var thread_id = new_event.thread_id; |
132 var end = start + length; | 158 |
133 var range = new Range(start, end); | 159 start = Math.max(last_time_stamp[thread_id] + kMinRangeLength, |
134 event.ranges.push(range); | 160 AccountForDistortionToMs(start)); |
135 sort_by_start.push(range); | 161 |
136 sort_by_end.push(range); | 162 // Last event on this thread is done with the start of this event. |
163 var last_event = event_stack[thread_id].top(); | |
164 if (last_event !== undefined) { | |
165 var new_range = new Range(last_time_stamp[thread_id], start); | |
166 last_event.ranges.push(new_range); | |
167 } | |
168 event_stack[thread_id].push(new_event); | |
169 last_time_stamp[thread_id] = start; | |
137 } | 170 } |
138 | 171 |
139 | 172 |
173 function ProcessTimerEventEnd(name, end) { | |
174 // Find out about the thread_id. | |
haraken
2013/01/28 12:35:49
Nit: Find out the thread_id.
Yang
2013/01/28 14:42:53
Done.
| |
175 var finished_event = TimerEvents[name]; | |
haraken
2013/01/28 12:35:49
if (finished_event === undefined) return;
Yang
2013/01/28 14:42:53
This should never happen, as asserted below. I mov
| |
176 var thread_id = finished_event.thread_id; | |
177 | |
178 end = Math.max(last_time_stamp[thread_id] + kMinRangeLength, | |
179 AccountForDistortionToMs(end)); | |
180 | |
181 assert(finished_event === event_stack[thread_id].pop()); | |
haraken
2013/01/28 12:35:49
I want to make the error message more verbose e.g.
| |
182 | |
183 var new_range = new Range(last_time_stamp[thread_id], end); | |
184 finished_event.ranges.push(new_range); | |
185 | |
186 last_time_stamp[thread_id] = end; | |
187 } | |
188 | |
189 | |
140 function ProcessCodeCreateEvent(type, kind, address, size, name) { | 190 function ProcessCodeCreateEvent(type, kind, address, size, name) { |
141 var code_entry = new CodeMap.CodeEntry(size, name); | 191 var code_entry = new CodeMap.CodeEntry(size, name); |
142 code_entry.kind = kind; | 192 code_entry.kind = kind; |
143 code_map.addCode(address, code_entry); | 193 code_map.addCode(address, code_entry); |
144 } | 194 } |
145 | 195 |
146 | 196 |
147 function ProcessCodeMoveEvent(from, to) { | 197 function ProcessCodeMoveEvent(from, to) { |
148 code_map.moveCode(from, to); | 198 code_map.moveCode(from, to); |
149 } | 199 } |
(...skipping 21 matching lines...) Expand all Loading... | |
171 function FindCodeKind(kind) { | 221 function FindCodeKind(kind) { |
172 for (name in CodeKinds) { | 222 for (name in CodeKinds) { |
173 if (CodeKinds[name].kinds.indexOf(kind) >= 0) { | 223 if (CodeKinds[name].kinds.indexOf(kind) >= 0) { |
174 return CodeKinds[name]; | 224 return CodeKinds[name]; |
175 } | 225 } |
176 } | 226 } |
177 } | 227 } |
178 | 228 |
179 | 229 |
180 function ProcessTickEvent(pc, sp, timer, unused_x, unused_y, vmstate, stack) { | 230 function ProcessTickEvent(pc, sp, timer, unused_x, unused_y, vmstate, stack) { |
181 timer /= 1000; | 231 timer = AccountForDistortionToMs(timer); |
182 var tick = new Tick(timer); | 232 var tick = new Tick(timer); |
183 | 233 |
184 var entered = false; | |
185 var entry = code_map.findEntry(pc); | 234 var entry = code_map.findEntry(pc); |
186 if (entry) { | 235 if (entry) FindCodeKind(entry.kind).in_execution.push(tick); |
187 FindCodeKind(entry.kind).in_execution.push(tick); | |
188 entered = true; | |
189 } | |
190 | 236 |
191 for (var i = 0; i < kStackFrames; i++) { | 237 for (var i = 0; i < kStackFrames; i++) { |
192 if (!stack[i]) break; | 238 if (!stack[i]) break; |
193 var entry = code_map.findEntry(stack[i]); | 239 var entry = code_map.findEntry(stack[i]); |
194 if (entry) { | 240 if (entry) FindCodeKind(entry.kind).stack_frames[i].push(tick); |
195 FindCodeKind(entry.kind).stack_frames[i].push(tick); | |
196 entered = true; | |
197 } | |
198 } | 241 } |
199 | |
200 if (entered) sorted_ticks.push(tick); | |
201 } | 242 } |
202 | 243 |
203 | 244 |
204 function ProcessDistortion(distortion_in_picoseconds) { | 245 function ProcessDistortion(distortion_in_picoseconds) { |
205 distortion_per_entry = distortion_in_picoseconds / 1000000; | 246 distortion_per_entry = distortion_in_picoseconds / 1000000; |
206 } | 247 } |
207 | 248 |
208 | 249 |
209 function ProcessPlotRange(start, end) { | 250 function ProcessPlotRange(start, end) { |
210 xrange_start_override = start; | 251 xrange_start_override = start; |
211 xrange_end_override = end; | 252 xrange_end_override = end; |
212 } | 253 } |
213 | 254 |
214 | 255 |
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() { | 256 function FindPlotRange() { |
272 var start_found = (xrange_start_override || xrange_start_override == 0); | 257 var start_found = (xrange_start_override || xrange_start_override == 0); |
273 var end_found = (xrange_end_override || xrange_end_override == 0); | 258 var end_found = (xrange_end_override || xrange_end_override == 0); |
274 xrange_start = start_found ? xrange_start_override : Infinity; | 259 xrange_start = start_found ? xrange_start_override : Infinity; |
275 xrange_end = end_found ? xrange_end_override : -Infinity; | 260 xrange_end = end_found ? xrange_end_override : -Infinity; |
276 | 261 |
277 if (start_found && end_found) return; | 262 if (start_found && end_found) return; |
278 | 263 |
279 for (name in TimerEvents) { | 264 for (name in TimerEvents) { |
280 var ranges = TimerEvents[name].ranges; | 265 var ranges = TimerEvents[name].ranges; |
(...skipping 11 matching lines...) Expand all Loading... | |
292 var ticks = CodeKinds[codekind].in_execution; | 277 var ticks = CodeKinds[codekind].in_execution; |
293 for (var i = 0; i < ticks.length; i++) { | 278 for (var i = 0; i < ticks.length; i++) { |
294 if (ticks[i].tick < xrange_start && !start_found) { | 279 if (ticks[i].tick < xrange_start && !start_found) { |
295 xrange_start = ticks[i].tick; | 280 xrange_start = ticks[i].tick; |
296 } | 281 } |
297 if (ticks[i].tick > xrange_end && !end_found) { | 282 if (ticks[i].tick > xrange_end && !end_found) { |
298 xrange_end = ticks[i].tick; | 283 xrange_end = ticks[i].tick; |
299 } | 284 } |
300 } | 285 } |
301 } | 286 } |
287 | |
288 // Set pause tolerance to something appropriate for the plot resolution | |
289 // to make it easier for gnuplot. | |
290 pause_tolerance = (xrange_end - xrange_start) / kResX / 10; | |
302 } | 291 } |
303 | 292 |
304 | 293 |
305 function CollectData() { | 294 function CollectData() { |
306 // Collect data from log. | 295 // Collect data from log. |
307 var logreader = new LogReader( | 296 var logreader = new LogReader( |
308 { 'timer-event' : { parsers: [null, parseInt, parseInt], | 297 { 'timer-event-start': { parsers: [null, parseInt], |
309 processor: ProcessTimerEvent }, | 298 processor: ProcessTimerEventStart }, |
299 'timer-event-end': { parsers: [null, parseInt], | |
300 processor: ProcessTimerEventEnd }, | |
310 'shared-library': { parsers: [null, parseInt, parseInt], | 301 'shared-library': { parsers: [null, parseInt, parseInt], |
311 processor: ProcessSharedLibrary }, | 302 processor: ProcessSharedLibrary }, |
312 'code-creation': { parsers: [null, parseInt, parseInt, parseInt, null], | 303 'code-creation': { parsers: [null, parseInt, parseInt, parseInt, null], |
313 processor: ProcessCodeCreateEvent }, | 304 processor: ProcessCodeCreateEvent }, |
314 'code-move': { parsers: [parseInt, parseInt], | 305 'code-move': { parsers: [parseInt, parseInt], |
315 processor: ProcessCodeMoveEvent }, | 306 processor: ProcessCodeMoveEvent }, |
316 'code-delete': { parsers: [parseInt], | 307 'code-delete': { parsers: [parseInt], |
317 processor: ProcessCodeDeleteEvent }, | 308 processor: ProcessCodeDeleteEvent }, |
318 'tick': { parsers: [parseInt, parseInt, parseInt, | 309 'tick': { parsers: [parseInt, parseInt, parseInt, |
319 null, null, parseInt, 'var-args'], | 310 null, null, parseInt, 'var-args'], |
320 processor: ProcessTickEvent }, | 311 processor: ProcessTickEvent }, |
321 'distortion': { parsers: [parseInt], | 312 'distortion': { parsers: [parseInt], |
322 processor: ProcessDistortion }, | 313 processor: ProcessDistortion }, |
323 'plot-range': { parsers: [parseInt, parseInt], | 314 'plot-range': { parsers: [parseInt, parseInt], |
324 processor: ProcessPlotRange }, | 315 processor: ProcessPlotRange }, |
325 }); | 316 }); |
326 | 317 |
327 var line; | 318 var line; |
328 while (line = readline()) { | 319 while (line = readline()) { |
329 logreader.processLogLine(line); | 320 logreader.processLogLine(line); |
330 } | 321 } |
331 | 322 |
332 Undistort(); | |
333 | |
334 // Collect execution pauses. | 323 // Collect execution pauses. |
335 for (name in TimerEvents) { | 324 for (name in TimerEvents) { |
336 var event = TimerEvents[name]; | 325 var event = TimerEvents[name]; |
337 if (!event.pause) continue; | 326 if (!event.pause) continue; |
338 var ranges = event.ranges; | 327 var ranges = event.ranges; |
339 for (var j = 0; j < ranges.length; j++) execution_pauses.push(ranges[j]); | 328 for (var j = 0; j < ranges.length; j++) execution_pauses.push(ranges[j]); |
340 } | 329 } |
341 execution_pauses = MergeRanges(execution_pauses); | 330 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 } | 331 } |
362 | 332 |
363 | 333 |
364 function DrawBar(row, color, start, end, width) { | 334 function DrawBar(row, color, start, end, width) { |
365 obj_index++; | 335 obj_index++; |
366 command = "set object " + obj_index + " rect"; | 336 command = "set object " + obj_index + " rect"; |
367 command += " from " + start + ", " + (row - width); | 337 command += " from " + start + ", " + (row - width); |
368 command += " to " + end + ", " + (row + width); | 338 command += " to " + end + ", " + (row + width); |
369 command += " fc rgb \"" + color + "\""; | 339 command += " fc rgb \"" + color + "\""; |
370 print(command); | 340 print(command); |
(...skipping 14 matching lines...) Expand all Loading... | |
385 ranges.sort(function(a, b) { return a.start - b.start; }); | 355 ranges.sort(function(a, b) { return a.start - b.start; }); |
386 var result = []; | 356 var result = []; |
387 var j = 0; | 357 var j = 0; |
388 for (var i = 0; i < ranges.length; i = j) { | 358 for (var i = 0; i < ranges.length; i = j) { |
389 var merge_start = ranges[i].start; | 359 var merge_start = ranges[i].start; |
390 if (merge_start > xrange_end) break; // Out of plot range. | 360 if (merge_start > xrange_end) break; // Out of plot range. |
391 var merge_end = ranges[i].end; | 361 var merge_end = ranges[i].end; |
392 for (j = i + 1; j < ranges.length; j++) { | 362 for (j = i + 1; j < ranges.length; j++) { |
393 var next_range = ranges[j]; | 363 var next_range = ranges[j]; |
394 // Don't merge ranges if there is no overlap (including merge tolerance). | 364 // Don't merge ranges if there is no overlap (including merge tolerance). |
395 if (next_range.start > merge_end + kPauseTolerance) break; | 365 if (next_range.start > merge_end + pause_tolerance) break; |
396 // Merge ranges. | 366 // Merge ranges. |
397 if (next_range.end > merge_end) { // Extend range end. | 367 if (next_range.end > merge_end) { // Extend range end. |
398 merge_end = next_range.end; | 368 merge_end = next_range.end; |
399 } | 369 } |
400 } | 370 } |
401 if (merge_end < xrange_start) continue; // Out of plot range. | 371 if (merge_end < xrange_start) continue; // Out of plot range. |
402 if (merge_end < merge_start) continue; // Not an actual range. | 372 if (merge_end < merge_start) continue; // Not an actual range. |
403 result.push(new Range(merge_start, merge_end)); | 373 result.push(new Range(merge_start, merge_end)); |
404 } | 374 } |
405 return result; | 375 return result; |
406 } | 376 } |
407 | 377 |
408 | 378 |
409 function ExcludeRanges(include, exclude) { | 379 function RestrictRangesTo(ranges, start, end) { |
410 // We assume that both input lists are sorted and merged with MergeRanges. | |
411 var result = []; | 380 var result = []; |
412 var exclude_index = 0; | 381 for (var i = 0; i < ranges.length; i++) { |
413 var include_index = 0; | 382 if (ranges[i].start <= end && ranges[i].end >= start) { |
414 var include_start, include_end, exclude_start, exclude_end; | 383 result.push(new Range(Math.max(ranges[i].start, start), |
415 | 384 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 } | 385 } |
485 } | 386 } |
486 | |
487 return result; | 387 return result; |
488 } | 388 } |
489 | 389 |
490 | 390 |
491 function GnuplotOutput() { | 391 function GnuplotOutput() { |
492 FindPlotRange(); | 392 FindPlotRange(); |
493 | 393 |
494 print("set terminal pngcairo size " + kResX + "," + kResY + | 394 print("set terminal pngcairo size " + kResX + "," + kResY + |
495 " enhanced font 'Helvetica,10'"); | 395 " enhanced font 'Helvetica,10'"); |
496 print("set yrange [0:" + (num_timer_event + 1) + "]"); | 396 print("set yrange [0:" + (num_timer_event + 1) + "]"); |
497 print("set xlabel \"execution time in ms\""); | 397 print("set xlabel \"execution time in ms\""); |
498 print("set xrange [" + xrange_start + ":" + xrange_end + "]"); | 398 print("set xrange [" + xrange_start + ":" + xrange_end + "]"); |
499 print("set style fill pattern 2 bo 1"); | 399 print("set style fill pattern 2 bo 1"); |
500 print("set style rect fs solid 1 noborder"); | 400 print("set style rect fs solid 1 noborder"); |
501 print("set style line 1 lt 1 lw 1 lc rgb \"#000000\""); | 401 print("set style line 1 lt 1 lw 1 lc rgb \"#000000\""); |
502 print("set xtics out nomirror"); | 402 print("set xtics out nomirror"); |
503 print("unset key"); | 403 print("unset key"); |
504 | 404 |
505 var percentages = {}; | 405 var percentages = {}; |
506 var total = 0; | 406 var total = 0; |
507 for (var name in TimerEvents) { | 407 for (var name in TimerEvents) { |
508 var event = TimerEvents[name]; | 408 var event = TimerEvents[name]; |
509 var ranges = MergeRanges(event.ranges); | 409 var ranges = RestrictRangesTo(event.ranges, xrange_start, xrange_end); |
510 var exclude_ranges = [new Range(-Infinity, xrange_start), | 410 ranges = MergeRanges(ranges); |
511 new Range(xrange_end, Infinity)]; | |
512 ranges = ExcludeRanges(ranges, exclude_ranges); | |
513 var sum = | 411 var sum = |
514 ranges.map(function(range) { return range.duration(); }) | 412 ranges.map(function(range) { return range.duration(); }) |
515 .reduce(function(a, b) { return a + b; }, 0); | 413 .reduce(function(a, b) { return a + b; }, 0); |
516 percentages[name] = (sum / (xrange_end - xrange_start) * 100).toFixed(1); | 414 percentages[name] = (sum / (xrange_end - xrange_start) * 100).toFixed(1); |
517 } | 415 } |
518 | 416 |
519 // Name Y-axis. | 417 // Name Y-axis. |
520 var ytics = []; | 418 var ytics = []; |
521 for (name in TimerEvents) { | 419 for (name in TimerEvents) { |
522 var index = TimerEvents[name].index; | 420 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++) { | 500 for (var i = 0; i < execution_pauses.length; i++) { |
603 var pause = execution_pauses[i]; | 501 var pause = execution_pauses[i]; |
604 print(pause.end + " " + pause.duration()); | 502 print(pause.end + " " + pause.duration()); |
605 } | 503 } |
606 print("e"); | 504 print("e"); |
607 } | 505 } |
608 | 506 |
609 | 507 |
610 CollectData(); | 508 CollectData(); |
611 GnuplotOutput(); | 509 GnuplotOutput(); |
OLD | NEW |