Commit b178c52a authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[tools] System-analyzer: support filtering timelines by types

Bug: v8:10644
Change-Id: I727f844f3796f37e92c8855e02d519abeee73dc1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2566760
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarSathya Gunasekaran  <gsathya@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71558}
parent d7859a0a
...@@ -31,10 +31,11 @@ class State { ...@@ -31,10 +31,11 @@ class State {
selectTimeRange(start, end) { selectTimeRange(start, end) {
this.timeSelection.start = start; this.timeSelection.start = start;
this.timeSelection.end = end; this.timeSelection.end = end;
this._icTimeline.selectTimeRange(start, end); if (start == 0 && end == Infinity) {
this._mapTimeline.selectTimeRange(start, end); this.timelines.forEach(each => each.clearSelection());
this._deoptTimeline.selectTimeRange(start, end); } else {
this._codeTimeline.selectTimeRange(start, end); this.timelines.forEach(each => each.selectTimeRange(start, end));
}
} }
setTimelines(mapTimeline, icTimeline, deoptTimeline, codeTimeline) { setTimelines(mapTimeline, icTimeline, deoptTimeline, codeTimeline) {
......
...@@ -116,10 +116,10 @@ class App { ...@@ -116,10 +116,10 @@ class App {
selectTimeRange(start, end) { selectTimeRange(start, end) {
this._state.selectTimeRange(start, end); this._state.selectTimeRange(start, end);
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.showCodeEntries(this._state.codeTimeline.selection); this.showCodeEntries(this._state.codeTimeline.selection ?? []);
this._view.timelinePanel.timeSelection = {start, end}; this._view.timelinePanel.timeSelection = {start, end};
} }
......
...@@ -115,9 +115,10 @@ class MapLogEntry extends LogEntry { ...@@ -115,9 +115,10 @@ class MapLogEntry extends LogEntry {
} }
position(chunks) { position(chunks) {
let index = this.chunkIndex(chunks); const index = this.chunkIndex(chunks);
let xFrom = (index + 1.5) * kChunkWidth; if (index === -1) return [0, 0];
let yFrom = kChunkHeight - chunks[index].yOffset(this); const xFrom = (index + 1.5) * kChunkWidth;
const yFrom = kChunkHeight - chunks[index].yOffset(this);
return [xFrom, yFrom]; return [xFrom, yFrom];
} }
......
...@@ -124,8 +124,8 @@ class Timeline { ...@@ -124,8 +124,8 @@ class Timeline {
let currentTime = this.first().time + increment; let currentTime = this.first().time + increment;
let index = 0; let index = 0;
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
let nextIndex = this.find(currentTime, index); const nextIndex = this.find(currentTime, index);
let nextTime = currentTime + increment; const nextTime = currentTime + increment;
fn(index, nextIndex, currentTime, nextTime); fn(index, nextIndex, currentTime, nextTime);
index = nextIndex; index = nextIndex;
currentTime = nextTime; currentTime = nextTime;
...@@ -133,15 +133,16 @@ class Timeline { ...@@ -133,15 +133,16 @@ class Timeline {
} }
chunkSizes(count) { chunkSizes(count) {
let chunks = []; const chunks = [];
this.forEachChunkSize(count, (start, end) => chunks.push(end - start)); this.forEachChunkSize(count, (start, end) => chunks.push(end - start));
return chunks; return chunks;
} }
chunks(count) { chunks(count, predicate = undefined) {
let chunks = []; const chunks = [];
this.forEachChunkSize(count, (start, end, startTime, endTime) => { this.forEachChunkSize(count, (start, end, startTime, endTime) => {
let items = this._values.slice(start, end); let items = this._values.slice(start, end);
if (predicate !== undefined) items = items.filter(predicate);
chunks.push(new Chunk(chunks.length, startTime, endTime, items)); chunks.push(new Chunk(chunks.length, startTime, endTime, items));
}); });
return chunks; return chunks;
......
...@@ -30,7 +30,7 @@ export class SelectTimeEvent extends CustomEvent { ...@@ -30,7 +30,7 @@ export class SelectTimeEvent extends CustomEvent {
static get name() { static get name() {
return 'timerangeselect'; return 'timerangeselect';
} }
constructor(start = -1, end = Infinity) { constructor(start = 0, end = Infinity) {
super(SelectTimeEvent.name, {bubbles: true, composed: true}); super(SelectTimeEvent.name, {bubbles: true, composed: true});
this.start = start; this.start = start;
this.end = end; this.end = end;
......
...@@ -16,7 +16,6 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -16,7 +16,6 @@ DOM.defineCustomElement('view/timeline/timeline-track',
_selectedEntry; _selectedEntry;
_timeToPixel; _timeToPixel;
_timeStartOffset; _timeStartOffset;
_typeToColor;
_legend; _legend;
_chunkMouseMoveHandler = this._handleChunkMouseMove.bind(this); _chunkMouseMoveHandler = this._handleChunkMouseMove.bind(this);
...@@ -27,6 +26,7 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -27,6 +26,7 @@ DOM.defineCustomElement('view/timeline/timeline-track',
super(templateText); super(templateText);
this._selectionHandler = new SelectionHandler(this); this._selectionHandler = new SelectionHandler(this);
this._legend = new Legend(this.$('#legend')); this._legend = new Legend(this.$('#legend'));
this._legend.onFilter = (type) => this._handleFilterTimeline();
this.timelineNode.addEventListener( this.timelineNode.addEventListener(
'scroll', e => this._handleTimelineScroll(e)); 'scroll', e => this._handleTimelineScroll(e));
this.timelineNode.ondblclick = (e) => this.timelineNode.ondblclick = (e) =>
...@@ -34,14 +34,14 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -34,14 +34,14 @@ DOM.defineCustomElement('view/timeline/timeline-track',
this.isLocked = false; this.isLocked = false;
} }
_handleFilterTimeline(type) {
this._updateChunks();
}
set data(timeline) { set data(timeline) {
this._timeline = timeline; this._timeline = timeline;
this._typeToColor = new Map();
timeline.getBreakdown().forEach(
each => this._typeToColor.set(each.type, CSSColor.at(each.id)));
this._legend.timeline = timeline; this._legend.timeline = timeline;
this._updateChunks(); this._updateChunks();
this.update();
} }
set timeSelection(selection) { set timeSelection(selection) {
...@@ -92,7 +92,6 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -92,7 +92,6 @@ DOM.defineCustomElement('view/timeline/timeline-track',
set nofChunks(count) { set nofChunks(count) {
this._nofChunks = count; this._nofChunks = count;
this._updateChunks(); this._updateChunks();
this.update();
} }
get nofChunks() { get nofChunks() {
...@@ -100,7 +99,9 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -100,7 +99,9 @@ DOM.defineCustomElement('view/timeline/timeline-track',
} }
_updateChunks() { _updateChunks() {
this._chunks = this._timeline.chunks(this.nofChunks); this._chunks = this._timeline.chunks(
this.nofChunks, each => this._legend.filter(each));
this.update();
} }
get chunks() { get chunks() {
...@@ -142,7 +143,7 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -142,7 +143,7 @@ DOM.defineCustomElement('view/timeline/timeline-track',
let lastHeight = 0.0; let lastHeight = 0.0;
const stops = []; const stops = [];
for (let breakdown of chunk.getBreakdown(map => map.type)) { for (let breakdown of chunk.getBreakdown(map => map.type)) {
const color = CSSColor.at(breakdown.id); const color = this._legend.colorForType(breakdown.type);
increment += breakdown.count; increment += breakdown.count;
let height = (increment / total * kHeight) | 0; let height = (increment / total * kHeight) | 0;
stops.push(`${color} ${lastHeight}px ${height}px`) stops.push(`${color} ${lastHeight}px ${height}px`)
...@@ -268,7 +269,7 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -268,7 +269,7 @@ DOM.defineCustomElement('view/timeline/timeline-track',
} }
setEdgeStyle(edge, ctx) { setEdgeStyle(edge, ctx) {
let color = this._typeToColor.get(edge.type); let color = this._legend.colorForType(edge.type);
ctx.strokeStyle = color; ctx.strokeStyle = color;
ctx.fillStyle = color; ctx.fillStyle = color;
} }
...@@ -366,7 +367,7 @@ DOM.defineCustomElement('view/timeline/timeline-track', ...@@ -366,7 +367,7 @@ DOM.defineCustomElement('view/timeline/timeline-track',
ctx.lineTo(centerX + offsetX, centerY - labelOffset); ctx.lineTo(centerX + offsetX, centerY - labelOffset);
ctx.stroke(); ctx.stroke();
ctx.textAlign = 'left'; ctx.textAlign = 'left';
ctx.fillStyle = this._typeToColor.get(edge.type); ctx.fillStyle = this._legend.colorForType(edge.type);
ctx.fillText( ctx.fillText(
edge.toString(), centerX + offsetX + 2, centerY - labelOffset); edge.toString(), centerX + offsetX + 2, centerY - labelOffset);
} }
...@@ -444,7 +445,8 @@ class SelectionHandler { ...@@ -444,7 +445,8 @@ class SelectionHandler {
} }
get hasSelection() { get hasSelection() {
return this._timeSelection.start >= 0; return this._timeSelection.start >= 0 &&
this._timeSelection.end != Infinity;
} }
get _timelineNode() { get _timelineNode() {
...@@ -516,6 +518,9 @@ class SelectionHandler { ...@@ -516,6 +518,9 @@ class SelectionHandler {
class Legend { class Legend {
_timeline; _timeline;
_typesFilters = new Map();
_typeClickHandler = this._handleTypeClick.bind(this);
onFilter = () => {};
constructor(table) { constructor(table) {
this._table = table; this._table = table;
...@@ -523,34 +528,71 @@ class Legend { ...@@ -523,34 +528,71 @@ class Legend {
set timeline(timeline) { set timeline(timeline) {
this._timeline = timeline; this._timeline = timeline;
this._typesFilters =
new Map(timeline.getBreakdown().map(each => [each.type, true]));
this._colors = new Map(
timeline.getBreakdown().map(each => [each.type, CSSColor.at(each.id)]));
}
get selection() {
return this._timeline.selection ?? this._timeline;
}
colorForType(type) {
return this._colors.get(type);
}
filter(logEntry) {
return this._typesFilters.get(logEntry.type);
} }
update() { update() {
const tbody = DOM.tbody(); const tbody = DOM.tbody();
const selection = this._timeline.selection ?? this._timeline; const missingTypes = new Set(this._typesFilters.keys());
const length = selection.length; this.selection.getBreakdown().forEach(each => {
selection.getBreakdown().forEach(breakdown => { tbody.appendChild(this._breakdownRow(each));
const color = CSSColor.at(breakdown.id); missingTypes.delete(each.type);
let colorDiv;
if (color !== null) {
colorDiv = DOM.div('colorbox');
colorDiv.style.backgroundColor = color;
}
let percent = `${(breakdown.count / length * 100).toFixed(1)}%`;
tbody.appendChild(
this.row(colorDiv, breakdown.type, breakdown.count, percent));
}); });
tbody.appendChild(this.row('', 'Selection', length, '100%')); missingTypes.forEach(
tbody.appendChild(this.row('', 'All', this._timeline.length, '100%')); each => tbody.appendChild(this._row('', each, 0, '0%')));
if (this._timeline.selection) {
tbody.appendChild(
this._row('', 'Selection', this.selection.length, '100%'));
}
tbody.appendChild(this._row('', 'All', this._timeline.length, ''));
this._table.tBodies[0].replaceWith(tbody); this._table.tBodies[0].replaceWith(tbody);
} }
row(color, type, count, percent) { _row(color, type, count, percent) {
const row = DOM.tr(); const row = DOM.tr();
row.appendChild(DOM.td(color)); row.appendChild(DOM.td(color));
row.appendChild(DOM.td(type)); row.appendChild(DOM.td(type));
row.appendChild(DOM.td(count)); row.appendChild(DOM.td(count.toString()));
row.appendChild(DOM.td(percent)); row.appendChild(DOM.td(percent));
return row return row
} }
_breakdownRow(breakdown) {
const color = this.colorForType(breakdown.type);
const colorDiv = DOM.div('colorbox');
if (this._typesFilters.get(breakdown.type)) {
colorDiv.style.backgroundColor = color;
} else {
colorDiv.style.borderColor = color;
colorDiv.style.backgroundColor = CSSColor.backgroundImage;
}
let percent =
`${(breakdown.count / this.selection.length * 100).toFixed(1)}%`;
const row = this._row(colorDiv, breakdown.type, breakdown.count, percent);
row.className = 'clickable';
row.onclick = this._typeClickHandler;
row.data = breakdown.type;
return row;
}
_handleTypeClick(e) {
const type = e.currentTarget.data;
this._typesFilters.set(type, !this._typesFilters.get(type));
this.onFilter(type);
}
} }
\ 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