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