| 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 |