| Index: runtime/observatory/lib/src/elements/code_view.dart
|
| diff --git a/runtime/observatory/lib/src/elements/code_view.dart b/runtime/observatory/lib/src/elements/code_view.dart
|
| index f44af1f597e39e5f86135788ebe83ce218cefdf2..743febcac801a1b07736caf1db81abc88d03fc3f 100644
|
| --- a/runtime/observatory/lib/src/elements/code_view.dart
|
| +++ b/runtime/observatory/lib/src/elements/code_view.dart
|
| @@ -6,29 +6,339 @@ library code_view_element;
|
|
|
| import 'dart:html';
|
| import 'observatory_element.dart';
|
| +import 'package:observatory/app.dart';
|
| import 'package:observatory/service.dart';
|
| +import 'package:observatory/cpu_profile.dart';
|
| import 'package:polymer/polymer.dart';
|
|
|
| +class DisassemblyTable extends SortedTable {
|
| + DisassemblyTable(columns) : super(columns);
|
| +}
|
| +
|
| +class InlineTable extends SortedTable {
|
| + InlineTable(columns) : super(columns);
|
| +}
|
| +
|
| @CustomTag('code-view')
|
| class CodeViewElement extends ObservatoryElement {
|
| - @published Code code;
|
| - CodeViewElement.created() : super.created();
|
| + @observable Code code;
|
| + ProfileCode get profile => code == null ? null : code.profile;
|
| + DisassemblyTable disassemblyTable;
|
| + InlineTable inlineTable;
|
| +
|
| + CodeViewElement.created() : super.created() {
|
| + // Create table models.
|
| + var columns = [
|
| + new SortedTableColumn('Address'),
|
| + new SortedTableColumn('Inclusive'),
|
| + new SortedTableColumn('Exclusive'),
|
| + new SortedTableColumn('Disassembly'),
|
| + ];
|
| + disassemblyTable = new DisassemblyTable(columns);
|
| + columns = [
|
| + new SortedTableColumn('Address'),
|
| + new SortedTableColumn('Inclusive'),
|
| + new SortedTableColumn('Exclusive'),
|
| + new SortedTableColumn('Functions'),
|
| + ];
|
| + inlineTable = new InlineTable(columns);
|
| + }
|
|
|
| @override
|
| void attached() {
|
| super.attached();
|
| + }
|
| +
|
| + void codeChanged(oldValue) {
|
| if (code == null) {
|
| return;
|
| }
|
| code.load().then((Code c) {
|
| c.loadScript();
|
| });
|
| + _updateDisassembly();
|
| + _updateInline();
|
| }
|
|
|
| void refresh(var done) {
|
| code.reload().whenComplete(done);
|
| }
|
|
|
| + void refreshTicks(var done) {
|
| + var isolate = code.isolate;
|
| + isolate.invokeRpc('getCpuProfile', { 'tags': 'None' })
|
| + .then((ServiceMap response) {
|
| + var cpuProfile = new CpuProfile();
|
| + cpuProfile.load(isolate, response);
|
| + _updateDisassembly();
|
| + _updateInline();
|
| + }).whenComplete(done);
|
| + }
|
| +
|
| + String formattedAddress(CodeInstruction instruction) {
|
| + if (instruction.address == 0) {
|
| + return '';
|
| + }
|
| + return '0x${instruction.address.toRadixString(16)}';
|
| + }
|
| +
|
| + String formattedAddressRange(CodeInlineInterval interval) {
|
| + String start = interval.start.toRadixString(16);
|
| + String end = interval.end.toRadixString(16);
|
| + return '[0x$start, 0x$end)';
|
| + }
|
| +
|
| + String formattedInclusiveInterval(CodeInlineInterval interval) {
|
| + if (code == null) {
|
| + return '';
|
| + }
|
| + if (code.profile == null) {
|
| + return '';
|
| + }
|
| + var intervalTick = code.profile.intervalTicks[interval.start];
|
| + if (intervalTick == null) {
|
| + return '';
|
| + }
|
| + // Don't show inclusive ticks if they are the same as exclusive ticks.
|
| + if (intervalTick.inclusiveTicks == intervalTick.exclusiveTicks) {
|
| + return '';
|
| + }
|
| + var pcent = Utils.formatPercent(intervalTick.inclusiveTicks,
|
| + code.profile.profile.sampleCount);
|
| + return '$pcent (${intervalTick.inclusiveTicks})';
|
| + }
|
| +
|
| + String formattedExclusiveInterval(CodeInlineInterval interval) {
|
| + if (code == null) {
|
| + return '';
|
| + }
|
| + if (code.profile == null) {
|
| + return '';
|
| + }
|
| + var intervalTick = code.profile.intervalTicks[interval.start];
|
| + if (intervalTick == null) {
|
| + return '';
|
| + }
|
| + var pcent = Utils.formatPercent(intervalTick.exclusiveTicks,
|
| + code.profile.profile.sampleCount);
|
| + return '$pcent (${intervalTick.exclusiveTicks})';
|
| + }
|
| +
|
| +
|
| + String formattedInclusive(CodeInstruction instruction) {
|
| + if (code == null) {
|
| + return '';
|
| + }
|
| + if (code.profile == null) {
|
| + return '';
|
| + }
|
| + var tick = code.profile.addressTicks[instruction.address];
|
| + if (tick == null) {
|
| + return '';
|
| + }
|
| + // Don't show inclusive ticks if they are the same as exclusive ticks.
|
| + if (tick.inclusiveTicks == tick.exclusiveTicks) {
|
| + return '';
|
| + }
|
| + var pcent = Utils.formatPercent(tick.inclusiveTicks,
|
| + code.profile.profile.sampleCount);
|
| + return '$pcent (${tick.inclusiveTicks})';
|
| + }
|
| +
|
| + String formattedExclusive(CodeInstruction instruction) {
|
| + if (code == null) {
|
| + return '';
|
| + }
|
| + if (code.profile == null) {
|
| + return '';
|
| + }
|
| + var tick = code.profile.addressTicks[instruction.address];
|
| + if (tick == null) {
|
| + return '';
|
| + }
|
| + var pcent = Utils.formatPercent(tick.exclusiveTicks,
|
| + code.profile.profile.sampleCount);
|
| + return '$pcent (${tick.exclusiveTicks})';
|
| + }
|
| +
|
| + void _updateDiasssemblyTable() {
|
| + disassemblyTable.clearRows();
|
| + if (code == null) {
|
| + return;
|
| + }
|
| + for (CodeInstruction instruction in code.instructions) {
|
| + var row = [formattedAddress(instruction),
|
| + formattedInclusive(instruction),
|
| + formattedExclusive(instruction),
|
| + instruction.human];
|
| + disassemblyTable.addRow(new SortedTableRow(row));
|
| + }
|
| + }
|
| +
|
| + void _addDisassemblyDOMRow() {
|
| + var tableBody = $['disassemblyTableBody'];
|
| + assert(tableBody != null);
|
| + var tr = new TableRowElement();
|
| +
|
| + var cell;
|
| +
|
| + // Add new space.
|
| + cell = tr.insertCell(-1);
|
| + cell.classes.add('monospace');
|
| + cell = tr.insertCell(-1);
|
| + cell.classes.add('monospace');
|
| + cell = tr.insertCell(-1);
|
| + cell.classes.add('monospace');
|
| + cell = tr.insertCell(-1);
|
| + cell.classes.add('monospace');
|
| +
|
| + tableBody.children.add(tr);
|
| + }
|
| +
|
| + void _fillDisassemblyDOMRow(TableRowElement tr, int rowIndex) {
|
| + var row = disassemblyTable.rows[rowIndex];
|
| + for (var i = 0; i < row.values.length; i++) {
|
| + var cell = tr.children[i];
|
| + cell.title = row.values[i].toString();
|
| + cell.text = row.values[i].toString();
|
| + }
|
| + }
|
| +
|
| + void _updateDisassemblyDOMTable() {
|
| + var tableBody = $['disassemblyTableBody'];
|
| + assert(tableBody != null);
|
| + // Resize DOM table.
|
| + if (tableBody.children.length > disassemblyTable.sortedRows.length) {
|
| + // Shrink the table.
|
| + var deadRows =
|
| + tableBody.children.length - disassemblyTable.sortedRows.length;
|
| + for (var i = 0; i < deadRows; i++) {
|
| + tableBody.children.removeLast();
|
| + }
|
| + } else if (tableBody.children.length < disassemblyTable.sortedRows.length) {
|
| + // Grow table.
|
| + var newRows =
|
| + disassemblyTable.sortedRows.length - tableBody.children.length;
|
| + for (var i = 0; i < newRows; i++) {
|
| + _addDisassemblyDOMRow();
|
| + }
|
| + }
|
| +
|
| + assert(tableBody.children.length == disassemblyTable.sortedRows.length);
|
| + // Fill table.
|
| + for (var i = 0; i < disassemblyTable.sortedRows.length; i++) {
|
| + var rowIndex = disassemblyTable.sortedRows[i];
|
| + var tr = tableBody.children[i];
|
| + _fillDisassemblyDOMRow(tr, rowIndex);
|
| + }
|
| + }
|
| +
|
| + void _updateDisassembly() {
|
| + notifyPropertyChange(#code, true, false);
|
| + _updateDiasssemblyTable();
|
| + _updateDisassemblyDOMTable();
|
| + }
|
| +
|
| + void _updateInlineTable() {
|
| + inlineTable.clearRows();
|
| + if (code == null) {
|
| + return;
|
| + }
|
| + for (CodeInlineInterval interval in code.inlineIntervals) {
|
| + var row = [interval,
|
| + formattedInclusiveInterval(interval),
|
| + formattedExclusiveInterval(interval),
|
| + interval.functions];
|
| + inlineTable.addRow(new SortedTableRow(row));
|
| + }
|
| + }
|
| +
|
| + void _addInlineDOMRow() {
|
| + var tableBody = shadowRoot.querySelector('#inlineRangeTableBody');
|
| + assert(tableBody != null);
|
| + var tr = new TableRowElement();
|
| +
|
| + var cell;
|
| +
|
| + // Add new space.
|
| + cell = tr.insertCell(-1);
|
| + cell.classes.add('monospace');
|
| + cell = tr.insertCell(-1);
|
| + cell.classes.add('monospace');
|
| + cell = tr.insertCell(-1);
|
| + cell.classes.add('monospace');
|
| + cell = tr.insertCell(-1);
|
| +
|
| + tableBody.children.add(tr);
|
| + }
|
| +
|
| + void _fillInlineDOMRow(TableRowElement tr, int rowIndex) {
|
| + var row = inlineTable.rows[rowIndex];
|
| + var columns = row.values.length;
|
| + var addressRangeColumn = 0;
|
| + var functionsColumn = columns - 1;
|
| +
|
| + {
|
| + var addressRangeCell = tr.children[addressRangeColumn];
|
| + var interval = row.values[addressRangeColumn];
|
| + var addressRangeString = formattedAddressRange(interval);
|
| + var addressRangeElement = new SpanElement();
|
| + addressRangeElement.classes.add('monospace');
|
| + addressRangeElement.text = addressRangeString;
|
| + addressRangeCell.children.clear();
|
| + addressRangeCell.children.add(addressRangeElement);
|
| + }
|
| +
|
| + for (var i = addressRangeColumn + 1; i < columns - 1; i++) {
|
| + var cell = tr.children[i];
|
| + cell.title = row.values[i].toString();
|
| + cell.text = row.values[i].toString();
|
| + }
|
| + var functions = row.values[functionsColumn];
|
| + var functionsCell = tr.children[functionsColumn];
|
| + functionsCell.children.clear();
|
| + for (var func in functions) {
|
| + var functionRef = new Element.tag('function-ref');
|
| + functionRef.ref = func;
|
| + functionsCell.children.add(functionRef);
|
| + var gap = new SpanElement();
|
| + gap.style.minWidth = '1em';
|
| + gap.text = ' ';
|
| + functionsCell.children.add(gap);
|
| + }
|
| + }
|
| +
|
| + void _updateInlineDOMTable() {
|
| + var tableBody = shadowRoot.querySelector('#inlineRangeTableBody');
|
| + // Resize DOM table.
|
| + if (tableBody.children.length > inlineTable.sortedRows.length) {
|
| + // Shrink the table.
|
| + var deadRows =
|
| + tableBody.children.length - inlineTable.sortedRows.length;
|
| + for (var i = 0; i < deadRows; i++) {
|
| + tableBody.children.removeLast();
|
| + }
|
| + } else if (tableBody.children.length < inlineTable.sortedRows.length) {
|
| + // Grow table.
|
| + var newRows = inlineTable.sortedRows.length - tableBody.children.length;
|
| + for (var i = 0; i < newRows; i++) {
|
| + _addInlineDOMRow();
|
| + }
|
| + }
|
| + assert(tableBody.children.length == inlineTable.sortedRows.length);
|
| + // Fill table.
|
| + for (var i = 0; i < inlineTable.sortedRows.length; i++) {
|
| + var rowIndex = inlineTable.sortedRows[i];
|
| + var tr = tableBody.children[i];
|
| + _fillInlineDOMRow(tr, rowIndex);
|
| + }
|
| + }
|
| +
|
| + void _updateInline() {
|
| + _updateInlineTable();
|
| + _updateInlineDOMTable();
|
| + }
|
| +
|
| Element _findJumpTarget(Element target) {
|
| var jumpTarget = target.attributes['data-jump-target'];
|
| if (jumpTarget == '') {
|
|
|