Commit 91ddeb06 authored by Camillo Bruni's avatar Camillo Bruni Committed by V8 LUCI CQ

[tools][system-analyzer] Various improvements

- Show related code object for Maps
- Fix opening transition trees
- Rename *LogEntry.prototype.codeLogEntry to .code
- Show Arrays as dropdowns in tooltips
- Avoid hiding the tooltip when clicking on the tooltip itself
- Show links to code variants (bytecode/baseline/optimized)
- Fix chunk offset calculation
- Fix code for browsers that don't support
  navigator.scheduling.isInputPending

Bug: v8:10644
Change-Id: I858dc410657d26d076214368814a52177b124f4c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2964592
Auto-Submit: Camillo Bruni <cbruni@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75169}
parent 88f4ca2b
......@@ -57,7 +57,7 @@ export class SourcePosition {
title: this.toString(),
__this__: this,
script: this.script,
entries: this.entries.length,
entries: this.entries,
}
}
}
......@@ -128,7 +128,7 @@ export class Script {
id: this.id,
url: this.url,
source: this.source,
sourcePositions: this.sourcePositions.length
sourcePositions: this.sourcePositions
}
}
......@@ -203,7 +203,7 @@ export class Profile {
/**
* Returns whether a function with the specified name must be skipped.
* Should be overriden by subclasses.
* Should be overridden by subclasses.
*
* @param {string} name Function name.
*/
......@@ -840,6 +840,10 @@ class FunctionEntry extends CodeEntry {
return this._codeEntries.values().next().value.getSourceCode();
}
get codeEntries() {
return this._codeEntries;
}
/**
* Returns node name.
*/
......
......@@ -263,13 +263,16 @@ class App {
this._view.mapTrack.focusedEntry = entry;
this._view.mapPanel.map = entry;
this._view.mapPanel.show();
if (focusSourcePosition) this.focusSourcePosition(entry.sourcePosition);
if (focusSourcePosition) {
this.focusCodeLogEntry(entry.code, false);
this.focusSourcePosition(entry.sourcePosition);
}
}
focusIcLogEntry(entry) {
this._state.ic = entry;
this.focusMapLogEntry(entry.map, false);
this.focusCodeLogEntry(entry.codeLogEntry, false);
this.focusCodeLogEntry(entry.code, false);
this.focusSourcePosition(entry.sourcePosition);
}
......@@ -282,7 +285,7 @@ class App {
focusDeoptLogEntry(entry) {
this._state.deoptLogEntry = entry;
this.focusCodeLogEntry(entry.codeLogEntry, false);
this.focusCodeLogEntry(entry.code, false);
this.focusSourcePosition(entry.sourcePosition);
}
......
......@@ -32,7 +32,7 @@ export class DeoptLogEntry extends LogEntry {
return this._entry;
}
get codeLogEntry() {
get code() {
return this._entry?.logEntry;
}
......@@ -43,7 +43,7 @@ export class DeoptLogEntry extends LogEntry {
static get propertyNames() {
return [
'type', 'reason', 'functionName', 'sourcePosition',
'functionSourcePosition', 'script'
'functionSourcePosition', 'script', 'code'
];
}
}
......@@ -85,6 +85,11 @@ export class CodeLogEntry extends LogEntry {
return this._entry?.source?.disassemble;
}
get variants() {
const entries = Array.from(this.entry?.func?.codeEntries ?? []);
return entries.map(each => each.logEntry);
}
toString() {
return `Code(${this.type})`;
}
......@@ -98,7 +103,7 @@ export class CodeLogEntry extends LogEntry {
static get propertyNames() {
return [
'functionName', 'sourcePosition', 'kindName', 'size', 'type', 'kind',
'script', 'source', 'code'
'script', 'source', 'code', 'variants'
];
}
}
......
......@@ -30,7 +30,7 @@ export class IcLogEntry extends LogEntry {
return this.oldState + ' → ' + this.newState;
}
get codeLogEntry() {
get code() {
return this.codeEntry?.logEntry;
}
......@@ -62,8 +62,8 @@ export class IcLogEntry extends LogEntry {
static get propertyNames() {
return [
'type', 'category', 'functionName', 'script', 'sourcePosition', 'state',
'key', 'map', 'reason'
'type', 'category', 'functionName', 'script', 'sourcePosition', 'code',
'state', 'key', 'map', 'reason'
];
}
}
......@@ -7,8 +7,9 @@ import {LogEntry} from './log.mjs';
// ===========================================================================
// Map Log Events
const kChunkHeight = 200;
const kChunkWidth = 10;
export const kChunkHeight = 200;
export const kChunkWidth = 10;
export const kChunkVisualWidth = 6;
function define(prototype, name, fn) {
Object.defineProperty(prototype, name, {value: fn, enumerable: false});
......@@ -33,7 +34,7 @@ define(Array.prototype, 'last', function() {
// ===========================================================================
// Map Log Events
class MapLogEntry extends LogEntry {
export class MapLogEntry extends LogEntry {
constructor(id, time) {
if (!time) throw new Error('Invalid time');
// Use MapLogEntry.type getter instead of property, since we only know the
......@@ -56,6 +57,10 @@ class MapLogEntry extends LogEntry {
return this.entry?.functionName;
}
get code() {
return this.entry?.logEntry;
}
toString() {
return `Map(${this.id})`;
}
......@@ -121,7 +126,7 @@ class MapLogEntry extends LogEntry {
position(chunks) {
const index = this.chunkIndex(chunks);
if (index === -1) return [0, 0];
const xFrom = (index + 0.5) * kChunkWidth | 0;
const xFrom = (index * kChunkWidth + kChunkVisualWidth / 2) | 0;
const yFrom = kChunkHeight - chunks[index].yOffset(this) | 0;
return [xFrom, yFrom];
}
......@@ -189,8 +194,8 @@ class MapLogEntry extends LogEntry {
static get propertyNames() {
return [
'type', 'reason', 'property', 'functionName', 'sourcePosition', 'script',
'id', 'parent', 'description'
'type', 'reason', 'property', 'parent', 'functionName', 'sourcePosition',
'script', 'code', 'id', 'description'
];
}
}
......@@ -198,7 +203,7 @@ class MapLogEntry extends LogEntry {
MapLogEntry.cache = new Map();
// ===========================================================================
class Edge {
export class Edge {
constructor(type, name, reason, time, from, to) {
this.type = type;
this.name = name;
......@@ -321,6 +326,4 @@ class Edge {
return this.type + ' ' + (this.reason ? this.reason : '') + ' ' +
(this.name ? this.name : '')
}
}
export {MapLogEntry, Edge, kChunkWidth, kChunkHeight};
}
\ No newline at end of file
......@@ -402,9 +402,9 @@ export class Processor extends LogReader {
}
// TODO: use SourcePosition directly.
let edge = new Edge(type, name, reason, time, from_, to_);
const profileEntry = this._profile.findEntry(pc)
to_.entry = profileEntry;
let script = this.getProfileEntryScript(profileEntry);
const codeEntry = this._profile.findEntry(pc)
to_.entry = codeEntry;
let script = this.getProfileEntryScript(codeEntry);
if (script) {
to_.sourcePosition = script.addSourcePosition(line, column, to_)
}
......
......@@ -36,10 +36,12 @@ DOM.defineCustomElement('view/code-panel',
'__this__': entry,
functionName: entry.functionName,
size: formatBytes(entry.size),
creationTime: formatMicroSeconds(entry.time / 1000),
sourcePosition: entry.sourcePosition,
script: entry.script,
type: entry.type,
kind: entry.kindName,
variants: entry.variants,
};
} else {
this.$('#properties').propertyDict = {};
......
......@@ -186,7 +186,7 @@ DOM.defineCustomElement('view/list-panel',
details.onclick = this._detailsClickHandler;
tr.appendChild(DOM.td(`${group.percent.toFixed(2)}%`, 'percentage'));
tr.appendChild(DOM.td(group.count, 'count'));
const valueTd = tr.appendChild(DOM.td(group.key.toString(), 'key'));
const valueTd = tr.appendChild(DOM.td(group.key?.toString(), 'key'));
if (App.isClickable(group.key)) {
tr.onclick = this._logEntryClickHandler;
tr.onmouseover = this._logEntryMouseOverHandler;
......
......@@ -11,7 +11,7 @@ DOM.defineCustomElement(
template => class PropertyLinkTable extends V8CustomElement {
_instance;
_propertyDict;
_instanceLinkButtons = true;
_instanceLinkButtons = false;
_logEntryClickHandler = this._handleLogEntryClick.bind(this);
_logEntryRelatedHandler = this._handleLogEntryRelated.bind(this);
......@@ -19,12 +19,8 @@ DOM.defineCustomElement(
super(template);
}
static get observedAttributes() {
return ['noButtons'];
}
attributeChangedCallback(name, oldValue, newValue) {
if (name == 'instanceLinkButtons') this._instanceLinkButtons = true;
set instanceLinkButtons(newValue) {
this._instanceLinkButtons = newValue;
}
set propertyDict(propertyDict) {
......@@ -63,6 +59,10 @@ DOM.defineCustomElement(
row.insertCell().innerText = key;
const cell = row.insertCell();
if (value == undefined) return;
if (Array.isArray(value)) {
cell.appendChild(this._addArrayValue(value));
return;
}
if (App.isClickable(value)) {
cell.className = 'clickable';
cell.onclick = this._logEntryClickHandler;
......@@ -71,6 +71,22 @@ DOM.defineCustomElement(
new ExpandableText(cell, value.toString());
}
_addArrayValue(array) {
if (array.length == 0) {
return DOM.text('empty');
} else if (array.length > 200) {
return DOM.text(`${array.length} items`);
}
const select = DOM.element('select');
for (let value of array) {
const option = DOM.element('option');
option.innerText = value.toString();
option.data = value;
select.add(option);
}
return select;
}
_addTitle(value) {
const title = DOM.element('h3');
title.innerText = value;
......
......@@ -3,7 +3,7 @@
// found in the LICENSE file.
import {delay} from '../../helper.mjs';
import {kChunkHeight, kChunkWidth} from '../../log/map.mjs';
import {kChunkHeight, kChunkVisualWidth, kChunkWidth} from '../../log/map.mjs';
import {SelectionEvent, SelectTimeEvent, SynchronizeSelectionEvent, ToolTipEvent,} from '../events.mjs';
import {CSSColor, DOM, SVG, V8CustomElement} from '../helper.mjs';
......@@ -12,7 +12,7 @@ export const kTimelineHeight = 200;
export class TimelineTrackBase extends V8CustomElement {
_timeline;
_nofChunks = 500;
_chunks;
_chunks = [];
_selectedEntry;
_timeToPixel;
_timeStartPixelOffset;
......@@ -275,8 +275,7 @@ export class TimelineTrackBase extends V8CustomElement {
lastHeight -= height;
const color = this._legend.colorForType(group.key);
buffer += `<rect x=${chunkIndex * kChunkWidth} y=${lastHeight} height=${
height} `
buffer += `width=6 fill=${color} />`
height} width=${kChunkVisualWidth} fill=${color} />`
}
return buffer;
}
......@@ -365,6 +364,7 @@ export class TimelineTrackBase extends V8CustomElement {
style.left = `${chunk.index * kChunkWidth}px`;
style.top = `${kTimelineHeight - chunk.height}px`;
style.height = `${chunk.height}px`;
style.width = `${kChunkVisualWidth}px`;
return logEntry;
}
};
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {MapLogEntry} from '../../log/map.mjs';
import {CSSColor, DOM, SVG, V8CustomElement} from '../helper.mjs';
import {kChunkVisualWidth, MapLogEntry} from '../../log/map.mjs';
import {CSSColor, DOM} from '../helper.mjs';
import {TimelineTrackBase} from './timeline-track-base.mjs'
......
......@@ -267,7 +267,8 @@ class Annotations {
this._markFlames(start - range, start - offset);
this._markFlames(start + offset, start + range);
offset = range;
if (navigator.scheduling.isInputPending({includeContinuous: true}) ||
if ((navigator?.scheduling?.isInputPending({includeContinuous: true}) ??
false) ||
performance.now() >= deadline) {
// Yield if we have to handle an input event, or we're out of time.
await delay(50);
......
......@@ -105,7 +105,7 @@ found in the LICENSE file. -->
<div id="body">
<div id="content">
<property-link-table id="properties" showButtons></property-link-table>
<property-link-table id="properties"></property-link-table>
</div>
<div class="tip">
<div class="tipThin"></div>
......
......@@ -20,7 +20,15 @@ DOM.defineCustomElement(
this.requestUpdate(true);
}
});
document.addEventListener('click', (e) => this.hide());
document.addEventListener('click', (event) => {
// Only hide the tooltip if we click anywhere outside of it.
let target = event.target;
while (target) {
if (target == this) return;
target = target.parentNode;
}
this.hide()
});
}
_update() {
......@@ -89,6 +97,7 @@ DOM.defineCustomElement(
this.contentNode.firstChild.propertyDict = content;
} else {
const node = DOM.element('property-link-table');
node.instanceLinkButtons = true;
node.propertyDict = content;
this._setContentNode(node);
}
......
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