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 class Entry { 44 constructor(id, line) { 45 this.id = id; 46 this.line = line; 47 var parts = line.split(" "); 48 if (parts.length < 6) return 49 this.isValid = false; 50 if (parts[0][0] !== "[") return; 51 if (parts[1] === "patching") return; 52 this.type = parts[0].substr(1); 53 this.category = "Other"; 54 this.map = undefined; 55 if (this.type.indexOf("Store") !== -1) { 56 this.category = "Store"; 57 } else if (this.type.indexOf("Load") !== -1) { 58 this.category = "Load"; 59 } 60 if (this.type.length == 0) return; 61 if (this.type.indexOf('BinaryOpIC(') === 0) { 62 this.type = "BinaryOpIC"; 63 var split = parts[0].split('('); 64 this.state = "(" + split[1] + " => " + parts[2]; 65 var offset = this.parsePositionAndFile(parts, 6); 66 if (offset == -1) return 67 if (this.file === undefined) return 68 this.file = this.file.slice(0, -1); 69 } else { 70 var offset = this.parsePositionAndFile(parts, 2); 71 if (offset == -1) return 72 this.state = parts[++offset]; 73 this.map = parts[offset + 1]; 74 if (this.map !== undefined && this.map.startsWith("map=")) { 75 this.map = this.map.substring(4); 76 offset++; 77 } else { 78 this.map = undefined; 79 } 80 if (this.type !== "CompareIC") { 81 // if there is no address we have a smi key 82 var address = parts[++offset]; 83 if (address !== undefined && address.indexOf("0x") === 0) { 84 this.key = parts.slice(++offset).join(" "); 85 } else { 86 this.key = address; 87 } 88 } 89 } 90 this.filePosition = this.file + " " + this.position; 91 if (this.key) { 92 var isStringKey = false 93 if (this.key.indexOf("<String[") === 0) { 94 isStringKey = true; 95 this.key = "\"" + this.key.slice(this.key.indexOf(']') + 3); 96 } else if (this.key.indexOf("<") === 0) { 97 this.key = this.key.slice(1); 98 } 99 if (this.key.endsWith(">]")) { 100 this.key = this.key.slice(0, -2); 101 } else if (this.key.endsWith("]")) { 102 this.key = this.key.slice(0, -1); 103 } 104 if (isStringKey) { 105 this.key = this.key + "\""; 106 } 107 } 108 this.isValid = true; 109 } 110 111 parsePositionAndFile(parts, start) { 112 // find the position of 'at' in the parts array. 113 var offset = start; 114 for (var i = start + 1; i < parts.length; i++) { 115 offset++; 116 if (parts[i] == 'at') break; 117 } 118 if (parts[offset] !== 'at') return -1; 119 this.position = parts.slice(start, offset).join(' '); 120 offset += 1; 121 this.isNative = parts[offset] == "native" 122 offset += this.isNative ? 1 : 0; 123 this.file = parts[offset]; 124 return offset; 125 } 126 } 127 128 function loadFile() { 129 var files = document.getElementById("uploadInput").files; 130 131 var file = files[0]; 132 var reader = new FileReader(); 133 134 reader.onload = function(evt) { 135 entries = []; 136 var end = this.result.length; 137 var current = 0; 138 var next = 0; 139 var line; 140 var i = 0; 141 var entry; 142 while (current < end) { 143 next = this.result.indexOf("\n", current); 144 if (next === -1) break; 145 i++; 146 line = this.result.substring(current, next); 147 current = next + 1; 148 entry = new Entry(i, line); 149 if (entry.isValid) entries.push(entry); 150 } 151 152 document.getElementById("count").innerHTML = i; 153 updateTable(); 154 } 155 reader.readAsText(file); 156 initGroupKeySelect(); 157 } 158 159 160 161 var properties = ['type', 'category', 'file', 'filePosition', 'state', 162 'key', 'isNative', 'map' 163 ] 164 165 class Group { 166 constructor(property, key, entry) { 167 this.property = property; 168 this.key = key; 169 this.count = 1; 170 this.entries = [entry]; 171 this.percentage = undefined; 172 this.groups = undefined; 173 } 174 175 add(entry) { 176 this.count++; 177 this.entries.push(entry) 178 } 179 180 createSubGroups() { 181 this.groups = {}; 182 for (var i = 0; i < properties.length; i++) { 183 var subProperty = properties[i]; 184 if (this.property == subProperty) continue; 185 this.groups[subProperty] = groupBy(this.entries, subProperty); 186 } 187 } 188 } 189 190 function groupBy(entries, property) { 191 var accumulator = {}; 192 accumulator.__proto__ = null; 193 var length = entries.length; 194 for (var i = 0; i < length; i++) { 195 var entry = entries[i]; 196 var key = entry[property]; 197 if (accumulator[key] == undefined) { 198 accumulator[key] = new Group(property, key, entry) 199 } else { 200 var group = accumulator[key]; 201 if (group.entries == undefined) console.log([group, entry]); 202 group.add(entry) 203 } 204 } 205 var result = [] 206 for (var key in accumulator) { 207 var group = accumulator[key]; 208 group.percentage = Math.round(group.count / length * 100 * 100) / 100; 209 result.push(group); 210 } 211 result.sort((a, b) => { 212 return b.count - a.count 213 }); 214 return result; 215 } 216 217 218 219 function escapeHtml(unsafe) { 220 if (!unsafe) return ""; 221 return unsafe.toString() 222 .replace(/&/g, "&") 223 .replace(/</g, "<") 224 .replace(/>/g, ">") 225 .replace(/"/g, """) 226 .replace(/'/g, "'"); 227 } 228 229 function updateTable() { 230 var select = document.getElementById("group-key"); 231 var key = select.options[select.selectedIndex].text; 232 console.log(key); 233 var tableBody = document.getElementById("table-body"); 234 removeAllChildren(tableBody); 235 var groups = groupBy(entries, key, true); 236 display(groups, tableBody); 237 } 238 239 function selecedOption(node) { 240 return node.options[node.selectedIndex] 241 } 242 243 function removeAllChildren(node) { 244 while (node.firstChild) { 245 node.removeChild(node.firstChild); 246 } 247 } 248 249 function display(entries, parent) { 250 var fragment = document.createDocumentFragment(); 251 252 function td(tr, content, className) { 253 var td = document.createElement("td"); 254 td.innerHTML = content; 255 td.className = className 256 tr.appendChild(td); 257 return td 258 } 259 var max = Math.min(1000, entries.length) 260 for (var i = 0; i < max; i++) { 261 var entry = entries[i]; 262 var tr = document.createElement("tr"); 263 tr.entry = entry; 264 td(tr, 'details', 'details'); 265 td(tr, entry.percentage + "%", 'percentage'); 266 td(tr, entry.count, 'count'); 267 td(tr, escapeHtml(entry.key), 'key'); 268 fragment.appendChild(tr); 269 } 270 var omitted = entries.length - max; 271 if (omitted > 0) { 272 var tr = document.createElement("tr"); 273 var td = td(tr, 'Omitted ' + omitted + " entries."); 274 td.colSpan = 4; 275 fragment.appendChild(tr); 276 } 277 parent.appendChild(fragment); 278 } 279 280 function displayDrilldown(entry, previousSibling) { 281 var tr = document.createElement('tr'); 282 tr.className = "entry-details"; 283 tr.style.display = "none"; 284 // indent by one td. 285 tr.appendChild(document.createElement("td")); 286 var td = document.createElement("td"); 287 td.colSpan = 3; 288 for (var key in entry.groups) { 289 td.appendChild(displayDrilldownGroup(entry, key)); 290 } 291 tr.appendChild(td); 292 // Append the new TR after previousSibling. 293 previousSibling.parentNode.insertBefore(tr, previousSibling.nextSibling) 294 } 295 296 function displayDrilldownGroup(entry, key) { 297 var max = 20; 298 var group = entry.groups[key]; 299 var div = document.createElement("div") 300 div.className = 'drilldown-group-title' 301 div.innerHTML = key + ' [top ' + max + ' out of ' + group.length + ']'; 302 var table = document.createElement("table"); 303 display(group.slice(0, max), table, false) 304 div.appendChild(table); 305 return div; 306 } 307 308 function toggleDetails(node) { 309 var tr = node.parentNode.parentNode; 310 var entry = tr.entry; 311 312 // Create subgroup in-place if the don't exist yet. 313 if (entry.groups === undefined) { 314 entry.createSubGroups(); 315 displayDrilldown(entry, tr); 316 } 317 var details = tr.nextSibling; 318 var display = details.style.display; 319 if (display != "none") { 320 display = "none"; 321 } else { 322 display = "table-row" 323 }; 324 details.style.display = display; 325 } 326 327 function initGroupKeySelect() { 328 var select = document.getElementById("group-key"); 329 for (var i in properties) { 330 var option = document.createElement("option"); 331 option.text = properties[i]; 332 select.add(option); 333 } 334 } 335 </script> 336 </head> 337 338 <body> 339 <h1> 340 <span style="color: #00FF00">I</span> 341 <span style="color: #FF00FF">C</span> 342 <span style="color: #00FFFF">E</span> 343 </h1> Your IC-Explorer. 344 <h2>Usage</h2> Run your script with <code>--trace_ic</code> and upload on this page:<br/> 345 <code>/path/to/d8 --trace_ic your_script.js > trace.txt</code> 346 <h2>Data</h2> 347 <form name="fileForm"> 348 <p> 349 <input id="uploadInput" type="file" name="files" onchange="loadFile();"> trace 350 entries: <span id="count">0</span> 351 </p> 352 </form> 353 <h2>Result</h2> 354 <p> 355 Group-Key: 356 <select id="group-key" onchange="updateTable()"></select> 357 </p> 358 <p> 359 <table id="table" width="100%"> 360 <tbody id="table-body"> 361 </tbody> 362 </table> 363 </p> 364 </body> 365 366 </html> 367