v8/tools/system-analyzer/ic-panel.mjs
zeynepCankara e7357f1902 [tools] Add Indicium
Indicium is a new tool that integrates all our Map and IC processing
tools into one tool.

This CL does not attempt to cleanly integrate the Map Processor
and IC explorer, but provides an in initial starting point for further
integration work.

Bug: v8:10644
Change-Id: I753c116fd409c8c07613bf15f22e14aa1e8c8a0e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2259935
Commit-Queue: Sathya Gunasekaran  <gsathya@chromium.org>
Reviewed-by: Sathya Gunasekaran  <gsathya@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68605}
2020-06-30 09:51:27 +00:00

218 lines
5.8 KiB
JavaScript

// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {Group, properties} from './ic-model.js';
defineCustomElement('ic-panel', (templateText) =>
class ICPanel extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = templateText;
this.groupKeySelect.addEventListener(
'change', e => this.updateTable(e));
this.filterICBtnSelect.addEventListener(
'click', e => this.handleICFilter(e));
this._noOfItems = 100;
}
$(id) {
return this.shadowRoot.querySelector(id);
}
querySelectorAll(query) {
return this.shadowRoot.querySelectorAll(query);
}
get groupKeySelect() {
return this.$('#group-key');
}
get filterICBtnSelect() {
return this.$('#filterICBtn');
}
get tableSelect() {
return this.$('#table');
}
get tableBodySelect() {
return this.$('#table-body');
}
get countSelect() {
return this.$('#count');
}
get spanSelectAll(){
return this.querySelectorAll("span");
}
updateTable(event) {
let select = this.groupKeySelect;
let key = select.options[select.selectedIndex].text;
let tableBody = this.tableBodySelect;
this.removeAllChildren(tableBody);
let groups = Group.groupBy(entries, key, true);
this.display(groups, tableBody);
}
escapeHtml(unsafe) {
if (!unsafe) return "";
return unsafe.toString()
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
processValue(unsafe) {
if (!unsafe) return "";
if (!unsafe.startsWith("http")) return this.escapeHtml(unsafe);
let a = document.createElement("a");
a.href = unsafe;
a.textContent = unsafe;
return a;
}
removeAllChildren(node) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
}
td(tr, content, className) {
let node = document.createElement("td");
if (typeof content == "object") {
node.appendChild(content);
} else {
node.innerHTML = content;
}
node.className = className;
tr.appendChild(node);
return node
}
set noOfItems(value){
this._noOfItems = value;
}
get noOfItems(){
return this._noOfItems;
}
display(entries, parent) {
let fragment = document.createDocumentFragment();
let max = Math.min(this.noOfItems, entries.length)
for (let i = 0; i < max; i++) {
let entry = entries[i];
let tr = document.createElement("tr");
tr.entry = entry;
let details = this.td(tr,'<span>&#8505;</a>', 'details');
details.onclick = _ => this.toggleDetails(details);
this.td(tr, entry.percentage + "%", 'percentage');
this.td(tr, entry.count, 'count');
this.td(tr, this.processValue(entry.key), 'key');
fragment.appendChild(tr);
}
let omitted = entries.length - max;
if (omitted > 0) {
let tr = document.createElement("tr");
let tdNode = td(tr, 'Omitted ' + omitted + " entries.");
tdNode.colSpan = 4;
fragment.appendChild(tr);
}
parent.appendChild(fragment);
}
displayDrilldown(entry, previousSibling) {
let tr = document.createElement('tr');
tr.className = "entry-details";
tr.style.display = "none";
// indent by one td.
tr.appendChild(document.createElement("td"));
let td = document.createElement("td");
td.colSpan = 3;
for (let key in entry.groups) {
td.appendChild(this.displayDrilldownGroup(entry, key));
}
tr.appendChild(td);
// Append the new TR after previousSibling.
previousSibling.parentNode.insertBefore(tr, previousSibling.nextSibling)
}
displayDrilldownGroup(entry, key) {
let max = 20;
let group = entry.groups[key];
let div = document.createElement("div")
div.className = 'drilldown-group-title'
div.textContent = key + ' [top ' + max + ' out of ' + group.length + ']';
let table = document.createElement("table");
this.display(group.slice(0, max), table, false)
div.appendChild(table);
return div;
}
toggleDetails(node) {
let tr = node.parentNode;
let entry = tr.entry;
// Create subgroup in-place if the don't exist yet.
if (entry.groups === undefined) {
entry.createSubGroups();
this.displayDrilldown(entry, tr);
}
let details = tr.nextSibling;
let display = details.style.display;
if (display != "none") {
display = "none";
} else {
display = "table-row"
};
details.style.display = display;
}
initGroupKeySelect() {
let select = this.groupKeySelect;
select.options.length = 0;
for (let i in properties) {
let option = document.createElement("option");
option.text = properties[i];
select.add(option);
}
}
//TODO(zc): Function processing the timestamps of ICEvents
// Processes the IC Events which have V8Map's in the map-processor
processICEventTime(){
let ICTimeToEvent = new Map();
// save the occurance time of V8Maps
let eventTimes = []
console.log("Num of stats: " + entries.length);
// fetch V8 Maps from the IC Events
entries.forEach(element => {
let v8Map = V8Map.get("0x" + element.map);
if(!v8Map){
ICTimeToEvent.set(-1, element);
} else {
ICTimeToEvent.set(v8Map.time, element);
eventTimes.push(v8Map.time);
}
});
eventTimes.sort();
// save the IC events which have Map states
let eventsList = [];
for(let i = 0; i < eventTimes.length; i++){
eventsList.push(ICTimeToEvent.get(eventTimes[i]));
}
return eventList;
}
handleICFilter(e){
let noOfItemsInput = parseInt(this.$('#filter-input').value);
this.noOfItems = noOfItemsInput;
this.updateTable(e);
}
});