Commit 77c8a3ce authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] Improve system-analyzer map view

- Limit transition view to 200 maps
- Avoid displaying parent maps multiple times
- Fix timeline-track selection, slow dragging works now

Bug: v8:10644
Change-Id: I2106ea8240977e0ea65083d296977ab0272304d3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2485851
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran  <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70677}
parent 517a3069
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { V8CustomElement, defineCustomElement } from "../helper.mjs";
import { FocusEvent } from "../events.mjs";
import { FocusEvent, SelectionEvent } from "../events.mjs";
defineCustomElement(
"./map-panel/map-transitions",
......@@ -10,6 +10,8 @@ defineCustomElement(
class MapTransitions extends V8CustomElement {
_map;
_selectedMapLogEntries;
_displayedMapsInTree;
_showMapsUpdateId;
constructor() {
super(templateText);
this.transitionView.addEventListener("mousemove", (e) =>
......@@ -46,29 +48,28 @@ defineCustomElement(
}
selectMap(map) {
this.currentMap = map;
this.showMap();
this.dispatchEvent(new FocusEvent(map));
}
dblClickSelectMap(map) {
this.dispatchEvent(new FocusEvent(map));
this.dispatchEvent(new SelectionEvent([map]));
}
showMap() {
// Called when a map selected
if (this.currentMap === this._map) return;
this.currentMap = this._map;
this.selectedMapLogEntries = [this._map];
this.dispatchEvent(new FocusEvent(this._map));
this.showMaps();
}
showMaps() {
// Timeline dbl click to show map transitions of selected maps
clearTimeout(this._showMapsUpdateId);
this._showMapsUpdateId = setTimeout(() => this._showMaps(), 250);
}
_showMaps() {
this.transitionView.style.display = "none";
this.removeAllChildren(this.transitionView);
this.selectedMapLogEntries.forEach((map) =>
this._displayedMapsInTree = new Set();
// Limit view to 200 maps for performance reasons.
this.selectedMapLogEntries.slice(0, 200).forEach((map) =>
this.addMapAndParentTransitions(map));
this._displayedMapsInTree = undefined;
this.transitionView.style.display = "";
}
......@@ -83,6 +84,8 @@ defineCustomElement(
addMapAndParentTransitions(map) {
if (map === void 0) return;
if (this._displayedMapsInTree.has(map)) return;
this._displayedMapsInTree.add(map);
this.currentNode = this.transitionView;
let parents = map.getParents();
if (parents.length > 0) {
......@@ -105,26 +108,6 @@ defineCustomElement(
}
}
addMapNode(map) {
let node = this.div("map");
if (map.edge) node.style.backgroundColor = map.edge.getColor();
node.map = map;
node.addEventListener("click", () => this.selectMap(map));
node.addEventListener("dblclick", () => this.dblClickSelectMap(map));
if (map.children.length > 1) {
node.innerText = map.children.length;
let showSubtree = this.div("showSubtransitions");
showSubtree.addEventListener("click", (e) =>
this.toggleSubtree(e, node)
);
node.appendChild(showSubtree);
} else if (map.children.length == 0) {
node.innerHTML = "&#x25CF;";
}
this.currentNode.appendChild(node);
return node;
}
addSubtransitions(map) {
let mapNode = this.addTransitionTo(map);
// Draw outgoing linear transition line.
......@@ -148,7 +131,7 @@ defineCustomElement(
addTransitionTo(map) {
// transition[ transitions[ transition[...], transition[...], ...]];
this._displayedMapsInTree.add(map);
let transition = this.div("transition");
if (map.isDeprecated()) transition.classList.add("deprecated");
if (map.edge) {
......@@ -166,6 +149,26 @@ defineCustomElement(
return mapNode;
}
addMapNode(map) {
let node = this.div("map");
if (map.edge) node.style.backgroundColor = map.edge.getColor();
node.map = map;
node.addEventListener("click", () => this.selectMap(map));
if (map.children.length > 1) {
node.innerText = map.children.length;
let showSubtree = this.div("showSubtransitions");
showSubtree.addEventListener("click", (e) =>
this.toggleSubtree(e, node)
);
node.appendChild(showSubtree);
} else if (map.children.length == 0) {
node.innerHTML = "&#x25CF;";
}
this.currentNode.appendChild(node);
return node;
}
toggleSubtree(event, node) {
let map = node.map;
event.target.classList.toggle("opened");
......
......@@ -43,9 +43,12 @@ defineCustomElement('timeline-panel', (templateText) =>
}
selectTimeRange(start, end) {
const timeSelection = {start, end};
if (start > end) {
throw new Error("Invalid time range");
}
for (const track of this.timelineTracks) {
track.startTime = start;
track.endTime = end;
track.timeSelection = timeSelection;;
}
}
});
......@@ -40,8 +40,6 @@ found in the LICENSE file. -->
.chunk {
width: 6px;
border: 0px var(--timeline-background-color) solid;
border-width: 0 2px 0 2px;
position: absolute;
background-size: 100% 100%;
image-rendering: pixelated;
......@@ -88,7 +86,7 @@ found in the LICENSE file. -->
.timeline {
background-color: var(--timeline-background-color);
}
#timeline .rightHandle,
#timeline .leftHandle {
background-color: rgba(200, 200, 200, 0.5);
......@@ -98,33 +96,36 @@ found in the LICENSE file. -->
z-index: 3;
cursor: col-resize;
}
#timeline .leftHandle {
border-left: 1px solid var(--on-surface-color);
}
#timeline .rightHandle {
border-right: 1px solid var(--on-surface-color);
}
#timeline .selection {
background-color: rgba(133, 68, 163, 0.5);
height: 100%;
position: absolute;
z-index: 2;
}
</style>
<div>
<table id="legend">
<thead>
<tr>
<td></td>
<td class="legendTypeColumn">Type</td>
<td>Count</td>
<td>Percent</td>
</tr>
</thead>
<tbody id="legendContent">
</tbody>
</table>
<div id="timeline">
<div class="leftHandle"></div>
<div class="selection"></div>
<div class="rightHandle"></div>
<div id="timelineLabel">Frequency</div>
<div id="timelineChunks"></div>
<canvas id="timelineCanvas"></canvas>
</div>
</div>
<table id="legend">
<thead>
<tr>
<td></td>
<td class="legendTypeColumn">Type</td>
<td>Count</td>
<td>Percent</td>
</tr>
</thead>
<tbody id="legendContent">
</tbody>
</table>
<div id="timeline">
<div class="leftHandle"></div>
<div class="selection"></div>
<div class="rightHandle"></div>
<div id="timelineLabel">Frequency</div>
<div id="timelineChunks"></div>
<canvas id="timelineCanvas"></canvas>
</div>
\ No newline at end of file
......@@ -15,16 +15,15 @@ import {
defineCustomElement('./timeline/timeline-track', (templateText) =>
class TimelineTrack extends V8CustomElement {
// TODO turn into static field once Safari supports it.
static get SELECTION_OFFSET() { return 20 };
static get SELECTION_OFFSET() { return 10 };
_timeline;
_nofChunks = 400;
_chunks;
_selectedEntry;
_timeToPixel;
_timeSelection = { start: 0, end: Infinity };
_isSelected = false;
_timeSelection = { start: -1, end: Infinity };
_timeStartOffset;
_mouseDownTime;
_selectionOriginTime;
_typeToColor;
constructor() {
super(templateText);
......@@ -41,32 +40,14 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
}
handleTimeSelectionMouseDown(e) {
if (e.target.className === "chunk") return;
this._isSelected = true;
this._mouseDownTime = this.positionToTime(e.clientX);
}
handleTimeSelectionMouseMove(e) {
if (!this._isSelected) return;
let mouseMoveTime = this.positionToTime(e.clientX);
let startTime = this._mouseDownTime;
let endTime = mouseMoveTime;
if (this.isOnLeftHandle(e.clientX)) {
startTime = mouseMoveTime;
endTime = this.positionToTime(this.rightHandlePosX);
} else if (this.isOnRightHandle(e.clientX)) {
startTime = this.positionToTime(this.leftHandlePosX);
endTime = mouseMoveTime;
let xPosition = e.clientX
// Update origin time in case we click on a handle.
if (this.isOnLeftHandle(xPosition)) {
xPosition = this.rightHandlePosX;
} else if (this.isOnRightHandle(xPosition)) {
xPosition = this.leftHandlePosX;
}
this.dispatchEvent(new SynchronizeSelectionEvent(
Math.min(startTime, endTime),
Math.max(startTime, endTime)));
}
handleTimeSelectionMouseUp(e) {
this._isSelected = false;
this.dispatchEvent(new SelectTimeEvent(this._timeSelection.start,
this._timeSelection.end));
this._selectionOriginTime = this.positionToTime(xPosition);
}
isOnLeftHandle(posX) {
......@@ -79,40 +60,46 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
<= TimelineTrack.SELECTION_OFFSET);
}
set startTime(value) {
console.assert(
value <= this._timeSelection.end,
"Selection start time greater than end time!");
this._timeSelection.start = value;
this.updateSelection();
handleTimeSelectionMouseMove(e) {
if (!this._isSelecting) return;
const currentTime = this.positionToTime(e.clientX);
this.dispatchEvent(new SynchronizeSelectionEvent(
Math.min(this._selectionOriginTime, currentTime),
Math.max(this._selectionOriginTime, currentTime)));
}
set endTime(value) {
console.assert(
value > this._timeSelection.start,
"Selection end time smaller than start time!");
this._timeSelection.end = value;
handleTimeSelectionMouseUp(e) {
this._selectionOriginTime = -1;
this.dispatchEvent(new SelectTimeEvent(this._timeSelection.start,
this._timeSelection.end));
}
set timeSelection(selection) {
this._timeSelection.start = selection.start;
this._timeSelection.end= selection.end;
this.updateSelection();
}
get _isSelecting() {
return this._selectionOriginTime >= 0;
}
updateSelection() {
let startTimePos = this.timeToPosition(this._timeSelection.start);
let endTimePos = this.timeToPosition(this._timeSelection.end);
this.leftHandle.style.left = startTimePos + "px";
this.selection.style.left = startTimePos + "px";
this.rightHandle.style.left = endTimePos + "px";
const startPosition = this.timeToPosition(this._timeSelection.start);
const endPosition = this.timeToPosition(this._timeSelection.end);
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";
}
get leftHandlePosX() {
let leftHandlePosX = this.leftHandle.getBoundingClientRect().x;
return leftHandlePosX;
return this.leftHandle.getBoundingClientRect().x;
}
get rightHandlePosX() {
let rightHandlePosX = this.rightHandle.getBoundingClientRect().x;
return rightHandlePosX;
return this.rightHandle.getBoundingClientRect().x;
}
// Maps the clicked x position to the x position on timeline canvas
......@@ -371,6 +358,7 @@ defineCustomElement('./timeline/timeline-track', (templateText) =>
handleChunkMouseMove(event) {
if (this.isLocked) return false;
if (this._isSelecting) return false;
let chunk = event.target.chunk;
if (!chunk) return;
// topmost map (at chunk.height) == map #0.
......
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