Commit ee0eb614 authored by Sigurd Schneider's avatar Sigurd Schneider Committed by Commit Bot

[turbolizer] Improve disassembly view

This CL makes both absolute address and opcode literal (byte sequence
of the instruction) display optional, which improves readability.

Additionally, jump offsets are parsed and can now once again be clicked.

TBR=neis@chromium.org

Bug: v8:7327
Notry: true
Change-Id: I709f44540b32f6d4afabdd1e5eb27e932208e7fc
Reviewed-on: https://chromium-review.googlesource.com/c/1388540
Commit-Queue: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: 's avatarSigurd Schneider <sigurds@chromium.org>
Cr-Commit-Position: refs/heads/master@{#58444}
parent 9365d090
...@@ -10,6 +10,13 @@ import { MySelection } from "./selection"; ...@@ -10,6 +10,13 @@ import { MySelection } from "./selection";
import { anyToString } from "./util"; import { anyToString } from "./util";
import { InstructionSelectionHandler } from "./selection-handler"; import { InstructionSelectionHandler } from "./selection-handler";
const toolboxHTML = `<div id="disassembly-toolbox">
<form>
<input id="show-instruction-address" type="checkbox" name="instruction-address">Show addresses</input>
<input id="show-instruction-binary" type="checkbox" name="instruction-binary">Show binary literal</input>
</form>
</div>`
export class DisassemblyView extends TextView { export class DisassemblyView extends TextView {
SOURCE_POSITION_HEADER_REGEX: any; SOURCE_POSITION_HEADER_REGEX: any;
addr_event_counts: any; addr_event_counts: any;
...@@ -18,6 +25,8 @@ export class DisassemblyView extends TextView { ...@@ -18,6 +25,8 @@ export class DisassemblyView extends TextView {
pos_lines: Array<any>; pos_lines: Array<any>;
instructionSelectionHandler: InstructionSelectionHandler; instructionSelectionHandler: InstructionSelectionHandler;
offsetSelection: MySelection; offsetSelection: MySelection;
showInstructionAddressHandler: () => void;
showInstructionBinaryHandler: () => void;
createViewElement() { createViewElement() {
const pane = document.createElement('div'); const pane = document.createElement('div');
...@@ -32,43 +41,63 @@ export class DisassemblyView extends TextView { ...@@ -32,43 +41,63 @@ export class DisassemblyView extends TextView {
} }
constructor(parentId, broker: SelectionBroker) { constructor(parentId, broker: SelectionBroker) {
super(parentId, broker, null); super(parentId, broker);
let view = this; let view = this;
let ADDRESS_STYLE = { let ADDRESS_STYLE = {
css: ['linkable-text', 'tag'], associateData: (text, fragment: HTMLElement) => {
associateData: (text, fragment) => { const matches = text.match(/(?<address>0?x?[0-9a-fA-F]{8,16})(?<addressSpace>\s+)(?<offset>[0-9a-f]+)(?<offsetSpace>\s*)/);
const matches = text.match(/0?x?[0-9a-fA-F]{8,16}\s*(?<offset>[0-9a-f]+)/);
const offset = Number.parseInt(matches.groups["offset"], 16); const offset = Number.parseInt(matches.groups["offset"], 16);
const addressElement = document.createElement("SPAN");
addressElement.className = "instruction-address";
addressElement.innerText = matches.groups["address"];
const offsetElement = document.createElement("SPAN");
offsetElement.innerText = matches.groups["offset"];
fragment.appendChild(addressElement);
fragment.appendChild(document.createTextNode(matches.groups["addressSpace"]))
fragment.appendChild(offsetElement);
fragment.appendChild(document.createTextNode(matches.groups["offsetSpace"]))
fragment.classList.add('tag');
if (!Number.isNaN(offset)) { if (!Number.isNaN(offset)) {
fragment.dataset.pcOffset = view.sourceResolver.getKeyPcOffset(offset); const pcOffset = view.sourceResolver.getKeyPcOffset(offset);
fragment.dataset.pcOffset = `${pcOffset}`;
addressElement.classList.add('linkable-text');
offsetElement.classList.add('linkable-text');
} }
} }
}; };
let ADDRESS_LINK_STYLE = {
css: 'tag'
};
let UNCLASSIFIED_STYLE = { let UNCLASSIFIED_STYLE = {
css: 'com' css: 'com'
}; };
let NUMBER_STYLE = { let NUMBER_STYLE = {
css: 'lit' css: ['instruction-binary', 'lit']
}; };
let COMMENT_STYLE = { let COMMENT_STYLE = {
css: 'com' css: 'com'
}; };
let POSITION_STYLE = { let OPCODE_ARGS = {
css: 'com', associateData: function (text, fragment) {
fragment.innerHTML = text;
const replacer = (match, hexOffset, stringOffset, string) => {
const offset = Number.parseInt(hexOffset, 16);
const keyOffset = view.sourceResolver.getKeyPcOffset(offset)
return `<span class="tag linkable-text" data-pc-offset="${keyOffset}">${match}</span>`
}
const html = text.replace(/<.0?x?([0-9a-fA-F]+)>/g, replacer)
fragment.innerHTML = html;
}
}; };
let OPCODE_STYLE = { let OPCODE_STYLE = {
css: 'kwd', css: 'kwd'
}; };
const BLOCK_HEADER_STYLE = { const BLOCK_HEADER_STYLE = {
css: ['com', 'block'],
associateData: function (text, fragment) { associateData: function (text, fragment) {
let matches = /\d+/.exec(text); let matches = /\d+/.exec(text);
if (!matches) return; if (!matches) return;
const blockId = matches[0]; const blockId = matches[0];
fragment.dataset.blockId = blockId; fragment.dataset.blockId = blockId;
fragment.innerHTML = text;
fragment.className = "com block";
} }
}; };
const SOURCE_POSITION_HEADER_STYLE = { const SOURCE_POSITION_HEADER_STYLE = {
...@@ -77,47 +106,38 @@ export class DisassemblyView extends TextView { ...@@ -77,47 +106,38 @@ export class DisassemblyView extends TextView {
view.SOURCE_POSITION_HEADER_REGEX = /^\s*--[^<]*<.*(not inlined|inlined\((\d+)\)):(\d+)>\s*--/; view.SOURCE_POSITION_HEADER_REGEX = /^\s*--[^<]*<.*(not inlined|inlined\((\d+)\)):(\d+)>\s*--/;
let patterns = [ let patterns = [
[ [
[/^0?x?[0-9a-fA-F]{8,16}\s*[0-9a-f]+\ /, ADDRESS_STYLE, 1], [/^0?x?[0-9a-fA-F]{8,16}\s+[0-9a-f]+\s+/, ADDRESS_STYLE, 1],
[view.SOURCE_POSITION_HEADER_REGEX, SOURCE_POSITION_HEADER_STYLE, -1], [view.SOURCE_POSITION_HEADER_REGEX, SOURCE_POSITION_HEADER_STYLE, -1],
[/^\s+-- B\d+ start.*/, BLOCK_HEADER_STYLE, -1], [/^\s+-- B\d+ start.*/, BLOCK_HEADER_STYLE, -1],
[/^.*/, UNCLASSIFIED_STYLE, -1] [/^.*/, UNCLASSIFIED_STYLE, -1]
], ],
[ [
[/^\s+[0-9a-f]+\s+/, NUMBER_STYLE, 2], [/^\s*[0-9a-f]+\s+/, NUMBER_STYLE, 2],
[/^\s+[0-9a-f]+\s+[0-9a-f]+\s+/, NUMBER_STYLE, 2], [/^\s*[0-9a-f]+\s+[0-9a-f]+\s+/, NUMBER_STYLE, 2],
[/^.*/, null, -1] [/^.*/, null, -1]
], ],
[ [
[/^REX.W \S+\s+/, OPCODE_STYLE, 3],
[/^\S+\s+/, OPCODE_STYLE, 3], [/^\S+\s+/, OPCODE_STYLE, 3],
[/^\S+$/, OPCODE_STYLE, -1], [/^\S+$/, OPCODE_STYLE, -1],
[/^.*/, null, -1] [/^.*/, null, -1]
], ],
[ [
[/^\s+/, null], [/^\s+/, null],
[/^[^\(;]+$/, null, -1], [/^[^;]+$/, OPCODE_ARGS, -1],
[/^[^\(;]+/, null], [/^[^;]+/, OPCODE_ARGS, 4],
[/^\(/, null, 4],
[/^;/, COMMENT_STYLE, 5] [/^;/, COMMENT_STYLE, 5]
], ],
[ [
[/^0x[0-9a-f]{8,16}/, ADDRESS_LINK_STYLE],
[/^[^\)]/, null],
[/^\)$/, null, -1],
[/^\)/, null, 3]
],
[
[/^; debug\: position /, COMMENT_STYLE, 6],
[/^.+$/, COMMENT_STYLE, -1] [/^.+$/, COMMENT_STYLE, -1]
],
[
[/^\d+$/, POSITION_STYLE, -1],
] ]
]; ];
view.setPatterns(patterns); view.setPatterns(patterns);
const linkHandler = (e) => { const linkHandler = (e: MouseEvent) => {
const offset = e.target.dataset.pcOffset; if (!(e.target instanceof HTMLElement)) return;
if (typeof offset != "undefined" && !Number.isNaN(offset)) { const offset = e.target.dataset.pcOffset ? e.target.dataset.pcOffset : e.target.parentElement.dataset.pcOffset;
if ((typeof offset) != "undefined" && !Number.isNaN(Number(offset))) {
view.offsetSelection.select([offset], true); view.offsetSelection.select([offset], true);
const [nodes, blockId] = view.sourceResolver.nodesForPCOffset(offset) const [nodes, blockId] = view.sourceResolver.nodesForPCOffset(offset)
if (nodes.length > 0) { if (nodes.length > 0) {
...@@ -171,6 +191,34 @@ export class DisassemblyView extends TextView { ...@@ -171,6 +191,34 @@ export class DisassemblyView extends TextView {
}; };
this.instructionSelectionHandler = instructionSelectionHandler; this.instructionSelectionHandler = instructionSelectionHandler;
broker.addInstructionHandler(instructionSelectionHandler); broker.addInstructionHandler(instructionSelectionHandler);
const toolbox = document.createElement("div")
toolbox.id = "toolbox-anchor";
toolbox.innerHTML = toolboxHTML
view.divNode.insertBefore(toolbox, view.divNode.firstChild);
const instructionAddressInput: HTMLInputElement = view.divNode.querySelector("#show-instruction-address");
const lastShowInstructionAddress = window.sessionStorage.getItem("show-instruction-address");
instructionAddressInput.checked = lastShowInstructionAddress == 'true';
const showInstructionAddressHandler = () => {
window.sessionStorage.setItem("show-instruction-address", `${instructionAddressInput.checked}`);
for (const el of view.divNode.querySelectorAll(".instruction-address")) {
el.classList.toggle("invisible", !instructionAddressInput.checked);
}
};
instructionAddressInput.addEventListener("change", showInstructionAddressHandler);
this.showInstructionAddressHandler = showInstructionAddressHandler;
const instructionBinaryInput: HTMLInputElement = view.divNode.querySelector("#show-instruction-binary");
const lastShowInstructionBinary = window.sessionStorage.getItem("show-instruction-binary");
instructionBinaryInput.checked = lastShowInstructionAddress == 'true';
const showInstructionBinaryHandler = () => {
window.sessionStorage.setItem("show-instruction-binary", `${instructionBinaryInput.checked}`);
for (const el of view.divNode.querySelectorAll(".instruction-binary")) {
el.classList.toggle("invisible", !instructionBinaryInput.checked);
}
};
instructionBinaryInput.addEventListener("change", showInstructionBinaryHandler);
this.showInstructionBinaryHandler = showInstructionBinaryHandler;
} }
updateSelection(scrollIntoView: boolean = false) { updateSelection(scrollIntoView: boolean = false) {
...@@ -231,6 +279,14 @@ export class DisassemblyView extends TextView { ...@@ -231,6 +279,14 @@ export class DisassemblyView extends TextView {
} }
} }
initializeContent(data, rememberedSelection) {
console.time("disassembly-view")
super.initializeContent(data, rememberedSelection);
this.showInstructionAddressHandler();
this.showInstructionBinaryHandler();
console.timeEnd("disassembly-view")
}
// Shorten decimals and remove trailing zeroes for readability. // Shorten decimals and remove trailing zeroes for readability.
humanize(num) { humanize(num) {
return num.toFixed(3).replace(/\.?0+$/, "") + "%"; return num.toFixed(3).replace(/\.?0+$/, "") + "%";
......
...@@ -12,7 +12,7 @@ import { View, PhaseView } from "../src/view" ...@@ -12,7 +12,7 @@ import { View, PhaseView } from "../src/view"
const multiviewID = "multiview"; const multiviewID = "multiview";
const toolboxHTML = ` const toolboxHTML = `
<div id="graph-toolbox"> <div class="graph-toolbox">
<input id="layout" type="image" title="layout graph" src="layout-icon.png" alt="layout graph" class="button-input"> <input id="layout" type="image" title="layout graph" src="layout-icon.png" alt="layout graph" class="button-input">
<input id="show-all" type="image" title="show all nodes" src="expand-all.jpg" alt="show all nodes" class="button-input"> <input id="show-all" type="image" title="show all nodes" src="expand-all.jpg" alt="show all nodes" class="button-input">
<input id="toggle-hide-dead" type="image" title="show only live nodes" src="live.png" alt="only live nodes" <input id="toggle-hide-dead" type="image" title="show only live nodes" src="live.png" alt="only live nodes"
...@@ -53,7 +53,7 @@ export class GraphMultiView extends View { ...@@ -53,7 +53,7 @@ export class GraphMultiView extends View {
view.sourceResolver = sourceResolver; view.sourceResolver = sourceResolver;
view.selectionBroker = selectionBroker; view.selectionBroker = selectionBroker;
const toolbox = document.createElement("div") const toolbox = document.createElement("div")
toolbox.id = "graph-toolbox-anchor"; toolbox.className = "toolbox-anchor";
toolbox.innerHTML = toolboxHTML toolbox.innerHTML = toolboxHTML
view.divNode.appendChild(toolbox); view.divNode.appendChild(toolbox);
const searchInput = toolbox.querySelector("#search-input") as HTMLInputElement; const searchInput = toolbox.querySelector("#search-input") as HTMLInputElement;
......
...@@ -18,7 +18,7 @@ export class ScheduleView extends TextView implements PhaseView { ...@@ -18,7 +18,7 @@ export class ScheduleView extends TextView implements PhaseView {
} }
constructor(parentId, broker) { constructor(parentId, broker) {
super(parentId, broker, null); super(parentId, broker);
this.sourceResolver = broker.sourceResolver; this.sourceResolver = broker.sourceResolver;
} }
......
...@@ -19,7 +19,7 @@ export class SequenceView extends TextView implements PhaseView { ...@@ -19,7 +19,7 @@ export class SequenceView extends TextView implements PhaseView {
} }
constructor(parentId, broker) { constructor(parentId, broker) {
super(parentId, broker, null); super(parentId, broker);
} }
attachSelection(s) { attachSelection(s) {
......
...@@ -23,11 +23,11 @@ export abstract class TextView extends View { ...@@ -23,11 +23,11 @@ export abstract class TextView extends View {
sourceResolver: SourceResolver; sourceResolver: SourceResolver;
broker: SelectionBroker; broker: SelectionBroker;
constructor(id, broker, patterns) { constructor(id, broker) {
super(id); super(id);
let view = this; let view = this;
view.textListNode = view.divNode.getElementsByTagName('ul')[0]; view.textListNode = view.divNode.getElementsByTagName('ul')[0];
view.patterns = patterns; view.patterns = null;
view.nodeIdToHtmlElementsMap = new Map(); view.nodeIdToHtmlElementsMap = new Map();
view.blockIdToHtmlElementsMap = new Map(); view.blockIdToHtmlElementsMap = new Map();
view.blockIdtoNodeIds = new Map(); view.blockIdtoNodeIds = new Map();
...@@ -157,8 +157,7 @@ export abstract class TextView extends View { ...@@ -157,8 +157,7 @@ export abstract class TextView extends View {
} }
setPatterns(patterns) { setPatterns(patterns) {
let view = this; this.patterns = patterns;
view.patterns = patterns;
} }
clearText() { clearText() {
...@@ -172,25 +171,18 @@ export abstract class TextView extends View { ...@@ -172,25 +171,18 @@ export abstract class TextView extends View {
let view = this; let view = this;
let fragment = document.createElement("SPAN"); let fragment = document.createElement("SPAN");
if (typeof style.link == 'function') {
fragment.classList.add('linkable-text');
fragment.onmouseup = function (e) {
e.stopPropagation();
style.link(text)
};
}
if (typeof style.associateData == 'function') { if (typeof style.associateData == 'function') {
style.associateData(text, fragment); style.associateData(text, fragment);
} } else {
if (style.css != undefined) {
if (style.css != undefined) { const css = isIterable(style.css) ? style.css : [style.css];
const css = isIterable(style.css) ? style.css : [style.css]; for (const cls of css) {
for (const cls of css) { fragment.classList.add(cls);
fragment.classList.add(cls); }
} }
fragment.innerHTML = text;
} }
fragment.innerHTML = text;
return fragment; return fragment;
} }
......
...@@ -226,9 +226,9 @@ window.onload = function () { ...@@ -226,9 +226,9 @@ window.onload = function () {
const jsonObj = JSON.parse(txtRes); const jsonObj = JSON.parse(txtRes);
let fnc = Object.assign(jsonObj.function, {backwardsCompatibility: false}); let fnc = null
// Backwards compatibility. // Backwards compatibility.
if (typeof fnc == 'string') { if (typeof jsonObj.function == 'string') {
fnc = { fnc = {
functionName: fnc, functionName: fnc,
sourceId: -1, sourceId: -1,
...@@ -237,6 +237,8 @@ window.onload = function () { ...@@ -237,6 +237,8 @@ window.onload = function () {
sourceText: jsonObj.source, sourceText: jsonObj.source,
backwardsCompatibility: true backwardsCompatibility: true
}; };
} else {
fnc = Object.assign(jsonObj.function, {backwardsCompatibility: false});
} }
sourceResolver.setInlinings(jsonObj.inlinings); sourceResolver.setInlinings(jsonObj.inlinings);
......
...@@ -337,11 +337,11 @@ input:hover, .collapse-pane:hover input { ...@@ -337,11 +337,11 @@ input:hover, .collapse-pane:hover input {
height: 100%; height: 100%;
} }
#graph-toolbox-anchor { .toolbox-anchor {
height: 0px; height: 0px;
} }
#graph-toolbox { .graph-toolbox {
position: relative; position: relative;
border-bottom: 2px solid #eee8d5; border-bottom: 2px solid #eee8d5;
padding-bottom: 3px; padding-bottom: 3px;
...@@ -353,14 +353,15 @@ input:hover, .collapse-pane:hover input { ...@@ -353,14 +353,15 @@ input:hover, .collapse-pane:hover input {
margin-right: 4px; margin-right: 4px;
} }
#disassembly-toolbox { .disassembly-toolbox {
position: relative; position: relative;
top: 1em; padding-bottom: 3px;
left: 0.7em;
border: 2px solid #eee8d5;
border-radius: 5px;
padding: 0.7em;
z-index: 5; z-index: 5;
background: rgba(100%, 100%, 100%, 0.7);
padding-top: 3px;
box-sizing: border-box;
margin-left: 4px;
margin-right: 4px;
} }
#load-file { #load-file {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment