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"use strict"; 6 7class DisassemblyView extends TextView { 8 constructor(id, broker) { 9 super(id, broker, null, false); 10 11 let view = this; 12 let ADDRESS_STYLE = { 13 css: 'tag', 14 location: function(text) { 15 ADDRESS_STYLE.last_address = text; 16 return undefined; 17 } 18 }; 19 let ADDRESS_LINK_STYLE = { 20 css: 'tag', 21 link: function(text) { 22 view.select(function(location) { return location.address == text; }, true, true); 23 } 24 }; 25 let UNCLASSIFIED_STYLE = { 26 css: 'com' 27 }; 28 let NUMBER_STYLE = { 29 css: 'lit' 30 }; 31 let COMMENT_STYLE = { 32 css: 'com' 33 }; 34 let POSITION_STYLE = { 35 css: 'com', 36 location: function(text) { 37 view.pos_start = Number(text); 38 } 39 }; 40 let OPCODE_STYLE = { 41 css: 'kwd', 42 location: function(text) { 43 if (BLOCK_HEADER_STYLE.block_id != undefined) { 44 return { 45 address: ADDRESS_STYLE.last_address, 46 block_id: BLOCK_HEADER_STYLE.block_id 47 }; 48 } else { 49 return { 50 address: ADDRESS_STYLE.last_address 51 }; 52 } 53 } 54 }; 55 const BLOCK_HEADER_STYLE = { 56 css: 'com', 57 block_id: -1, 58 location: function(text) { 59 let matches = /\d+/.exec(text); 60 if (!matches) return undefined; 61 BLOCK_HEADER_STYLE.block_id = Number(matches[0]); 62 return { 63 block_id: BLOCK_HEADER_STYLE.block_id 64 }; 65 }, 66 }; 67 const SOURCE_POSITION_HEADER_STYLE = { 68 css: 'com', 69 location: function(text) { 70 let matches = /(\d+):(\d+)/.exec(text); 71 if (!matches) return undefined; 72 let li = Number(matches[1]); 73 if (view.pos_lines === null) return undefined; 74 let pos = view.pos_lines[li-1] + Number(matches[2]); 75 return { 76 pos_start: pos, 77 pos_end: pos + 1 78 }; 79 }, 80 }; 81 view.SOURCE_POSITION_HEADER_REGEX = /^(\s*-- .+:)(\d+:\d+)( --)/; 82 let patterns = [ 83 [ 84 [/^0x[0-9a-f]{8,16}/, ADDRESS_STYLE, 1], 85 [view.SOURCE_POSITION_HEADER_REGEX, SOURCE_POSITION_HEADER_STYLE, -1], 86 [/^\s+-- B\d+ start.*/, BLOCK_HEADER_STYLE, -1], 87 [/^.*/, UNCLASSIFIED_STYLE, -1] 88 ], 89 [ 90 [/^\s+\d+\s+[0-9a-f]+\s+/, NUMBER_STYLE, 2], 91 [/^.*/, null, -1] 92 ], 93 [ 94 [/^\S+\s+/, OPCODE_STYLE, 3], 95 [/^\S+$/, OPCODE_STYLE, -1], 96 [/^.*/, null, -1] 97 ], 98 [ 99 [/^\s+/, null], 100 [/^[^\(;]+$/, null, -1], 101 [/^[^\(;]+/, null], 102 [/^\(/, null, 4], 103 [/^;/, COMMENT_STYLE, 5] 104 ], 105 [ 106 [/^0x[0-9a-f]{8,16}/, ADDRESS_LINK_STYLE], 107 [/^[^\)]/, null], 108 [/^\)$/, null, -1], 109 [/^\)/, null, 3] 110 ], 111 [ 112 [/^; debug\: position /, COMMENT_STYLE, 6], 113 [/^.+$/, COMMENT_STYLE, -1] 114 ], 115 [ 116 [/^\d+$/, POSITION_STYLE, -1], 117 ] 118 ]; 119 view.setPatterns(patterns); 120 } 121 122 lineLocation(li) { 123 let view = this; 124 let result = undefined; 125 for (let i = 0; i < li.children.length; ++i) { 126 let fragment = li.children[i]; 127 let location = fragment.location; 128 if (location != null) { 129 if (location.block_id != undefined) { 130 if (result === undefined) result = {}; 131 result.block_id = location.block_id; 132 } 133 if (location.address != undefined) { 134 if (result === undefined) result = {}; 135 result.address = location.address; 136 } 137 if (location.pos_start != undefined && location.pos_end != undefined) { 138 if (result === undefined) result = {}; 139 result.pos_start = location.pos_start; 140 result.pos_end = location.pos_end; 141 } 142 else if (view.pos_start != -1) { 143 if (result === undefined) result = {}; 144 result.pos_start = view.pos_start; 145 result.pos_end = result.pos_start + 1; 146 } 147 } 148 } 149 return result; 150 } 151 152 initializeContent(data, rememberedSelection) { 153 this.data = data; 154 super.initializeContent(data, rememberedSelection); 155 } 156 157 initializeCode(sourceText, sourcePosition) { 158 let view = this; 159 view.pos_start = -1; 160 view.addr_event_counts = null; 161 view.total_event_counts = null; 162 view.max_event_counts = null; 163 view.pos_lines = new Array(); 164 // Comment lines for line 0 include sourcePosition already, only need to 165 // add sourcePosition for lines > 0. 166 view.pos_lines[0] = sourcePosition; 167 if (sourceText != "") { 168 let base = sourcePosition; 169 let current = 0; 170 let source_lines = sourceText.split("\n"); 171 for (let i = 1; i < source_lines.length; i++) { 172 // Add 1 for newline character that is split off. 173 current += source_lines[i-1].length + 1; 174 view.pos_lines[i] = base + current; 175 } 176 } 177 } 178 179 initializePerfProfile(eventCounts) { 180 let view = this; 181 if (eventCounts !== undefined) { 182 view.addr_event_counts = eventCounts; 183 184 view.total_event_counts = {}; 185 view.max_event_counts = {}; 186 for (let ev_name in view.addr_event_counts) { 187 let keys = Object.keys(view.addr_event_counts[ev_name]); 188 let values = keys.map(key => view.addr_event_counts[ev_name][key]); 189 view.total_event_counts[ev_name] = values.reduce((a, b) => a + b); 190 view.max_event_counts[ev_name] = values.reduce((a, b) => Math.max(a, b)); 191 } 192 } 193 else { 194 view.addr_event_counts = null; 195 view.total_event_counts = null; 196 view.max_event_counts = null; 197 } 198 } 199 200 // Shorten decimals and remove trailing zeroes for readability. 201 humanize(num) { 202 return num.toFixed(3).replace(/\.?0+$/, "") + "%"; 203 } 204 205 // Interpolate between the given start and end values by a fraction of val/max. 206 interpolate(val, max, start, end) { 207 return start + (end - start) * (val / max); 208 } 209 210 processLine(line) { 211 let view = this; 212 let func = function(match, p1, p2, p3) { 213 let nums = p2.split(":"); 214 let li = Number(nums[0]); 215 let pos = Number(nums[1]); 216 if(li === 0) 217 pos -= view.pos_lines[0]; 218 li++; 219 return p1 + li + ":" + pos + p3; 220 }; 221 line = line.replace(view.SOURCE_POSITION_HEADER_REGEX, func); 222 let fragments = super.processLine(line); 223 224 // Add profiling data per instruction if available. 225 if (view.total_event_counts) { 226 let matches = /^(0x[0-9a-fA-F]+)\s+\d+\s+[0-9a-fA-F]+/.exec(line); 227 if (matches) { 228 let newFragments = []; 229 for (let event in view.addr_event_counts) { 230 let count = view.addr_event_counts[event][matches[1]]; 231 let str = " "; 232 let css_cls = "prof"; 233 if(count !== undefined) { 234 let perc = count / view.total_event_counts[event] * 100; 235 236 let col = { r: 255, g: 255, b: 255 }; 237 for (let i = 0; i < PROF_COLS.length; i++) { 238 if (perc === PROF_COLS[i].perc) { 239 col = PROF_COLS[i].col; 240 break; 241 } 242 else if (perc > PROF_COLS[i].perc && perc < PROF_COLS[i + 1].perc) { 243 let col1 = PROF_COLS[i].col; 244 let col2 = PROF_COLS[i + 1].col; 245 246 let val = perc - PROF_COLS[i].perc; 247 let max = PROF_COLS[i + 1].perc - PROF_COLS[i].perc; 248 249 col.r = Math.round(view.interpolate(val, max, col1.r, col2.r)); 250 col.g = Math.round(view.interpolate(val, max, col1.g, col2.g)); 251 col.b = Math.round(view.interpolate(val, max, col1.b, col2.b)); 252 break; 253 } 254 } 255 256 str = UNICODE_BLOCK; 257 258 let fragment = view.createFragment(str, css_cls); 259 fragment.title = event + ": " + view.humanize(perc) + " (" + count + ")"; 260 fragment.style.color = "rgb(" + col.r + ", " + col.g + ", " + col.b + ")"; 261 262 newFragments.push(fragment); 263 } 264 else 265 newFragments.push(view.createFragment(str, css_cls)); 266 267 } 268 fragments = newFragments.concat(fragments); 269 } 270 } 271 return fragments; 272 } 273} 274