• 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    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, "&amp;")
220        .replace(/</g, "&lt;")
221        .replace(/>/g, "&gt;")
222        .replace(/"/g, "&quot;")
223        .replace(/'/g, "&#039;");
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, '<span onclick="toggleDetails(this)">&#8505;</a>', '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