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