Home | History | Annotate | Download | only in src
      1 // Copyright 2015 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 import {PROF_COLS, UNICODE_BLOCK} from "./constants.js"
      6 import {SelectionBroker} from "./selection-broker.js"
      7 import {TextView} from "./text-view.js"
      8 
      9 export class DisassemblyView extends TextView {
     10   SOURCE_POSITION_HEADER_REGEX: any;
     11   addr_event_counts: any;
     12   total_event_counts: any;
     13   max_event_counts: any;
     14   pos_lines: Array<any>;
     15 
     16   createViewElement() {
     17     const pane = document.createElement('div');
     18     pane.setAttribute('id', "disassembly");
     19     pane.innerHTML =
     20       `<pre id='disassembly-text-pre' class='prettyprint prettyprinted'>
     21        <ul id='disassembly-list' class='nolinenums noindent'>
     22        </ul>
     23      </pre>`;
     24     return pane;
     25   }
     26 
     27   constructor(parentId, broker: SelectionBroker) {
     28     super(parentId, broker, null);
     29     let view = this;
     30     const sourceResolver = broker.sourceResolver;
     31     let ADDRESS_STYLE = {
     32       css: 'tag',
     33       linkHandler: function (text, fragment) {
     34         const matches = text.match(/0x[0-9a-f]{8,16}\s*(?<offset>[0-9a-f]+)/);
     35         const offset = Number.parseInt(matches.groups["offset"], 16);
     36         if (!Number.isNaN(offset)) {
     37           const [nodes, blockId] = sourceResolver.nodesForPCOffset(offset)
     38           console.log("nodes for", offset, offset.toString(16), " are ", nodes);
     39           if (nodes.length > 0) {
     40             for (const nodeId of nodes) {
     41               view.addHtmlElementForNodeId(nodeId, fragment);
     42             }
     43             return (e) => {
     44               console.log(offset, nodes);
     45               e.stopPropagation();
     46               if (!e.shiftKey) {
     47                 view.selectionHandler.clear();
     48               }
     49               view.selectionHandler.select(nodes, true);
     50             };
     51           }
     52         }
     53         return undefined;
     54       }
     55     };
     56     let ADDRESS_LINK_STYLE = {
     57       css: 'tag'
     58     };
     59     let UNCLASSIFIED_STYLE = {
     60       css: 'com'
     61     };
     62     let NUMBER_STYLE = {
     63       css: 'lit'
     64     };
     65     let COMMENT_STYLE = {
     66       css: 'com'
     67     };
     68     let POSITION_STYLE = {
     69       css: 'com',
     70     };
     71     let OPCODE_STYLE = {
     72       css: 'kwd',
     73     };
     74     const BLOCK_HEADER_STYLE = {
     75       css: ['com', 'block'],
     76       block_id: null,
     77       blockId: function (text) {
     78         let matches = /\d+/.exec(text);
     79         if (!matches) return undefined;
     80         BLOCK_HEADER_STYLE.block_id = Number(matches[0]);
     81         return BLOCK_HEADER_STYLE.block_id;
     82       },
     83       linkHandler: function (text) {
     84         let matches = /\d+/.exec(text);
     85         if (!matches) return undefined;
     86         const blockId = matches[0];
     87         return function (e) {
     88           e.stopPropagation();
     89           if (!e.shiftKey) {
     90             view.selectionHandler.clear();
     91           }
     92           view.blockSelectionHandler.select([blockId], true);
     93         };
     94       }
     95     };
     96     const SOURCE_POSITION_HEADER_STYLE = {
     97       css: 'com'
     98     };
     99     view.SOURCE_POSITION_HEADER_REGEX = /^\s*--[^<]*<.*(not inlined|inlined\((\d+)\)):(\d+)>\s*--/;
    100     let patterns = [
    101       [
    102         [/^0x[0-9a-f]{8,16}\s*[0-9a-f]+\ /, ADDRESS_STYLE, 1],
    103         [view.SOURCE_POSITION_HEADER_REGEX, SOURCE_POSITION_HEADER_STYLE, -1],
    104         [/^\s+-- B\d+ start.*/, BLOCK_HEADER_STYLE, -1],
    105         [/^.*/, UNCLASSIFIED_STYLE, -1]
    106       ],
    107       [
    108         [/^\s+[0-9a-f]+\s+/, NUMBER_STYLE, 2],
    109         [/^\s+[0-9a-f]+\s+[0-9a-f]+\s+/, NUMBER_STYLE, 2],
    110         [/^.*/, null, -1]
    111       ],
    112       [
    113         [/^\S+\s+/, OPCODE_STYLE, 3],
    114         [/^\S+$/, OPCODE_STYLE, -1],
    115         [/^.*/, null, -1]
    116       ],
    117       [
    118         [/^\s+/, null],
    119         [/^[^\(;]+$/, null, -1],
    120         [/^[^\(;]+/, null],
    121         [/^\(/, null, 4],
    122         [/^;/, COMMENT_STYLE, 5]
    123       ],
    124       [
    125         [/^0x[0-9a-f]{8,16}/, ADDRESS_LINK_STYLE],
    126         [/^[^\)]/, null],
    127         [/^\)$/, null, -1],
    128         [/^\)/, null, 3]
    129       ],
    130       [
    131         [/^; debug\: position /, COMMENT_STYLE, 6],
    132         [/^.+$/, COMMENT_STYLE, -1]
    133       ],
    134       [
    135         [/^\d+$/, POSITION_STYLE, -1],
    136       ]
    137     ];
    138     view.setPatterns(patterns);
    139   }
    140 
    141   initializeCode(sourceText, sourcePosition) {
    142     let view = this;
    143     view.addr_event_counts = null;
    144     view.total_event_counts = null;
    145     view.max_event_counts = null;
    146     view.pos_lines = new Array();
    147     // Comment lines for line 0 include sourcePosition already, only need to
    148     // add sourcePosition for lines > 0.
    149     view.pos_lines[0] = sourcePosition;
    150     if (sourceText && sourceText != "") {
    151       let base = sourcePosition;
    152       let current = 0;
    153       let source_lines = sourceText.split("\n");
    154       for (let i = 1; i < source_lines.length; i++) {
    155         // Add 1 for newline character that is split off.
    156         current += source_lines[i - 1].length + 1;
    157         view.pos_lines[i] = base + current;
    158       }
    159     }
    160   }
    161 
    162   initializePerfProfile(eventCounts) {
    163     let view = this;
    164     if (eventCounts !== undefined) {
    165       view.addr_event_counts = eventCounts;
    166 
    167       view.total_event_counts = {};
    168       view.max_event_counts = {};
    169       for (let ev_name in view.addr_event_counts) {
    170         let keys = Object.keys(view.addr_event_counts[ev_name]);
    171         let values = keys.map(key => view.addr_event_counts[ev_name][key]);
    172         view.total_event_counts[ev_name] = values.reduce((a, b) => a + b);
    173         view.max_event_counts[ev_name] = values.reduce((a, b) => Math.max(a, b));
    174       }
    175     }
    176     else {
    177       view.addr_event_counts = null;
    178       view.total_event_counts = null;
    179       view.max_event_counts = null;
    180     }
    181   }
    182 
    183   // Shorten decimals and remove trailing zeroes for readability.
    184   humanize(num) {
    185     return num.toFixed(3).replace(/\.?0+$/, "") + "%";
    186   }
    187 
    188   // Interpolate between the given start and end values by a fraction of val/max.
    189   interpolate(val, max, start, end) {
    190     return start + (end - start) * (val / max);
    191   }
    192 
    193   processLine(line) {
    194     let view = this;
    195     let fragments = super.processLine(line);
    196 
    197     // Add profiling data per instruction if available.
    198     if (view.total_event_counts) {
    199       let matches = /^(0x[0-9a-fA-F]+)\s+\d+\s+[0-9a-fA-F]+/.exec(line);
    200       if (matches) {
    201         let newFragments = [];
    202         for (let event in view.addr_event_counts) {
    203           let count = view.addr_event_counts[event][matches[1]];
    204           let str = " ";
    205           let css_cls = "prof";
    206           if (count !== undefined) {
    207             let perc = count / view.total_event_counts[event] * 100;
    208 
    209             let col = { r: 255, g: 255, b: 255 };
    210             for (let i = 0; i < PROF_COLS.length; i++) {
    211               if (perc === PROF_COLS[i].perc) {
    212                 col = PROF_COLS[i].col;
    213                 break;
    214               }
    215               else if (perc > PROF_COLS[i].perc && perc < PROF_COLS[i + 1].perc) {
    216                 let col1 = PROF_COLS[i].col;
    217                 let col2 = PROF_COLS[i + 1].col;
    218 
    219                 let val = perc - PROF_COLS[i].perc;
    220                 let max = PROF_COLS[i + 1].perc - PROF_COLS[i].perc;
    221 
    222                 col.r = Math.round(view.interpolate(val, max, col1.r, col2.r));
    223                 col.g = Math.round(view.interpolate(val, max, col1.g, col2.g));
    224                 col.b = Math.round(view.interpolate(val, max, col1.b, col2.b));
    225                 break;
    226               }
    227             }
    228 
    229             str = UNICODE_BLOCK;
    230 
    231             let fragment = view.createFragment(str, css_cls);
    232             fragment.title = event + ": " + view.humanize(perc) + " (" + count + ")";
    233             fragment.style.color = "rgb(" + col.r + ", " + col.g + ", " + col.b + ")";
    234 
    235             newFragments.push(fragment);
    236           }
    237           else
    238             newFragments.push(view.createFragment(str, css_cls));
    239 
    240         }
    241         fragments = newFragments.concat(fragments);
    242       }
    243     }
    244     return fragments;
    245   }
    246 
    247   detachSelection() { return null; }
    248 }
    249