1 <html> 2 <!-- 3 Copyright 2016 the V8 project authors. All rights reserved. Use of this source 4 code is governed by a BSD-style license that can be found in the LICENSE file. 5 --> 6 7 <head> 8 <style> 9 .entry-details {} 10 11 .entry-details TD {} 12 13 .details { 14 width: 2em; 15 border: 1px black dotted; 16 } 17 18 .count { 19 text-align: right; 20 width: 5em; 21 font-family: monospace; 22 } 23 24 .percentage { 25 text-align: right; 26 width: 5em; 27 font-family: monospace; 28 } 29 30 .key { 31 padding-left: 1em; 32 } 33 34 .drilldown-group-title { 35 font-weight: bold; 36 padding: 0.5em 0 0.2em 0; 37 } 38 </style> 39 <script> 40 "use strict" 41 var entries = []; 42 43 var properties = ['type', 'category', 'file', 'filePosition', 'state', 44 'key', 'isNative', 'map', 'propertiesMode', 'numberOfOwnProperties', 45 'instanceType' 46 ] 47 48 class Entry { 49 constructor(id, line) { 50 this.id = id; 51 this.line = line; 52 var parts = line.split(" "); 53 if (parts.length < 6) return 54 this.isValid = false; 55 if (parts[0][0] !== "[") return; 56 if (parts[1] === "patching") return; 57 this.type = parts[0].substr(1); 58 this.category = "unknown"; 59 this.map = "unknown"; 60 this.propertiesMode = "unknown"; 61 this.numberOfOwnProperties = 0; 62 this.instanceType = "unknown"; 63 if (this.type.indexOf("Store") !== -1) { 64 this.category = "Store"; 65 } else if (this.type.indexOf("Load") !== -1) { 66 this.category = "Load"; 67 } 68 if (this.type.length == 0) return; 69 if (this.type.indexOf('BinaryOpIC(') === 0) { 70 this.type = "BinaryOpIC"; 71 var split = parts[0].split('('); 72 this.state = "(" + split[1] + " => " + parts[2]; 73 var offset = this.parsePositionAndFile(parts, 6); 74 if (offset == -1) return 75 if (this.file === undefined) return 76 this.file = this.file.slice(0, -1); 77 } else { 78 var offset = this.parsePositionAndFile(parts, 2); 79 if (offset == -1) return 80 this.state = parts[++offset]; 81 var mapPart = parts[offset + 1]; 82 if (mapPart !== undefined && mapPart.startsWith("map=")) { 83 if (mapPart[4] == "(") { 84 if (mapPart.endsWith(")")) { 85 this.map = mapPart.substr(5, mapPart.length-6); 86 } else { 87 this.map = mapPart.substr(5); 88 } 89 offset++; 90 offset = this.parseMapProperties(parts, offset); 91 } else { 92 this.map = mapPart.substr(4); 93 offset++; 94 } 95 if (this.map == "(nil)") this.map = "unknown"; 96 } 97 if (this.type !== "CompareIC") { 98 // if there is no address we have a smi key 99 var address = parts[++offset]; 100 if (address !== undefined && address.indexOf("0x") === 0) { 101 this.key = parts.slice(++offset).join(" "); 102 } else { 103 this.key = address; 104 } 105 } 106 } 107 this.filePosition = this.file + " " + this.position; 108 if (this.key) { 109 var isStringKey = false 110 if (this.key.indexOf("<String[") === 0) { 111 isStringKey = true; 112 this.key = "\"" + this.key.slice(this.key.indexOf(']') + 3); 113 } else if (this.key.indexOf("<") === 0) { 114 this.key = this.key.slice(1); 115 } 116 if (this.key.endsWith(">]")) { 117 this.key = this.key.slice(0, -2); 118 } else if (this.key.endsWith("]")) { 119 this.key = this.key.slice(0, -1); 120 } 121 if (isStringKey) { 122 this.key = this.key + "\""; 123 } 124 } 125 this.isValid = true; 126 } 127 128 parseMapProperties(parts, offset) { 129 var next = parts[++offset]; 130 if (!next.startsWith('dict')) return offset; 131 this.propertiesMode = 132 next.substr(5) == "0" ? "fast" : "slow"; 133 this.numberOfOwnProperties = parts[++offset].substr(4); 134 next = parts[++offset]; 135 this.instanceType = next.substr(5, next.length-6); 136 return offset; 137 } 138 139 parsePositionAndFile(parts, start) { 140 // find the position of 'at' in the parts array. 141 var offset = start; 142 for (var i = start + 1; i < parts.length; i++) { 143 offset++; 144 if (parts[i] == 'at') break; 145 } 146 if (parts[offset] !== 'at') return -1; 147 this.position = parts.slice(start, offset).join(' '); 148 offset += 1; 149 this.isNative = parts[offset] == "native" 150 offset += this.isNative ? 1 : 0; 151 this.file = parts[offset]; 152 return offset; 153 } 154 } 155 156 function loadFile() { 157 var files = document.getElementById("uploadInput").files; 158 159 var file = files[0]; 160 var reader = new FileReader(); 161 162 reader.onload = function(evt) { 163 entries = []; 164 var end = this.result.length; 165 var current = 0; 166 var next = 0; 167 var line; 168 var i = 0; 169 var entry; 170 while (current < end) { 171 next = this.result.indexOf("\n", current); 172 if (next === -1) break; 173 i++; 174 line = this.result.substring(current, next); 175 current = next + 1; 176 entry = new Entry(i, line); 177 if (entry.isValid) entries.push(entry); 178 } 179 180 document.getElementById("count").innerHTML = i; 181 updateTable(); 182 } 183 reader.readAsText(file); 184 initGroupKeySelect(); 185 } 186 187 188 class Group { 189 constructor(property, key, entry) { 190 this.property = property; 191 this.key = key; 192 this.count = 1; 193 this.entries = [entry]; 194 this.percentage = undefined; 195 this.groups = undefined; 196 } 197 198 add(entry) { 199 this.count++; 200 this.entries.push(entry) 201 } 202 203 createSubGroups() { 204 this.groups = {}; 205 for (var i = 0; i < properties.length; i++) { 206 var subProperty = properties[i]; 207 if (this.property == subProperty) continue; 208 this.groups[subProperty] = groupBy(this.entries, subProperty); 209 } 210 } 211 } 212 213 function groupBy(entries, property) { 214 var accumulator = {}; 215 accumulator.__proto__ = null; 216 var length = entries.length; 217 for (var i = 0; i < length; i++) { 218 var entry = entries[i]; 219 var key = entry[property]; 220 if (accumulator[key] == undefined) { 221 accumulator[key] = new Group(property, key, entry) 222 } else { 223 var group = accumulator[key]; 224 if (group.entries == undefined) console.log([group, entry]); 225 group.add(entry) 226 } 227 } 228 var result = [] 229 for (var key in accumulator) { 230 var group = accumulator[key]; 231 group.percentage = Math.round(group.count / length * 100 * 100) / 100; 232 result.push(group); 233 } 234 result.sort((a, b) => { 235 return b.count - a.count 236 }); 237 return result; 238 } 239 240 241 242 function escapeHtml(unsafe) { 243 if (!unsafe) return ""; 244 return unsafe.toString() 245 .replace(/&/g, "&") 246 .replace(/</g, "<") 247 .replace(/>/g, ">") 248 .replace(/"/g, """) 249 .replace(/'/g, "'"); 250 } 251 252 function updateTable() { 253 var select = document.getElementById("group-key"); 254 var key = select.options[select.selectedIndex].text; 255 console.log(key); 256 var tableBody = document.getElementById("table-body"); 257 removeAllChildren(tableBody); 258 var groups = groupBy(entries, key, true); 259 display(groups, tableBody); 260 } 261 262 function selecedOption(node) { 263 return node.options[node.selectedIndex] 264 } 265 266 function removeAllChildren(node) { 267 while (node.firstChild) { 268 node.removeChild(node.firstChild); 269 } 270 } 271 272 function display(entries, parent) { 273 var fragment = document.createDocumentFragment(); 274 275 function td(tr, content, className) { 276 var td = document.createElement("td"); 277 td.innerHTML = content; 278 td.className = className 279 tr.appendChild(td); 280 return td 281 } 282 var max = Math.min(1000, entries.length) 283 for (var i = 0; i < max; i++) { 284 var entry = entries[i]; 285 var tr = document.createElement("tr"); 286 tr.entry = entry; 287 td(tr, 'details', 'details'); 288 td(tr, entry.percentage + "%", 'percentage'); 289 td(tr, entry.count, 'count'); 290 td(tr, escapeHtml(entry.key), 'key'); 291 fragment.appendChild(tr); 292 } 293 var omitted = entries.length - max; 294 if (omitted > 0) { 295 var tr = document.createElement("tr"); 296 var td = td(tr, 'Omitted ' + omitted + " entries."); 297 td.colSpan = 4; 298 fragment.appendChild(tr); 299 } 300 parent.appendChild(fragment); 301 } 302 303 function displayDrilldown(entry, previousSibling) { 304 var tr = document.createElement('tr'); 305 tr.className = "entry-details"; 306 tr.style.display = "none"; 307 // indent by one td. 308 tr.appendChild(document.createElement("td")); 309 var td = document.createElement("td"); 310 td.colSpan = 3; 311 for (var key in entry.groups) { 312 td.appendChild(displayDrilldownGroup(entry, key)); 313 } 314 tr.appendChild(td); 315 // Append the new TR after previousSibling. 316 previousSibling.parentNode.insertBefore(tr, previousSibling.nextSibling) 317 } 318 319 function displayDrilldownGroup(entry, key) { 320 var max = 20; 321 var group = entry.groups[key]; 322 var div = document.createElement("div") 323 div.className = 'drilldown-group-title' 324 div.innerHTML = key + ' [top ' + max + ' out of ' + group.length + ']'; 325 var table = document.createElement("table"); 326 display(group.slice(0, max), table, false) 327 div.appendChild(table); 328 return div; 329 } 330 331 function toggleDetails(node) { 332 var tr = node.parentNode.parentNode; 333 var entry = tr.entry; 334 335 // Create subgroup in-place if the don't exist yet. 336 if (entry.groups === undefined) { 337 entry.createSubGroups(); 338 displayDrilldown(entry, tr); 339 } 340 var details = tr.nextSibling; 341 var display = details.style.display; 342 if (display != "none") { 343 display = "none"; 344 } else { 345 display = "table-row" 346 }; 347 details.style.display = display; 348 } 349 350 function initGroupKeySelect() { 351 var select = document.getElementById("group-key"); 352 for (var i in properties) { 353 var option = document.createElement("option"); 354 option.text = properties[i]; 355 select.add(option); 356 } 357 } 358 359 function handleOnLoad() { 360 document.querySelector("#uploadInput").focus(); 361 } 362 </script> 363 </head> 364 365 <body onload="handleOnLoad()"> 366 <h1> 367 <span style="color: #00FF00">I</span> 368 <span style="color: #FF00FF">C</span> 369 <span style="color: #00FFFF">E</span> 370 </h1> Your IC-Explorer. 371 372 <div id="legend" style="padding-right: 200px"> 373 <div style="float:right; border-style: solid; border-width: 1px; padding:20px"> 374 0 uninitialized<br> 375 . premonomorphic<br> 376 1 monomorphic<br> 377 ^ recompute handler<br> 378 P polymorphic<br> 379 N megamorphic<br> 380 G generic 381 </div> 382 </div> 383 384 <h2>Usage</h2> Run your script with <code>--trace_ic</code> and upload on this page:<br/> 385 <code>/path/to/d8 --trace_ic your_script.js > trace.txt</code> 386 <h2>Data</h2> 387 <form name="fileForm"> 388 <p> 389 <input id="uploadInput" type="file" name="files" onchange="loadFile();"> trace 390 entries: <span id="count">0</span> 391 </p> 392 </form> 393 <h2>Result</h2> 394 <p> 395 Group-Key: 396 <select id="group-key" onchange="updateTable()"></select> 397 </p> 398 <p> 399 <table id="table" width="100%"> 400 <tbody id="table-body"> 401 </tbody> 402 </table> 403 </p> 404 </body> 405 406 </html> 407