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