• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<html>
2<!--
3Copyright 2016 the V8 project authors. All rights reserved.  Use of this source
4code 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, '<span onclick="toggleDetails(this)">details</a>', '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