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

[tools][system-analyzer] Improve flamechart

- Vertically adjust flamechart to show deep stacks
- Highlight currently hovered function in the complete flamechart

Bug: v8:10644, v8:11835
Change-Id: Ibb5839c332f28c552162943f3eb65435de11a36a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2950244Reviewed-by: 's avatarPatrick Thier <pthier@chromium.org>
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75074}
parent 900d6b93
......@@ -191,6 +191,11 @@ export class TimelineTrackBase extends V8CustomElement {
this._scalableContentNode.style.transform = `scale(${ratio}, 1)`;
}
_adjustHeight(height) {
this.querySelectorAll('.dataSized')
.forEach(node => {node.style.height = height + 'px'});
}
_update() {
this._legend.update();
this._drawContent();
......@@ -261,7 +266,7 @@ export class TimelineTrackBase extends V8CustomElement {
this.timelineMarkersNode.innerHTML = buffer;
}
_drawAnnotations(logEntry) {
_drawAnnotations(logEntry, time) {
// Subclass responsibility.
}
......@@ -288,7 +293,8 @@ export class TimelineTrackBase extends V8CustomElement {
const {logEntry, target} = this._getEntryForEvent(event);
if (!logEntry) return false;
this.dispatchEvent(new ToolTipEvent(logEntry, target));
this._drawAnnotations(logEntry);
const time = this.positionToTime(event.pageX);
this._drawAnnotations(logEntry, time);
}
_getEntryForEvent(event) {
......
......@@ -24,11 +24,13 @@ found in the LICENSE file. -->
font-size: 10px;
opacity: 0.5;
}
.dataSized {
min-height: 200px;
}
#timelineSamples, #timelineChunks,
#timelineMarkers, #timelineAnnotations {
top: 0px;
height: 200px;
position: absolute;
margin-right: 100px;
}
......@@ -36,39 +38,6 @@ found in the LICENSE file. -->
pointer-events: none;
}
#timelineCanvas {
height: 200px;
position: relative;
overflow: visible;
pointer-events: none;
}
.chunk {
width: 6px;
position: absolute;
background-size: 100% 100%;
image-rendering: pixelated;
bottom: 0px;
background-color: var(--on-surface-color);
cursor: pointer;
content-visibility: auto;
}
.chunk:hover {
border-radius: 2px 2px 0 0;
margin: 0 0 -2px -2px;
border: 2px var(--primary-color) solid;
}
.timestamp {
height: 200px;
width: 100px;
border-left: 1px var(--on-surface-color) dashed;
padding-left: 4px;
position: absolute;
pointer-events: none;
font-size: 10px;
}
.title {
position: relative;
float: left;
......@@ -221,6 +190,10 @@ found in the LICENSE file. -->
dominant-baseline: hanging;
font-size: 9px;
}
.flameSelected {
fill: none;
stroke: var(--on-background-color);
}
#scalableContent {
}
</style>
......@@ -231,17 +204,17 @@ found in the LICENSE file. -->
<h3 class="title" id="title"></h3>
<div id="timeline">
<div id="selection">
<div id="selection" class="dataSized">
<div id="leftHandle"></div>
<div id="selectionBackground"></div>
<div id="rightHandle"></div>
</div>
<div id="timelineLabel">Frequency</div>
<svg id="timelineChunks" xmlns="http://www.w3.org/2000/svg">
<svg id="timelineChunks" xmlns="http://www.w3.org/2000/svg" class="dataSized">
<g id="scalableContent"></g>
</svg>
<svg id="timelineAnnotations" xmlns="http://www.w3.org/2000/svg"></svg>
<svg id="timelineMarkers" xmlns="http://www.w3.org/2000/svg"></svg>
<svg id="timelineAnnotations" xmlns="http://www.w3.org/2000/svg" class="dataSized"></svg>
<svg id="timelineMarkers" xmlns="http://www.w3.org/2000/svg" class="dataSized"></svg>
<canvas id="timelineCanvas"></canvas>
</div>
......
......@@ -11,18 +11,18 @@ import {TimelineTrackBase} from './timeline-track-base.mjs'
class Flame {
constructor(time, entry, depth, id) {
this.start = time;
this.end = this.start;
this.time = time;
this.entry = entry;
this.depth = depth;
this.id = id;
}
stop(time) {
this.end = time;
this.duration = time - this.start
this.duration = time - this.time
}
}
const kFlameHeight = 10;
DOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-tick',
(templateText) =>
class TimelineTrackTick extends TimelineTrackBase {
......@@ -31,6 +31,8 @@ DOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-tick',
constructor() {
super(templateText);
this._annotations = new Annotations(this);
this.timelineNode.style.overflowY = 'scroll';
}
_updateChunks() {
......@@ -62,9 +64,11 @@ DOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-tick',
const tmpFlames = [];
const stack = [];
const ticks = this._timeline.values;
let maxDepth = 0;
for (let tickIndex = 0; tickIndex < ticks.length; tickIndex++) {
const tick = ticks[tickIndex];
maxDepth = Math.max(maxDepth, tick.stack.length);
for (let stackIndex = 0; stackIndex < tick.stack.length; stackIndex++) {
const entry = tick.stack[stackIndex];
if (stack.length <= stackIndex) {
......@@ -91,6 +95,10 @@ DOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-tick',
stack[stackIndex].stop(lastTime);
}
this._flames = new Timeline(Flame, tmpFlames);
this._annotations.flames = this._flames;
// Account for empty top line
maxDepth++;
this._adjustHeight(maxDepth * kFlameHeight);
}
_scaleContent(currentWidth) {
......@@ -123,19 +131,23 @@ DOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-tick',
add();
}
drawFlame(flame) {
drawFlame(flame, outline = false) {
const x = this.timeToPosition(flame.time);
const y = (flame.depth + 1) * kFlameHeight;
let width = (flame.duration * this._timeToPixel) * 0.9;
let height = kFlameHeight - 1;
if (outline) {
return `<rect x=${x} y=${y} width=${width} height=${
height} class=flameSelected />`;
}
let type = 'native';
if (flame.entry?.state) {
type = Profile.getKindFromState(flame.entry.state);
}
const kHeight = 9;
const x = this.timeToPosition(flame.start);
const y = flame.depth * (kHeight + 1);
let width = flame.duration * this._timeToPixel;
width -= width * 0.1;
const color = this._legend.colorForType(type);
return `<rect x=${x} y=${y} width=${width} height=${kHeight} fill=${
return `<rect x=${x} y=${y} width=${width} height=${height} fill=${
color} data-id=${flame.id} />`;
}
......@@ -145,7 +157,7 @@ DOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-tick',
type = Profile.getKindFromState(flame.entry.state);
}
const kHeight = 9;
const x = this.timeToPosition(flame.start);
const x = this.timeToPosition(flame.time);
const y = flame.depth * (kHeight + 1);
let width = flame.duration * this._timeToPixel;
width -= width * 0.1;
......@@ -160,4 +172,69 @@ DOM.defineCustomElement('view/timeline/timeline-track', 'timeline-track-tick',
buffer += `<text x=${x + 1} y=${y - 3} class=txt>${text}</text>`
return buffer;
}
_drawAnnotations(logEntry, time) {
if (time === undefined) {
time = this.relativePositionToTime(this.timelineNode.scrollLeft);
}
this._annotations.update(logEntry, time);
}
})
class Annotations {
_flames;
_logEntry;
_buffer;
constructor(track) {
this._track = track;
}
set flames(flames) {
this._flames = flames;
}
get _node() {
return this._track.timelineAnnotationsNode;
}
async update(logEntry, time) {
if (this._logEntry == logEntry) return;
this._logEntry = logEntry;
this._node.innerHTML = '';
if (logEntry === undefined) return;
this._buffer = '';
const start = this._flames.find(time);
let offset = 0;
// Draw annotations gradually outwards starting form the given time.
for (let range = 0; range < this._flames.length; range += 10000) {
this._markFlames(start - range, start - offset);
this._markFlames(start + offset, start + range);
offset = range;
await delay(50);
// Abort if we started another update asynchronously.
if (this._logEntry != logEntry) return;
}
}
_markFlames(start, end) {
const rawFlames = this._flames.values;
if (start < 0) start = 0;
if (end > rawFlames.length) end = rawFlames.length;
const code = this._logEntry.entry;
for (let i = start; i < end; i++) {
const flame = rawFlames[i];
if (flame.entry != code) continue;
this._buffer += this._track.drawFlame(flame, true);
}
this._drawBuffer();
}
_drawBuffer() {
if (this._buffer.length == 0) return;
const svg = SVG.svg();
svg.innerHTML = this._buffer;
this._node.appendChild(svg);
this._buffer = '';
}
}
\ No newline at end of file
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