OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library code_view_element; | 5 library code_view_element; |
6 | 6 |
7 import 'dart:html'; | 7 import 'dart:html'; |
8 import 'observatory_element.dart'; | 8 import 'observatory_element.dart'; |
| 9 import 'package:observatory/app.dart'; |
9 import 'package:observatory/service.dart'; | 10 import 'package:observatory/service.dart'; |
| 11 import 'package:observatory/cpu_profile.dart'; |
10 import 'package:polymer/polymer.dart'; | 12 import 'package:polymer/polymer.dart'; |
11 | 13 |
| 14 class DisassemblyTable extends SortedTable { |
| 15 DisassemblyTable(columns) : super(columns); |
| 16 } |
| 17 |
| 18 class InlineTable extends SortedTable { |
| 19 InlineTable(columns) : super(columns); |
| 20 } |
| 21 |
12 @CustomTag('code-view') | 22 @CustomTag('code-view') |
13 class CodeViewElement extends ObservatoryElement { | 23 class CodeViewElement extends ObservatoryElement { |
14 @published Code code; | 24 @observable Code code; |
15 CodeViewElement.created() : super.created(); | 25 ProfileCode get profile => code == null ? null : code.profile; |
| 26 DisassemblyTable disassemblyTable; |
| 27 InlineTable inlineTable; |
| 28 |
| 29 CodeViewElement.created() : super.created() { |
| 30 // Create table models. |
| 31 var columns = [ |
| 32 new SortedTableColumn('Address'), |
| 33 new SortedTableColumn('Inclusive'), |
| 34 new SortedTableColumn('Exclusive'), |
| 35 new SortedTableColumn('Disassembly'), |
| 36 ]; |
| 37 disassemblyTable = new DisassemblyTable(columns); |
| 38 columns = [ |
| 39 new SortedTableColumn('Address'), |
| 40 new SortedTableColumn('Inclusive'), |
| 41 new SortedTableColumn('Exclusive'), |
| 42 new SortedTableColumn('Functions'), |
| 43 ]; |
| 44 inlineTable = new InlineTable(columns); |
| 45 } |
16 | 46 |
17 @override | 47 @override |
18 void attached() { | 48 void attached() { |
19 super.attached(); | 49 super.attached(); |
| 50 } |
| 51 |
| 52 void codeChanged(oldValue) { |
20 if (code == null) { | 53 if (code == null) { |
21 return; | 54 return; |
22 } | 55 } |
23 code.load().then((Code c) { | 56 code.load().then((Code c) { |
24 c.loadScript(); | 57 c.loadScript(); |
25 }); | 58 }); |
| 59 _updateDisassembly(); |
| 60 _updateInline(); |
26 } | 61 } |
27 | 62 |
28 void refresh(var done) { | 63 void refresh(var done) { |
29 code.reload().whenComplete(done); | 64 code.reload().whenComplete(done); |
30 } | 65 } |
31 | 66 |
| 67 void refreshTicks(var done) { |
| 68 var isolate = code.isolate; |
| 69 isolate.invokeRpc('getCpuProfile', { 'tags': 'None' }) |
| 70 .then((ServiceMap response) { |
| 71 var cpuProfile = new CpuProfile(); |
| 72 cpuProfile.load(isolate, response); |
| 73 _updateDisassembly(); |
| 74 _updateInline(); |
| 75 }).whenComplete(done); |
| 76 } |
| 77 |
| 78 String formattedAddress(CodeInstruction instruction) { |
| 79 if (instruction.address == 0) { |
| 80 return ''; |
| 81 } |
| 82 return '0x${instruction.address.toRadixString(16)}'; |
| 83 } |
| 84 |
| 85 String formattedAddressRange(CodeInlineInterval interval) { |
| 86 String start = interval.start.toRadixString(16); |
| 87 String end = interval.end.toRadixString(16); |
| 88 return '[0x$start, 0x$end)'; |
| 89 } |
| 90 |
| 91 String formattedInclusiveInterval(CodeInlineInterval interval) { |
| 92 if (code == null) { |
| 93 return ''; |
| 94 } |
| 95 if (code.profile == null) { |
| 96 return ''; |
| 97 } |
| 98 var intervalTick = code.profile.intervalTicks[interval.start]; |
| 99 if (intervalTick == null) { |
| 100 return ''; |
| 101 } |
| 102 // Don't show inclusive ticks if they are the same as exclusive ticks. |
| 103 if (intervalTick.inclusiveTicks == intervalTick.exclusiveTicks) { |
| 104 return ''; |
| 105 } |
| 106 var pcent = Utils.formatPercent(intervalTick.inclusiveTicks, |
| 107 code.profile.profile.sampleCount); |
| 108 return '$pcent (${intervalTick.inclusiveTicks})'; |
| 109 } |
| 110 |
| 111 String formattedExclusiveInterval(CodeInlineInterval interval) { |
| 112 if (code == null) { |
| 113 return ''; |
| 114 } |
| 115 if (code.profile == null) { |
| 116 return ''; |
| 117 } |
| 118 var intervalTick = code.profile.intervalTicks[interval.start]; |
| 119 if (intervalTick == null) { |
| 120 return ''; |
| 121 } |
| 122 var pcent = Utils.formatPercent(intervalTick.exclusiveTicks, |
| 123 code.profile.profile.sampleCount); |
| 124 return '$pcent (${intervalTick.exclusiveTicks})'; |
| 125 } |
| 126 |
| 127 |
| 128 String formattedInclusive(CodeInstruction instruction) { |
| 129 if (code == null) { |
| 130 return ''; |
| 131 } |
| 132 if (code.profile == null) { |
| 133 return ''; |
| 134 } |
| 135 var tick = code.profile.addressTicks[instruction.address]; |
| 136 if (tick == null) { |
| 137 return ''; |
| 138 } |
| 139 // Don't show inclusive ticks if they are the same as exclusive ticks. |
| 140 if (tick.inclusiveTicks == tick.exclusiveTicks) { |
| 141 return ''; |
| 142 } |
| 143 var pcent = Utils.formatPercent(tick.inclusiveTicks, |
| 144 code.profile.profile.sampleCount); |
| 145 return '$pcent (${tick.inclusiveTicks})'; |
| 146 } |
| 147 |
| 148 String formattedExclusive(CodeInstruction instruction) { |
| 149 if (code == null) { |
| 150 return ''; |
| 151 } |
| 152 if (code.profile == null) { |
| 153 return ''; |
| 154 } |
| 155 var tick = code.profile.addressTicks[instruction.address]; |
| 156 if (tick == null) { |
| 157 return ''; |
| 158 } |
| 159 var pcent = Utils.formatPercent(tick.exclusiveTicks, |
| 160 code.profile.profile.sampleCount); |
| 161 return '$pcent (${tick.exclusiveTicks})'; |
| 162 } |
| 163 |
| 164 void _updateDiasssemblyTable() { |
| 165 disassemblyTable.clearRows(); |
| 166 if (code == null) { |
| 167 return; |
| 168 } |
| 169 for (CodeInstruction instruction in code.instructions) { |
| 170 var row = [formattedAddress(instruction), |
| 171 formattedInclusive(instruction), |
| 172 formattedExclusive(instruction), |
| 173 instruction.human]; |
| 174 disassemblyTable.addRow(new SortedTableRow(row)); |
| 175 } |
| 176 } |
| 177 |
| 178 void _addDisassemblyDOMRow() { |
| 179 var tableBody = $['disassemblyTableBody']; |
| 180 assert(tableBody != null); |
| 181 var tr = new TableRowElement(); |
| 182 |
| 183 var cell; |
| 184 |
| 185 // Add new space. |
| 186 cell = tr.insertCell(-1); |
| 187 cell.classes.add('monospace'); |
| 188 cell = tr.insertCell(-1); |
| 189 cell.classes.add('monospace'); |
| 190 cell = tr.insertCell(-1); |
| 191 cell.classes.add('monospace'); |
| 192 cell = tr.insertCell(-1); |
| 193 cell.classes.add('monospace'); |
| 194 |
| 195 tableBody.children.add(tr); |
| 196 } |
| 197 |
| 198 void _fillDisassemblyDOMRow(TableRowElement tr, int rowIndex) { |
| 199 var row = disassemblyTable.rows[rowIndex]; |
| 200 for (var i = 0; i < row.values.length; i++) { |
| 201 var cell = tr.children[i]; |
| 202 cell.title = row.values[i].toString(); |
| 203 cell.text = row.values[i].toString(); |
| 204 } |
| 205 } |
| 206 |
| 207 void _updateDisassemblyDOMTable() { |
| 208 var tableBody = $['disassemblyTableBody']; |
| 209 assert(tableBody != null); |
| 210 // Resize DOM table. |
| 211 if (tableBody.children.length > disassemblyTable.sortedRows.length) { |
| 212 // Shrink the table. |
| 213 var deadRows = |
| 214 tableBody.children.length - disassemblyTable.sortedRows.length; |
| 215 for (var i = 0; i < deadRows; i++) { |
| 216 tableBody.children.removeLast(); |
| 217 } |
| 218 } else if (tableBody.children.length < disassemblyTable.sortedRows.length) { |
| 219 // Grow table. |
| 220 var newRows = |
| 221 disassemblyTable.sortedRows.length - tableBody.children.length; |
| 222 for (var i = 0; i < newRows; i++) { |
| 223 _addDisassemblyDOMRow(); |
| 224 } |
| 225 } |
| 226 |
| 227 assert(tableBody.children.length == disassemblyTable.sortedRows.length); |
| 228 // Fill table. |
| 229 for (var i = 0; i < disassemblyTable.sortedRows.length; i++) { |
| 230 var rowIndex = disassemblyTable.sortedRows[i]; |
| 231 var tr = tableBody.children[i]; |
| 232 _fillDisassemblyDOMRow(tr, rowIndex); |
| 233 } |
| 234 } |
| 235 |
| 236 void _updateDisassembly() { |
| 237 notifyPropertyChange(#code, true, false); |
| 238 _updateDiasssemblyTable(); |
| 239 _updateDisassemblyDOMTable(); |
| 240 } |
| 241 |
| 242 void _updateInlineTable() { |
| 243 inlineTable.clearRows(); |
| 244 if (code == null) { |
| 245 return; |
| 246 } |
| 247 for (CodeInlineInterval interval in code.inlineIntervals) { |
| 248 var row = [interval, |
| 249 formattedInclusiveInterval(interval), |
| 250 formattedExclusiveInterval(interval), |
| 251 interval.functions]; |
| 252 inlineTable.addRow(new SortedTableRow(row)); |
| 253 } |
| 254 } |
| 255 |
| 256 void _addInlineDOMRow() { |
| 257 var tableBody = shadowRoot.querySelector('#inlineRangeTableBody'); |
| 258 assert(tableBody != null); |
| 259 var tr = new TableRowElement(); |
| 260 |
| 261 var cell; |
| 262 |
| 263 // Add new space. |
| 264 cell = tr.insertCell(-1); |
| 265 cell.classes.add('monospace'); |
| 266 cell = tr.insertCell(-1); |
| 267 cell.classes.add('monospace'); |
| 268 cell = tr.insertCell(-1); |
| 269 cell.classes.add('monospace'); |
| 270 cell = tr.insertCell(-1); |
| 271 |
| 272 tableBody.children.add(tr); |
| 273 } |
| 274 |
| 275 void _fillInlineDOMRow(TableRowElement tr, int rowIndex) { |
| 276 var row = inlineTable.rows[rowIndex]; |
| 277 var columns = row.values.length; |
| 278 var addressRangeColumn = 0; |
| 279 var functionsColumn = columns - 1; |
| 280 |
| 281 { |
| 282 var addressRangeCell = tr.children[addressRangeColumn]; |
| 283 var interval = row.values[addressRangeColumn]; |
| 284 var addressRangeString = formattedAddressRange(interval); |
| 285 var addressRangeElement = new SpanElement(); |
| 286 addressRangeElement.classes.add('monospace'); |
| 287 addressRangeElement.text = addressRangeString; |
| 288 addressRangeCell.children.clear(); |
| 289 addressRangeCell.children.add(addressRangeElement); |
| 290 } |
| 291 |
| 292 for (var i = addressRangeColumn + 1; i < columns - 1; i++) { |
| 293 var cell = tr.children[i]; |
| 294 cell.title = row.values[i].toString(); |
| 295 cell.text = row.values[i].toString(); |
| 296 } |
| 297 var functions = row.values[functionsColumn]; |
| 298 var functionsCell = tr.children[functionsColumn]; |
| 299 functionsCell.children.clear(); |
| 300 for (var func in functions) { |
| 301 var functionRef = new Element.tag('function-ref'); |
| 302 functionRef.ref = func; |
| 303 functionsCell.children.add(functionRef); |
| 304 var gap = new SpanElement(); |
| 305 gap.style.minWidth = '1em'; |
| 306 gap.text = ' '; |
| 307 functionsCell.children.add(gap); |
| 308 } |
| 309 } |
| 310 |
| 311 void _updateInlineDOMTable() { |
| 312 var tableBody = shadowRoot.querySelector('#inlineRangeTableBody'); |
| 313 // Resize DOM table. |
| 314 if (tableBody.children.length > inlineTable.sortedRows.length) { |
| 315 // Shrink the table. |
| 316 var deadRows = |
| 317 tableBody.children.length - inlineTable.sortedRows.length; |
| 318 for (var i = 0; i < deadRows; i++) { |
| 319 tableBody.children.removeLast(); |
| 320 } |
| 321 } else if (tableBody.children.length < inlineTable.sortedRows.length) { |
| 322 // Grow table. |
| 323 var newRows = inlineTable.sortedRows.length - tableBody.children.length; |
| 324 for (var i = 0; i < newRows; i++) { |
| 325 _addInlineDOMRow(); |
| 326 } |
| 327 } |
| 328 assert(tableBody.children.length == inlineTable.sortedRows.length); |
| 329 // Fill table. |
| 330 for (var i = 0; i < inlineTable.sortedRows.length; i++) { |
| 331 var rowIndex = inlineTable.sortedRows[i]; |
| 332 var tr = tableBody.children[i]; |
| 333 _fillInlineDOMRow(tr, rowIndex); |
| 334 } |
| 335 } |
| 336 |
| 337 void _updateInline() { |
| 338 _updateInlineTable(); |
| 339 _updateInlineDOMTable(); |
| 340 } |
| 341 |
32 Element _findJumpTarget(Element target) { | 342 Element _findJumpTarget(Element target) { |
33 var jumpTarget = target.attributes['data-jump-target']; | 343 var jumpTarget = target.attributes['data-jump-target']; |
34 if (jumpTarget == '') { | 344 if (jumpTarget == '') { |
35 return null; | 345 return null; |
36 } | 346 } |
37 var address = int.parse(jumpTarget); | 347 var address = int.parse(jumpTarget); |
38 var node = shadowRoot.querySelector('#addr-$address'); | 348 var node = shadowRoot.querySelector('#addr-$address'); |
39 if (node == null) { | 349 if (node == null) { |
40 return null; | 350 return null; |
41 } | 351 } |
42 return node; | 352 return node; |
43 } | 353 } |
44 | 354 |
45 void mouseOver(Event e, var detail, Node target) { | 355 void mouseOver(Event e, var detail, Node target) { |
46 var jt = _findJumpTarget(target); | 356 var jt = _findJumpTarget(target); |
47 if (jt == null) { | 357 if (jt == null) { |
48 return; | 358 return; |
49 } | 359 } |
50 jt.classes.add('highlight'); | 360 jt.classes.add('highlight'); |
51 } | 361 } |
52 | 362 |
53 void mouseOut(Event e, var detail, Node target) { | 363 void mouseOut(Event e, var detail, Node target) { |
54 var jt = _findJumpTarget(target); | 364 var jt = _findJumpTarget(target); |
55 if (jt == null) { | 365 if (jt == null) { |
56 return; | 366 return; |
57 } | 367 } |
58 jt.classes.remove('highlight'); | 368 jt.classes.remove('highlight'); |
59 } | 369 } |
60 } | 370 } |
OLD | NEW |