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

Side by Side Diff: tools/callstats.html

Issue 1947413004: [tools] Add callstats.html to analize --runtime-call-stats output (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: addressing nits Created 4 years, 7 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 | « no previous file | tools/ic-explorer.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 <html>
2 <!--
3 Copyright 2016 the V8 project authors. All rights reserved. Use of this source
4 code is governed by a BSD-style license that can be found in the LICENSE file.
5 -->
6
7 <head>
8 <meta charset="UTF-8">
9 <style>
10 body {
11 font-family: arial;
12 }
13
14 table {
15 display: table;
16 border-spacing: 0px;
17 }
18
19 tr {
20 border-spacing: 0px;
21 padding: 10px;
22 }
23
24 td,
25 th {
26 padding: 3px 10px 3px 5px;
27 }
28
29 .inline {
30 display: inline-block;
31 vertical-align: top;
32 }
33
34 h2,
35 h3 {
36 margin-bottom: 0px;
37 }
38
39 .hidden {
40 display: none;
41 }
42
43 .view {
44 display: table;
45 }
46
47 .column {
48 display: table-cell;
49 border-right: 1px black dotted;
50 min-width: 200px;
51 }
52
53 .column .header {
54 padding: 0 10px 0 10px
55 }
56
57 #column {
58 display: none;
59 }
60
61 .list {
62 width: 100%;
63 }
64
65 select {
66 width: 100%
67 }
68
69 .list tbody {
70 cursor: pointer;
71 }
72
73 .list tr:nth-child(even) {
74 background-color: #EFEFEF;
75 }
76
77 .list tr:nth-child(even).selected {
78 background-color: #DDD;
79 }
80
81 .list tr.child {
82 display: none;
83 }
84
85 .list tr.child.visible {
86 display: table-row;
87 }
88
89 .list .child .name {
90 padding-left: 20px;
91 }
92
93 .list .parent td {
94 border-top: 1px solid #AAA;
95 }
96
97 .list .total {
98 font-weight: bold
99 }
100
101 .list tr.parent {
102 background-color: #FFF;
103 }
104
105 .list tr.parent.selected {
106 background-color: #DDD;
107 }
108
109 tr.selected {
110 background-color: #DDD;
111 }
112
113 .list .position {
114 text-align: right;
115 display: none;
116 }
117
118 .list div.toggle {
119 cursor: pointer;
120 }
121
122 #column_0 .position {
123 display: table-cell;
124 }
125
126 #column_0 .name {
127 display: table-cell;
128 }
129
130 .list .name {
131 display: none;
132 white-space: nowrap;
133 }
134
135 .value {
136 text-align: right;
137 }
138
139 .selectedVersion {
140 font-weight: bold;
141 }
142
143 #baseline {
144 width: auto;
145 }
146
147 .compareSelector {
148 padding-bottom: 20px;
149 }
150
151 .pageDetailTable tbody {
152 cursor: pointer
153 }
154
155 #popover {
156 position: absolute;
157 transform: translateY(-50%) translateX(40px);
158 box-shadow: -2px 10px 44px -10px #000;
159 border-radius: 5px;
160 z-index: 1;
161 background-color: #FFF;
162 display: none;
163 white-space: nowrap;
164 }
165
166 #popover table {
167 position: relative;
168 z-index: 1;
169 text-align: right;
170 margin: 10px;
171 }
172 #popover td {
173 padding: 3px 0px 3px 5px;
174 white-space: nowrap;
175 }
176
177 .popoverArrow {
178 background-color: #FFF;
179 position: absolute;
180 width: 30px;
181 height: 30px;
182 transform: translateY(-50%)rotate(45deg);
183 top: 50%;
184 left: -10px;
185 z-index: 0;
186 }
187
188 #popover .name {
189 padding: 5px;
190 font-weight: bold;
191 text-align: center;
192 }
193
194 #popover table .compare {
195 display: none
196 }
197
198 #popover table.compare .compare {
199 display: table-cell;
200 }
201 </style>
202 <script>
203 "use strict"
204
205 // Did anybody say monkeypatching?
206 if (!NodeList.prototype.forEach) {
207 NodeList.prototype.forEach = function(func) {
208 for (var i = 0; i < this.length; i++) {
209 func(this[i]);
210 }
211 }
212 }
213
214 var versions;
215 var selectedPage;
216 var baselineVersion;
217 var selectedEntry;
218
219 function initialize() {
220 var original = $("column");
221 for (var i = 0; i < versions.length || i < 2; i++) {
222 // add column
223 var column = original.cloneNode(true);
224 column.id = "column_" + i;
225 // Fill in all versions
226 var select = column.querySelector(".version");
227 select.id = "selectVersion_" + i;
228 // add all select options
229 versions.forEach((version) => {
230 var option = document.createElement("option");
231 option.textContent = version.name;
232 option.version = version;
233 select.appendChild(option);
234 });
235 // Fill in all page versions
236 select = column.querySelector(".pageVersion");
237 select.id = "select_" + i;
238 // add all select options
239 versions.forEach((version) => {
240 var optgroup = document.createElement("optgroup");
241 optgroup.label = version.name;
242 optgroup.version = version;
243 version.pages.forEach((page) => {
244 var option = document.createElement("option");
245 option.textContent = page.name;
246 option.page = page;
247 optgroup.appendChild(option);
248 });
249 select.appendChild(optgroup);
250 });
251 $("view").appendChild(column);
252 }
253 var select = $('baseline');
254 removeAllChildren(select);
255 select.appendChild(document.createElement('option'));
256 versions.forEach((version) => {
257 var option = document.createElement("option");
258 option.textContent = version.name;
259 option.version = version;
260 select.appendChild(option);
261 });
262 $('results').querySelectorAll('#results > .hidden').forEach((node) => {
263 toggleCssClass(node, 'hidden', false);
264 });
265 }
266
267 function showPage(firstPage) {
268 selectedPage = firstPage;
269 selectedPage.sort();
270 showPageInColumn(firstPage, 0);
271 // Show the other versions of this page in the following columns.
272 var pageVersions = versions.pageVersions(firstPage.name);
273 var index = 1;
274 pageVersions.forEach((page) => {
275 if (page !== firstPage) {
276 showPageInColumn(page, index);
277 index++;
278 }
279 });
280 showImpactList(selectedPage);
281 }
282
283 function showPageInColumn(page, columnIndex) {
284 page.sort();
285 var showDiff = (baselineVersion === undefined && columnIndex !== 0) ||
286 (baselineVersion !== undefined && page.version !== baselineVersion);
287 var diffStatus = (td, a, b) => {};
288 if (showDiff) {
289 if (baselineVersion !== undefined) {
290 diffStatus = (td, a, b) => {
291 if (a == 0) return;
292 td.style.color = a < 0 ? '#FF0000' : '#00BB00';
293 };
294 } else {
295 diffStatus = (td, a, b) => {
296 if (a == b) return;
297 var color;
298 var ratio = a / b;
299 if (ratio > 1) {
300 ratio = Math.min(Math.round((ratio - 1) * 255 * 10), 200);
301 color = '#' + ratio.toString(16) + "0000";
302 } else {
303 ratio = Math.min(Math.round((1 - ratio) * 255 * 10), 200);
304 color = '#00' + ratio.toString(16) + "00";
305 }
306 td.style.color = color;
307 }
308 }
309 }
310
311 var column = $('column_' + columnIndex);
312 var select = $('select_' + columnIndex);
313 // Find the matching option
314 selectOption(select, (i, option) => {
315 return option.page == page
316 });
317 var table = column.querySelector("table");
318 var oldTbody = table.querySelector('tbody');
319 var tbody = document.createElement('tbody');
320 var referencePage = selectedPage;
321 page.forEachSorted(selectedPage,
322 (parentEntry, entry, referenceEntry) => {
323 // Filter out entries that do not exist in the first column for the
324 // default view.
325 if (baselineVersion === undefined && referenceEntry &&
326 referenceEntry.time == 0) {
327 return;
328 }
329 var tr = document.createElement('tr');
330 tbody.appendChild(tr);
331 tr.entry = entry;
332 tr.parentEntry = parentEntry;
333 if (!parentEntry) {
334 tr.className = 'parent'
335 } else {
336 tr.className = 'child'
337 }
338 // Don't show entries that do not exist on the current page or if we
339 // compare against the current page
340 if (entry !== undefined && page.version !== baselineVersion) {
341 // If we show a diff, use the baselineVersion as the referenceEntry
342 if (baselineVersion !== undefined) {
343 var baselineEntry = baselineVersion.getEntry(entry);
344 if (baselineEntry !== undefined) referenceEntry = baselineEntry
345 }
346 if (!parentEntry) {
347 var node = td(tr, '<div class="toggle">►</div>', 'position');
348 node.firstChild.addEventListener('click', handleToggleGroup);
349 } else {
350 td(tr, entry.position == 0 ? '' : entry.position, 'position');
351 }
352 td(tr, entry.name, 'name ' + entry.cssClass());
353 diffStatus(
354 td(tr, ms(entry.time), 'value time'),
355 entry.time, referenceEntry.time);
356 diffStatus(
357 td(tr, percent(entry.timePercent), 'value time'),
358 entry.time, referenceEntry.time);
359 diffStatus(
360 td(tr, count(entry.count), 'value count'),
361 entry.count, referenceEntry.count);
362 } else if (baselineVersion !== undefined && referenceEntry
363 && page.version !== baselineVersion) {
364 // Show comparison of entry that does not exist on the current page.
365 tr.entry = referenceEntry;
366 td(tr, '-', 'position');
367 td(tr, referenceEntry.name, 'name');
368 diffStatus(
369 td(tr, ms(referenceEntry.time), 'value time'),
370 referenceEntry.time, 0);
371 diffStatus(
372 td(tr, percent(referenceEntry.timePercent), 'value time'),
373 referenceEntry.timePercent, 0);
374 diffStatus(
375 td(tr, count(referenceEntry.count), 'value count'),
376 referenceEntry.count, 0);
377 } else {
378 // Display empty entry / baseline entry
379 if (entry !== undefined) {
380 if (!parentEntry) {
381 var node = td(tr, '<div class="toggle">►</div>', 'position');
382 node.firstChild.addEventListener('click', handleToggleGroup);
383 } else {
384 td(tr, entry.position == 0 ? '' : entry.position, 'position');
385 }
386 td(tr, entry.name, 'name');
387 } else {
388 td(tr, '-', 'position');
389 td(tr, '-', 'name');
390 }
391 td(tr, '-', 'value time');
392 td(tr, '-', 'value time');
393 td(tr, '-', 'value count');
394 }
395 });
396 table.replaceChild(tbody, oldTbody);
397 var versionSelect = column.querySelector('select.version');
398 selectOption(versionSelect, (index, option) => {
399 return option.version == page.version
400 });
401 }
402
403 function selectEntry(entry, updateSelectedPage) {
404 if (updateSelectedPage) {
405 entry = selectedPage.version.getEntry(entry);
406 }
407 var rowIndex;
408 var needsPageSwitch = updateSelectedPage && entry.page != selectedPage;
409 // If clicked in the detail row change the first column to that page.
410 if (needsPageSwitch) showPage(entry.page);
411 var childNodes = $('column_0').querySelector('.list tbody').childNodes;
412 for (var i = 0; i < childNodes.length; i++) {
413 if (childNodes[i].entry.name == entry.name) {
414 rowIndex = i;
415 break;
416 }
417 }
418 var firstEntry = childNodes[rowIndex].entry;
419 if (rowIndex) {
420 if (firstEntry.parent) showGroup(firstEntry.parent);
421 }
422 // Deselect all
423 $('view').querySelectorAll('.list tbody tr').forEach((tr) => {
424 toggleCssClass(tr, 'selected', false);
425 });
426 // Select the entry row
427 $('view').querySelectorAll("tbody").forEach((body) => {
428 var row = body.childNodes[rowIndex];
429 if (!row) return;
430 toggleCssClass(row, 'selected',
431 row.entry && row.entry.name == firstEntry.name);
432 });
433 if (updateSelectedPage) {
434 entry = selectedEntry.page.version.getEntry(entry);
435 }
436 selectedEntry = entry;
437 showEntryDetail(entry);
438 }
439
440 function showEntryDetail(entry) {
441 var table, tbody, entries;
442 table = $('detailView').querySelector('.versionDetailTable');
443 tbody = document.createElement('tbody');
444 if (entry !== undefined) {
445 $('detailView').querySelector('.versionDetail h3 span').innerHTML
446 = entry.name;
447 entries = versions.pageVersions(entry.page.name).map(
448 (page) => {
449 return page.get(entry.name)
450 });
451 entries.sort((a, b) => {
452 return a.time - b.time
453 });
454 entries.forEach((pageEntry) => {
455 if (pageEntry === undefined) return;
456 var tr = document.createElement('tr');
457 if (pageEntry == entry) tr.className += 'selected';
458 tr.entry = pageEntry;
459 td(tr, pageEntry.page.version.name, 'version');
460 td(tr, pageEntry.position, 'value position');
461 td(tr, ms(pageEntry.time), 'value time');
462 td(tr, percent(pageEntry.timePercent), 'value time');
463 td(tr, count(pageEntry.count), 'value count');
464 tbody.appendChild(tr);
465 });
466 }
467 table.replaceChild(tbody, table.querySelector('tbody'));
468
469 table = $('detailView').querySelector('.pageDetailTable');
470 tbody = document.createElement('tbody');
471 if (entry !== undefined) {
472 var version = entry.page.version;
473 $('detailView').querySelector('.pageDetail h3 span').innerHTML
474 = version.name;
475 entries = version.pages.map(
476 (page) => {
477 return page.get(entry.name)
478 });
479 entries.sort((a, b) => {
480 return b.timePercent - a.timePercent
481 });
482 entries.forEach((pageEntry) => {
483 if (pageEntry === undefined) return;
484 var tr = document.createElement('tr');
485 if (pageEntry === entry) tr.className += 'selected';
486 tr.entry = pageEntry;
487 td(tr, pageEntry.page.name, 'name');
488 td(tr, pageEntry.position, 'value position');
489 td(tr, ms(pageEntry.time), 'value time');
490 td(tr, percent(pageEntry.timePercent), 'value time');
491 td(tr, count(pageEntry.count), 'value count');
492 tbody.appendChild(tr);
493 });
494 // show the total for all pages
495 var tds = table.querySelectorAll('tfoot td');
496 tds[2].innerHTML = ms(entry.getTimeImpact());
497 // Only show the percentage total if we are in diff mode:
498 if (baselineVersion !== undefined) {
499 tds[3].innerHTML = percent(entry.getTimePercentImpact());
500 } else {
501 tds[3].innerHTML = ''
502 }
503 tds[4].innerHTML = count(entry.getCountImpact());
504 }
505 table.replaceChild(tbody, table.querySelector('tbody'));
506 showImpactList(entry.page);
507 }
508
509 function showImpactList(page) {
510 var impactView = $('detailView').querySelector('.impactView');
511 impactView.querySelector('h3 span').innerHTML = page.version.name;
512
513 var table = impactView.querySelector('table');
514 var tbody = document.createElement('tbody');
515 var version = page.version;
516 var entries = version.allEntries();
517 if (selectedEntry !== undefined && selectedEntry.isGroup) {
518 impactView.querySelector('h3 span').innerHTML
519 += " " + selectedEntry.name;
520 entries = entries.filter((entry) => {
521 return entry.name == selectedEntry.name ||
522 (entry.parent && entry.parent.name == selectedEntry.name)
523 });
524 }
525 entries.sort((a, b) => {
526 return b.getTimePercentImpact() - a.getTimePercentImpact();
527 });
528 entries.forEach((entry) => {
529 var tr = document.createElement('tr');
530 tr.entry = entry;
531 td(tr, entry.name, 'name');
532 td(tr, ms(entry.getTimeImpact()), 'value time');
533 td(tr, percent(entry.getTimePercentImpact()), 'value time');
534 var topPages = entry.getPagesByPercentImpact().slice(0, 2)
535 .map((each) => {
536 return each.name + ' ('
537 + percent(each.getEntry(entry).timePercent) + ')'
538 });
539 td(tr, topPages.join(', '), 'name');
540 tbody.appendChild(tr);
541 });
542 table.replaceChild(tbody, table.querySelector('tbody'));
543 }
544
545 function showGroup(entry) {
546 toggleGroup(entry, true);
547 }
548
549 function toggleGroup(group, show) {
550 $('view').querySelectorAll(".child").forEach((tr) => {
551 var entry = tr.parentEntry;
552 if (!entry) return;
553 if (entry.name !== group.name) return;
554 toggleCssClass(tr, 'visible', show);
555 });
556 }
557
558 function showPopover(entry) {
559 var popover = $('popover');
560 popover.querySelector('td.name').innerHTML = entry.name;
561 popover.querySelector('td.page').innerHTML = entry.page.name;
562 setPopoverDetail(popover, entry, '');
563 popover.querySelector('table').className = "";
564 if (baselineVersion !== undefined) {
565 entry = baselineVersion.getEntry(entry);
566 if (entry === undefined) return;
567 setPopoverDetail(popover, entry, '.compare');
568 popover.querySelector('table').className = "compare";
569 }
570 }
571
572 function setPopoverDetail(popover, entry, prefix) {
573 popover.querySelector(prefix + '.version').innerHTML
574 = entry.page.version.name;
575 popover.querySelector(prefix + '.time').innerHTML
576 = ms(entry._time, false);
577 popover.querySelector(prefix + '.timeVariance').innerHTML
578 = percent(entry.timeVariancePercent, false);
579 popover.querySelector(prefix + '.percent').innerHTML
580 = percent(entry.timePercent, false);
581 popover.querySelector(prefix + '.percentVariance').innerHTML
582 = percent(entry.timePercentVariancePercent, false);
583 popover.querySelector(prefix + '.count').innerHTML
584 = count(entry._count, false);
585 popover.querySelector(prefix + '.countVariance').innerHTML
586 = percent(entry.timeVariancePercent, false);
587 popover.querySelector(prefix + '.timeImpact').innerHTML
588 = ms(entry.getTimeImpact(false), false);
589 popover.querySelector(prefix + '.timePercentImpact').innerHTML
590 = percent(entry.getTimeImpactVariancePercent(false), false);
591 }
592
593 // ========================================================================
594 // Helpers
595 function $(id) {
596 return document.getElementById(id)
597 }
598
599 function removeAllChildren(node) {
600 while (node.firstChild) {
601 node.removeChild(node.firstChild);
602 }
603 }
604
605 function selectOption(select, match) {
606 var options = select.options;
607 for (var i = 0; i < options.length; i++) {
608 if (match(i, options[i])) {
609 select.selectedIndex = i;
610 return;
611 }
612 }
613 }
614
615 function td(tr, content, className) {
616 var td = document.createElement("td");
617 td.innerHTML = content;
618 td.className = className
619 tr.appendChild(td);
620 return td
621 }
622
623 function nodeIndex(node) {
624 var children = node.parentNode.childNodes,
625 i = 0;
626 for (; i < children.length; i++) {
627 if (children[i] == node) {
628 return i;
629 }
630 }
631 return -1;
632 }
633
634 function toggleCssClass(node, cssClass, toggleState) {
635 var index = -1;
636 var classes;
637 if (node.className != undefined) {
638 classes = node.className.split(' ');
639 index = classes.indexOf(cssClass);
640 }
641 if (index == -1) {
642 if (toggleState === false) return;
643 node.className += ' ' + cssClass;
644 return;
645 }
646 if (toggleState === true) return;
647 classes.splice(index, 1);
648 node.className = classes.join(' ');
649 }
650
651 function diffSign(value, showDiff) {
652 if (value <= 0 || showDiff === false) return '';
653 if (baselineVersion == undefined) return '';
654 return '+';
655 }
656
657 function ms(time, showDiff) {
658 return diffSign(time, showDiff) + time.toFixed(1) + 'ms';
659 }
660
661 function count(time, showDiff) {
662 return diffSign(time, showDiff) + time.toFixed(0) + '#';
663 }
664
665 function percent(time, showDiff) {
666 return diffSign(time, showDiff) + time.toFixed(1) + '%';
667 }
668 // ========================================================================
669 // EventHandlers
670 function handleLoadFile() {
671 var files = document.getElementById("uploadInput").files;
672 var file = files[0];
673 var reader = new FileReader();
674
675 reader.onload = function(evt) {
676 versions = Versions.fromJSON(JSON.parse(this.result));
677 initialize()
678 showPage(versions.versions[0].pages[0]);
679 }
680 reader.readAsText(file);
681 }
682
683 function handleToggleGroup(event) {
684 var group = event.target.parentNode.parentNode.entry;
685 toggleGroup(selectedPage.get(group.name));
686 }
687
688 function handleSelectPage(select, event) {
689 var option = select.options[select.selectedIndex];
690 if (select.id == "select_0") {
691 showPage(option.page);
692 } else {
693 var columnIndex = select.id.split('_')[1];
694 showPageInColumn(option.page, columnIndex);
695 }
696 }
697
698 function handleSelectVersion(select, event) {
699 var option = select.options[select.selectedIndex];
700 var version = option.version;
701 if (select.id == "selectVersion_0") {
702 var page = version.get(selectedPage.name);
703 showPage(page);
704 } else {
705 var columnIndex = select.id.split('_')[1];
706 var pageSelect = $('select_' + columnIndex);
707 var page = pageSelect.options[select.selectedIndex].page;
708 page = version.get(page.name);
709 showPageInColumn(page, columnIndex);
710 }
711 }
712
713 function handleSelectDetailRow(table, event) {
714 if (event.target.tagName != 'TD') return;
715 var tr = event.target.parentNode;
716 if (tr.tagName != 'TR') return;
717 if (tr.entry === undefined) return;
718 selectEntry(tr.entry, true);
719 }
720
721 function handleSelectRow(table, event, fromDetail) {
722 if (event.target.tagName != 'TD') return;
723 var tr = event.target.parentNode;
724 if (tr.tagName != 'TR') return;
725 if (tr.entry === undefined) return;
726 selectEntry(tr.entry, false);
727 }
728
729 function handleSelectBaseline(select, event) {
730 var option = select.options[select.selectedIndex];
731 baselineVersion = option.version
732 showPage(selectedPage);
733 if (selectedEntry === undefined) return;
734 selectEntry(selectedEntry, true);
735 }
736
737 function handleUpdatePopover(event) {
738 var popover = $('popover');
739 popover.style.left = event.pageX + 'px';
740 popover.style.top = event.pageY + 'px';
741 popover.style.display = event.shiftKey ? 'block' : 'none';
742 var target = event.target;
743 while (target.entry === undefined) {
744 target = target.parentNode;
745 if (!target) return;
746 }
747 showPopover(target.entry);
748 }
749
750 // ========================================================================
751
752 class Versions {
753 constructor() {
754 this.versions = [];
755 }
756 add(version) {
757 this.versions.push(version)
758 }
759 pageVersions(name) {
760 var result = [];
761 this.versions.forEach((version) => {
762 var page = version.get(name);
763 if (page !== undefined) result.push(page);
764 });
765 return result;
766 }
767 get length() {
768 return this.versions.length
769 }
770 get(index) {
771 return this.versions[index]
772 };
773 forEach(f) {
774 this.versions.forEach(f);
775 }
776 sort() {
777 this.versions.sort((a, b) => {
778 if (a.name > b.name) return 1;
779 if (a.name < b.name) return -1;
780 return 0
781 })
782 }
783 }
784 Versions.fromJSON = function(json) {
785 var versions = new Versions();
786 for (var version in json) {
787 versions.add(Version.fromJSON(version, json[version]));
788 }
789 versions.sort();
790 return versions;
791 }
792
793 class Version {
794 constructor(name) {
795 this.name = name;
796 this.pages = []
797 }
798 add(page) {
799 this.pages.push(page);
800 }
801 indexOf(name) {
802 for (var i = 0; i < this.pages.length; i++) {
803 if (this.pages[i].name == name) return i;
804 }
805 return -1;
806 }
807 get(name) {
808 var index = this.indexOf(name);
809 if (0 <= index) return this.pages[index];
810 return undefined
811 }
812 get length() {
813 return this.versions.length
814 }
815 getEntry(entry) {
816 if (entry === undefined) return undefined;
817 var page = this.get(entry.page.name);
818 if (page === undefined) return undefined;
819 return page.get(entry.name);
820 }
821 forEachEntry(fun) {
822 this.pages.forEach((page) => {
823 page.forEach(fun);
824 });
825 }
826 allEntries() {
827 var map = new Map();
828 this.forEachEntry((group, entry) => {
829 if (!map.has(entry.name)) map.set(entry.name, entry);
830 });
831 return Array.from(map.values());
832 }
833 getTotalValue(name, property) {
834 if (name === undefined) name = this.pages[0].total.name;
835 var sum = 0;
836 this.pages.forEach((page) => {
837 var entry = page.get(name);
838 if (entry !== undefined) sum += entry[property];
839 });
840 return sum;
841 }
842 getTotalTime(name, showDiff) {
843 return this.getTotalValue(name, showDiff === false ? '_time' : 'time');
844 }
845 getTotalTimePercent(name, showDiff) {
846 if (baselineVersion === undefined) {
847 return this.getTotalValue(name, 'timePercent');
848 }
849 var baselineValue = baselineVersion.getTotalTime(name);
850 return this.getTotalValue(name, 'time') / baselineValue * 100;
851 }
852 getTotalTimeVariance(name, showDiff) {
853 // Calculate the overall error for a given entry name
854 var sum = 0;
855 this.pages.forEach((page) => {
856 var entry = page.get(name);
857 if (entry === undefined) return;
858 sum += entry.timeVariance * entry.timeVariance;
859 });
860 return Math.sqrt(sum);
861 }
862 getTotalTimeVariancePercent(name, showDiff) {
863 return this.getTotalTimeVariance(name, showDiff) /
864 this.getTotalTime(name, showDiff) * 100;
865 }
866 getTotalCount(name, showDiff) {
867 var property = showDiff === false ? '_count' : 'count';
868 return this.getTotalValue(name, property);
869 }
870 getPagesByPercentImpact(name) {
871 var sortedPages =
872 this.pages.filter((each) => {
873 return each.get(name) !== undefined
874 });
875 sortedPages.sort((a, b) => {
876 return b.get(name).timePercent - a.get(name).timePercent;
877 });
878 return sortedPages;
879 }
880 }
881 Version.fromJSON = function(name, data) {
882 var version = new Version(name);
883 for (var page in data) {
884 version.add(Page.fromJSON(version, page, data[page]));
885 }
886 return version;
887 }
888
889
890 class Page {
891 constructor(version, name) {
892 this.name = name;
893 this.total = new GroupedEntry('Total', /.*Total.*/);
894 this.unclassified = new UnclassifiedEntry(this)
895 this.groups = [
896 this.total,
897 new GroupedEntry('IC', /.*IC.*/),
898 new GroupedEntry('Optimize',
899 /StackGuard|.*Optimize.*|.*Deoptimize.*|Recompile.*/),
900 new GroupedEntry('Compile', /.*Compile.*|Parse.*/),
901 new GroupedEntry('Callback', /.*Callback$/),
902 new GroupedEntry('API', /.*API.*/),
903 new GroupedEntry('GC', /GC|AllocateInTargetSpace/),
904 new GroupedEntry('JavaScript', /JS_Execution/),
905 this.unclassified
906 ];
907 this.entryDict = new Map();
908 this.groups.forEach((entry) => {
909 entry.page = this;
910 this.entryDict.set(entry.name, entry);
911 });
912 this.version = version;
913 }
914 add(entry) {
915 entry.page = this;
916 this.entryDict.set(entry.name, entry);
917 var added = false;
918 this.groups.forEach((group) => {
919 if (!added) added = group.add(entry);
920 });
921 if (added) return;
922 this.unclassified.push(entry);
923 }
924 get(name) {
925 return this.entryDict.get(name)
926 }
927 getEntry(entry) {
928 if (entry === undefined) return undefined;
929 return this.get(entry.name);
930 }
931 get length() {
932 return this.versions.length
933 }
934 forEachSorted(referencePage, func) {
935 // Iterate over all the entries in the order they appear on the
936 // reference page.
937 referencePage.forEach((parent, referenceEntry) => {
938 var entry;
939 if (parent) parent = this.entryDict.get(parent.name);
940 if (referenceEntry) entry = this.entryDict.get(referenceEntry.name);
941 func(parent, entry, referenceEntry);
942 });
943 }
944 forEach(fun) {
945 this.forEachGroup((group) => {
946 fun(undefined, group);
947 group.forEach((entry) => {
948 fun(group, entry)
949 });
950 });
951 }
952 forEachGroup(fun) {
953 this.groups.forEach(fun)
954 }
955 sort() {
956 this.groups.sort((a, b) => {
957 return b.time - a.time;
958 });
959 this.groups.forEach((group) => {
960 group.sort()
961 });
962 }
963 }
964 Page.fromJSON = function(version, name, data) {
965 var page = new Page(version, name);
966 for (var i = 0; i < data.length; i++) {
967 page.add(Entry.fromJSON(i, data[data.length - i - 1]));
968 }
969 page.sort();
970 return page
971 }
972
973
974 class Entry {
975 constructor(position, name, time, timeVariance, timeVariancePercent,
976 count,
977 countVariance, countVariancePercent) {
978 this.position = position;
979 this.name = name;
980 this._time = time;
981 this._timeVariance = timeVariance;
982 this._timeVariancePercent = timeVariancePercent;
983 this._count = count;
984 this.countVariance = countVariance;
985 this.countVariancePercent = countVariancePercent;
986 this.page = undefined;
987 this.parent = undefined;
988 }
989 getCompareWithBaseline(value, property) {
990 if (baselineVersion == undefined) return value;
991 var baselineEntry = baselineVersion.getEntry(this);
992 if (!baselineEntry) return value;
993 if (baselineVersion === this.page.version) return value;
994 return value - baselineEntry[property];
995 }
996 cssClass() {
997 return ''
998 }
999 get time() {
1000 return this.getCompareWithBaseline(this._time, '_time');
1001 }
1002 get count() {
1003 return this.getCompareWithBaseline(this._count, '_count');
1004 }
1005 get timePercent() {
1006 var value = this._time / this.page.total._time * 100;
1007 if (baselineVersion == undefined) return value;
1008 var baselineEntry = baselineVersion.getEntry(this);
1009 if (!baselineEntry) return value;
1010 return (this._time - baselineEntry._time) /
1011 this.page.total._time * 100;
1012 }
1013 get timePercentVariancePercent() {
1014 // Get the absolute values for the percentages
1015 return this.timeVariance / this.page.total._time * 100;
1016 }
1017 getTimeImpact(showDiff) {
1018 return this.page.version.getTotalTime(this.name, showDiff);
1019 }
1020 getTimeImpactVariancePercent(showDiff) {
1021 return this.page.version
1022 .getTotalTimeVariancePercent(this.name, showDiff);
1023 }
1024 getTimePercentImpact(showDiff) {
1025 return this.page.version.getTotalTimePercent(this.name, showDiff);
1026 }
1027 getCountImpact(showDiff) {
1028 return this.page.version.getTotalCount(this.name, showDiff);
1029 }
1030 getPagesByPercentImpact() {
1031 return this.page.version.getPagesByPercentImpact(this.name);
1032 }
1033 get isGroup() {
1034 return false
1035 }
1036 get timeVariance() {
1037 return this._timeVariance
1038 }
1039 get timeVariancePercent() {
1040 return this._timeVariancePercent
1041 }
1042 }
1043 Entry.fromJSON = function(position, data) {
1044 return new Entry(position, ...data);
1045 }
1046
1047
1048 class GroupedEntry extends Entry {
1049 constructor(name, regexp) {
1050 super(0, 'Group-' + name, 0, 0, 0, 0, 0, 0);
1051 this.regexp = regexp;
1052 this.entries = [];
1053 }
1054 add(entry) {
1055 if (!entry.name.match(this.regexp)) return false;
1056 this._time += entry.time;
1057 this._count += entry.count;
1058 // TODO: sum up variance
1059 this.entries.push(entry);
1060 entry.parent = this;
1061 return true;
1062 }
1063 forEach(fun) {
1064 if (baselineVersion === undefined) {
1065 this.entries.forEach(fun);
1066 return;
1067 }
1068 // If we have a baslineVersion to compare against show also all entries
1069 // from the other group.
1070 var tmpEntries = baselineVersion.getEntry(this)
1071 .entries.filter((entry) => {
1072 return this.page.get(entry.name) == undefined
1073 });
1074
1075 // The compared entries are sorted by absolute impact.
1076 tmpEntries = tmpEntries.map((entry) => {
1077 var tmpEntry = new Entry(0, entry.name, 0, 0, 0, 0, 0, 0);
1078 tmpEntry.page = this.page;
1079 return tmpEntry;
1080 });
1081 tmpEntries = tmpEntries.concat(this.entries);
1082 tmpEntries.sort((a, b) => {
1083 return a.time - b.time
1084 });
1085 tmpEntries.forEach(fun);
1086 }
1087 sort() {
1088 this.entries.sort((a, b) => {
1089 return b.time - a.time;
1090 });
1091 }
1092 cssClass() {
1093 if (this.page.total == this) return 'total';
1094 return '';
1095 }
1096 get isGroup() {
1097 return true
1098 }
1099 getVarianceForProperty(property) {
1100 var sum = 0;
1101 this.entries.forEach((entry) => {
1102 sum += entry[property + 'Variance'] * entry[property +
1103 'Variance'];
1104 });
1105 return Math.sqrt(sum);
1106 }
1107 get timeVariancePercent() {
1108 if (this._time == 0) return 0;
1109 return this.getVarianceForProperty('time') / this._time * 100
1110 }
1111 get timeVariance() {
1112 return this.getVarianceForProperty('time')
1113 }
1114 }
1115
1116 class UnclassifiedEntry extends GroupedEntry {
1117 constructor(page) {
1118 super('Unclassified');
1119 this.page = page;
1120 this._time = undefined;
1121 this._count = undefined;
1122 }
1123 add(entry) {
1124 this.entries.push(entry);
1125 entry.parent = this;
1126 return true;
1127 }
1128 forEachPageGroup(fun) {
1129 this.page.forEachGroup((group) => {
1130 if (group == this) return;
1131 if (group == this.page.total) return;
1132 fun(group);
1133 });
1134 }
1135 get time() {
1136 if (this._time === undefined) {
1137 this._time = this.page.total._time;
1138 this.forEachPageGroup((group) => {
1139 this._time -= group._time;
1140 });
1141 }
1142 return this.getCompareWithBaseline(this._time, '_time');
1143 }
1144 get count() {
1145 if (this._count === undefined) {
1146 this._count = this.page.total._count;
1147 this.forEachPageGroup((group) => {
1148 this._count -= group._count;
1149 });
1150 }
1151 return this.getCompareWithBaseline(this._count, '_count');
1152 }
1153 }
1154 </script>
1155 </head>
1156
1157 <body onmousemove="handleUpdatePopover(event)">
1158 <h1>Runtime Stats Komparator</h1>
1159
1160 <div id="results">
1161 <div class="inline">
1162 <h2>Data</h2>
1163 <form name="fileForm">
1164 <p>
1165 <input id="uploadInput" type="file" name="files" onchange="handleLoadF ile();">
1166 </p>
1167 </form>
1168 </div>
1169 <div class="inline hidden">
1170 <h2>Result</h2>
1171 <div class="compareSelector">
1172 Compare against:&nbsp;<select id="baseline" onchange="handleSelectBaseli ne(this, event)"></select><br/>
1173 <span style="color: #060">Green</span> the selected version above perfor ms
1174 better on this measurement.
1175 </div>
1176 </div>
1177 <div id="view">
1178 </div>
1179
1180 <div id="detailView" class="hidden">
1181 <h2></h2>
1182 <div class="versionDetail inline">
1183 <h3><span></span></h3>
1184 <table class="versionDetailTable" onclick="handleSelectDetailRow(this, e vent);">
1185 <thead>
1186 <tr>
1187 <th class="version">Version&nbsp;</th>
1188 <th class="position">Pos.&nbsp;</th>
1189 <th class="value time">Time▴&nbsp;</th>
1190 <th class="value time">Percent&nbsp;</th>
1191 <th class="value count">Count&nbsp;</th>
1192 </tr>
1193 </thead>
1194 <tbody></tbody>
1195 </table>
1196 </div>
1197 <div class="pageDetail inline">
1198 <h3><span></span></h3>
1199 <table class="pageDetailTable" onclick="handleSelectDetailRow(this, even t);">
1200 <thead>
1201 <tr>
1202 <th class="page">Page&nbsp;</th>
1203 <th class="position">Pos.&nbsp;</th>
1204 <th class="value time">Time&nbsp;</th>
1205 <th class="value time">Percent▾&nbsp;</th>
1206 <th class="value count">Count&nbsp;</th>
1207 </tr>
1208 </thead>
1209 <tfoot>
1210 <tr>
1211 <td class="page">Total:</td>
1212 <td class="position"></td>
1213 <td class="value time"></td>
1214 <td class="value time"></td>
1215 <td class="value count"></td>
1216 </tr>
1217 </tfoot>
1218 <tbody></tbody>
1219 </table>
1220 </div>
1221 <div class="impactView inline">
1222 <h3>Impact list for <span></span></h3>
1223 <table class="pageDetailTable" onclick="handleSelectDetailRow(this, even t);">
1224 <thead>
1225 <tr>
1226 <th class="page">Name&nbsp;</th>
1227 <th class="value time">Time&nbsp;</th>
1228 <th class="value time">Percent▾&nbsp;</th>
1229 <th class="">Top Pages</th>
1230 </tr>
1231 </thead>
1232 <tbody></tbody>
1233 </table>
1234 </div>
1235 </div>
1236
1237 <div id="column" class="column">
1238 <div class="header">
1239 <select class="version" onchange="handleSelectVersion(this, event);"></s elect>
1240 <select class="pageVersion" onchange="handleSelectPage(this, event);"></ select>
1241 </div>
1242 <table class="list" onclick="handleSelectRow(this, event);">
1243 <thead>
1244 <tr>
1245 <th class="position">Pos.&nbsp;</th>
1246 <th class="name">Name&nbsp;</th>
1247 <th class="value time">Time&nbsp;</th>
1248 <th class="value time">Percent&nbsp;</th>
1249 <th class="value count">Count&nbsp;</th>
1250 </tr>
1251 </thead>
1252 <tbody></tbody>
1253 </table>
1254 </div>
1255 </div>
1256
1257 <div class="inline">
1258 <h2>Usage</h2>
1259 <ol>
1260 <li>Build chrome with the <a href="https://codereview.chromium.org/1923893 002">extended runtime callstats</a>.</li>
1261 <li>Run callstats.py with a web-page-replay archive:
1262 <pre>./callstats.py run \
1263 --replay-bin=$CHROME_SRC/third_party/webpagereplay/replay.py \
1264 --replay-wpr=top25.wpr \
1265 --js-flags="" \
1266 --with-chrome=$CHROME_SRC/out/Release/chrome \
1267 --sites-file=top25.json</pre>
1268 </li>
1269 <li>Move results file to a subdirectory: <code>mkdir $VERSION; mv *.txt $V ERSION</code></li>
1270 <li>Repeat from step 1 with a different configuration (e.g. <code>--js-fla gs="--nolazy"</code>).</li>
1271 <li>Create the final results file: <code>./callstats.py json $VERSION1 $VE RSION2 > result.json</code></li>
1272 <li>Use <code>results.json</code> on this site.</code>
1273 </ol>
1274 </div>
1275
1276 <div id="popover">
1277 <div class="popoverArrow"></div>
1278 <table>
1279 <tr>
1280 <td class="name" colspan="6"></td>
1281 </tr>
1282 <tr>
1283 <td>Page:</td>
1284 <td class="page name" colspan="6"></td>
1285 </tr>
1286 <tr>
1287 <td>Version:</td>
1288 <td class="version name" colspan="3"></td>
1289 <td class="compare version name" colspan="3"></td>
1290 </tr>
1291 <tr>
1292 <td>Time:</td>
1293 <td class="time"></td><td>±</td><td class="timeVariance"></td>
1294 <td class="compare time"></td><td class="compare"> ± </td><td class="com pare timeVariance"></td>
1295 </tr>
1296 <tr>
1297 <td>Percent:</td>
1298 <td class="percent"></td><td>±</td><td class="percentVariance"></td>
1299 <td class="compare percent"></td><td class="compare"> ± </td><td class=" compare percentVariance"></td>
1300 </tr>
1301 <tr>
1302 <td>Count:</td>
1303 <td class="count"></td><td>±</td><td class="countVariance"></td>
1304 <td class="compare count"></td><td class="compare"> ± </td><td class="co mpare countVariance"></td>
1305 </tr>
1306 <tr>
1307 <td>Overall Impact:</td>
1308 <td class="timeImpact"></td><td>±</td><td class="timePercentImpact"></td >
1309 <td class="compare timeImpact"></td><td class="compare"> ± </td><td clas s="compare timePercentImpact"></td>
1310 </tr>
1311 </table>
1312 </div>
1313
1314 </body>
1315
1316 </html>
OLDNEW
« no previous file with comments | « no previous file | tools/ic-explorer.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698