OLD | NEW |
---|---|
1 // Copyright 2017 the V8 project authors. All rights reserved. | 1 // Copyright 2017 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 "use strict" | 5 "use strict" |
6 | 6 |
7 function $(id) { | 7 function $(id) { |
8 return document.getElementById(id); | 8 return document.getElementById(id); |
9 } | 9 } |
10 | 10 |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
144 }, | 144 }, |
145 | 145 |
146 setFile(file) { | 146 setFile(file) { |
147 if (file != main.currentState.file) { | 147 if (file != main.currentState.file) { |
148 main.currentState = Object.assign({}, main.currentState); | 148 main.currentState = Object.assign({}, main.currentState); |
149 main.currentState.file = file; | 149 main.currentState.file = file; |
150 main.delayRender(); | 150 main.delayRender(); |
151 } | 151 } |
152 }, | 152 }, |
153 | 153 |
154 setCurrentCode(codeId) { | |
155 if (codeId != main.currentState.currentCodeId) { | |
156 main.currentState = Object.assign({}, main.currentState); | |
157 main.currentState.currentCodeId = codeId; | |
158 main.delayRender(); | |
159 } | |
160 }, | |
161 | |
154 onResize() { | 162 onResize() { |
155 main.setTimeLineDimensions( | 163 main.setTimeLineDimensions( |
156 window.innerWidth - 20, window.innerHeight / 5); | 164 window.innerWidth - 20, window.innerHeight / 5); |
157 }, | 165 }, |
158 | 166 |
159 onLoad() { | 167 onLoad() { |
160 function loadHandler(evt) { | 168 function loadHandler(evt) { |
161 let f = evt.target.files[0]; | 169 let f = evt.target.files[0]; |
162 if (f) { | 170 if (f) { |
163 let reader = new FileReader(); | 171 let reader = new FileReader(); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
234 let bucket = bucketDescriptors[i]; | 242 let bucket = bucketDescriptors[i]; |
235 for (let j = 0; j < bucket.kinds.length; j++) { | 243 for (let j = 0; j < bucket.kinds.length; j++) { |
236 if (bucket.kinds[j] === kind) { | 244 if (bucket.kinds[j] === kind) { |
237 return bucket; | 245 return bucket; |
238 } | 246 } |
239 } | 247 } |
240 } | 248 } |
241 return null; | 249 return null; |
242 } | 250 } |
243 | 251 |
252 function codeTypeToText(type) { | |
253 switch (type) { | |
254 case "UNKNOWN": | |
255 return "Unknown"; | |
256 case "CPPCOMP": | |
257 return "C++ (compiler)"; | |
258 case "CPPGC": | |
259 return "C++"; | |
260 case "CPPEXT": | |
261 return "C++ External"; | |
262 case "CPP": | |
263 return "C++"; | |
264 case "LIB": | |
265 return "Library"; | |
266 case "IC": | |
267 return "IC"; | |
268 case "BC": | |
269 return "Bytecode"; | |
270 case "STUB": | |
271 return "Stub"; | |
272 case "BUILTIN": | |
273 return "Builtin"; | |
274 case "REGEXP": | |
275 return "RegExp"; | |
276 case "JSOPT": | |
277 return "JS opt"; | |
278 case "JSUNOPT": | |
279 return "JS unopt"; | |
280 } | |
281 console.error("Unknown type: " + type); | |
282 } | |
283 | |
284 function createTypeDiv(type) { | |
285 if (type === "CAT") { | |
286 return document.createTextNode(""); | |
287 } | |
288 let div = document.createElement("div"); | |
289 div.classList.add("code-type-chip"); | |
290 | |
291 let span = document.createElement("span"); | |
292 span.classList.add("code-type-chip"); | |
293 span.textContent = codeTypeToText(type); | |
294 div.appendChild(span); | |
295 | |
296 span = document.createElement("span"); | |
297 span.classList.add("code-type-chip-space"); | |
298 div.appendChild(span); | |
299 | |
300 return div; | |
301 } | |
302 | |
303 function isBytecodeHandler(kind) { | |
304 return kind === "BytecodeHandler"; | |
305 } | |
306 | |
307 function filterFromFilterId(id) { | |
308 switch (id) { | |
309 case "full-tree": | |
310 return (type, kind) => true; | |
311 case "js-funs": | |
312 return (type, kind) => type !== 'CODE'; | |
313 case "js-exclude-bc": | |
314 return (type, kind) => | |
315 type !== 'CODE' || !isBytecodeHandler(kind); | |
316 } | |
317 } | |
318 | |
244 class CallTreeView { | 319 class CallTreeView { |
245 constructor() { | 320 constructor() { |
246 this.element = $("calltree"); | 321 this.element = $("calltree"); |
247 this.treeElement = $("calltree-table"); | 322 this.treeElement = $("calltree-table"); |
248 this.selectAttribution = $("calltree-attribution"); | 323 this.selectAttribution = $("calltree-attribution"); |
249 this.selectCategories = $("calltree-categories"); | 324 this.selectCategories = $("calltree-categories"); |
250 this.selectSort = $("calltree-sort"); | 325 this.selectSort = $("calltree-sort"); |
251 | 326 |
252 this.selectAttribution.onchange = () => { | 327 this.selectAttribution.onchange = () => { |
253 main.setCallTreeAttribution(this.selectAttribution.value); | 328 main.setCallTreeAttribution(this.selectAttribution.value); |
254 }; | 329 }; |
255 | 330 |
256 this.selectCategories.onchange = () => { | 331 this.selectCategories.onchange = () => { |
257 main.setCallTreeCategories(this.selectCategories.value); | 332 main.setCallTreeCategories(this.selectCategories.value); |
258 }; | 333 }; |
259 | 334 |
260 this.selectSort.onchange = () => { | 335 this.selectSort.onchange = () => { |
261 main.setCallTreeSort(this.selectSort.value); | 336 main.setCallTreeSort(this.selectSort.value); |
262 }; | 337 }; |
263 | 338 |
264 this.currentState = null; | 339 this.currentState = null; |
265 } | 340 } |
266 | 341 |
267 filterFromFilterId(id) { | |
268 switch (id) { | |
269 case "full-tree": | |
270 return (type, kind) => true; | |
271 case "js-funs": | |
272 return (type, kind) => type !== 'CODE'; | |
273 case "js-exclude-bc": | |
274 return (type, kind) => | |
275 type !== 'CODE' || !CallTreeView.IsBytecodeHandler(kind); | |
276 } | |
277 } | |
278 | |
279 sortFromId(id) { | 342 sortFromId(id) { |
280 switch (id) { | 343 switch (id) { |
281 case "time": | 344 case "time": |
282 return (c1, c2) => { | 345 return (c1, c2) => { |
283 if (c1.ticks < c2.ticks) return 1; | 346 if (c1.ticks < c2.ticks) return 1; |
284 else if (c1.ticks > c2.ticks) return -1; | 347 else if (c1.ticks > c2.ticks) return -1; |
285 return c2.ownTicks - c1.ownTicks; | 348 return c2.ownTicks - c1.ownTicks; |
286 } | 349 } |
287 case "own-time": | 350 case "own-time": |
288 return (c1, c2) => { | 351 return (c1, c2) => { |
289 if (c1.ownTicks < c2.ownTicks) return 1; | 352 if (c1.ownTicks < c2.ownTicks) return 1; |
290 else if (c1.ownTicks > c2.ownTicks) return -1; | 353 else if (c1.ownTicks > c2.ownTicks) return -1; |
291 return c2.ticks - c1.ticks; | 354 return c2.ticks - c1.ticks; |
292 } | 355 } |
293 case "category-time": | 356 case "category-time": |
294 return (c1, c2) => { | 357 return (c1, c2) => { |
295 if (c1.type === c2.type) return c2.ticks - c1.ticks; | 358 if (c1.type === c2.type) return c2.ticks - c1.ticks; |
296 if (c1.type < c2.type) return 1; | 359 if (c1.type < c2.type) return 1; |
297 return -1; | 360 return -1; |
298 }; | 361 }; |
299 case "category-own-time": | 362 case "category-own-time": |
300 return (c1, c2) => { | 363 return (c1, c2) => { |
301 if (c1.type === c2.type) return c2.ownTicks - c1.ownTicks; | 364 if (c1.type === c2.type) return c2.ownTicks - c1.ownTicks; |
302 if (c1.type < c2.type) return 1; | 365 if (c1.type < c2.type) return 1; |
303 return -1; | 366 return -1; |
304 }; | 367 }; |
305 } | 368 } |
306 } | 369 } |
307 | 370 |
308 static IsBytecodeHandler(kind) { | |
309 return kind === "BytecodeHandler"; | |
310 } | |
311 | |
312 createExpander(indent) { | 371 createExpander(indent) { |
313 let div = document.createElement("div"); | 372 let div = document.createElement("div"); |
314 div.style.width = (1 + indent) + "em"; | 373 div.style.width = (1 + indent) + "em"; |
315 div.style.display = "inline-block"; | 374 div.style.display = "inline-block"; |
316 div.style.textAlign = "right"; | 375 div.style.textAlign = "right"; |
317 return div; | 376 return div; |
318 } | 377 } |
319 | 378 |
320 codeTypeToText(type) { | 379 createFunctionNode(name, codeId) { |
321 switch (type) { | 380 if (codeId == -1) { |
322 case "UNKNOWN": | 381 return document.createTextNode(name); |
323 return "Unknown"; | |
324 case "CPPCOMP": | |
325 return "C++ (compiler)"; | |
326 case "CPPGC": | |
327 return "C++"; | |
328 case "CPPEXT": | |
329 return "C++ External"; | |
330 case "CPP": | |
331 return "C++"; | |
332 case "LIB": | |
333 return "Library"; | |
334 case "IC": | |
335 return "IC"; | |
336 case "BC": | |
337 return "Bytecode"; | |
338 case "STUB": | |
339 return "Stub"; | |
340 case "BUILTIN": | |
341 return "Builtin"; | |
342 case "REGEXP": | |
343 return "RegExp"; | |
344 case "JSOPT": | |
345 return "JS opt"; | |
346 case "JSUNOPT": | |
347 return "JS unopt"; | |
348 } | 382 } |
349 console.error("Unknown type: " + type); | 383 let nameElement = document.createElement("span"); |
350 } | 384 nameElement.classList.add("codeid-link") |
351 | 385 nameElement.onclick = function() { |
352 createTypeDiv(type) { | 386 main.setCurrentCode(codeId); |
353 if (type === "CAT") { | 387 }; |
354 return document.createTextNode(""); | 388 nameElement.appendChild(document.createTextNode(name)); |
355 } | 389 return nameElement; |
356 let div = document.createElement("div"); | |
357 div.classList.add("code-type-chip"); | |
358 | |
359 let span = document.createElement("span"); | |
360 span.classList.add("code-type-chip"); | |
361 span.textContent = this.codeTypeToText(type); | |
362 div.appendChild(span); | |
363 | |
364 span = document.createElement("span"); | |
365 span.classList.add("code-type-chip-space"); | |
366 div.appendChild(span); | |
367 | |
368 return div; | |
369 } | 390 } |
370 | 391 |
371 expandTree(tree, indent) { | 392 expandTree(tree, indent) { |
372 let that = this; | 393 let that = this; |
373 let index = 0; | 394 let index = 0; |
374 let id = "R/"; | 395 let id = "R/"; |
375 let row = tree.row; | 396 let row = tree.row; |
376 let expander = tree.expander; | 397 let expander = tree.expander; |
377 | 398 |
378 if (row) { | 399 if (row) { |
379 console.assert("expander"); | 400 console.assert("expander"); |
380 index = row.rowIndex; | 401 index = row.rowIndex; |
381 id = row.id; | 402 id = row.id; |
382 | 403 |
383 // Make sure we collapse the children when the row is clicked | 404 // Make sure we collapse the children when the row is clicked |
384 // again. | 405 // again. |
385 expander.textContent = "\u25BE"; | 406 expander.textContent = "\u25BE"; |
386 let expandHandler = expander.onclick; | 407 let expandHandler = expander.onclick; |
387 expander.onclick = () => { | 408 expander.onclick = () => { |
388 that.collapseRow(tree, expander, expandHandler); | 409 that.collapseRow(tree, expander, expandHandler); |
389 } | 410 } |
390 } | 411 } |
391 | 412 |
392 // Collect the children, and sort them by ticks. | 413 // Collect the children, and sort them by ticks. |
393 let children = []; | 414 let children = []; |
394 let filter = | 415 let filter = |
395 this.filterFromFilterId(this.currentState.callTree.attribution); | 416 filterFromFilterId(this.currentState.callTree.attribution); |
396 for (let childId in tree.children) { | 417 for (let childId in tree.children) { |
397 let child = tree.children[childId]; | 418 let child = tree.children[childId]; |
398 if (child.ticks > 0) { | 419 if (child.ticks > 0) { |
399 children.push(child); | 420 children.push(child); |
400 if (child.delayedExpansion) { | 421 if (child.delayedExpansion) { |
401 expandTreeNode(this.currentState.file, child, filter); | 422 expandTreeNode(this.currentState.file, child, filter); |
402 } | 423 } |
403 } | 424 } |
404 } | 425 } |
405 children.sort(this.sortFromId(this.currentState.callTree.sort)); | 426 children.sort(this.sortFromId(this.currentState.callTree.sort)); |
(...skipping 19 matching lines...) Expand all Loading... | |
425 if (this.currentState.callTree.mode !== "bottom-up") { | 446 if (this.currentState.callTree.mode !== "bottom-up") { |
426 c = row.insertCell(-1); | 447 c = row.insertCell(-1); |
427 c.textContent = (node.ownTicks * 100 / this.tickCount).toFixed(2) + "%"; | 448 c.textContent = (node.ownTicks * 100 / this.tickCount).toFixed(2) + "%"; |
428 c.style.textAlign = "right"; | 449 c.style.textAlign = "right"; |
429 } | 450 } |
430 | 451 |
431 // Create the name cell. | 452 // Create the name cell. |
432 let nameCell = row.insertCell(); | 453 let nameCell = row.insertCell(); |
433 let expander = this.createExpander(indent); | 454 let expander = this.createExpander(indent); |
434 nameCell.appendChild(expander); | 455 nameCell.appendChild(expander); |
435 nameCell.appendChild(this.createTypeDiv(node.type)); | 456 nameCell.appendChild(createTypeDiv(node.type)); |
436 nameCell.appendChild(document.createTextNode(node.name)); | 457 nameCell.appendChild(this.createFunctionNode(node.name, node.codeId)); |
437 | 458 |
438 // Inclusive ticks cell. | 459 // Inclusive ticks cell. |
439 c = row.insertCell(); | 460 c = row.insertCell(); |
440 c.textContent = node.ticks; | 461 c.textContent = node.ticks; |
441 c.style.textAlign = "right"; | 462 c.style.textAlign = "right"; |
442 if (this.currentState.callTree.mode !== "bottom-up") { | 463 if (this.currentState.callTree.mode !== "bottom-up") { |
443 // Exclusive ticks cell. | 464 // Exclusive ticks cell. |
444 c = row.insertCell(-1); | 465 c = row.insertCell(-1); |
445 c.textContent = node.ownTicks; | 466 c.textContent = node.ownTicks; |
446 c.style.textAlign = "right"; | 467 c.style.textAlign = "right"; |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
566 } | 587 } |
567 | 588 |
568 let ownTimeClass = (mode === "bottom-up") ? "numeric-hidden" : "numeric"; | 589 let ownTimeClass = (mode === "bottom-up") ? "numeric-hidden" : "numeric"; |
569 let ownTimeTh = $(this.treeElement.id + "-own-time-header"); | 590 let ownTimeTh = $(this.treeElement.id + "-own-time-header"); |
570 ownTimeTh.classList = ownTimeClass; | 591 ownTimeTh.classList = ownTimeClass; |
571 let ownTicksTh = $(this.treeElement.id + "-own-ticks-header"); | 592 let ownTicksTh = $(this.treeElement.id + "-own-ticks-header"); |
572 ownTicksTh.classList = ownTimeClass; | 593 ownTicksTh.classList = ownTimeClass; |
573 | 594 |
574 // Build the tree. | 595 // Build the tree. |
575 let stackProcessor; | 596 let stackProcessor; |
576 let filter = this.filterFromFilterId(this.currentState.callTree.attribution) ; | 597 let filter = filterFromFilterId(this.currentState.callTree.attribution); |
577 if (mode === "top-down") { | 598 if (mode === "top-down") { |
578 stackProcessor = | 599 stackProcessor = |
579 new PlainCallTreeProcessor(filter, false); | 600 new PlainCallTreeProcessor(filter, false); |
580 } else if (mode === "function-list") { | 601 } else if (mode === "function-list") { |
581 stackProcessor = new FunctionListTree( | 602 stackProcessor = new FunctionListTree( |
582 filter, this.currentState.callTree.categories === "code-type"); | 603 filter, this.currentState.callTree.categories === "code-type"); |
583 | 604 |
584 } else { | 605 } else { |
585 console.assert(mode === "bottom-up"); | 606 console.assert(mode === "bottom-up"); |
586 if (this.currentState.callTree.categories == "none") { | 607 if (this.currentState.callTree.categories == "none") { |
(...skipping 25 matching lines...) Expand all Loading... | |
612 // Swap in the new rows. | 633 // Swap in the new rows. |
613 this.treeElement.replaceChild(newRows, oldRows[0]); | 634 this.treeElement.replaceChild(newRows, oldRows[0]); |
614 } | 635 } |
615 } | 636 } |
616 | 637 |
617 class TimelineView { | 638 class TimelineView { |
618 constructor() { | 639 constructor() { |
619 this.element = $("timeline"); | 640 this.element = $("timeline"); |
620 this.canvas = $("timeline-canvas"); | 641 this.canvas = $("timeline-canvas"); |
621 this.legend = $("timeline-legend"); | 642 this.legend = $("timeline-legend"); |
643 this.currentCode = $("timeline-currentCode"); | |
622 | 644 |
623 this.canvas.onmousedown = this.onMouseDown.bind(this); | 645 this.canvas.onmousedown = this.onMouseDown.bind(this); |
624 this.canvas.onmouseup = this.onMouseUp.bind(this); | 646 this.canvas.onmouseup = this.onMouseUp.bind(this); |
625 this.canvas.onmousemove = this.onMouseMove.bind(this); | 647 this.canvas.onmousemove = this.onMouseMove.bind(this); |
626 | 648 |
627 this.selectionStart = null; | 649 this.selectionStart = null; |
628 this.selectionEnd = null; | 650 this.selectionEnd = null; |
629 this.selecting = false; | 651 this.selecting = false; |
630 | 652 |
631 this.fontSize = 12; | 653 this.fontSize = 12; |
632 this.imageOffset = this.fontSize * 1.2; | 654 this.imageOffset = this.fontSize * 1.2; |
655 this.functionTimelineHeight = 12; | |
633 | 656 |
634 this.currentState = null; | 657 this.currentState = null; |
635 } | 658 } |
636 | 659 |
637 onMouseDown(e) { | 660 onMouseDown(e) { |
638 this.selectionStart = | 661 this.selectionStart = |
639 e.clientX - this.canvas.getBoundingClientRect().left; | 662 e.clientX - this.canvas.getBoundingClientRect().left; |
640 this.selectionEnd = this.selectionStart + 1; | 663 this.selectionEnd = this.selectionStart + 1; |
641 this.selecting = true; | 664 this.selecting = true; |
642 } | 665 } |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
691 // Draw the timeline image. | 714 // Draw the timeline image. |
692 ctx.drawImage(this.buffer, 0, this.imageOffset); | 715 ctx.drawImage(this.buffer, 0, this.imageOffset); |
693 | 716 |
694 // Draw the current interval highlight. | 717 // Draw the current interval highlight. |
695 let left; | 718 let left; |
696 let right; | 719 let right; |
697 if (this.selectionStart !== null && this.selectionEnd !== null) { | 720 if (this.selectionStart !== null && this.selectionEnd !== null) { |
698 ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; | 721 ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; |
699 left = Math.min(this.selectionStart, this.selectionEnd); | 722 left = Math.min(this.selectionStart, this.selectionEnd); |
700 right = Math.max(this.selectionStart, this.selectionEnd); | 723 right = Math.max(this.selectionStart, this.selectionEnd); |
701 ctx.fillRect(0, this.imageOffset, left, this.buffer.height); | 724 let height = this.buffer.height - this.functionTimelineHeight; |
702 ctx.fillRect(right, this.imageOffset, this.buffer.width - right, | 725 ctx.fillRect(0, this.imageOffset, left, height); |
703 this.buffer.height); | 726 ctx.fillRect(right, this.imageOffset, this.buffer.width - right, height); |
704 } else { | 727 } else { |
705 left = 0; | 728 left = 0; |
706 right = this.buffer.width; | 729 right = this.buffer.width; |
707 } | 730 } |
708 | 731 |
709 // Draw the scale text. | 732 // Draw the scale text. |
710 let file = this.currentState.file; | 733 let file = this.currentState.file; |
711 ctx.fillStyle = "white"; | 734 ctx.fillStyle = "white"; |
712 ctx.fillRect(0, 0, this.canvas.width, this.imageOffset); | 735 ctx.fillRect(0, 0, this.canvas.width, this.imageOffset); |
713 if (file && file.ticks.length > 0) { | 736 if (file && file.ticks.length > 0) { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
756 if (!newState.file) { | 779 if (!newState.file) { |
757 this.element.style.display = "none"; | 780 this.element.style.display = "none"; |
758 return; | 781 return; |
759 } | 782 } |
760 | 783 |
761 this.currentState = newState; | 784 this.currentState = newState; |
762 if (oldState) { | 785 if (oldState) { |
763 if (newState.timeLine.width === oldState.timeLine.width && | 786 if (newState.timeLine.width === oldState.timeLine.width && |
764 newState.timeLine.height === oldState.timeLine.height && | 787 newState.timeLine.height === oldState.timeLine.height && |
765 newState.file === oldState.file && | 788 newState.file === oldState.file && |
789 newState.currentCodeId === oldState.currentCodeId && | |
766 newState.start === oldState.start && | 790 newState.start === oldState.start && |
767 newState.end === oldState.end) { | 791 newState.end === oldState.end) { |
768 // No change, nothing to do. | 792 // No change, nothing to do. |
769 return; | 793 return; |
770 } | 794 } |
771 } | 795 } |
772 | 796 |
773 this.element.style.display = "inherit"; | 797 this.element.style.display = "inherit"; |
774 | 798 |
775 // Make sure the canvas has the right dimensions. | 799 // Make sure the canvas has the right dimensions. |
776 let width = this.currentState.timeLine.width; | 800 let width = this.currentState.timeLine.width; |
777 let height = this.currentState.timeLine.height; | 801 let height = this.currentState.timeLine.height; |
778 this.canvas.width = width; | 802 this.canvas.width = width; |
779 this.canvas.height = height; | 803 this.canvas.height = height; |
780 | 804 |
781 // Make space for the selection text. | 805 // Make space for the selection text. |
782 height -= this.imageOffset; | 806 height -= this.imageOffset; |
783 | 807 |
784 let file = this.currentState.file; | 808 let file = this.currentState.file; |
785 if (!file) return; | 809 if (!file) return; |
786 | 810 |
811 let currentCodeId = this.currentState.currentCodeId; | |
812 | |
787 let firstTime = file.ticks[0].tm; | 813 let firstTime = file.ticks[0].tm; |
788 let lastTime = file.ticks[file.ticks.length - 1].tm; | 814 let lastTime = file.ticks[file.ticks.length - 1].tm; |
789 let start = Math.max(this.currentState.start, firstTime); | 815 let start = Math.max(this.currentState.start, firstTime); |
790 let end = Math.min(this.currentState.end, lastTime); | 816 let end = Math.min(this.currentState.end, lastTime); |
791 | 817 |
792 this.selectionStart = (start - firstTime) / (lastTime - firstTime) * width; | 818 this.selectionStart = (start - firstTime) / (lastTime - firstTime) * width; |
793 this.selectionEnd = (end - firstTime) / (lastTime - firstTime) * width; | 819 this.selectionEnd = (end - firstTime) / (lastTime - firstTime) * width; |
794 | 820 |
795 let tickCount = file.ticks.length; | 821 let tickCount = file.ticks.length; |
796 | 822 |
797 let minBucketPixels = 10; | 823 let minBucketPixels = 10; |
798 let minBucketSamples = 30; | 824 let minBucketSamples = 30; |
799 let bucketCount = Math.min(width / minBucketPixels, | 825 let bucketCount = Math.min(width / minBucketPixels, |
800 tickCount / minBucketSamples); | 826 tickCount / minBucketSamples); |
801 | 827 |
802 let stackProcessor = new CategorySampler(file, bucketCount); | 828 let stackProcessor = new CategorySampler(file, bucketCount); |
803 generateTree(file, 0, Infinity, stackProcessor); | 829 generateTree(file, 0, Infinity, stackProcessor); |
830 let codeIdProcessor = new FunctionTimelineProcessor( | |
831 currentCodeId, | |
832 filterFromFilterId(this.currentState.callTree.attribution)); | |
833 generateTree(file, 0, Infinity, codeIdProcessor); | |
Jarin
2017/03/08 16:35:56
One thought: for delayed expansions we already hav
| |
804 | 834 |
805 let buffer = document.createElement("canvas"); | 835 let buffer = document.createElement("canvas"); |
806 | 836 |
807 buffer.width = width; | 837 buffer.width = width; |
808 buffer.height = height; | 838 buffer.height = height; |
809 | 839 |
810 // Calculate the bar heights for each bucket. | 840 // Calculate the bar heights for each bucket. |
811 let graphHeight = height; | 841 let graphHeight = height - this.functionTimelineHeight; |
812 let buckets = stackProcessor.buckets; | 842 let buckets = stackProcessor.buckets; |
813 let bucketsGraph = []; | 843 let bucketsGraph = []; |
814 for (let i = 0; i < buckets.length; i++) { | 844 for (let i = 0; i < buckets.length; i++) { |
815 let sum = 0; | 845 let sum = 0; |
816 let bucketData = []; | 846 let bucketData = []; |
817 let total = buckets[i].total; | 847 let total = buckets[i].total; |
818 for (let j = 0; j < bucketDescriptors.length; j++) { | 848 for (let j = 0; j < bucketDescriptors.length; j++) { |
819 let desc = bucketDescriptors[j]; | 849 let desc = bucketDescriptors[j]; |
820 for (let k = 0; k < desc.kinds.length; k++) { | 850 for (let k = 0; k < desc.kinds.length; k++) { |
821 sum += buckets[i][desc.kinds[k]]; | 851 sum += buckets[i][desc.kinds[k]]; |
(...skipping 13 matching lines...) Expand all Loading... | |
835 ctx.beginPath(); | 865 ctx.beginPath(); |
836 ctx.moveTo(i * bucketWidth, j && bucketData[j - 1]); | 866 ctx.moveTo(i * bucketWidth, j && bucketData[j - 1]); |
837 ctx.lineTo((i + 1) * bucketWidth, j && nextBucketData[j - 1]); | 867 ctx.lineTo((i + 1) * bucketWidth, j && nextBucketData[j - 1]); |
838 ctx.lineTo((i + 1) * bucketWidth, nextBucketData[j]); | 868 ctx.lineTo((i + 1) * bucketWidth, nextBucketData[j]); |
839 ctx.lineTo(i * bucketWidth, bucketData[j]); | 869 ctx.lineTo(i * bucketWidth, bucketData[j]); |
840 ctx.closePath(); | 870 ctx.closePath(); |
841 ctx.fillStyle = bucketDescriptors[j].color; | 871 ctx.fillStyle = bucketDescriptors[j].color; |
842 ctx.fill(); | 872 ctx.fill(); |
843 } | 873 } |
844 } | 874 } |
875 let functionTimelineYOffset = graphHeight; | |
876 let functionTimelineHeight = this.functionTimelineHeight; | |
877 let timestampScaler = width / (lastTime - firstTime); | |
878 ctx.fillStyle = "white"; | |
879 ctx.fillRect( | |
880 0, | |
881 functionTimelineYOffset, | |
882 buffer.width, | |
883 functionTimelineHeight); | |
884 for (let i = 0; i < codeIdProcessor.blocks.length; i++) { | |
885 let block = codeIdProcessor.blocks[i]; | |
886 ctx.fillStyle = "#000000"; | |
887 ctx.fillRect( | |
888 Math.round((block.start - firstTime) * timestampScaler), | |
889 functionTimelineYOffset, | |
890 Math.max(1, Math.round((block.end - block.start) * timestampScaler)), | |
891 block.topOfStack ? functionTimelineHeight : functionTimelineHeight / 2); | |
892 } | |
845 | 893 |
846 // Remember stuff for later. | 894 // Remember stuff for later. |
847 this.buffer = buffer; | 895 this.buffer = buffer; |
848 | 896 |
849 // Draw the buffer. | 897 // Draw the buffer. |
850 this.drawSelection(); | 898 this.drawSelection(); |
851 | 899 |
852 // (Re-)Populate the graph legend. | 900 // (Re-)Populate the graph legend. |
853 while (this.legend.cells.length > 0) { | 901 while (this.legend.cells.length > 0) { |
854 this.legend.deleteCell(0); | 902 this.legend.deleteCell(0); |
855 } | 903 } |
856 let cell = this.legend.insertCell(-1); | 904 let cell = this.legend.insertCell(-1); |
857 cell.textContent = "Legend: "; | 905 cell.textContent = "Legend: "; |
858 cell.style.padding = "1ex"; | 906 cell.style.padding = "1ex"; |
859 for (let i = 0; i < bucketDescriptors.length; i++) { | 907 for (let i = 0; i < bucketDescriptors.length; i++) { |
860 let cell = this.legend.insertCell(-1); | 908 let cell = this.legend.insertCell(-1); |
861 cell.style.padding = "1ex"; | 909 cell.style.padding = "1ex"; |
862 let desc = bucketDescriptors[i]; | 910 let desc = bucketDescriptors[i]; |
863 let div = document.createElement("div"); | 911 let div = document.createElement("div"); |
864 div.style.display = "inline-block"; | 912 div.style.display = "inline-block"; |
865 div.style.width = "0.6em"; | 913 div.style.width = "0.6em"; |
866 div.style.height = "1.2ex"; | 914 div.style.height = "1.2ex"; |
867 div.style.backgroundColor = desc.color; | 915 div.style.backgroundColor = desc.color; |
868 div.style.borderStyle = "solid"; | 916 div.style.borderStyle = "solid"; |
869 div.style.borderWidth = "1px"; | 917 div.style.borderWidth = "1px"; |
870 div.style.borderColor = "Black"; | 918 div.style.borderColor = "Black"; |
871 cell.appendChild(div); | 919 cell.appendChild(div); |
872 cell.appendChild(document.createTextNode(" " + desc.text)); | 920 cell.appendChild(document.createTextNode(" " + desc.text)); |
873 } | 921 } |
922 | |
923 while (this.currentCode.firstChild) { | |
924 this.currentCode.removeChild(this.currentCode.firstChild); | |
925 } | |
926 if (currentCodeId) { | |
927 let currentCode = file.code[currentCodeId]; | |
928 this.currentCode.appendChild(createTypeDiv(resolveCodeKind(currentCode))); | |
929 this.currentCode.appendChild(document.createTextNode(currentCode.name)); | |
930 | |
931 } else { | |
932 this.currentCode.appendChild(document.createTextNode("<none>")); | |
933 } | |
874 } | 934 } |
875 } | 935 } |
876 | 936 |
877 class HelpView { | 937 class HelpView { |
878 constructor() { | 938 constructor() { |
879 this.element = $("help"); | 939 this.element = $("help"); |
880 } | 940 } |
881 | 941 |
882 render(newState) { | 942 render(newState) { |
883 this.element.style.display = newState.file ? "none" : "inherit"; | 943 this.element.style.display = newState.file ? "none" : "inherit"; |
884 } | 944 } |
885 } | 945 } |
OLD | NEW |