Commit 95e1fa50 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools][system-analyzer] Improve timeline track

- Use initial color for TimelineTrack chunks
- Use async functions for TimelineTrack setChunkBackgrounds
- Introduce timeSelection setter on TimelinePanel
- Propagate time selection down to the TimelinePanel
- Avoid layout recalculation in TimelineTrack updateSelection

drive-by-cleanups:
- Add common event listeners only to top-level document
- Use console.warn instead of console.error for better debugging in
  DevTools

Bug: v8:10644
Change-Id: Ie4f80f1c6c3f4fa193f8ac01225e0fb76981551d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2497175
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarShu-yu Guo <syg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70886}
parent 71b29be4
......@@ -186,7 +186,11 @@ class V8CustomElement extends HTMLElement {
removeAllChildren(node) { return removeAllChildren(node); }
}
function delay(time) {
return new Promise(resolver => setTimeout(resolver, time));
}
export {
defineCustomElement, V8CustomElement, removeAllChildren,
$, div, typeToColor, CSSColor
$, div, typeToColor, CSSColor, delay
};
......@@ -51,14 +51,12 @@ class App {
]);
document.addEventListener('keydown',
e => this._navigation?.handleKeyDown(e));
Object.entries(this._view).forEach(([_, panel]) => {
panel.addEventListener(SelectionEvent.name,
e => this.handleShowEntries(e));
panel.addEventListener(FocusEvent.name,
e => this.handleShowEntryDetail(e));
panel.addEventListener(SelectTimeEvent.name,
e => this.handleTimeRangeSelect(e));
});
document.addEventListener(SelectionEvent.name,
e => this.handleShowEntries(e));
document.addEventListener(FocusEvent.name,
e => this.handleShowEntryDetail(e));
document.addEventListener(SelectTimeEvent.name,
e => this.handleTimeRangeSelect(e));
}
handleShowEntries(e) {
......@@ -100,6 +98,7 @@ class App {
this.showMapEntries(this._state.mapTimeline.selection);
this.showIcEntries(this._state.icTimeline.selection);
this.showDeoptEntries(this._state.deoptTimeline.selection);
this._view.timelinePanel.timeSelection = {start,end};
}
handleShowEntryDetail(e) {
......
......@@ -56,7 +56,7 @@ class MapLogEntry extends LogEntry {
while (stack.length > 0) {
let current = stack.pop();
if (current.leftId !== 0) {
console.error('Skipping potential parent loop between maps:', current)
console.warn('Skipping potential parent loop between maps:', current)
continue;
}
current.finalize(id)
......@@ -192,11 +192,11 @@ class Edge {
if (from === undefined) return;
if (to === from) throw 'From and to must be distinct.';
if (to.time < from.time) {
console.error('invalid time order');
console.warn('invalid time order');
}
let newDepth = from.depth + 1;
if (to.depth > 0 && to.depth != newDepth) {
console.error('Depth has already been initialized');
console.warn('Depth has already been initialized');
}
to.depth = newDepth;
}
......
......@@ -13,7 +13,8 @@ defineCustomElement('timeline-panel', (templateText) =>
this.addEventListener(
'scrolltrack', e => this.handleTrackScroll(e));
this.addEventListener(
SynchronizeSelectionEvent.name, e => this.handleMouseMoveSelection(e));
SynchronizeSelectionEvent.name,
e => this.handleSelectionSyncronization(e));
}
set nofChunks(count) {
......@@ -38,13 +39,12 @@ defineCustomElement('timeline-panel', (templateText) =>
}
}
handleMouseMoveSelection(event) {
this.selectTimeRange(event.start, event.end);
handleSelectionSyncronization(event) {
this.timeSelection = {start:event.start, end:event.end};
}
selectTimeRange(start, end) {
const timeSelection = {start, end};
if (start > end) {
set timeSelection(timeSelection) {
if (timeSelection.start > timeSelection.end) {
throw new Error("Invalid time range");
}
for (const track of this.timelineTracks) {
......
......@@ -44,6 +44,13 @@ found in the LICENSE file. -->
background-size: 100% 100%;
image-rendering: pixelated;
bottom: 0px;
background-color: var(--on-surface-color);
cursor: pointer;
}
.chunk:hover {
border-radius: 2px 2px 0 0;
margin: 0 0 -2px -2px;
border: 2px var(--primary-color) solid;
}
.timestamp {
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
import {
defineCustomElement, V8CustomElement, CSSColor
defineCustomElement, V8CustomElement, CSSColor, delay
} from '../helper.mjs';
import { kChunkWidth, kChunkHeight } from "../log/map.mjs";
import {
......@@ -82,6 +82,8 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
handleTimeSelectionMouseUp(e) {
this._selectionOriginTime = -1;
const delta = this._timeSelection.end - this._timeSelection.start;
if (delta <= 1 || isNaN(delta)) return;
this.dispatchEvent(new SelectTimeEvent(this._timeSelection.start,
this._timeSelection.end));
}
......@@ -99,11 +101,11 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
updateSelection() {
const startPosition = this.timeToPosition(this._timeSelection.start);
const endPosition = this.timeToPosition(this._timeSelection.end);
const delta = endPosition - startPosition;
this.leftHandle.style.left = startPosition + "px";
this.selection.style.left = startPosition + "px";
this.rightHandle.style.left = endPosition + "px";
this.selection.style.width =
Math.abs(this.rightHandlePosX - this.leftHandlePosX) + "px";
this.selection.style.width = delta + "px";
}
get leftHandlePosX() {
......@@ -222,7 +224,6 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
let timelineLegend = this.timelineLegend;
let timelineLegendContent = this.timelineLegendContent;
this.removeAllChildren(timelineLegendContent);
let colorIterator = 0;
this._timeline.uniqueTypes.forEach((entries, type) => {
let row = this.tr();
row.entries = entries;
......@@ -242,7 +243,6 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
let percent = (entries.length / this.data.all.length) * 100;
row.appendChild(this.td(percent.toFixed(1) + "%"));
timelineLegendContent.appendChild(row);
colorIterator += 1;
});
// Add Total row.
let row = this.tr();
......@@ -271,23 +271,20 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
}));
}
asyncSetTimelineChunkBackground(backgroundTodo) {
const kIncrement = 100;
let start = 0;
let delay = 1;
while (start < backgroundTodo.length) {
let end = Math.min(start + kIncrement, backgroundTodo.length);
setTimeout((from, to) => {
for (let i = from; i < to; i++) {
let [chunk, node] = backgroundTodo[i];
this.setTimelineChunkBackground(chunk, node);
async setChunkBackgrounds(backgroundTodo) {
const kMaxDuration = 50;
let lastTime = 0;
for (let [chunk, node] of backgroundTodo) {
const current = performance.now();
if (current - lastTime > kMaxDuration) {
await delay(25);
lastTime = current;
}
}, delay++, start, end);
start = end;
this.setChunkBackground(chunk, node);
}
}
setTimelineChunkBackground(chunk, node) {
setChunkBackground(chunk, node) {
// Render the types of transitions as bar charts
const kHeight = chunk.height;
const kWidth = 1;
......@@ -351,7 +348,7 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
backgroundTodo.push([chunk, node])
chunksNode.appendChild(node);
}
this.asyncSetTimelineChunkBackground(backgroundTodo)
this.setChunkBackgrounds(backgroundTodo);
// Put a time marker roughly every 20 chunks.
let expected = duration / chunks.length * 20;
......@@ -385,11 +382,9 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
}
handleChunkDoubleClick(event) {
this.isLocked = true;
let chunk = event.target.chunk;
if (!chunk) return;
let maps = chunk.items;
this.dispatchEvent(new SelectionEvent(maps));
this.dispatchEvent(new SelectTimeEvent(chunk.start, chunk.end));
}
redraw() {
......
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