ic-explorer.html 10.7 KB
Newer Older
1
<!DOCTYPE html>
2
<html>
3 4 5 6
<!--
Copyright 2016 the V8 project authors. All rights reserved.  Use of this source
code is governed by a BSD-style license that can be found in the LICENSE file.
-->
7

8
<head>
9 10
  <meta charset="utf-8">
  <title>V8 IC explorer</title>
11
  <style>
12 13 14 15
    html {
      font-family: monospace;
    }

16
    .entry-details {}
17

18
    .entry-details TD {}
19

20
    .details {
21 22 23 24 25 26 27 28 29 30
      width: 0.1em;
    }

    .details span {
      padding: 0 0.4em 0 0.4em;
      background-color: black;
      color: white;
      border-radius: 25px;
      text-align: center;
      cursor: -webkit-zoom-in;
31
    }
32

33 34 35 36
    .count {
      text-align: right;
      width: 5em;
    }
37

38 39 40 41
    .percentage {
      text-align: right;
      width: 5em;
    }
42

43 44
    .key {
      padding-left: 1em;
45
    }
46

47 48 49 50 51
    .drilldown-group-title {
      font-weight: bold;
      padding: 0.5em 0 0.2em 0;
    }
  </style>
52 53 54 55 56 57 58 59 60 61
  <script src="./splaytree.js"></script>
  <script src="./codemap.js"></script>
  <script src="./csvparser.js"></script>
  <script src="./consarray.js"></script>
  <script src="./profile.js"></script>
  <script src="./profile_view.js"></script>
  <script src="./logreader.js"></script>
  <script src="./arguments.js"></script>
  <script src="./ic-processor.js"></script>
  <script src="./SourceMap.js"></script>
62

63 64
  <script>
    "use strict"
65 66 67 68 69 70
    let entries = [];

    let properties = ['type', 'category', 'functionName', 'filePosition',
      'state', 'key', 'map', 'reason', 'file',
    ];

71
    // For compatibility with console scripts:
72 73
    print = console.log;

74 75 76 77
  class CustomIcProcessor extends IcProcessor {
    constructor() {
      super();
      this.entries = [];
78 79
    }

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    functionName(pc) {
      let entry = this.profile_.findEntry(pc);
      return this.formatName(entry);
    }

    processPropertyIC(
        type, pc, time, line, column, old_state, new_state, map, key, modifier,
        slow_reason) {
      let fnName = this.functionName(pc);
      this.entries.push(new Entry(
          type, fnName, time, line, column, key, old_state, new_state, map,
          slow_reason));
    }
  };


  class Entry {
  constructor(
      type, fn_file, time, line, column, key, oldState, newState, map, reason,
      additional) {
    this.time = time;
    this.type = type;
    this.category = 'other';
    if (this.type.indexOf('Store') !== -1) {
      this.category = 'Store';
    } else if (this.type.indexOf('Load') !== -1) {
      this.category = 'Load';
    }
    let parts = fn_file.split(' ');
    this.functionName = parts[0];
    this.file = parts[1];
    let position = line + ':' + column;
    this.filePosition = this.file + ':' + position;
    this.oldState = oldState;
    this.newState = newState;
    this.state = this.oldState + ' → ' + this.newState;
    this.key = key;
    this.map = map.toString(16);
    this.reason = reason;
    this.additional = additional;
  }

  parseMapProperties(parts, offset) {
    let next = parts[++offset];
    if (!next.startsWith('dict')) return offset;
    this.propertiesMode = next.substr(5) == '0' ? 'fast' : 'slow';
    this.numberOfOwnProperties = parts[++offset].substr(4);
    next = parts[++offset];
    this.instanceType = next.substr(5, next.length - 6);
    return offset;
  }

  parsePositionAndFile(parts, start) {
    // find the position of 'at' in the parts array.
    let offset = start;
    for (let i = start + 1; i < parts.length; i++) {
      offset++;
      if (parts[i] == 'at') break;
    }
    if (parts[offset] !== 'at') return -1;
    this.position = parts.slice(start, offset).join(' ');
    offset += 1;
    this.isNative = parts[offset] == 'native'
    offset += this.isNative ? 1 : 0;
    this.file = parts[offset];
    return offset;
  }
}

