[tools][system-analyzer] Various improvements
- Change Group.prototype.size to .length - Use window.requestAnimationFrame when streaming-loading files to show the loading animation - Limit width of the timeline-track legend and add 'title' attribute to show the full text when cropped - Add duration for selected timeline events in timeline-track legend - Better error message when the local symbol server is not available Bug: v8:10644 Change-Id: Icdf2042341c9355ecb55e2fd8e6a4fa0feb5968f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3003151 Reviewed-by: Patrick Thier <pthier@chromium.org> Commit-Queue: Camillo Bruni <cbruni@chromium.org> Cr-Commit-Position: refs/heads/master@{#75549}
This commit is contained in:
parent
3e1d2221ac
commit
212d6678e7
@ -63,21 +63,21 @@ export class Group {
|
||||
constructor(key, id, parentTotal, entries) {
|
||||
this.key = key;
|
||||
this.id = id;
|
||||
this.count = 1;
|
||||
this.entries = entries;
|
||||
this.length = entries.length;
|
||||
this.parentTotal = parentTotal;
|
||||
}
|
||||
|
||||
get percent() {
|
||||
return this.count / this.parentTotal * 100;
|
||||
return this.length / this.parentTotal * 100;
|
||||
}
|
||||
|
||||
add() {
|
||||
this.count++;
|
||||
this.length++;
|
||||
}
|
||||
|
||||
addEntry(entry) {
|
||||
this.count++;
|
||||
this.length++;
|
||||
this.entries.push(entry);
|
||||
}
|
||||
}
|
||||
@ -87,6 +87,7 @@ export function groupBy(array, keyFunction, collect = false) {
|
||||
if (keyFunction === undefined) keyFunction = each => each;
|
||||
const keyToGroup = new Map();
|
||||
const groups = [];
|
||||
const sharedEmptyArray = [];
|
||||
let id = 0;
|
||||
// This is performance critical, resorting to for-loop
|
||||
for (let each of array) {
|
||||
@ -96,8 +97,7 @@ export function groupBy(array, keyFunction, collect = false) {
|
||||
collect ? group.addEntry(each) : group.add();
|
||||
continue;
|
||||
}
|
||||
let entries = undefined;
|
||||
if (collect) entries = [each];
|
||||
let entries = collect ? [each] : sharedEmptyArray;
|
||||
group = new Group(key, id++, array.length, entries);
|
||||
groups.push(group);
|
||||
keyToGroup.set(key, group);
|
||||
|
@ -207,8 +207,13 @@ class Timeline {
|
||||
return minIndex;
|
||||
}
|
||||
|
||||
getBreakdown(keyFunction) {
|
||||
if (keyFunction) return groupBy(this._values, keyFunction);
|
||||
getBreakdown(keyFunction, collect = false) {
|
||||
if (keyFunction || collect) {
|
||||
if (!keyFunction) {
|
||||
keyFunction = each => each.type;
|
||||
}
|
||||
return groupBy(this._values, keyFunction, collect);
|
||||
}
|
||||
if (this._breakdown === undefined) {
|
||||
this._breakdown = groupBy(this._values, each => each.type);
|
||||
}
|
||||
|
@ -458,7 +458,7 @@ export function gradientStopsFromGroups(
|
||||
const stops = [];
|
||||
for (let group of groups) {
|
||||
const color = colorFn(group.key);
|
||||
increment += group.count;
|
||||
increment += group.length;
|
||||
const height = (increment / totalLength * kMaxHeight) | 0;
|
||||
stops.push(`${color} ${lastHeight}${kUnit} ${height}${kUnit}`)
|
||||
lastHeight = height;
|
||||
|
@ -176,7 +176,7 @@ DOM.defineCustomElement('view/list-panel',
|
||||
_render(groups, table) {
|
||||
let last;
|
||||
new LazyTable(table, groups, group => {
|
||||
if (last && last.count < group.count) {
|
||||
if (last && last.count < group.length) {
|
||||
console.log(last, group);
|
||||
}
|
||||
last = group;
|
||||
@ -185,7 +185,7 @@ DOM.defineCustomElement('view/list-panel',
|
||||
const details = tr.appendChild(DOM.td('', 'toggle'));
|
||||
details.onclick = this._detailsClickHandler;
|
||||
tr.appendChild(DOM.td(`${group.percent.toFixed(2)}%`, 'percentage'));
|
||||
tr.appendChild(DOM.td(group.count, 'count'));
|
||||
tr.appendChild(DOM.td(group.length, 'count'));
|
||||
const valueTd = tr.appendChild(DOM.td(group.key?.toString(), 'key'));
|
||||
if (App.isClickable(group.key)) {
|
||||
tr.onclick = this._logEntryClickHandler;
|
||||
|
@ -66,12 +66,11 @@ DOM.defineCustomElement('view/log-file-reader',
|
||||
}
|
||||
this.fileReader.blur();
|
||||
this.root.className = 'loading';
|
||||
this.asyncReadFile(file);
|
||||
// Delay the loading a bit to allow for CSS animations to happen.
|
||||
window.requestAnimationFrame(() => this.asyncReadFile(file));
|
||||
}
|
||||
|
||||
async asyncReadFile(file) {
|
||||
// Delay the loading a bit to allow for CSS animations to happen.
|
||||
await delay(5);
|
||||
const decoder = globalThis.TextDecoderStream;
|
||||
if (decoder) {
|
||||
await this._streamFile(file, decoder);
|
||||
|
@ -151,10 +151,10 @@ DOM.defineCustomElement('view/script-panel',
|
||||
const entries = sourcePosition.entries;
|
||||
let text = groupBy(entries, each => each.constructor, true)
|
||||
.map(group => {
|
||||
let text = `${group.key.name}: ${group.count}\n`
|
||||
let text = `${group.key.name}: ${group.length}\n`
|
||||
text += groupBy(group.entries, each => each.type, true)
|
||||
.map(group => {
|
||||
return ` - ${group.key}: ${group.count}`;
|
||||
return ` - ${group.key}: ${group.length}`;
|
||||
})
|
||||
.join('\n');
|
||||
return text;
|
||||
|
@ -5,7 +5,7 @@
|
||||
import {delay} from '../../helper.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';
|
||||
import {CSSColor, DOM, formatDurationMicros, SVG, V8CustomElement} from '../helper.mjs';
|
||||
|
||||
export const kTimelineHeight = 200;
|
||||
|
||||
@ -242,8 +242,8 @@ export class TimelineTrackBase extends V8CustomElement {
|
||||
let lastHeight = kTimelineHeight;
|
||||
for (let i = 0; i < groups.length; i++) {
|
||||
const group = groups[i];
|
||||
if (group.count == 0) break;
|
||||
const height = (group.count / chunk.size() * kHeight) | 0;
|
||||
if (group.length == 0) break;
|
||||
const height = (group.length / chunk.size() * kHeight) | 0;
|
||||
lastHeight -= height;
|
||||
const color = this._legend.colorForType(group.key);
|
||||
buffer += `<rect x=${chunkIndex * kChunkWidth} y=${lastHeight} height=${
|
||||
@ -511,6 +511,7 @@ class Legend {
|
||||
|
||||
constructor(table) {
|
||||
this._table = table;
|
||||
this._enableDuration = false;
|
||||
}
|
||||
|
||||
set timeline(timeline) {
|
||||
@ -548,10 +549,12 @@ class Legend {
|
||||
update() {
|
||||
const tbody = DOM.tbody();
|
||||
const missingTypes = new Set(this._typesFilters.keys());
|
||||
this.selection.getBreakdown().forEach(group => {
|
||||
tbody.appendChild(this._addTypeRow(group));
|
||||
missingTypes.delete(group.key);
|
||||
});
|
||||
this._checkDurationField();
|
||||
this.selection.getBreakdown(undefined, this._enableDuration)
|
||||
.forEach(group => {
|
||||
tbody.appendChild(this._addTypeRow(group));
|
||||
missingTypes.delete(group.key);
|
||||
});
|
||||
missingTypes.forEach(key => tbody.appendChild(this._row('', key, 0, '0%')));
|
||||
if (this._timeline.selection) {
|
||||
tbody.appendChild(
|
||||
@ -561,12 +564,26 @@ class Legend {
|
||||
this._table.tBodies[0].replaceWith(tbody);
|
||||
}
|
||||
|
||||
_row(color, type, count, percent) {
|
||||
_checkDurationField() {
|
||||
if (this._enableDuration) return;
|
||||
const example = this.selection.at(0);
|
||||
if (!example || !('duration' in example)) return;
|
||||
this._enableDuration = true;
|
||||
this._table.tHead.appendChild(DOM.td('Duration'));
|
||||
this._table.tHead.appendChild(DOM.td(''));
|
||||
}
|
||||
|
||||
_row(colorNode, type, count, countPercent, duration, durationPercent) {
|
||||
const row = DOM.tr();
|
||||
row.appendChild(DOM.td(color));
|
||||
row.appendChild(DOM.td(type));
|
||||
row.appendChild(DOM.td(colorNode));
|
||||
const typeCell = row.appendChild(DOM.td(type));
|
||||
typeCell.setAttribute('title', type);
|
||||
row.appendChild(DOM.td(count.toString()));
|
||||
row.appendChild(DOM.td(percent));
|
||||
row.appendChild(DOM.td(countPercent));
|
||||
if (this._enableDuration) {
|
||||
row.appendChild(DOM.td(formatDurationMicros(duration ?? 0)));
|
||||
row.appendChild(DOM.td(durationPercent ?? '0%'));
|
||||
}
|
||||
return row
|
||||
}
|
||||
|
||||
@ -579,8 +596,17 @@ class Legend {
|
||||
colorDiv.style.borderColor = color;
|
||||
colorDiv.style.backgroundColor = CSSColor.backgroundImage;
|
||||
}
|
||||
let percent = `${(group.count / this.selection.length * 100).toFixed(1)}%`;
|
||||
const row = this._row(colorDiv, group.key, group.count, percent);
|
||||
let duration = 0;
|
||||
if (this._enableDuration) {
|
||||
const entries = group.entries;
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
duration += entries[i].duration;
|
||||
}
|
||||
}
|
||||
let countPercent =
|
||||
`${(group.length / this.selection.length * 100).toFixed(1)}%`;
|
||||
const row = this._row(
|
||||
colorDiv, group.key, group.length, countPercent, duration, '');
|
||||
row.className = 'clickable';
|
||||
row.onclick = this._typeClickHandler;
|
||||
row.data = group.key;
|
||||
|
@ -93,6 +93,9 @@ found in the LICENSE file. -->
|
||||
#legendTable td:nth-of-type(4n+2) {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
max-width: 200px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* right align numbers */
|
||||
#legendTable td:nth-of-type(4n+3),
|
||||
@ -225,7 +228,7 @@ found in the LICENSE file. -->
|
||||
<td></td>
|
||||
<td>Type</td>
|
||||
<td>Count</td>
|
||||
<td>Percent</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
|
@ -138,6 +138,10 @@ class CppEntriesProvider {
|
||||
let json;
|
||||
try {
|
||||
response = await fetch(url);
|
||||
if (response.status == 404) {
|
||||
throw new Error(
|
||||
`Local symbol server returned 404: ${await response.text()}`);
|
||||
}
|
||||
json = await response.json();
|
||||
if (json.error) console.warn(json.error);
|
||||
} catch (e) {
|
||||
@ -145,6 +149,7 @@ class CppEntriesProvider {
|
||||
// Assume that the local symbol server is not reachable.
|
||||
console.error("Disabling remote symbol loading:", e);
|
||||
this._isEnabled = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
this._handleRemoteSymbolsResult(json);
|
||||
|
Loading…
Reference in New Issue
Block a user