Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(141)

Side by Side Diff: tools/profview/profview.js

Issue 2753543006: [profiler] Web UI: add summary of opts/deopts. (Closed)
Patch Set: Address reviewer comments Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/profview/profile-utils.js ('k') | tools/tickprocessor.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
11 let components = []; 11 let components = [];
12 12
13 function createViews() { 13 function createViews() {
14 components.push(new CallTreeView()); 14 components.push(new CallTreeView());
15 components.push(new TimelineView()); 15 components.push(new TimelineView());
16 components.push(new HelpView()); 16 components.push(new HelpView());
17 components.push(new SummaryView());
18 components.push(new ModeBarView());
17 19
18 let modeBar = $("mode-bar"); 20 main.setMode("summary");
19
20 function addMode(id, text, active) {
21 let div = document.createElement("div");
22 div.classList = "mode-button" + (active ? " active-mode-button" : "");
23 div.id = "mode-" + id;
24 div.textContent = text;
25 div.onclick = () => {
26 if (main.currentState.callTree.mode === id) return;
27 let old = $("mode-" + main.currentState.callTree.mode);
28 old.classList = "mode-button";
29 div.classList = "mode-button active-mode-button";
30 main.setMode(id);
31 };
32 modeBar.appendChild(div);
33 }
34
35 addMode("bottom-up", "Bottom up", true);
36 addMode("top-down", "Top down");
37 addMode("function-list", "Functions");
38
39 main.setMode("bottom-up");
40 } 21 }
41 22
42 function emptyState() { 23 function emptyState() {
43 return { 24 return {
44 file : null, 25 file : null,
26 mode : "none",
27 currentCodeId : null,
45 start : 0, 28 start : 0,
46 end : Infinity, 29 end : Infinity,
47 timeLine : { 30 timeLine : {
48 width : 100, 31 width : 100,
49 height : 100 32 height : 100
50 }, 33 },
51 callTree : { 34 callTree : {
52 mode : "none",
53 attribution : "js-exclude-bc", 35 attribution : "js-exclude-bc",
54 categories : "code-type", 36 categories : "code-type",
55 sort : "time" 37 sort : "time"
56 } 38 }
57 }; 39 };
58 } 40 }
59 41
60 function setCallTreeState(state, callTreeState) { 42 function setCallTreeState(state, callTreeState) {
61 state = Object.assign({}, state); 43 state = Object.assign({}, state);
62 state.callTree = callTreeState; 44 state.callTree = callTreeState;
63 return state; 45 return state;
64 } 46 }
65 47
66 let main = { 48 let main = {
67 currentState : emptyState(), 49 currentState : emptyState(),
68 50
69 setMode(mode) { 51 setMode(mode) {
70 if (mode != main.currentState.mode) { 52 if (mode != main.currentState.mode) {
71 let callTreeState = Object.assign({}, main.currentState.callTree); 53
72 callTreeState.mode = mode; 54 function setCallTreeModifiers(attribution, categories, sort) {
55 let callTreeState = Object.assign({}, main.currentState.callTree);
56 callTreeState.attribution = attribution;
57 callTreeState.categories = categories;
58 callTreeState.sort = sort;
59 return callTreeState;
60 }
61
62 let state = Object.assign({}, main.currentState);
63
73 switch (mode) { 64 switch (mode) {
74 case "bottom-up": 65 case "bottom-up":
75 callTreeState.attribution = "js-exclude-bc"; 66 state.callTree =
76 callTreeState.categories = "code-type"; 67 setCallTreeModifiers("js-exclude-bc", "code-type", "time");
77 callTreeState.sort = "time";
78 break; 68 break;
79 case "top-down": 69 case "top-down":
80 callTreeState.attribution = "js-exclude-bc"; 70 state.callTree =
81 callTreeState.categories = "none"; 71 setCallTreeModifiers("js-exclude-bc", "none", "time");
82 callTreeState.sort = "time";
83 break; 72 break;
84 case "function-list": 73 case "function-list":
85 callTreeState.attribution = "js-exclude-bc"; 74 state.callTree =
86 callTreeState.categories = "code-type"; 75 setCallTreeModifiers("js-exclude-bc", "code-type", "own-time");
87 callTreeState.sort = "own-time";
88 break; 76 break;
89 default:
90 console.error("Invalid mode");
91 } 77 }
92 main.currentState = setCallTreeState(main.currentState, callTreeState); 78
79 state.mode = mode;
80
81 main.currentState = state;
93 main.delayRender(); 82 main.delayRender();
94 } 83 }
95 }, 84 },
96 85
97 setCallTreeAttribution(attribution) { 86 setCallTreeAttribution(attribution) {
98 if (attribution != main.currentState.attribution) { 87 if (attribution != main.currentState.attribution) {
99 let callTreeState = Object.assign({}, main.currentState.callTree); 88 let callTreeState = Object.assign({}, main.currentState.callTree);
100 callTreeState.attribution = attribution; 89 callTreeState.attribution = attribution;
101 main.currentState = setCallTreeState(main.currentState, callTreeState); 90 main.currentState = setCallTreeState(main.currentState, callTreeState);
102 main.delayRender(); 91 main.delayRender();
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
194 Promise.resolve().then(() => { 183 Promise.resolve().then(() => {
195 for (let c of components) { 184 for (let c of components) {
196 c.render(main.currentState); 185 c.render(main.currentState);
197 } 186 }
198 }); 187 });
199 } 188 }
200 }; 189 };
201 190
202 let bucketDescriptors = 191 let bucketDescriptors =
203 [ { kinds : [ "JSOPT" ], 192 [ { kinds : [ "JSOPT" ],
193 color : "#00ff00",
194 backgroundColor : "#c0ffc0",
195 text : "JS Optimized" },
196 { kinds : [ "JSUNOPT", "BC" ],
204 color : "#ffb000", 197 color : "#ffb000",
205 backgroundColor : "#ffe0c0", 198 backgroundColor : "#ffe0c0",
206 text : "JS Optimized" },
207 { kinds : [ "JSUNOPT", "BC" ],
208 color : "#00ff00",
209 backgroundColor : "#c0ffc0",
210 text : "JS Unoptimized" }, 199 text : "JS Unoptimized" },
211 { kinds : [ "IC" ], 200 { kinds : [ "IC" ],
212 color : "#ffff00", 201 color : "#ffff00",
213 backgroundColor : "#ffffc0", 202 backgroundColor : "#ffffc0",
214 text : "IC" }, 203 text : "IC" },
215 { kinds : [ "STUB", "BUILTIN", "REGEXP" ], 204 { kinds : [ "STUB", "BUILTIN", "REGEXP" ],
216 color : "#ffb0b0", 205 color : "#ffb0b0",
217 backgroundColor : "#fff0f0", 206 backgroundColor : "#fff0f0",
218 text : "Other generated" }, 207 text : "Other generated" },
219 { kinds : [ "CPP", "LIB" ], 208 { kinds : [ "CPP", "LIB" ],
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 case "full-tree": 307 case "full-tree":
319 return (type, kind) => true; 308 return (type, kind) => true;
320 case "js-funs": 309 case "js-funs":
321 return (type, kind) => type !== 'CODE'; 310 return (type, kind) => type !== 'CODE';
322 case "js-exclude-bc": 311 case "js-exclude-bc":
323 return (type, kind) => 312 return (type, kind) =>
324 type !== 'CODE' || !isBytecodeHandler(kind); 313 type !== 'CODE' || !isBytecodeHandler(kind);
325 } 314 }
326 } 315 }
327 316
317 function createTableExpander(indent) {
318 let div = document.createElement("div");
319 div.style.width = (indent + 0.5) + "em";
320 div.style.display = "inline-block";
321 div.style.textAlign = "right";
322 return div;
323 }
324
325 function createFunctionNode(name, codeId) {
326 if (codeId == -1) {
327 return document.createTextNode(name);
328 }
329 let nameElement = document.createElement("span");
330 nameElement.classList.add("codeid-link")
331 nameElement.onclick = function() {
332 main.setCurrentCode(codeId);
333 };
334 nameElement.appendChild(document.createTextNode(name));
335 return nameElement;
336 }
337
328 class CallTreeView { 338 class CallTreeView {
329 constructor() { 339 constructor() {
330 this.element = $("calltree"); 340 this.element = $("calltree");
331 this.treeElement = $("calltree-table"); 341 this.treeElement = $("calltree-table");
332 this.selectAttribution = $("calltree-attribution"); 342 this.selectAttribution = $("calltree-attribution");
333 this.selectCategories = $("calltree-categories"); 343 this.selectCategories = $("calltree-categories");
334 this.selectSort = $("calltree-sort"); 344 this.selectSort = $("calltree-sort");
335 345
336 this.selectAttribution.onchange = () => { 346 this.selectAttribution.onchange = () => {
337 main.setCallTreeAttribution(this.selectAttribution.value); 347 main.setCallTreeAttribution(this.selectAttribution.value);
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
370 }; 380 };
371 case "category-own-time": 381 case "category-own-time":
372 return (c1, c2) => { 382 return (c1, c2) => {
373 if (c1.type === c2.type) return c2.ownTicks - c1.ownTicks; 383 if (c1.type === c2.type) return c2.ownTicks - c1.ownTicks;
374 if (c1.type < c2.type) return 1; 384 if (c1.type < c2.type) return 1;
375 return -1; 385 return -1;
376 }; 386 };
377 } 387 }
378 } 388 }
379 389
380 createExpander(indent) {
381 let div = document.createElement("div");
382 div.style.width = (1 + indent) + "em";
383 div.style.display = "inline-block";
384 div.style.textAlign = "right";
385 return div;
386 }
387
388 createFunctionNode(name, codeId) {
389 if (codeId == -1) {
390 return document.createTextNode(name);
391 }
392 let nameElement = document.createElement("span");
393 nameElement.classList.add("codeid-link")
394 nameElement.onclick = function() {
395 main.setCurrentCode(codeId);
396 };
397 nameElement.appendChild(document.createTextNode(name));
398 return nameElement;
399 }
400
401 expandTree(tree, indent) { 390 expandTree(tree, indent) {
402 let that = this; 391 let that = this;
403 let index = 0; 392 let index = 0;
404 let id = "R/"; 393 let id = "R/";
405 let row = tree.row; 394 let row = tree.row;
406 let expander = tree.expander; 395 let expander = tree.expander;
407 396
408 if (row) { 397 if (row) {
409 console.assert("expander");
410 index = row.rowIndex; 398 index = row.rowIndex;
411 id = row.id; 399 id = row.id;
412 400
413 // Make sure we collapse the children when the row is clicked 401 // Make sure we collapse the children when the row is clicked
414 // again. 402 // again.
415 expander.textContent = "\u25BE"; 403 expander.textContent = "\u25BE";
416 let expandHandler = expander.onclick; 404 let expandHandler = expander.onclick;
417 expander.onclick = () => { 405 expander.onclick = () => {
418 that.collapseRow(tree, expander, expandHandler); 406 that.collapseRow(tree, expander, expandHandler);
419 } 407 }
(...skipping 25 matching lines...) Expand all
445 433
446 // Inclusive time % cell. 434 // Inclusive time % cell.
447 let c = row.insertCell(); 435 let c = row.insertCell();
448 c.textContent = (node.ticks * 100 / this.tickCount).toFixed(2) + "%"; 436 c.textContent = (node.ticks * 100 / this.tickCount).toFixed(2) + "%";
449 c.style.textAlign = "right"; 437 c.style.textAlign = "right";
450 // Percent-of-parent cell. 438 // Percent-of-parent cell.
451 c = row.insertCell(); 439 c = row.insertCell();
452 c.textContent = (node.ticks * 100 / tree.ticks).toFixed(2) + "%"; 440 c.textContent = (node.ticks * 100 / tree.ticks).toFixed(2) + "%";
453 c.style.textAlign = "right"; 441 c.style.textAlign = "right";
454 // Exclusive time % cell. 442 // Exclusive time % cell.
455 if (this.currentState.callTree.mode !== "bottom-up") { 443 if (this.currentState.mode !== "bottom-up") {
456 c = row.insertCell(-1); 444 c = row.insertCell(-1);
457 c.textContent = (node.ownTicks * 100 / this.tickCount).toFixed(2) + "%"; 445 c.textContent = (node.ownTicks * 100 / this.tickCount).toFixed(2) + "%";
458 c.style.textAlign = "right"; 446 c.style.textAlign = "right";
459 } 447 }
460 448
461 // Create the name cell. 449 // Create the name cell.
462 let nameCell = row.insertCell(); 450 let nameCell = row.insertCell();
463 let expander = this.createExpander(indent); 451 let expander = createTableExpander(indent + 1);
464 nameCell.appendChild(expander); 452 nameCell.appendChild(expander);
465 nameCell.appendChild(createTypeDiv(node.type)); 453 nameCell.appendChild(createTypeDiv(node.type));
466 nameCell.appendChild(this.createFunctionNode(node.name, node.codeId)); 454 nameCell.appendChild(createFunctionNode(node.name, node.codeId));
467 455
468 // Inclusive ticks cell. 456 // Inclusive ticks cell.
469 c = row.insertCell(); 457 c = row.insertCell();
470 c.textContent = node.ticks; 458 c.textContent = node.ticks;
471 c.style.textAlign = "right"; 459 c.style.textAlign = "right";
472 if (this.currentState.callTree.mode !== "bottom-up") { 460 if (this.currentState.mode !== "bottom-up") {
473 // Exclusive ticks cell. 461 // Exclusive ticks cell.
474 c = row.insertCell(-1); 462 c = row.insertCell(-1);
475 c.textContent = node.ownTicks; 463 c.textContent = node.ownTicks;
476 c.style.textAlign = "right"; 464 c.style.textAlign = "right";
477 } 465 }
478 if (node.children.length > 0) { 466 if (node.children.length > 0) {
479 expander.textContent = "\u25B8"; 467 expander.textContent = "\u25B8";
480 expander.onclick = () => { that.expandTree(node, indent + 1); }; 468 expander.onclick = () => { that.expandTree(node, indent + 1); };
481 } 469 }
482 470
(...skipping 10 matching lines...) Expand all
493 let index = row.rowIndex; 481 let index = row.rowIndex;
494 while (row.rowIndex < this.rows.rows.length && 482 while (row.rowIndex < this.rows.rows.length &&
495 this.rows.rows[index].id.startsWith(id)) { 483 this.rows.rows[index].id.startsWith(id)) {
496 this.rows.deleteRow(index); 484 this.rows.deleteRow(index);
497 } 485 }
498 486
499 expander.textContent = "\u25B8"; 487 expander.textContent = "\u25B8";
500 expander.onclick = expandHandler; 488 expander.onclick = expandHandler;
501 } 489 }
502 490
503 fillSelects(calltree) { 491 fillSelects(mode, calltree) {
504 function addOptions(e, values, current) { 492 function addOptions(e, values, current) {
505 while (e.options.length > 0) { 493 while (e.options.length > 0) {
506 e.remove(0); 494 e.remove(0);
507 } 495 }
508 for (let i = 0; i < values.length; i++) { 496 for (let i = 0; i < values.length; i++) {
509 let option = document.createElement("option"); 497 let option = document.createElement("option");
510 option.value = values[i].value; 498 option.value = values[i].value;
511 option.textContent = values[i].text; 499 option.textContent = values[i].text;
512 e.appendChild(option); 500 e.appendChild(option);
513 } 501 }
514 e.value = current; 502 e.value = current;
515 } 503 }
516 504
517 let attributions = [ 505 let attributions = [
518 { value : "js-exclude-bc", 506 { value : "js-exclude-bc",
519 text : "Attribute bytecode handlers to caller" }, 507 text : "Attribute bytecode handlers to caller" },
520 { value : "full-tree", 508 { value : "full-tree",
521 text : "Count each code object separately" }, 509 text : "Count each code object separately" },
522 { value : "js-funs", 510 { value : "js-funs",
523 text : "Attribute non-functions to JS functions" } 511 text : "Attribute non-functions to JS functions" }
524 ]; 512 ];
525 513
526 switch (calltree.mode) { 514 switch (mode) {
527 case "bottom-up": 515 case "bottom-up":
528 addOptions(this.selectAttribution, attributions, calltree.attribution); 516 addOptions(this.selectAttribution, attributions, calltree.attribution);
529 addOptions(this.selectCategories, [ 517 addOptions(this.selectCategories, [
530 { value : "code-type", text : "Code type" }, 518 { value : "code-type", text : "Code type" },
531 { value : "none", text : "None" } 519 { value : "none", text : "None" }
532 ], calltree.categories); 520 ], calltree.categories);
533 addOptions(this.selectSort, [ 521 addOptions(this.selectSort, [
534 { value : "time", text : "Time (including children)" }, 522 { value : "time", text : "Time (including children)" },
535 { value : "category-time", text : "Code category, time" }, 523 { value : "category-time", text : "Code category, time" },
536 ], calltree.sort); 524 ], calltree.sort);
(...skipping 20 matching lines...) Expand all
557 { value : "own-time", text : "Own time" }, 545 { value : "own-time", text : "Own time" },
558 { value : "time", text : "Time (including children)" }, 546 { value : "time", text : "Time (including children)" },
559 { value : "category-own-time", text : "Code category, own time"}, 547 { value : "category-own-time", text : "Code category, own time"},
560 { value : "category-time", text : "Code category, time" }, 548 { value : "category-time", text : "Code category, time" },
561 ], calltree.sort); 549 ], calltree.sort);
562 return; 550 return;
563 } 551 }
564 console.error("Unexpected mode"); 552 console.error("Unexpected mode");
565 } 553 }
566 554
555 static isCallTreeMode(mode) {
556 switch (mode) {
557 case "bottom-up":
558 case "top-down":
559 case "function-list":
560 return true;
561 default:
562 return false;
563 }
564 }
565
567 render(newState) { 566 render(newState) {
568 let oldState = this.currentState; 567 let oldState = this.currentState;
569 if (!newState.file) { 568 if (!newState.file || !CallTreeView.isCallTreeMode(newState.mode)) {
570 this.element.style.display = "none"; 569 this.element.style.display = "none";
570 this.currentState = null;
571 return; 571 return;
572 } 572 }
573 573
574 this.currentState = newState; 574 this.currentState = newState;
575 if (oldState) { 575 if (oldState) {
576 if (newState.file === oldState.file && 576 if (newState.file === oldState.file &&
577 newState.start === oldState.start && 577 newState.start === oldState.start &&
578 newState.end === oldState.end && 578 newState.end === oldState.end &&
579 newState.callTree.mode === oldState.callTree.mode && 579 newState.mode === oldState.mode &&
580 newState.callTree.attribution === oldState.callTree.attribution && 580 newState.callTree.attribution === oldState.callTree.attribution &&
581 newState.callTree.categories === oldState.callTree.categories && 581 newState.callTree.categories === oldState.callTree.categories &&
582 newState.callTree.sort === oldState.callTree.sort) { 582 newState.callTree.sort === oldState.callTree.sort) {
583 // No change => just return. 583 // No change => just return.
584 return; 584 return;
585 } 585 }
586 } 586 }
587 587
588 this.element.style.display = "inherit"; 588 this.element.style.display = "inherit";
589 589
590 let mode = this.currentState.callTree.mode; 590 let mode = this.currentState.mode;
591 if (!oldState || mode !== oldState.callTree.mode) { 591 if (!oldState || mode !== oldState.mode) {
592 // Technically, we should also call this if attribution, categories or 592 // Technically, we should also call this if attribution, categories or
593 // sort change, but the selection is already highlighted by the combobox 593 // sort change, but the selection is already highlighted by the combobox
594 // itself, so we do need to do anything here. 594 // itself, so we do need to do anything here.
595 this.fillSelects(newState.callTree); 595 this.fillSelects(newState.mode, newState.callTree);
596 } 596 }
597 597
598 let ownTimeClass = (mode === "bottom-up") ? "numeric-hidden" : "numeric"; 598 let ownTimeClass = (mode === "bottom-up") ? "numeric-hidden" : "numeric";
599 let ownTimeTh = $(this.treeElement.id + "-own-time-header"); 599 let ownTimeTh = $(this.treeElement.id + "-own-time-header");
600 ownTimeTh.classList = ownTimeClass; 600 ownTimeTh.classList = ownTimeClass;
601 let ownTicksTh = $(this.treeElement.id + "-own-ticks-header"); 601 let ownTicksTh = $(this.treeElement.id + "-own-ticks-header");
602 ownTicksTh.classList = ownTimeClass; 602 ownTicksTh.classList = ownTimeClass;
603 603
604 // Build the tree. 604 // Build the tree.
605 let stackProcessor; 605 let stackProcessor;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
654 this.canvas.onmousedown = this.onMouseDown.bind(this); 654 this.canvas.onmousedown = this.onMouseDown.bind(this);
655 this.canvas.onmouseup = this.onMouseUp.bind(this); 655 this.canvas.onmouseup = this.onMouseUp.bind(this);
656 this.canvas.onmousemove = this.onMouseMove.bind(this); 656 this.canvas.onmousemove = this.onMouseMove.bind(this);
657 657
658 this.selectionStart = null; 658 this.selectionStart = null;
659 this.selectionEnd = null; 659 this.selectionEnd = null;
660 this.selecting = false; 660 this.selecting = false;
661 661
662 this.fontSize = 12; 662 this.fontSize = 12;
663 this.imageOffset = Math.round(this.fontSize * 1.2); 663 this.imageOffset = Math.round(this.fontSize * 1.2);
664 this.functionTimelineHeight = 16; 664 this.functionTimelineHeight = 24;
665 this.functionTimelineTickHeight = 16;
665 666
666 this.currentState = null; 667 this.currentState = null;
667 } 668 }
668 669
669 onMouseDown(e) { 670 onMouseDown(e) {
670 this.selectionStart = 671 this.selectionStart =
671 e.clientX - this.canvas.getBoundingClientRect().left; 672 e.clientX - this.canvas.getBoundingClientRect().left;
672 this.selectionEnd = this.selectionStart + 1; 673 this.selectionEnd = this.selectionStart + 1;
673 this.selecting = true; 674 this.selecting = true;
674 } 675 }
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
857 for (let j = 0; j < bucketDescriptors.length; j++) { 858 for (let j = 0; j < bucketDescriptors.length; j++) {
858 let desc = bucketDescriptors[j]; 859 let desc = bucketDescriptors[j];
859 for (let k = 0; k < desc.kinds.length; k++) { 860 for (let k = 0; k < desc.kinds.length; k++) {
860 sum += buckets[i][desc.kinds[k]]; 861 sum += buckets[i][desc.kinds[k]];
861 } 862 }
862 bucketData.push(Math.round(graphHeight * sum / total)); 863 bucketData.push(Math.round(graphHeight * sum / total));
863 } 864 }
864 bucketsGraph.push(bucketData); 865 bucketsGraph.push(bucketData);
865 } 866 }
866 867
867 // Draw the graph into the buffer. 868 // Draw the category graph into the buffer.
868 let bucketWidth = width / (bucketsGraph.length - 1); 869 let bucketWidth = width / bucketsGraph.length;
869 let ctx = buffer.getContext('2d'); 870 let ctx = buffer.getContext('2d');
870 for (let i = 0; i < bucketsGraph.length - 1; i++) { 871 for (let i = 0; i < bucketsGraph.length - 1; i++) {
871 let bucketData = bucketsGraph[i]; 872 let bucketData = bucketsGraph[i];
872 let nextBucketData = bucketsGraph[i + 1]; 873 let nextBucketData = bucketsGraph[i + 1];
873 for (let j = 0; j < bucketData.length; j++) { 874 for (let j = 0; j < bucketData.length; j++) {
874 let x1 = Math.round(i * bucketWidth); 875 let x1 = Math.round(i * bucketWidth);
875 let x2 = Math.round((i + 1) * bucketWidth); 876 let x2 = Math.round((i + 1) * bucketWidth);
876 ctx.beginPath(); 877 ctx.beginPath();
877 ctx.moveTo(x1, j && bucketData[j - 1]); 878 ctx.moveTo(x1, j && bucketData[j - 1]);
878 ctx.lineTo(x2, j && nextBucketData[j - 1]); 879 ctx.lineTo(x2, j && nextBucketData[j - 1]);
879 ctx.lineTo(x2, nextBucketData[j]); 880 ctx.lineTo(x2, nextBucketData[j]);
880 ctx.lineTo(x1, bucketData[j]); 881 ctx.lineTo(x1, bucketData[j]);
881 ctx.closePath(); 882 ctx.closePath();
882 ctx.fillStyle = bucketDescriptors[j].color; 883 ctx.fillStyle = bucketDescriptors[j].color;
883 ctx.fill(); 884 ctx.fill();
884 } 885 }
885 } 886 }
887
888 // Draw the function ticks.
886 let functionTimelineYOffset = graphHeight; 889 let functionTimelineYOffset = graphHeight;
887 let functionTimelineHeight = this.functionTimelineHeight; 890 let functionTimelineTickHeight = this.functionTimelineTickHeight;
888 let functionTimelineHalfHeight = Math.round(functionTimelineHeight / 2); 891 let functionTimelineHalfHeight =
892 Math.round(functionTimelineTickHeight / 2);
889 let timestampScaler = width / (lastTime - firstTime); 893 let timestampScaler = width / (lastTime - firstTime);
894 let timestampToX = (t) => Math.round((t - firstTime) * timestampScaler);
890 ctx.fillStyle = "white"; 895 ctx.fillStyle = "white";
891 ctx.fillRect( 896 ctx.fillRect(
892 0, 897 0,
893 functionTimelineYOffset, 898 functionTimelineYOffset,
894 buffer.width, 899 buffer.width,
895 functionTimelineHeight); 900 this.functionTimelineHeight);
896 for (let i = 0; i < codeIdProcessor.blocks.length; i++) { 901 for (let i = 0; i < codeIdProcessor.blocks.length; i++) {
897 let block = codeIdProcessor.blocks[i]; 902 let block = codeIdProcessor.blocks[i];
898 let bucket = kindToBucketDescriptor[block.kind]; 903 let bucket = kindToBucketDescriptor[block.kind];
899 ctx.fillStyle = bucket.color; 904 ctx.fillStyle = bucket.color;
900 ctx.fillRect( 905 ctx.fillRect(
901 Math.round((block.start - firstTime) * timestampScaler), 906 timestampToX(block.start),
902 functionTimelineYOffset, 907 functionTimelineYOffset,
903 Math.max(1, Math.round((block.end - block.start) * timestampScaler)), 908 Math.max(1, Math.round((block.end - block.start) * timestampScaler)),
904 block.topOfStack ? functionTimelineHeight : functionTimelineHalfHeight); 909 block.topOfStack ?
910 functionTimelineTickHeight : functionTimelineHalfHeight);
905 } 911 }
906 ctx.strokeStyle = "black"; 912 ctx.strokeStyle = "black";
907 ctx.lineWidth = "1"; 913 ctx.lineWidth = "1";
908 ctx.beginPath(); 914 ctx.beginPath();
909 ctx.moveTo(0, functionTimelineYOffset + 0.5); 915 ctx.moveTo(0, functionTimelineYOffset + 0.5);
910 ctx.lineTo(buffer.width, functionTimelineYOffset + 0.5); 916 ctx.lineTo(buffer.width, functionTimelineYOffset + 0.5);
911 ctx.stroke(); 917 ctx.stroke();
912 ctx.strokeStyle = "rgba(0,0,0,0.2)"; 918 ctx.strokeStyle = "rgba(0,0,0,0.2)";
913 ctx.lineWidth = "1"; 919 ctx.lineWidth = "1";
914 ctx.beginPath(); 920 ctx.beginPath();
915 ctx.moveTo(0, functionTimelineYOffset + functionTimelineHalfHeight - 0.5); 921 ctx.moveTo(0, functionTimelineYOffset + functionTimelineHalfHeight - 0.5);
916 ctx.lineTo(buffer.width, 922 ctx.lineTo(buffer.width,
917 functionTimelineYOffset + functionTimelineHalfHeight - 0.5); 923 functionTimelineYOffset + functionTimelineHalfHeight - 0.5);
918 ctx.stroke(); 924 ctx.stroke();
919 925
926 // Draw marks for optimizations and deoptimizations in the function
927 // timeline.
928 if (currentCodeId && currentCodeId >= 0 &&
929 file.code[currentCodeId].func) {
930 let y = Math.round(functionTimelineYOffset + functionTimelineTickHeight +
931 (this.functionTimelineHeight - functionTimelineTickHeight) / 2);
932 let func = file.functions[file.code[currentCodeId].func];
933 for (let i = 0; i < func.codes.length; i++) {
934 let code = file.code[func.codes[i]];
935 if (code.kind === "Opt") {
936 if (code.deopt) {
937 // Draw deoptimization mark.
938 let x = timestampToX(code.deopt.tm);
939 ctx.lineWidth = 0.7;
940 ctx.strokeStyle = "red";
941 ctx.beginPath();
942 ctx.moveTo(x - 3, y - 3);
943 ctx.lineTo(x + 3, y + 3);
944 ctx.stroke();
945 ctx.beginPath();
946 ctx.moveTo(x - 3, y + 3);
947 ctx.lineTo(x + 3, y - 3);
948 ctx.stroke();
949 }
950 // Draw optimization mark.
951 let x = timestampToX(code.tm);
952 ctx.lineWidth = 0.7;
953 ctx.strokeStyle = "blue";
954 ctx.beginPath();
955 ctx.moveTo(x - 3, y - 3);
956 ctx.lineTo(x, y);
957 ctx.stroke();
958 ctx.beginPath();
959 ctx.moveTo(x - 3, y + 3);
960 ctx.lineTo(x, y);
961 ctx.stroke();
962 } else {
963 // Draw code creation mark.
964 let x = Math.round(timestampToX(code.tm));
965 ctx.beginPath();
966 ctx.fillStyle = "black";
967 ctx.arc(x, y, 3, 0, 2 * Math.PI);
968 ctx.fill();
969 }
970 }
971 }
972
920 // Remember stuff for later. 973 // Remember stuff for later.
921 this.buffer = buffer; 974 this.buffer = buffer;
922 975
923 // Draw the buffer. 976 // Draw the buffer.
924 this.drawSelection(); 977 this.drawSelection();
925 978
926 // (Re-)Populate the graph legend. 979 // (Re-)Populate the graph legend.
927 while (this.legend.cells.length > 0) { 980 while (this.legend.cells.length > 0) {
928 this.legend.deleteCell(0); 981 this.legend.deleteCell(0);
929 } 982 }
(...skipping 21 matching lines...) Expand all
951 } 1004 }
952 if (currentCodeId) { 1005 if (currentCodeId) {
953 let currentCode = file.code[currentCodeId]; 1006 let currentCode = file.code[currentCodeId];
954 this.currentCode.appendChild(document.createTextNode(currentCode.name)); 1007 this.currentCode.appendChild(document.createTextNode(currentCode.name));
955 } else { 1008 } else {
956 this.currentCode.appendChild(document.createTextNode("<none>")); 1009 this.currentCode.appendChild(document.createTextNode("<none>"));
957 } 1010 }
958 } 1011 }
959 } 1012 }
960 1013
1014 class ModeBarView {
1015 constructor() {
1016 let modeBar = this.element = $("mode-bar");
1017
1018 function addMode(id, text, active) {
1019 let div = document.createElement("div");
1020 div.classList = "mode-button" + (active ? " active-mode-button" : "");
1021 div.id = "mode-" + id;
1022 div.textContent = text;
1023 div.onclick = () => {
1024 if (main.currentState.mode === id) return;
1025 let old = $("mode-" + main.currentState.mode);
1026 old.classList = "mode-button";
1027 div.classList = "mode-button active-mode-button";
1028 main.setMode(id);
1029 };
1030 modeBar.appendChild(div);
1031 }
1032
1033 addMode("summary", "Summary", true);
1034 addMode("bottom-up", "Bottom up");
1035 addMode("top-down", "Top down");
1036 addMode("function-list", "Functions");
1037 }
1038
1039 render(newState) {
1040 if (!newState.file) {
1041 this.element.style.display = "none";
1042 return;
1043 }
1044
1045 this.element.style.display = "inherit";
1046 }
1047 }
1048
1049 class SummaryView {
1050 constructor() {
1051 this.element = $("summary");
1052 this.currentState = null;
1053 }
1054
1055 render(newState) {
1056 let oldState = this.currentState;
1057
1058 if (!newState.file || newState.mode !== "summary") {
1059 this.element.style.display = "none";
1060 this.currentState = null;
1061 return;
1062 }
1063
1064 this.currentState = newState;
1065 if (oldState) {
1066 if (newState.file === oldState.file &&
1067 newState.start === oldState.start &&
1068 newState.end === oldState.end) {
1069 // No change, nothing to do.
1070 return;
1071 }
1072 }
1073
1074 this.element.style.display = "inherit";
1075
1076 while (this.element.firstChild) {
1077 this.element.removeChild(this.element.firstChild);
1078 }
1079
1080 let stats = computeOptimizationStats(
1081 this.currentState.file, newState.start, newState.end);
1082
1083 let table = document.createElement("table");
1084 let rows = document.createElement("tbody");
1085
1086 function addRow(text, number, indent) {
1087 let row = rows.insertRow(-1);
1088 let textCell = row.insertCell(-1);
1089 textCell.textContent = text;
1090 let numberCell = row.insertCell(-1);
1091 numberCell.textContent = number;
1092 if (indent) {
1093 textCell.style.textIndent = indent + "em";
1094 numberCell.style.textIndent = indent + "em";
1095 }
1096 return row;
1097 }
1098
1099 function makeCollapsible(row, expander) {
1100 expander.textContent = "\u25BE";
1101 let expandHandler = expander.onclick;
1102 expander.onclick = () => {
1103 let id = row.id;
1104 let index = row.rowIndex + 1;
1105 while (index < rows.rows.length &&
1106 rows.rows[index].id.startsWith(id)) {
1107 rows.deleteRow(index);
1108 }
1109 expander.textContent = "\u25B8";
1110 expander.onclick = expandHandler;
1111 }
1112 }
1113
1114 function expandDeoptInstances(row, expander, instances, indent, kind) {
1115 let index = row.rowIndex;
1116 for (let i = 0; i < instances.length; i++) {
1117 let childRow = rows.insertRow(index + 1);
1118 childRow.id = row.id + i + "/";
1119
1120 let deopt = instances[i].deopt;
1121
1122 let textCell = childRow.insertCell(-1);
1123 textCell.appendChild(document.createTextNode(deopt.posText));
1124 textCell.style.textIndent = indent + "em";
1125 let reasonCell = childRow.insertCell(-1);
1126 reasonCell.appendChild(
1127 document.createTextNode("Reason: " + deopt.reason));
1128 reasonCell.style.textIndent = indent + "em";
1129 }
1130 makeCollapsible(row, expander);
1131 }
1132
1133 function expandDeoptFunctionList(row, expander, list, indent, kind) {
1134 let index = row.rowIndex;
1135 for (let i = 0; i < list.length; i++) {
1136 let childRow = rows.insertRow(index + 1);
1137 childRow.id = row.id + i + "/";
1138
1139 let textCell = childRow.insertCell(-1);
1140 let expander = createTableExpander(indent);
1141 textCell.appendChild(expander);
1142 textCell.appendChild(
1143 createFunctionNode(list[i].f.name, list[i].f.codes[0]));
1144
1145 let numberCell = childRow.insertCell(-1);
1146 numberCell.textContent = list[i].instances.length;
1147 numberCell.style.textIndent = indent + "em";
1148
1149 expander.textContent = "\u25B8";
1150 expander.onclick = () => {
1151 expandDeoptInstances(
1152 childRow, expander, list[i].instances, indent + 1);
1153 };
1154 }
1155 makeCollapsible(row, expander);
1156 }
1157
1158 function expandOptimizedFunctionList(row, expander, list, indent, kind) {
1159 let index = row.rowIndex;
1160 for (let i = 0; i < list.length; i++) {
1161 let childRow = rows.insertRow(index + 1);
1162 childRow.id = row.id + i + "/";
1163
1164 let textCell = childRow.insertCell(-1);
1165 textCell.appendChild(
1166 createFunctionNode(list[i].f.name, list[i].f.codes[0]));
1167 textCell.style.textIndent = indent + "em";
1168
1169 let numberCell = childRow.insertCell(-1);
1170 numberCell.textContent = list[i].instances.length;
1171 numberCell.style.textIndent = indent + "em";
1172 }
1173 makeCollapsible(row, expander);
1174 }
1175
1176 function addExpandableRow(text, list, indent, kind) {
1177 let row = rows.insertRow(-1);
1178
1179 row.id = "opt-table/" + kind + "/";
1180
1181 let textCell = row.insertCell(-1);
1182 let expander = createTableExpander(indent);
1183 textCell.appendChild(expander);
1184 textCell.appendChild(document.createTextNode(text));
1185
1186 let numberCell = row.insertCell(-1);
1187 numberCell.textContent = list.count;
1188 if (indent) {
1189 numberCell.style.textIndent = indent + "em";
1190 }
1191
1192 if (list.count > 0) {
1193 expander.textContent = "\u25B8";
1194 if (kind === "opt") {
1195 expander.onclick = () => {
1196 expandOptimizedFunctionList(
1197 row, expander, list.functions, indent + 1, kind);
1198 };
1199 } else {
1200 expander.onclick = () => {
1201 expandDeoptFunctionList(
1202 row, expander, list.functions, indent + 1, kind);
1203 };
1204 }
1205 }
1206 return row;
1207 }
1208
1209 addRow("Total function count:", stats.functionCount);
1210 addRow("Optimized function count:", stats.optimizedFunctionCount, 1);
1211 addRow("Deoptimized function count:", stats.deoptimizedFunctionCount, 2);
1212
1213 addExpandableRow("Optimization count:", stats.optimizations, 0, "opt");
1214 let deoptCount = stats.eagerDeoptimizations.count +
1215 stats.softDeoptimizations.count + stats.lazyDeoptimizations.count;
1216 addRow("Deoptimization count:", deoptCount);
1217 addExpandableRow("Eager:", stats.eagerDeoptimizations, 1, "eager");
1218 addExpandableRow("Lazy:", stats.lazyDeoptimizations, 1, "lazy");
1219 addExpandableRow("Soft:", stats.softDeoptimizations, 1, "soft");
1220
1221 table.appendChild(rows);
1222 this.element.appendChild(table);
1223 }
1224 }
1225
961 class HelpView { 1226 class HelpView {
962 constructor() { 1227 constructor() {
963 this.element = $("help"); 1228 this.element = $("help");
964 } 1229 }
965 1230
966 render(newState) { 1231 render(newState) {
967 this.element.style.display = newState.file ? "none" : "inherit"; 1232 this.element.style.display = newState.file ? "none" : "inherit";
968 } 1233 }
969 } 1234 }
OLDNEW
« no previous file with comments | « tools/profview/profile-utils.js ('k') | tools/tickprocessor.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698