149
    function loadFile() {
150
      let files = document.getElementById("uploadInput").files;
151

152 153
      let file = files[0];
      let reader = new FileReader();
154

155
      reader.onload = function(evt) {
156 157 158
        let icProcessor = new CustomIcProcessor();
        icProcessor.processString(this.result);
        entries = icProcessor.entries;
159

160
        document.getElementById("count").innerHTML = entries.length;
161 162 163 164
        updateTable();
      }
      reader.readAsText(file);
      initGroupKeySelect();
165 166 167
    }


168 169 170 171 172 173 174 175 176 177 178 179 180 181
    class Group {
      constructor(property, key, entry) {
        this.property = property;
        this.key = key;
        this.count = 1;
        this.entries = [entry];
        this.percentage = undefined;
        this.groups = undefined;
      }

      add(entry) {
        this.count++;
        this.entries.push(entry)
      }
182

183 184
      createSubGroups() {
        this.groups = {};
185 186
        for (let i = 0; i < properties.length; i++) {
          let subProperty = properties[i];
187 188 189 190
          if (this.property == subProperty) continue;
          this.groups[subProperty] = groupBy(this.entries, subProperty);
        }
      }
191 192
    }

193
    function groupBy(entries, property) {
194 195 196 197 198
      let accumulator = Object.create(null);
      let length = entries.length;
      for (let i = 0; i < length; i++) {
        let entry = entries[i];
        let key = entry[property];
199 200 201
        if (accumulator[key] == undefined) {
          accumulator[key] = new Group(property, key, entry)
        } else {
202
          let group = accumulator[key];
203 204 205 206
          if (group.entries == undefined) console.log([group, entry]);
          group.add(entry)
        }
      }
207 208 209
      let result = []
      for (let key in accumulator) {
        let group = accumulator[key];
210 211 212 213 214 215 216 217
        group.percentage = Math.round(group.count / length * 100 * 100) / 100;
        result.push(group);
      }
      result.sort((a, b) => {
        return b.count - a.count
      });
      return result;
    }
218 219 220



221
    function escapeHtml(unsafe) {
222
      if (!unsafe) return "";
223
      return unsafe.toString()
224 225 226 227 228 229 230 231 232 233 234 235 236 237
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#039;");
    }

    function processValue(unsafe) {
      if (!unsafe) return "";
      if (!unsafe.startsWith("http")) return escapeHtml(unsafe);
      let a = document.createElement("a");
      a.href = unsafe;
      a.textContent = unsafe;
      return a;
238
    }
239

240
    function updateTable() {
241 242 243
      let select = document.getElementById("group-key");
      let key = select.options[select.selectedIndex].text;
      let tableBody = document.getElementById("table-body");
244
      removeAllChildren(tableBody);
245
      let groups = groupBy(entries, key, true);
246 247
      display(groups, tableBody);
    }
248

249 250 251
    function selecedOption(node) {
      return node.options[node.selectedIndex]
    }
252

253 254 255 256 257
    function removeAllChildren(node) {
      while (node.firstChild) {
        node.removeChild(node.firstChild);
      }
    }
258

259
    function display(entries, parent) {
260
      let fragment = document.createDocumentFragment();
261

262
      function td(tr, content, className) {
263
        let node = document.createElement("td");
264
        if (typeof content == "object") {
265
          node.appendChild(content);
266
        } else {
267
          node.innerHTML = content;
268
        }
269 270 271
        node.className = className
        tr.appendChild(node);
        return node
272
      }
273

274 275 276 277
      let max = Math.min(1000, entries.length)
      for (let i = 0; i < max; i++) {
        let entry = entries[i];
        let tr = document.createElement("tr");
278
        tr.entry = entry;
279
        td(tr, '<span onclick="toggleDetails(this)">&#8505;</a>', 'details');
280 281
        td(tr, entry.percentage + "%", 'percentage');
        td(tr, entry.count, 'count');
282
        td(tr, processValue(entry.key), 'key');
283 284
        fragment.appendChild(tr);
      }
285
      let omitted = entries.length - max;
286
      if (omitted > 0) {
287
        let tr = document.createElement("tr");
288 289
        let tdNode = td(tr, 'Omitted ' + omitted + " entries.");
        tdNode.colSpan = 4;
290 291 292 293
        fragment.appendChild(tr);
      }
      parent.appendChild(fragment);
    }
