Home | History | Annotate | Download | only in tools
      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, "&amp;")
    223            .replace(/</g, "&lt;")
    224            .replace(/>/g, "&gt;")
    225            .replace(/"/g, "&quot;")
    226            .replace(/'/g, "&#039;");
    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