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 / 8); | 164 window.innerWidth - 20, window.innerHeight / 8); |
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.currentState = null; | 653 this.currentState = null; |
| 654 |
| 655 this.functionTimelineHeight = 12; |
632 } | 656 } |
633 | 657 |
634 onMouseDown(e) { | 658 onMouseDown(e) { |
635 this.selectionStart = | 659 this.selectionStart = |
636 e.clientX - this.canvas.getBoundingClientRect().left; | 660 e.clientX - this.canvas.getBoundingClientRect().left; |
637 this.selectionEnd = this.selectionStart + 1; | 661 this.selectionEnd = this.selectionStart + 1; |
638 this.selecting = true; | 662 this.selecting = true; |
639 } | 663 } |
640 | 664 |
641 onMouseMove(e) { | 665 onMouseMove(e) { |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
683 } | 707 } |
684 | 708 |
685 drawSelection() { | 709 drawSelection() { |
686 let ctx = this.canvas.getContext("2d"); | 710 let ctx = this.canvas.getContext("2d"); |
687 ctx.drawImage(this.buffer, 0, 0); | 711 ctx.drawImage(this.buffer, 0, 0); |
688 | 712 |
689 if (this.selectionStart !== null && this.selectionEnd !== null) { | 713 if (this.selectionStart !== null && this.selectionEnd !== null) { |
690 ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; | 714 ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; |
691 let left = Math.min(this.selectionStart, this.selectionEnd); | 715 let left = Math.min(this.selectionStart, this.selectionEnd); |
692 let right = Math.max(this.selectionStart, this.selectionEnd); | 716 let right = Math.max(this.selectionStart, this.selectionEnd); |
693 ctx.fillRect(0, 0, left, this.buffer.height); | 717 let height = this.buffer.height - this.functionTimelineHeight; |
694 ctx.fillRect(right, 0, this.buffer.width - right, this.buffer.height); | 718 ctx.fillRect(0, 0, left, height); |
| 719 ctx.fillRect(right, 0, this.buffer.width - right, height); |
695 } | 720 } |
696 } | 721 } |
697 | 722 |
698 | 723 |
699 render(newState) { | 724 render(newState) { |
700 let oldState = this.currentState; | 725 let oldState = this.currentState; |
701 | 726 |
702 if (!newState.file) { | 727 if (!newState.file) { |
703 this.element.style.display = "none"; | 728 this.element.style.display = "none"; |
704 return; | 729 return; |
705 } | 730 } |
706 | 731 |
707 this.currentState = newState; | 732 this.currentState = newState; |
708 if (oldState) { | 733 if (oldState) { |
709 if (newState.timeLine.width === oldState.timeLine.width && | 734 if (newState.timeLine.width === oldState.timeLine.width && |
710 newState.timeLine.height === oldState.timeLine.height && | 735 newState.timeLine.height === oldState.timeLine.height && |
711 newState.file === oldState.file && | 736 newState.file === oldState.file && |
| 737 newState.currentCodeId === oldState.currentCodeId && |
712 newState.start === oldState.start && | 738 newState.start === oldState.start && |
713 newState.end === oldState.end) { | 739 newState.end === oldState.end) { |
714 // No change, nothing to do. | 740 // No change, nothing to do. |
715 return; | 741 return; |
716 } | 742 } |
717 } | 743 } |
718 | 744 |
719 this.element.style.display = "inherit"; | 745 this.element.style.display = "inherit"; |
720 | 746 |
721 // Make sure the canvas has the right dimensions. | 747 // Make sure the canvas has the right dimensions. |
722 let width = this.currentState.timeLine.width; | 748 let width = this.currentState.timeLine.width; |
723 this.canvas.width = width; | 749 this.canvas.width = width; |
724 this.canvas.height = this.currentState.timeLine.height; | 750 this.canvas.height = this.currentState.timeLine.height; |
725 | 751 |
726 let file = this.currentState.file; | 752 let file = this.currentState.file; |
727 if (!file) return; | 753 if (!file) return; |
728 | 754 |
| 755 let currentCodeId = this.currentState.currentCodeId; |
| 756 |
729 let firstTime = file.ticks[0].tm; | 757 let firstTime = file.ticks[0].tm; |
730 let lastTime = file.ticks[file.ticks.length - 1].tm; | 758 let lastTime = file.ticks[file.ticks.length - 1].tm; |
731 let start = Math.max(this.currentState.start, firstTime); | 759 let start = Math.max(this.currentState.start, firstTime); |
732 let end = Math.min(this.currentState.end, lastTime); | 760 let end = Math.min(this.currentState.end, lastTime); |
733 | 761 |
734 this.selectionStart = (start - firstTime) / (lastTime - firstTime) * width; | 762 this.selectionStart = (start - firstTime) / (lastTime - firstTime) * width; |
735 this.selectionEnd = (end - firstTime) / (lastTime - firstTime) * width; | 763 this.selectionEnd = (end - firstTime) / (lastTime - firstTime) * width; |
736 | 764 |
737 let tickCount = file.ticks.length; | 765 let tickCount = file.ticks.length; |
738 | 766 |
739 let minBucketPixels = 10; | 767 let minBucketPixels = 10; |
740 let minBucketSamples = 30; | 768 let minBucketSamples = 30; |
741 let bucketCount = Math.min(width / minBucketPixels, | 769 let bucketCount = Math.min(width / minBucketPixels, |
742 tickCount / minBucketSamples); | 770 tickCount / minBucketSamples); |
743 | 771 |
744 let stackProcessor = new CategorySampler(file, bucketCount); | 772 let stackProcessor = new CategorySampler(file, bucketCount); |
745 generateTree(file, 0, Infinity, stackProcessor); | 773 generateTree(file, 0, Infinity, stackProcessor); |
| 774 let codeIdProcessor = new FunctionTimelineProcessor( |
| 775 currentCodeId, |
| 776 filterFromFilterId(this.currentState.callTree.attribution)); |
| 777 generateTree(file, 0, Infinity, codeIdProcessor); |
746 | 778 |
747 let buffer = document.createElement("canvas"); | 779 let buffer = document.createElement("canvas"); |
748 | 780 |
749 buffer.width = this.canvas.width; | 781 buffer.width = this.canvas.width; |
750 buffer.height = this.canvas.height; | 782 buffer.height = this.canvas.height; |
751 | 783 |
752 // Calculate the bar heights for each bucket. | 784 // Calculate the bar heights for each bucket. |
753 let graphHeight = buffer.height; | 785 let graphHeight = buffer.height - this.functionTimelineHeight; |
754 let buckets = stackProcessor.buckets; | 786 let buckets = stackProcessor.buckets; |
755 let bucketsGraph = []; | 787 let bucketsGraph = []; |
756 for (let i = 0; i < buckets.length; i++) { | 788 for (let i = 0; i < buckets.length; i++) { |
757 let sum = 0; | 789 let sum = 0; |
758 let bucketData = []; | 790 let bucketData = []; |
759 let total = buckets[i].total; | 791 let total = buckets[i].total; |
760 for (let j = 0; j < bucketDescriptors.length; j++) { | 792 for (let j = 0; j < bucketDescriptors.length; j++) { |
761 let desc = bucketDescriptors[j]; | 793 let desc = bucketDescriptors[j]; |
762 for (let k = 0; k < desc.kinds.length; k++) { | 794 for (let k = 0; k < desc.kinds.length; k++) { |
763 sum += buckets[i][desc.kinds[k]]; | 795 sum += buckets[i][desc.kinds[k]]; |
(...skipping 13 matching lines...) Expand all Loading... |
777 ctx.beginPath(); | 809 ctx.beginPath(); |
778 ctx.moveTo(i * bucketWidth, j && bucketData[j - 1]); | 810 ctx.moveTo(i * bucketWidth, j && bucketData[j - 1]); |
779 ctx.lineTo((i + 1) * bucketWidth, j && nextBucketData[j - 1]); | 811 ctx.lineTo((i + 1) * bucketWidth, j && nextBucketData[j - 1]); |
780 ctx.lineTo((i + 1) * bucketWidth, nextBucketData[j]); | 812 ctx.lineTo((i + 1) * bucketWidth, nextBucketData[j]); |
781 ctx.lineTo(i * bucketWidth, bucketData[j]); | 813 ctx.lineTo(i * bucketWidth, bucketData[j]); |
782 ctx.closePath(); | 814 ctx.closePath(); |
783 ctx.fillStyle = bucketDescriptors[j].color; | 815 ctx.fillStyle = bucketDescriptors[j].color; |
784 ctx.fill(); | 816 ctx.fill(); |
785 } | 817 } |
786 } | 818 } |
| 819 let functionTimelineYOffset = graphHeight; |
| 820 let functionTimelineHeight = this.functionTimelineHeight; |
| 821 let timestampScaler = width / (lastTime - firstTime); |
| 822 ctx.fillStyle = "white"; |
| 823 ctx.fillRect( |
| 824 0, |
| 825 functionTimelineYOffset, |
| 826 buffer.width, |
| 827 functionTimelineHeight); |
| 828 for (let i = 0; i < codeIdProcessor.blocks.length; i++) { |
| 829 let block = codeIdProcessor.blocks[i]; |
| 830 ctx.fillStyle = "#000000"; |
| 831 ctx.fillRect( |
| 832 Math.round((block.start - firstTime) * timestampScaler), |
| 833 functionTimelineYOffset, |
| 834 Math.max(1, Math.round((block.end - block.start) * timestampScaler)), |
| 835 block.topOfStack ? functionTimelineHeight : functionTimelineHeight / 2); |
| 836 } |
787 | 837 |
788 // Remember stuff for later. | 838 // Remember stuff for later. |
789 this.buffer = buffer; | 839 this.buffer = buffer; |
790 | 840 |
791 // Draw the buffer. | 841 // Draw the buffer. |
792 this.drawSelection(); | 842 this.drawSelection(); |
793 | 843 |
794 // (Re-)Populate the graph legend. | 844 // (Re-)Populate the graph legend. |
795 while (this.legend.cells.length > 0) { | 845 while (this.legend.cells.length > 0) { |
796 this.legend.deleteCell(0); | 846 this.legend.deleteCell(0); |
797 } | 847 } |
798 let cell = this.legend.insertCell(-1); | 848 let cell = this.legend.insertCell(-1); |
799 cell.textContent = "Legend: "; | 849 cell.textContent = "Legend: "; |
800 cell.style.padding = "1ex"; | 850 cell.style.padding = "1ex"; |
801 for (let i = 0; i < bucketDescriptors.length; i++) { | 851 for (let i = 0; i < bucketDescriptors.length; i++) { |
802 let cell = this.legend.insertCell(-1); | 852 let cell = this.legend.insertCell(-1); |
803 cell.style.padding = "1ex"; | 853 cell.style.padding = "1ex"; |
804 let desc = bucketDescriptors[i]; | 854 let desc = bucketDescriptors[i]; |
805 let div = document.createElement("div"); | 855 let div = document.createElement("div"); |
806 div.style.display = "inline-block"; | 856 div.style.display = "inline-block"; |
807 div.style.width = "0.6em"; | 857 div.style.width = "0.6em"; |
808 div.style.height = "1.2ex"; | 858 div.style.height = "1.2ex"; |
809 div.style.backgroundColor = desc.color; | 859 div.style.backgroundColor = desc.color; |
810 div.style.borderStyle = "solid"; | 860 div.style.borderStyle = "solid"; |
811 div.style.borderWidth = "1px"; | 861 div.style.borderWidth = "1px"; |
812 div.style.borderColor = "Black"; | 862 div.style.borderColor = "Black"; |
813 cell.appendChild(div); | 863 cell.appendChild(div); |
814 cell.appendChild(document.createTextNode(" " + desc.text)); | 864 cell.appendChild(document.createTextNode(" " + desc.text)); |
815 } | 865 } |
| 866 |
| 867 while (this.currentCode.firstChild) { |
| 868 this.currentCode.removeChild(this.currentCode.firstChild); |
| 869 } |
| 870 if (currentCodeId) { |
| 871 let currentCode = file.code[currentCodeId]; |
| 872 this.currentCode.appendChild(createTypeDiv(resolveCodeKind(currentCode))); |
| 873 this.currentCode.appendChild(document.createTextNode(currentCode.name)); |
| 874 |
| 875 } else { |
| 876 this.currentCode.appendChild(document.createTextNode("<none>")); |
| 877 } |
816 } | 878 } |
817 } | 879 } |
818 | 880 |
819 class HelpView { | 881 class HelpView { |
820 constructor() { | 882 constructor() { |
821 this.element = $("help"); | 883 this.element = $("help"); |
822 } | 884 } |
823 | 885 |
824 render(newState) { | 886 render(newState) { |
825 this.element.style.display = newState.file ? "none" : "inherit"; | 887 this.element.style.display = newState.file ? "none" : "inherit"; |
826 } | 888 } |
827 } | 889 } |
OLD | NEW |