294

295
    function displayDrilldown(entry, previousSibling) {
296
      let tr = document.createElement('tr');
297 298 299 300
      tr.className = "entry-details";
      tr.style.display = "none";
      // indent by one td.
      tr.appendChild(document.createElement("td"));
301
      let td = document.createElement("td");
302
      td.colSpan = 3;
303
      for (let key in entry.groups) {
304 305 306 307 308 309
        td.appendChild(displayDrilldownGroup(entry, key));
      }
      tr.appendChild(td);
      // Append the new TR after previousSibling.
      previousSibling.parentNode.insertBefore(tr, previousSibling.nextSibling)
    }
310

311
    function displayDrilldownGroup(entry, key) {
312 313 314
      let max = 20;
      let group = entry.groups[key];
      let div = document.createElement("div")
315
      div.className = 'drilldown-group-title'
316 317
      div.textContent = key + ' [top ' + max + ' out of ' + group.length + ']';
      let table = document.createElement("table");
318 319 320 321
      display(group.slice(0, max), table, false)
      div.appendChild(table);
      return div;
    }
322

323
    function toggleDetails(node) {
324 325
      let tr = node.parentNode.parentNode;
      let entry = tr.entry;
326

327 328 329 330 331
      // Create subgroup in-place if the don't exist yet.
      if (entry.groups === undefined) {
        entry.createSubGroups();
        displayDrilldown(entry, tr);
      }
332 333
      let details = tr.nextSibling;
      let display = details.style.display;
334 335 336 337 338 339 340
      if (display != "none") {
        display = "none";
      } else {
        display = "table-row"
      };
      details.style.display = display;
    }
341

342
    function initGroupKeySelect() {
343
      let select = document.getElementById("group-key");
344
      select.options.length = 0;
345 346
      for (let i in properties) {
        let option = document.createElement("option");
347 348 349 350
        option.text = properties[i];
        select.add(option);
      }
    }
351

352 353 354
    function handleOnLoad() {
      document.querySelector("#uploadInput").focus();
    }
355
  </script>
356 357
</head>

358
<body onload="handleOnLoad()">
359
  <h1>
360 361 362 363
    <span style="color: #00FF00">I</span>
    <span style="color: #FF00FF">C</span>
    <span style="color: #00FFFF">E</span>
  </h1> Your IC-Explorer.
364 365 366

  <div id="legend" style="padding-right: 200px">
    <div style="float:right;  border-style: solid; border-width: 1px; padding:20px">
367
      0 uninitialized<br>
368
      X no feedback<br>
369 370 371 372 373
      1 monomorphic<br>
      ^ recompute handler<br>
      P polymorphic<br>
      N megamorphic<br>
      G generic
374 375 376
    </div>
  </div>

377 378
  <h2>Usage</h2> Run your script with <code>--trace_ic</code> and upload <code>v8.log</code> on this page:<br/>
  <code>/path/to/d8 --trace_ic your_script.js</code>
379 380
  <h2>Data</h2>
  <form name="fileForm">
381
    <p>
382
      <input id="uploadInput" type="file" name="files" onchange="loadFile();"> trace entries: <span id="count">0</span>
383 384 385 386
    </p>
  </form>
  <h2>Result</h2>
  <p>
387 388
    Group-Key:
    <select id="group-key" onchange="updateTable()"></select>
389 390 391 392 393 394 395 396 397
  </p>
  <p>
    <table id="table" width="100%">
      <tbody id="table-body">
      </tbody>
    </table>
  </p>
</body>

398
</html>