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     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, "&amp;")
    246            .replace(/</g, "&lt;")
    247            .replace(/>/g, "&gt;")
    248            .replace(/"/g, "&quot;")
    249            .replace(/'/g, "&#039;");
    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