Commit 2afb2dcd authored by Zeynep Cankara's avatar Zeynep Cankara Committed by Commit Bot

[tools][system-analyzer] Add stats table to timeline-tracks

This CL adds a table to the right side of the each
timeline-tracks to display statistics about the log
events. Double clicking on an event type notifies other
panels about the selected log events with the selected type.

Bug: v8:10644

Change-Id: Iae523d46da4f0b6a007b02a2beac23d9c48aca02
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2353457
Commit-Queue: Zeynep Cankara <zcankara@google.com>
Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69498}
parent db15c5e3
...@@ -26,9 +26,9 @@ function defineCustomElement(path, generator) { ...@@ -26,9 +26,9 @@ function defineCustomElement(path, generator) {
let name = path.substring(path.lastIndexOf("/") + 1, path.length); let name = path.substring(path.lastIndexOf("/") + 1, path.length);
path = path + '-template.html'; path = path + '-template.html';
fetch(path) fetch(path)
.then(stream => stream.text()) .then(stream => stream.text())
.then( .then(
templateText => customElements.define(name, generator(templateText))); templateText => customElements.define(name, generator(templateText)));
} }
// DOM Helpers // DOM Helpers
...@@ -104,7 +104,7 @@ class CSSColor { ...@@ -104,7 +104,7 @@ class CSSColor {
} }
function transitionTypeToColor(type) { function typeToColor(type) {
switch (type) { switch (type) {
case 'new': case 'new':
return CSSColor.green; return CSSColor.green;
...@@ -149,7 +149,7 @@ function div(classes) { ...@@ -149,7 +149,7 @@ function div(classes) {
class V8CustomElement extends HTMLElement { class V8CustomElement extends HTMLElement {
constructor(templateText) { constructor(templateText) {
super(); super();
const shadowRoot = this.attachShadow({mode: 'open'}); const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.innerHTML = templateText; shadowRoot.innerHTML = templateText;
} }
$(id) { $(id) {
...@@ -160,7 +160,7 @@ class V8CustomElement extends HTMLElement { ...@@ -160,7 +160,7 @@ class V8CustomElement extends HTMLElement {
return this.shadowRoot.querySelectorAll(query); return this.shadowRoot.querySelectorAll(query);
} }
div(classes) {return div(classes)} div(classes) { return div(classes) }
table(className) { table(className) {
let node = document.createElement('table') let node = document.createElement('table')
...@@ -178,12 +178,14 @@ class V8CustomElement extends HTMLElement { ...@@ -178,12 +178,14 @@ class V8CustomElement extends HTMLElement {
return node; return node;
} }
tr(){ tr() {
return document.createElement('tr'); return document.createElement('tr');
} }
removeAllChildren(node) { return removeAllChildren(node); } removeAllChildren(node) { return removeAllChildren(node); }
} }
export {defineCustomElement, V8CustomElement, removeAllChildren, export {
$, div, transitionTypeToColor, CSSColor}; defineCustomElement, V8CustomElement, removeAllChildren,
$, div, typeToColor, CSSColor
};
...@@ -2,19 +2,19 @@ ...@@ -2,19 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import {transitionTypeToColor} from './helper.mjs'; import { typeToColor } from './helper.mjs';
import {Timeline} from './timeline.mjs'; import { Timeline } from './timeline.mjs';
// =========================================================================== // ===========================================================================
import {Event} from './event.mjs'; import { Event } from './event.mjs';
const kChunkHeight = 250; const kChunkHeight = 250;
const kChunkWidth = 10; const kChunkWidth = 10;
function define(prototype, name, fn) { function define(prototype, name, fn) {
Object.defineProperty(prototype, name, {value: fn, enumerable: false}); Object.defineProperty(prototype, name, { value: fn, enumerable: false });
} }
define(Array.prototype, 'max', function(fn) { define(Array.prototype, 'max', function (fn) {
if (this.length === 0) return undefined; if (this.length === 0) return undefined;
if (fn === undefined) fn = (each) => each; if (fn === undefined) fn = (each) => each;
let max = fn(this[0]); let max = fn(this[0]);
...@@ -23,10 +23,10 @@ define(Array.prototype, 'max', function(fn) { ...@@ -23,10 +23,10 @@ define(Array.prototype, 'max', function(fn) {
} }
return max; return max;
}) })
define(Array.prototype, 'first', function() { define(Array.prototype, 'first', function () {
return this[0] return this[0]
}); });
define(Array.prototype, 'last', function() { define(Array.prototype, 'last', function () {
return this[this.length - 1] return this[this.length - 1]
}); });
// =========================================================================== // ===========================================================================
...@@ -57,12 +57,12 @@ class MapProcessor extends LogReader { ...@@ -57,12 +57,12 @@ class MapProcessor extends LogReader {
'code-move': { 'code-move': {
parsers: [parseInt, parseInt], parsers: [parseInt, parseInt],
'sfi-move': 'sfi-move':
{parsers: [parseInt, parseInt], processor: this.processCodeMove}, { parsers: [parseInt, parseInt], processor: this.processCodeMove },
'code-delete': {parsers: [parseInt], processor: this.processCodeDelete}, 'code-delete': { parsers: [parseInt], processor: this.processCodeDelete },
processor: this.processFunctionMove processor: this.processFunctionMove
}, },
'map-create': 'map-create':
{parsers: [parseInt, parseString], processor: this.processMapCreate}, { parsers: [parseInt, parseString], processor: this.processMapCreate },
'map': { 'map': {
parsers: [ parsers: [
parseString, parseInt, parseString, parseString, parseInt, parseInt, parseString, parseInt, parseString, parseString, parseInt, parseInt,
...@@ -116,8 +116,8 @@ class MapProcessor extends LogReader { ...@@ -116,8 +116,8 @@ class MapProcessor extends LogReader {
} }
} catch (e) { } catch (e) {
console.error( console.error(
'Error occurred during parsing line ' + i + 'Error occurred during parsing line ' + i +
', trying to continue: ' + e); ', trying to continue: ' + e);
} }
return this.finalize(); return this.finalize();
} }
...@@ -165,19 +165,19 @@ class MapProcessor extends LogReader { ...@@ -165,19 +165,19 @@ class MapProcessor extends LogReader {
let funcAddr = parseInt(maybe_func[0]); let funcAddr = parseInt(maybe_func[0]);
let state = this.parseState(maybe_func[1]); let state = this.parseState(maybe_func[1]);
this.#profile.addFuncCode( this.#profile.addFuncCode(
type, name, timestamp, start, size, funcAddr, state); type, name, timestamp, start, size, funcAddr, state);
} else { } else {
this.#profile.addCode(type, name, timestamp, start, size); this.#profile.addCode(type, name, timestamp, start, size);
} }
} }
processV8Version(majorVersion, minorVersion){ processV8Version(majorVersion, minorVersion) {
if( if (
(majorVersion == this.MAJOR_VERSION && minorVersion <= this.MINOR_VERSION) (majorVersion == this.MAJOR_VERSION && minorVersion <= this.MINOR_VERSION)
|| (majorVersion < this.MAJOR_VERSION)){ || (majorVersion < this.MAJOR_VERSION)) {
window.alert( window.alert(
`Unsupported version ${majorVersion}.${minorVersion}. \n` + `Unsupported version ${majorVersion}.${minorVersion}. \n` +
`Please use the matching tool for given the V8 version.`); `Please use the matching tool for given the V8 version.`);
} }
} }
...@@ -196,9 +196,9 @@ class MapProcessor extends LogReader { ...@@ -196,9 +196,9 @@ class MapProcessor extends LogReader {
formatPC(pc, line, column) { formatPC(pc, line, column) {
let entry = this.#profile.findEntry(pc); let entry = this.#profile.findEntry(pc);
if (!entry) return '<unknown>' if (!entry) return '<unknown>'
if (entry.type === 'Builtin') { if (entry.type === 'Builtin') {
return entry.name; return entry.name;
} }
let name = entry.func.getName(); let name = entry.func.getName();
let array = this.#formatPCRegexp.exec(name); let array = this.#formatPCRegexp.exec(name);
if (array === null) { if (array === null) {
...@@ -262,15 +262,15 @@ class MapLogEvent extends Event { ...@@ -262,15 +262,15 @@ class MapLogEvent extends Event {
// TODO(zcankara): Change this to private class field. // TODO(zcankara): Change this to private class field.
#isDeprecated = false; #isDeprecated = false;
deprecatedTargets = null; deprecatedTargets = null;
leftId= 0; leftId = 0;
rightId = 0; rightId = 0;
filePosition = ''; filePosition = '';
id = -1; id = -1;
constructor(id, time) { constructor(id, time) {
if (!time) throw new Error('Invalid time'); if (!time) throw new Error('Invalid time');
super(id, time); super(id, time);
MapLogEvent.set(id, this); MapLogEvent.set(id, this);
this.id = id; this.id = id;
} }
finalizeRootMap(id) { finalizeRootMap(id) {
...@@ -407,7 +407,7 @@ class Edge { ...@@ -407,7 +407,7 @@ class Edge {
} }
getColor() { getColor() {
return transitionTypeToColor(this.type); return typeToColor(this.type);
} }
finishSetup() { finishSetup() {
...@@ -509,7 +509,7 @@ class Edge { ...@@ -509,7 +509,7 @@ class Edge {
return this.type + ' ' + this.symbol() + this.name; return this.type + ' ' + this.symbol() + this.name;
} }
return this.type + ' ' + (this.reason ? this.reason : '') + ' ' + return this.type + ' ' + (this.reason ? this.reason : '') + ' ' +
(this.name ? this.name : '') (this.name ? this.name : '')
} }
} }
...@@ -519,7 +519,7 @@ class ArgumentsProcessor extends BaseArgumentsProcessor { ...@@ -519,7 +519,7 @@ class ArgumentsProcessor extends BaseArgumentsProcessor {
getArgsDispatch() { getArgsDispatch() {
return { return {
'--range': '--range':
['range', 'auto,auto', 'Specify the range limit as [start],[end]'], ['range', 'auto,auto', 'Specify the range limit as [start],[end]'],
'--source-map': [ '--source-map': [
'sourceMap', null, 'sourceMap', null,
'Specify the source map that should be used for output' 'Specify the source map that should be used for output'
...@@ -535,4 +535,4 @@ class ArgumentsProcessor extends BaseArgumentsProcessor { ...@@ -535,4 +535,4 @@ class ArgumentsProcessor extends BaseArgumentsProcessor {
} }
} }
export { MapProcessor, MapLogEvent, kChunkWidth, kChunkHeight}; export { MapProcessor, MapLogEvent, kChunkWidth, kChunkHeight };
...@@ -65,27 +65,41 @@ found in the LICENSE file. --> ...@@ -65,27 +65,41 @@ found in the LICENSE file. -->
opacity: 0.5; opacity: 0.5;
} }
#timelineLegend { #legend {
position: relative; position: relative;
float: right; float: right;
text-align: center; text-align: center;
width: 100%;
max-width: 280px;
padding-left: 20px;
padding-top: 10px;
} }
.timeline { th,
background-color: var(--timeline-background-color); td {
width: 200px;
text-align: center;
padding: 5px;
} }
#timelineLegendContent { .timeline {
float: right; background-color: var(--timeline-background-color);
padding: 20px;
width: 200px;
} }
</style> </style>
<div class="timeline"> <div class="timeline">
<div id="timelineLegend"> <div id="legend">
<p>Category</p> <table>
<dl id="timelineLegendContent" style="float:right; padding:20px"> <thead>
</dl> <tr>
<td>Color</td>
<td>Type</td>
<td>Count</td>
<td>Percent</td>
</tr>
</thead>
<tbody id="legendContent">
</tbody>
</table>
</div> </div>
<div id="timeline"> <div id="timeline">
<div id="timelineLabel">Frequency</div> <div id="timelineLabel">Frequency</div>
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import { import {
defineCustomElement, V8CustomElement, defineCustomElement, V8CustomElement,
transitionTypeToColor, CSSColor typeToColor, CSSColor
} from '../helper.mjs'; } from '../helper.mjs';
import { kChunkWidth, kChunkHeight } from '../map-processor.mjs'; import { kChunkWidth, kChunkHeight } from '../map-processor.mjs';
import { SelectionEvent, FocusEvent, SelectTimeEvent } from '../events.mjs'; import { SelectionEvent, FocusEvent, SelectTimeEvent } from '../events.mjs';
...@@ -41,15 +41,18 @@ defineCustomElement('./timeline/timeline-track', (templateText) => ...@@ -41,15 +41,18 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
return this.$('#timeline'); return this.$('#timeline');
} }
get timelineLegendContent() { get timelineLegend() {
return this.$('#timelineLegendContent'); return this.$('#legend');
} }
get timelineLegendContent() {
return this.$('#legendContent');
}
set data(value) { set data(value) {
this.#timeline = value; this.#timeline = value;
this.updateChunks(); this.updateChunks();
this.updateTimeline(); this.updateTimeline();
this.updateStats(); this.updateLegend();
} }
get data() { get data() {
...@@ -81,42 +84,58 @@ defineCustomElement('./timeline/timeline-track', (templateText) => ...@@ -81,42 +84,58 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
set scrollLeft(offset) { set scrollLeft(offset) {
this.timeline.scrollLeft = offset; this.timeline.scrollLeft = offset;
} }
//TODO(zcankara) Carry to the Model, nothing UI related
updateStats() { updateLegend() {
let unique = new Map(); const uniqueTypes = new Map();
for (const entry of this.data.all) { for (const entry of this.data.all) {
if (!unique.has(entry.type)) { if (!uniqueTypes.has(entry.type)) {
unique.set(entry.type, [entry]); uniqueTypes.set(entry.type, [entry]);
} else { } else {
unique.get(entry.type).push(entry); uniqueTypes.get(entry.type).push(entry);
} }
} }
this.renderStatsWindow(unique); this.renderLegend(uniqueTypes);
} }
renderStatsWindow(unique) { renderLegend(uniqueTypes) {
let timelineLegend = this.timelineLegend;
let timelineLegendContent = this.timelineLegendContent; let timelineLegendContent = this.timelineLegendContent;
this.removeAllChildren(timelineLegendContent); this.removeAllChildren(timelineLegendContent);
let fragment = document.createDocumentFragment(); let row = this.tr();
row.entries = this.data.all;
row.addEventListener('dblclick', e => this.handleEntryTypeDblClick(e));
row.appendChild(this.td(""));
let td = this.td("All");
row.appendChild(td);
row.appendChild(this.td(this.data.all.length));
row.appendChild(this.td("100%"));
timelineLegendContent.appendChild(row);
let colorIterator = 0; let colorIterator = 0;
unique.forEach((entries, type) => { uniqueTypes.forEach((entries, type) => {
let dt = document.createElement("dt"); let row = this.tr();
dt.innerHTML = entries.length; row.entries = entries;
dt.style.backgroundColor = transitionTypeToColor(type); row.addEventListener('dblclick', e => this.handleEntryTypeDblClick(e));
dt.style.color = CSSColor.surfaceColor; let color = typeToColor(type);
fragment.appendChild(dt); if (color !== null) {
let dd = document.createElement("dd"); let div = this.div(["colorbox"]);
dd.innerHTML = type; div.style.backgroundColor = color;
dd.entries = entries; row.appendChild(this.td(div));
dd.addEventListener('dblclick', e => this.handleEntryTypeDblClick(e)); } else {
fragment.appendChild(dd); row.appendChild(this.td(""));
}
let td = this.td(type);
row.appendChild(td);
row.appendChild(this.td(entries.length));
let percent = (entries.length / this.data.all.length) * 100;
row.appendChild(this.td(percent.toFixed(1) + "%"));
timelineLegendContent.appendChild(row);
colorIterator += 1; colorIterator += 1;
}); });
timelineLegendContent.appendChild(fragment); timelineLegend.appendChild(timelineLegendContent);
} }
handleEntryTypeDblClick(e) { handleEntryTypeDblClick(e) {
this.dispatchEvent(new SelectionEvent(e.target.entries)); this.dispatchEvent(new SelectionEvent(e.target.parentNode.entries));
} }
timelineIndicatorMove(offset) { timelineIndicatorMove(offset) {
...@@ -179,14 +198,14 @@ defineCustomElement('./timeline/timeline-track', (templateText) => ...@@ -179,14 +198,14 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
let type, count; let type, count;
if (true) { if (true) {
chunk.getBreakdown(map => map.type).forEach(([type, count]) => { chunk.getBreakdown(map => map.type).forEach(([type, count]) => {
ctx.fillStyle = transitionTypeToColor(type); ctx.fillStyle = typeToColor(type);
let height = count / total * kHeight; let height = count / total * kHeight;
ctx.fillRect(0, y, kWidth, y + height); ctx.fillRect(0, y, kWidth, y + height);
y += height; y += height;
}); });
} else { } else {
chunk.items.forEach(map => { chunk.items.forEach(map => {
ctx.fillStyle = transitionTypeToColor(map.type); ctx.fillStyle = typeToColor(map.type);
let y = chunk.yOffset(map); let y = chunk.yOffset(map);
ctx.fillRect(0, y, kWidth, y + 1); ctx.fillRect(0, y, kWidth, y + 1);
}); });
...@@ -313,7 +332,7 @@ defineCustomElement('./timeline/timeline-track', (templateText) => ...@@ -313,7 +332,7 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
} }
setEdgeStyle(edge, ctx) { setEdgeStyle(edge, ctx) {
let color = transitionTypeToColor(edge.type); let color = typeToColor(edge.type);
ctx.strokeStyle = color; ctx.strokeStyle = color;
ctx.fillStyle = color; ctx.fillStyle = color;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment