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