[tools] Move system-analyzer view files to separate directory

- introduce view specific helper.mjs module
- clean up some imports

Bug: v8:10644
Change-Id: I0497c1a962c90f61f2beca667aca4a3f53a11e59
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2545705
Reviewed-by: Sathya Gunasekaran  <gsathya@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71269}
This commit is contained in:
Camillo Bruni 2020-11-18 16:51:07 +01:00 committed by Commit Bot
parent 993f1db9a7
commit 95eeed52e4
22 changed files with 302 additions and 295 deletions

View File

@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const KB = 1024;
const MB = KB * KB;
const GB = MB * KB;
const kMillis2Seconds = 1 / 1000;
export const KB = 1024;
export const MB = KB * KB;
export const GB = MB * KB;
export const kMillis2Seconds = 1 / 1000;
function formatBytes(bytes) {
export function formatBytes(bytes) {
const units = ['B', 'KiB', 'MiB', 'GiB'];
const divisor = 1024;
let index = 0;
@ -18,244 +18,10 @@ function formatBytes(bytes) {
return bytes.toFixed(2) + units[index];
}
function formatSeconds(millis) {
export function formatSeconds(millis) {
return (millis * kMillis2Seconds).toFixed(2) + 's';
}
class CSSColor {
static _cache = new Map();
static get(name) {
let color = this._cache.get(name);
if (color !== undefined) return color;
const style = getComputedStyle(document.body);
color = style.getPropertyValue(`--${name}`);
if (color === undefined) {
throw new Error(`CSS color does not exist: ${name}`);
}
this._cache.set(name, color);
return color;
}
static reset() {
this._cache.clear();
}
static get backgroundColor() {
return this.get('background-color');
}
static get surfaceColor() {
return this.get('surface-color');
}
static get primaryColor() {
return this.get('primary-color');
}
static get secondaryColor() {
return this.get('secondary-color');
}
static get onSurfaceColor() {
return this.get('on-surface-color');
}
static get onBackgroundColor() {
return this.get('on-background-color');
}
static get onPrimaryColor() {
return this.get('on-primary-color');
}
static get onSecondaryColor() {
return this.get('on-secondary-color');
}
static get defaultColor() {
return this.get('default-color');
}
static get errorColor() {
return this.get('error-color');
}
static get mapBackgroundColor() {
return this.get('map-background-color');
}
static get timelineBackgroundColor() {
return this.get('timeline-background-color');
}
static get red() {
return this.get('red');
}
static get green() {
return this.get('green');
}
static get yellow() {
return this.get('yellow');
}
static get blue() {
return this.get('blue');
}
static get orange() {
return this.get('orange');
}
static get violet() {
return this.get('violet');
}
}
function typeToColor(type) {
switch (type) {
case 'new':
return CSSColor.green;
case 'Normalize':
return CSSColor.violet;
case 'SlowToFast':
return CSSColor.orange;
case 'InitialMap':
return CSSColor.yellow;
case 'Transition':
return CSSColor.primaryColor;
case 'ReplaceDescriptors':
return CSSColor.red;
case 'LoadGlobalIC':
return CSSColor.green;
case 'LoadIC':
return CSSColor.primaryColor;
case 'StoreInArrayLiteralIC':
return CSSColor.violet;
case 'StoreGlobalIC':
return CSSColor.blue;
case 'StoreIC':
return CSSColor.orange;
case 'KeyedLoadIC':
return CSSColor.red;
case 'KeyedStoreIC':
return CSSColor.yellow;
}
return CSSColor.secondaryColor;
}
class DOM {
static div(classes) {
const node = document.createElement('div');
if (classes !== void 0) {
if (typeof classes === 'string') {
node.className = classes;
} else {
classes.forEach(cls => node.classList.add(cls));
}
}
return node;
}
static table(className) {
const node = document.createElement('table');
if (className) node.className = className;
return node;
}
static td(textOrNode, className) {
const node = document.createElement('td');
if (typeof textOrNode === 'object') {
node.appendChild(textOrNode);
} else if (textOrNode) {
node.innerText = textOrNode;
}
if (className) node.className = className;
return node;
}
static tr(className) {
const node = document.createElement('tr');
if (className) node.className = className;
return node;
}
static text(string) {
return document.createTextNode(string);
}
static removeAllChildren(node) {
let range = document.createRange();
range.selectNodeContents(node);
range.deleteContents();
}
static defineCustomElement(path, generator) {
let name = path.substring(path.lastIndexOf('/') + 1, path.length);
path = path + '-template.html';
fetch(path)
.then(stream => stream.text())
.then(
templateText =>
customElements.define(name, generator(templateText)));
}
}
function $(id) {
return document.querySelector(id)
}
class V8CustomElement extends HTMLElement {
_updateTimeoutId;
_updateCallback = this._update.bind(this);
constructor(templateText) {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this._updateCallback = this._update.bind(this);
}
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
update() {
// Use timeout tasks to asynchronously update the UI without blocking.
clearTimeout(this._updateTimeoutId);
const kDelayMs = 5;
this._updateTimeoutId = setTimeout(this._updateCallback, kDelayMs);
}
_update() {
throw Error('Subclass responsibility');
}
}
class LazyTable {
constructor(table, rowData, rowElementCreator) {
this._table = table;
this._rowData = rowData;
this._rowElementCreator = rowElementCreator;
const tbody = table.querySelector('tbody');
table.replaceChild(document.createElement('tbody'), tbody);
table.querySelector('tfoot td').onclick = (e) => this._addMoreRows();
this._addMoreRows();
}
_nextRowDataSlice() {
return this._rowData.splice(0, 100);
}
_addMoreRows() {
const fragment = new DocumentFragment();
for (let row of this._nextRowDataSlice()) {
const tr = this._rowElementCreator(row);
fragment.appendChild(tr);
}
this._table.querySelector('tbody').appendChild(fragment);
}
}
function delay(time) {
export function delay(time) {
return new Promise(resolver => setTimeout(resolver, time));
}
export {
DOM,
$,
V8CustomElement,
formatBytes,
typeToColor,
CSSColor,
delay,
LazyTable,
};
}

View File

@ -8,12 +8,13 @@ found in the LICENSE file. -->
<title>Indicium</title>
<!-- <link rel="icon" type="image/png" href="/images/favicon.png"/> -->
<link rel="modulepreload" href="./log-file-reader.mjs" >
<link rel="modulepreload" href="./helper.mjs" >
<link rel="preload" href="./log-file-reader-template.html" as="fetch" crossorigin="anonymous">
<link rel="modulepreload" href="./view/log-file-reader.mjs" >
<link rel="modulepreload" href="./view/helper.mjs" >
<link rel="preload" href="./view/log-file-reader-template.html" as="fetch" crossorigin="anonymous">
<script type="module">
// Force instatiating the log-reader before anything else.
import "./log-file-reader.mjs";
import "./view/log-file-reader.mjs";
// Delay loading of the main App
(async function() {
let module = await import('./index.mjs');

View File

@ -6,11 +6,10 @@ import {SourcePosition} from '../profile.mjs';
import {State} from './app-model.mjs';
import {FocusEvent, SelectionEvent, SelectTimeEvent} from './events.mjs';
import {CSSColor} from './helper.mjs';
import {$} from './helper.mjs';
import {IcLogEntry} from './log/ic.mjs';
import {MapLogEntry} from './log/map.mjs';
import {Processor} from './processor.mjs';
import {$, CSSColor} from './view/helper.mjs';
class App {
_state;
@ -43,11 +42,11 @@ class App {
async runAsyncInitialize() {
await Promise.all([
import('./ic-panel.mjs'),
import('./timeline-panel.mjs'),
import('./stats-panel.mjs'),
import('./map-panel.mjs'),
import('./source-panel.mjs'),
import('./view/ic-panel.mjs'),
import('./view/timeline-panel.mjs'),
import('./view/stats-panel.mjs'),
import('./view/map-panel.mjs'),
import('./view/source-panel.mjs'),
]);
document.addEventListener(
'keydown', e => this._navigation?.handleKeyDown(e));

View File

@ -0,0 +1,216 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
class CSSColor {
static _cache = new Map();
static get(name) {
let color = this._cache.get(name);
if (color !== undefined) return color;
const style = getComputedStyle(document.body);
color = style.getPropertyValue(`--${name}`);
if (color === undefined) {
throw new Error(`CSS color does not exist: ${name}`);
}
this._cache.set(name, color);
return color;
}
static reset() {
this._cache.clear();
}
static get backgroundColor() {
return this.get('background-color');
}
static get surfaceColor() {
return this.get('surface-color');
}
static get primaryColor() {
return this.get('primary-color');
}
static get secondaryColor() {
return this.get('secondary-color');
}
static get onSurfaceColor() {
return this.get('on-surface-color');
}
static get onBackgroundColor() {
return this.get('on-background-color');
}
static get onPrimaryColor() {
return this.get('on-primary-color');
}
static get onSecondaryColor() {
return this.get('on-secondary-color');
}
static get defaultColor() {
return this.get('default-color');
}
static get errorColor() {
return this.get('error-color');
}
static get mapBackgroundColor() {
return this.get('map-background-color');
}
static get timelineBackgroundColor() {
return this.get('timeline-background-color');
}
static get red() {
return this.get('red');
}
static get green() {
return this.get('green');
}
static get yellow() {
return this.get('yellow');
}
static get blue() {
return this.get('blue');
}
static get orange() {
return this.get('orange');
}
static get violet() {
return this.get('violet');
}
}
const kColors = [
CSSColor.green,
CSSColor.violet,
CSSColor.orange,
CSSColor.yellow,
CSSColor.primaryColor,
CSSColor.red,
CSSColor.blue,
CSSColor.yellow,
CSSColor.secondaryColor,
];
class DOM {
static div(classes) {
const node = document.createElement('div');
if (classes !== void 0) {
if (typeof classes === 'string') {
node.className = classes;
} else {
classes.forEach(cls => node.classList.add(cls));
}
}
return node;
}
static table(className) {
const node = document.createElement('table');
if (className) node.className = className;
return node;
}
static td(textOrNode, className) {
const node = document.createElement('td');
if (typeof textOrNode === 'object') {
node.appendChild(textOrNode);
} else if (textOrNode) {
node.innerText = textOrNode;
}
if (className) node.className = className;
return node;
}
static tr(className) {
const node = document.createElement('tr');
if (className) node.className = className;
return node;
}
static text(string) {
return document.createTextNode(string);
}
static removeAllChildren(node) {
let range = document.createRange();
range.selectNodeContents(node);
range.deleteContents();
}
static defineCustomElement(path, generator) {
let name = path.substring(path.lastIndexOf('/') + 1, path.length);
path = path + '-template.html';
fetch(path)
.then(stream => stream.text())
.then(
templateText =>
customElements.define(name, generator(templateText)));
}
}
function $(id) {
return document.querySelector(id)
}
class V8CustomElement extends HTMLElement {
_updateTimeoutId;
_updateCallback = this._update.bind(this);
constructor(templateText) {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this._updateCallback = this._update.bind(this);
}
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
update() {
// Use timeout tasks to asynchronously update the UI without blocking.
clearTimeout(this._updateTimeoutId);
const kDelayMs = 5;
this._updateTimeoutId = setTimeout(this._updateCallback, kDelayMs);
}
_update() {
throw Error('Subclass responsibility');
}
}
class LazyTable {
constructor(table, rowData, rowElementCreator) {
this._table = table;
this._rowData = rowData;
this._rowElementCreator = rowElementCreator;
const tbody = table.querySelector('tbody');
table.replaceChild(document.createElement('tbody'), tbody);
table.querySelector('tfoot td').onclick = (e) => this._addMoreRows();
this._addMoreRows();
}
_nextRowDataSlice() {
return this._rowData.splice(0, 100);
}
_addMoreRows() {
const fragment = new DocumentFragment();
for (let row of this._nextRowDataSlice()) {
const tr = this._rowElementCreator(row);
fragment.appendChild(tr);
}
this._table.querySelector('tbody').appendChild(fragment);
}
}
export * from '../helper.mjs';
export {
DOM,
$,
kColors,
V8CustomElement,
CSSColor,
LazyTable,
};

View File

@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {FocusEvent, SelectionEvent, SelectTimeEvent} from './events.mjs';
import {delay, DOM, V8CustomElement} from './helper.mjs';
import {Group} from './ic-model.mjs';
import {IcLogEntry} from './log/ic.mjs';
import {MapLogEntry} from './log/map.mjs';
import {FocusEvent, SelectionEvent, SelectTimeEvent} from '../events.mjs';
import {Group} from '../ic-model.mjs';
import {IcLogEntry} from '../log/ic.mjs';
import {MapLogEntry} from '../log/map.mjs';
import {DOM, V8CustomElement} from './helper.mjs';
DOM.defineCustomElement(
'ic-panel', (templateText) => class ICPanel extends V8CustomElement {
'view/ic-panel', (templateText) => class ICPanel extends V8CustomElement {
_selectedLogEntries;
_timeline;

View File

@ -3,7 +3,7 @@
// found in the LICENSE file.
import {DOM, V8CustomElement} from './helper.mjs';
DOM.defineCustomElement('log-file-reader',
DOM.defineCustomElement('view/log-file-reader',
(templateText) =>
class LogFileReader extends V8CustomElement {
constructor() {

View File

@ -5,11 +5,12 @@ import './stats-panel.mjs';
import './map-panel/map-details.mjs';
import './map-panel/map-transitions.mjs';
import {FocusEvent} from './events.mjs';
import {DOM, V8CustomElement} from './helper.mjs';
import {MapLogEntry} from './log/map.mjs';
import {FocusEvent} from '../events.mjs';
import {MapLogEntry} from '../log/map.mjs';
DOM.defineCustomElement('map-panel',
import {DOM, V8CustomElement} from './helper.mjs';
DOM.defineCustomElement('view/map-panel',
(templateText) =>
class MapPanel extends V8CustomElement {
_map;

View File

@ -1,11 +1,11 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {FocusEvent} from '../events.mjs';
import {FocusEvent} from '../../events.mjs';
import {DOM, V8CustomElement} from '../helper.mjs';
DOM.defineCustomElement(
'./map-panel/map-details',
'./view/map-panel/map-details',
(templateText) => class MapDetails extends V8CustomElement {
_map;

View File

@ -1,11 +1,12 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {FocusEvent, SelectionEvent} from '../events.mjs';
import {DOM, typeToColor, V8CustomElement} from '../helper.mjs';
import {FocusEvent, SelectionEvent} from '../../events.mjs';
import {CSSColor} from '../helper.mjs';
import {DOM, V8CustomElement} from '../helper.mjs';
DOM.defineCustomElement(
'./map-panel/map-transitions',
'./view/map-panel/map-transitions',
(templateText) => class MapTransitions extends V8CustomElement {
_map;
_selectedMapLogEntries;
@ -38,6 +39,35 @@ DOM.defineCustomElement(
this._showMap();
}
set selectedMapLogEntries(list) {
this._selectedMapLogEntries = list;
this.update();
}
get selectedMapLogEntries() {
return this._selectedMapLogEntries;
}
_typeToColor(type) {
switch (type) {
case 'new':
return CSSColor.green;
case 'Normalize':
return CSSColor.violet;
case 'SlowToFast':
return CSSColor.orange;
case 'InitialMap':
return CSSColor.yellow;
case 'Transition':
return CSSColor.primaryColor;
case 'ReplaceDescriptors':
return CSSColor.red;
case 'LoadGlobalIC':
return CSSColor.green;
}
return CSSColor.secondaryColor;
}
_handleTransitionViewChange(e) {
this.tooltip.style.left = e.pageX + 'px';
this.tooltip.style.top = e.pageY + 'px';
@ -69,15 +99,6 @@ DOM.defineCustomElement(
this.transitionView.style.display = '';
}
set selectedMapLogEntries(list) {
this._selectedMapLogEntries = list;
this.update();
}
get selectedMapLogEntries() {
return this._selectedMapLogEntries;
}
_addMapAndParentTransitions(map) {
if (map === void 0) return;
if (this._displayedMapsInTree.has(map)) return;
@ -116,7 +137,7 @@ DOM.defineCustomElement(
_addTransitionEdge(map) {
let classes = ['transitionEdge'];
let edge = DOM.div(classes);
edge.style.backgroundColor = typeToColor(map.edge);
edge.style.backgroundColor = this._typeToColor(map.edge);
let labelNode = DOM.div('transitionLabel');
labelNode.innerText = map.edge.toString();
edge.appendChild(labelNode);
@ -145,7 +166,7 @@ DOM.defineCustomElement(
_addMapNode(map) {
let node = DOM.div('map');
if (map.edge) node.style.backgroundColor = typeToColor(map.edge);
if (map.edge) node.style.backgroundColor = this._typeToColor(map.edge);
node.map = map;
node.onclick = this._selectMapHandler
if (map.children.length > 1) {

View File

@ -1,12 +1,13 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {FocusEvent, SelectionEvent} from './events.mjs';
import {delay, DOM, formatBytes, V8CustomElement} from './helper.mjs';
import {IcLogEntry} from './log/ic.mjs';
import {MapLogEntry} from './log/map.mjs';
import {FocusEvent, SelectionEvent} from '../events.mjs';
import {IcLogEntry} from '../log/ic.mjs';
import {MapLogEntry} from '../log/map.mjs';
DOM.defineCustomElement('source-panel',
import {delay, DOM, formatBytes, V8CustomElement} from './helper.mjs';
DOM.defineCustomElement('view/source-panel',
(templateText) =>
class SourcePanel extends V8CustomElement {
_selectedSourcePositions = [];

View File

@ -1,12 +1,13 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {SelectionEvent} from './events.mjs';
import {DOM, V8CustomElement} from './helper.mjs';
import {delay, LazyTable} from './helper.mjs';
import {SelectionEvent} from '../events.mjs';
import {DOM, LazyTable, V8CustomElement} from './helper.mjs';
DOM.defineCustomElement(
'stats-panel', (templateText) => class StatsPanel extends V8CustomElement {
'view/stats-panel',
(templateText) => class StatsPanel extends V8CustomElement {
_timeline;
_transitions;
_selectedLogEntries;

View File

@ -4,11 +4,11 @@
import './timeline/timeline-track.mjs';
import {SynchronizeSelectionEvent} from './events.mjs';
import {SynchronizeSelectionEvent} from '../events.mjs';
import {DOM, V8CustomElement} from './helper.mjs';
DOM.defineCustomElement(
'timeline-panel',
'view/timeline-panel',
(templateText) => class TimelinePanel extends V8CustomElement {
constructor() {
super(templateText);

View File

@ -2,10 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {FocusEvent, SelectionEvent, SelectTimeEvent, SynchronizeSelectionEvent} from '../events.mjs';
import {CSSColor, delay, DOM, V8CustomElement} from '../helper.mjs';
import {kChunkHeight, kChunkWidth} from '../log/map.mjs';
import {MapLogEntry} from '../log/map.mjs';
import {FocusEvent, SelectionEvent, SelectTimeEvent, SynchronizeSelectionEvent} from '../../events.mjs';
import {kChunkHeight, kChunkWidth} from '../../log/map.mjs';
import {MapLogEntry} from '../../log/map.mjs';
import {CSSColor, DOM, V8CustomElement} from '../helper.mjs';
const kColors = [
CSSColor.green,
@ -19,7 +19,7 @@ const kColors = [
CSSColor.secondaryColor,
];
DOM.defineCustomElement('./timeline/timeline-track',
DOM.defineCustomElement('view/timeline/timeline-track',
(templateText) =>
class TimelineTrack extends V8CustomElement {
// TODO turn into static field once Safari supports it.