<html> <!-- Copyright 2016 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. --> <head> <style> .entry-details {} .entry-details TD {} .details { width: 2em; border: 1px black dotted; } .count { text-align: right; width: 5em; font-family: monospace; } .percentage { text-align: right; width: 5em; font-family: monospace; } .key { padding-left: 1em; } .drilldown-group-title { font-weight: bold; padding: 0.5em 0 0.2em 0; } </style> <script> "use strict" var entries = []; var properties = ['type', 'category', 'file', 'filePosition', 'state', 'key', 'isNative', 'map', 'propertiesMode', 'numberOfOwnProperties', 'instanceType' ] class Entry { constructor(id, line) { this.id = id; this.line = line; var parts = line.split(" "); if (parts.length < 6) return this.isValid = false; if (parts[0][0] !== "[") return; if (parts[1] === "patching") return; this.type = parts[0].substr(1); this.category = "unknown"; this.map = "unknown"; this.propertiesMode = "unknown"; this.numberOfOwnProperties = 0; this.instanceType = "unknown"; if (this.type.indexOf("Store") !== -1) { this.category = "Store"; } else if (this.type.indexOf("Load") !== -1) { this.category = "Load"; } if (this.type.length == 0) return; if (this.type.indexOf('BinaryOpIC(') === 0) { this.type = "BinaryOpIC"; var split = parts[0].split('('); this.state = "(" + split[1] + " => " + parts[2]; var offset = this.parsePositionAndFile(parts, 6); if (offset == -1) return if (this.file === undefined) return this.file = this.file.slice(0, -1); } else { var offset = this.parsePositionAndFile(parts, 2); if (offset == -1) return this.state = parts[++offset]; var mapPart = parts[offset + 1]; if (mapPart !== undefined && mapPart.startsWith("map=")) { if (mapPart[4] == "(") { if (mapPart.endsWith(")")) { this.map = mapPart.substr(5, mapPart.length-6); } else { this.map = mapPart.substr(5); } offset++; offset = this.parseMapProperties(parts, offset); } else { this.map = mapPart.substr(4); offset++; } if (this.map == "(nil)") this.map = "unknown"; } if (this.type !== "CompareIC") { // if there is no address we have a smi key var address = parts[++offset]; if (address !== undefined && address.indexOf("0x") === 0) { this.key = parts.slice(++offset).join(" "); } else { this.key = address; } } } this.filePosition = this.file + " " + this.position; if (this.key) { var isStringKey = false if (this.key.indexOf("<String[") === 0) { isStringKey = true; this.key = "\"" + this.key.slice(this.key.indexOf(']') + 3); } else if (this.key.indexOf("<") === 0) { this.key = this.key.slice(1); } if (this.key.endsWith(">]")) { this.key = this.key.slice(0, -2); } else if (this.key.endsWith("]")) { this.key = this.key.slice(0, -1); } if (isStringKey) { this.key = this.key + "\""; } } this.isValid = true; } parseMapProperties(parts, offset) { var next = parts[++offset]; if (!next.startsWith('dict')) return offset; this.propertiesMode = next.substr(5) == "0" ? "fast" : "slow"; this.numberOfOwnProperties = parts[++offset].substr(4); next = parts[++offset]; this.instanceType = next.substr(5, next.length-6); return offset; } parsePositionAndFile(parts, start) { // find the position of 'at' in the parts array. var offset = start; for (var i = start + 1; i < parts.length; i++) { offset++; if (parts[i] == 'at') break; } if (parts[offset] !== 'at') return -1; this.position = parts.slice(start, offset).join(' '); offset += 1; this.isNative = parts[offset] == "native" offset += this.isNative ? 1 : 0; this.file = parts[offset]; return offset; } } function loadFile() { var files = document.getElementById("uploadInput").files; var file = files[0]; var reader = new FileReader(); reader.onload = function(evt) { entries = []; var end = this.result.length; var current = 0; var next = 0; var line; var i = 0; var entry; while (current < end) { next = this.result.indexOf("\n", current); if (next === -1) break; i++; line = this.result.substring(current, next); current = next + 1; entry = new Entry(i, line); if (entry.isValid) entries.push(entry); } document.getElementById("count").innerHTML = i; updateTable(); } reader.readAsText(file); initGroupKeySelect(); } class Group { constructor(property, key, entry) { this.property = property; this.key = key; this.count = 1; this.entries = [entry]; this.percentage = undefined; this.groups = undefined; } add(entry) { this.count++; this.entries.push(entry) } createSubGroups() { this.groups = {}; for (var i = 0; i < properties.length; i++) { var subProperty = properties[i]; if (this.property == subProperty) continue; this.groups[subProperty] = groupBy(this.entries, subProperty); } } } function groupBy(entries, property) { var accumulator = {}; accumulator.__proto__ = null; var length = entries.length; for (var i = 0; i < length; i++) { var entry = entries[i]; var key = entry[property]; if (accumulator[key] == undefined) { accumulator[key] = new Group(property, key, entry) } else { var group = accumulator[key]; if (group.entries == undefined) console.log([group, entry]); group.add(entry) } } var result = [] for (var key in accumulator) { var group = accumulator[key]; group.percentage = Math.round(group.count / length * 100 * 100) / 100; result.push(group); } result.sort((a, b) => { return b.count - a.count }); return result; } function escapeHtml(unsafe) { if (!unsafe) return ""; return unsafe.toString() .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function updateTable() { var select = document.getElementById("group-key"); var key = select.options[select.selectedIndex].text; console.log(key); var tableBody = document.getElementById("table-body"); removeAllChildren(tableBody); var groups = groupBy(entries, key, true); display(groups, tableBody); } function selecedOption(node) { return node.options[node.selectedIndex] } function removeAllChildren(node) { while (node.firstChild) { node.removeChild(node.firstChild); } } function display(entries, parent) { var fragment = document.createDocumentFragment(); function td(tr, content, className) { var td = document.createElement("td"); td.innerHTML = content; td.className = className tr.appendChild(td); return td } var max = Math.min(1000, entries.length) for (var i = 0; i < max; i++) { var entry = entries[i]; var tr = document.createElement("tr"); tr.entry = entry; td(tr, '<span onclick="toggleDetails(this)">details</a>', 'details'); td(tr, entry.percentage + "%", 'percentage'); td(tr, entry.count, 'count'); td(tr, escapeHtml(entry.key), 'key'); fragment.appendChild(tr); } var omitted = entries.length - max; if (omitted > 0) { var tr = document.createElement("tr"); var td = td(tr, 'Omitted ' + omitted + " entries."); td.colSpan = 4; fragment.appendChild(tr); } parent.appendChild(fragment); } function displayDrilldown(entry, previousSibling) { var tr = document.createElement('tr'); tr.className = "entry-details"; tr.style.display = "none"; // indent by one td. tr.appendChild(document.createElement("td")); var td = document.createElement("td"); td.colSpan = 3; for (var key in entry.groups) { td.appendChild(displayDrilldownGroup(entry, key)); } tr.appendChild(td); // Append the new TR after previousSibling. previousSibling.parentNode.insertBefore(tr, previousSibling.nextSibling) } function displayDrilldownGroup(entry, key) { var max = 20; var group = entry.groups[key]; var div = document.createElement("div") div.className = 'drilldown-group-title' div.innerHTML = key + ' [top ' + max + ' out of ' + group.length + ']'; var table = document.createElement("table"); display(group.slice(0, max), table, false) div.appendChild(table); return div; } function toggleDetails(node) { var tr = node.parentNode.parentNode; var entry = tr.entry; // Create subgroup in-place if the don't exist yet. if (entry.groups === undefined) { entry.createSubGroups(); displayDrilldown(entry, tr); } var details = tr.nextSibling; var display = details.style.display; if (display != "none") { display = "none"; } else { display = "table-row" }; details.style.display = display; } function initGroupKeySelect() { var select = document.getElementById("group-key"); for (var i in properties) { var option = document.createElement("option"); option.text = properties[i]; select.add(option); } } function handleOnLoad() { document.querySelector("#uploadInput").focus(); } </script> </head> <body onload="handleOnLoad()"> <h1> <span style="color: #00FF00">I</span> <span style="color: #FF00FF">C</span> <span style="color: #00FFFF">E</span> </h1> Your IC-Explorer. <div id="legend" style="padding-right: 200px"> <div style="float:right; border-style: solid; border-width: 1px; padding:20px"> 0 uninitialized<br> . premonomorphic<br> 1 monomorphic<br> ^ recompute handler<br> P polymorphic<br> N megamorphic<br> G generic </div> </div> <h2>Usage</h2> Run your script with <code>--trace_ic</code> and upload on this page:<br/> <code>/path/to/d8 --trace_ic your_script.js > trace.txt</code> <h2>Data</h2> <form name="fileForm"> <p> <input id="uploadInput" type="file" name="files" onchange="loadFile();"> trace entries: <span id="count">0</span> </p> </form> <h2>Result</h2> <p> Group-Key: <select id="group-key" onchange="updateTable()"></select> </p> <p> <table id="table" width="100%"> <tbody id="table-body"> </tbody> </table> </p> </body> </html>