Skip to content

Commit e5b8c01

Browse files
authored
Add basic disassembly view (#5)
* Add basic disassembly view, highlights the currently active line when created Currently, the window does not automatically update if the pc changes. * Register a listener when opening the disassembly window * Strip away part of the wasm-objdump output + enable assembler syntax highlighting It's not wasm highlighting, but it does work reasonably well. * Move disassembly view into a class + properly remove listener when closing the window * Show an error message when wabt is not installed
1 parent df95bb5 commit e5b8c01

3 files changed

Lines changed: 101 additions & 2 deletions

File tree

src/main/kotlin/be/ugent/topl/mio/debugger/Debugger.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ open class Debugger(private val connection: Connection, start: Boolean = true, p
103103
}
104104
}
105105
}
106-
val history = mutableListOf<WOODDumpResponse>()
107106
val checkpoints = mutableListOf<Checkpoint?>()
107+
private val stateListeners = mutableListOf<(WOODDumpResponse) -> Unit>()
108108
private var commandBreakpoint = false
109109

110110
init {
@@ -310,7 +310,26 @@ open class Debugger(private val connection: Connection, start: Boolean = true, p
310310
// Results:
311311
checkpointsUpdated()
312312
}
313-
open fun checkpointsUpdated() {}
313+
314+
open fun checkpointsUpdated() {
315+
val currentState = checkpoints.last()
316+
if (currentState == null) {
317+
return
318+
}
319+
320+
for (listener in stateListeners) {
321+
listener(currentState.snapshot)
322+
}
323+
}
324+
325+
fun registerCurrentStateListener(listener: (WOODDumpResponse) -> Unit) {
326+
stateListeners.add(listener)
327+
}
328+
329+
fun removeCurrentStateListener(listener: (WOODDumpResponse) -> Unit) {
330+
stateListeners.remove(listener)
331+
}
332+
314333
fun addBreakpoint(address: Int) {
315334
send(6, String.format("%08x", address))
316335
messageQueue.waitForResponse("BP $address!")
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package be.ugent.topl.mio.ui
2+
3+
import be.ugent.topl.mio.debugger.Debugger
4+
import be.ugent.topl.mio.woodstate.WOODDumpResponse
5+
import com.formdev.flatlaf.FlatLaf
6+
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea
7+
import org.fife.ui.rsyntaxtextarea.SyntaxConstants
8+
import org.fife.ui.rtextarea.RTextScrollPane
9+
import java.awt.Color
10+
import java.awt.Dimension
11+
import java.awt.event.WindowAdapter
12+
import java.awt.event.WindowEvent
13+
import java.io.IOException
14+
import javax.swing.JFrame
15+
16+
class DisassemblyWindow(debugger: Debugger, wasmFile: String) : JFrame("Disassembly") {
17+
private val disassemblyTextArea = RSyntaxTextArea(disassemble(wasmFile)).apply {
18+
syntaxEditingStyle = SyntaxConstants.SYNTAX_STYLE_ASSEMBLER_X86
19+
isEditable = false
20+
}
21+
22+
init {
23+
add(RTextScrollPane(disassemblyTextArea))
24+
minimumSize = Dimension(200, 200)
25+
preferredSize = Dimension(400, 600)
26+
isVisible = true
27+
28+
highlightCurrentLine(debugger.checkpoints.last()!!.snapshot)
29+
30+
val listener = this::highlightCurrentLine
31+
addWindowListener(object : WindowAdapter() {
32+
override fun windowClosing(e: WindowEvent?) {
33+
debugger.removeCurrentStateListener(listener)
34+
}
35+
})
36+
debugger.registerCurrentStateListener(listener)
37+
}
38+
39+
private fun disassemble(wasmFile: String): String {
40+
try {
41+
val command = listOf("wasm-objdump", "-d", wasmFile)
42+
println("Running command: ${command.joinToString(" ")}")
43+
val process = ProcessBuilder(command).redirectErrorStream(true).start()
44+
process.waitFor()
45+
var result = process.inputStream.readAllBytes().toString(Charsets.UTF_8)
46+
val startString = "Code Disassembly:\n\n"
47+
return result.substring(result.indexOf(startString) + startString.length)
48+
} catch (e: IOException) {
49+
// Catch exception and add additional information.
50+
throw IOException("An error occurred when attempting to disassemble the program, make sure the The WebAssembly Binary Toolkit is installed on your system.\n" + e.message)
51+
}
52+
}
53+
54+
private fun highlightCurrentLine(currentState: WOODDumpResponse) {
55+
// IDEA: Would maybe be fun to highlight the instructions associated with the current line with the same color.
56+
val pc = currentState.pc!!
57+
val lines = disassemblyTextArea.text.lines()
58+
for (lineIndex in lines.indices) {
59+
val line = lines[lineIndex]
60+
// TODO: Use binary search instead of just linearly searching the address
61+
if (line.trim().startsWith(String.format("%06x", pc))) {
62+
disassemblyTextArea.removeAllLineHighlights()
63+
disassemblyTextArea.addLineHighlight(lineIndex, if (!FlatLaf.isLafDark()) Color(255, 255, 186, 255) else Color(207, 207, 131, 75))
64+
}
65+
}
66+
}
67+
}

src/main/kotlin/be/ugent/topl/mio/ui/InteractiveDebugger.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import java.awt.event.MouseListener
2626
import java.awt.event.WindowAdapter
2727
import java.awt.event.WindowEvent
2828
import java.io.File
29+
import java.io.IOException
2930
import javax.swing.*
3031
import javax.swing.event.DocumentEvent
3132
import javax.swing.event.DocumentListener
@@ -276,6 +277,15 @@ class InteractiveDebugger(
276277
textArea.isEditable = false
277278
textArea.text = sourceMapping?.getSourceFile(0) ?: "Source mapping unavailable"
278279
textArea.syntaxEditingStyle = sourceMapping?.getStyle() ?: SyntaxConstants.SYNTAX_STYLE_ASSEMBLER_X86
280+
textArea.popupMenu.add(JMenuItem("Disassemble").apply {
281+
addActionListener {
282+
try {
283+
DisassemblyWindow(debugger, wasmFile)
284+
} catch(e: IOException) {
285+
JOptionPane.showMessageDialog(null, e.message, "Error", JOptionPane.ERROR_MESSAGE)
286+
}
287+
}
288+
})
279289
scrollPane.isIconRowHeaderEnabled = true
280290
scrollPane.gutter.iconRowHeaderInheritsGutterBackground = true
281291
if (sourceMapping != null) {
@@ -757,6 +767,9 @@ class WatchWindow : JTable() {
757767
for (global in snapshot.globals!!) {
758768
tableModel.addRow(arrayOf("global ${global.idx}", global.type, global.value))
759769
}
770+
if (snapshot.callstack!!.isNotEmpty()) {
771+
tableModel.addRow(arrayOf("fp", "i32", snapshot.callstack.last().fp))
772+
}
760773
val stack = snapshot.stack!!
761774
for (stackElement in stack) {
762775
tableModel.addRow(arrayOf("stack[${stackElement.idx}]", stackElement.type, stackElement.value))

0 commit comments

Comments
 (0)