from PyQt5.QtWidgets import * from PyQt5.QtGui import * from PyQt5.QtCore import * import socket import struct import string import sys import os import disassemble """"""""""""""""""""""""""" Signals """"""""""""""""""""""""""" class EventHolder(QObject): Connected = pyqtSignal() Closed = pyqtSignal() BreakPointChanged = pyqtSignal() Exception = pyqtSignal(object) Continue = pyqtSignal(object) Step = pyqtSignal(object) events = EventHolder() """"""""""""""""""""""""""" Debugger client """"""""""""""""""""""""""" class ExceptionState: exceptionNames = ["DSI", "ISI", "Program"] def __init__(self, message): #Convert tuple to list to make it mutable data = message.data self.gpr = list(struct.unpack_from(">32I", data, 8)) self.cr, self.lr, self.ctr, self.xer = struct.unpack_from(">4I", data, 0x88) self.srr0, self.srr1, self.ex0, self.ex1 = struct.unpack_from(">4I", data, 0x98) self.fpr = list(struct.unpack_from(">32d", data, 0xB8)) self.gqr = list(struct.unpack_from(">8I", data, 0x1BC)) self.psf = list(struct.unpack_from(">32d", data, 0x1E0)) self.thread = message.arg self.exceptionName = self.exceptionNames[message.type] self.stepping = False def isBreakPoint(self): return self.exceptionName == "Program" and self.srr1 & 0x20000 def isFatal(self): return not self.isBreakPoint() class Message: #Server messages DSI = 0 ISI = 1 Program = 2 #Client messages Continue = 0 Step = 1 StepOver = 2 def __init__(self, type, data, arg): self.type = type self.data = data self.arg = arg class Thread: cores = { 1: "Core 0", 2: "Core 1", 4: "Core 2" } def __init__(self, data, offs=0): self.handle = struct.unpack_from(">I", data, offs)[0] self.core = self.cores[struct.unpack_from(">I", data, offs + 4)[0]] self.priority = struct.unpack_from(">I", data, offs + 8)[0] self.stackBase = struct.unpack_from(">I", data, offs + 12)[0] self.stackEnd = struct.unpack_from(">I", data, offs + 16)[0] self.entryPoint = struct.unpack_from(">I", data, offs + 20)[0] self.namelen = struct.unpack_from(">I", data, offs + 24)[0] self.name = data[offs + 28 : offs + 28 + self.namelen].decode("shift-jis") class Module: def __init__(self, data, offs=0): self.textAddr = struct.unpack_from(">I", data, offs)[0] self.textSize = struct.unpack_from(">I", data, offs + 4)[0] self.dataAddr = struct.unpack_from(">I", data, offs + 8)[0] self.dataSize = struct.unpack_from(">I", data, offs + 12)[0] self.entryPoint = struct.unpack_from(">I", data, offs + 16)[0] self.namelen = struct.unpack_from(">I", data, offs + 20)[0] self.name = data[offs + 24 : offs + 24 + self.namelen].decode("ascii") COMMAND_CLOSE = 0 COMMAND_READ = 1 COMMAND_WRITE = 2 COMMAND_WRITE_CODE = 3 COMMAND_GET_MODULE_NAME = 4 COMMAND_GET_MODULE_LIST = 5 COMMAND_GET_THREAD_LIST = 6 COMMAND_GET_STACK_TRACE = 7 COMMAND_TOGGLE_BREAKPOINT = 8 COMMAND_POKE_REGISTERS = 9 COMMAND_RECEIVE_MESSAGES = 10 COMMAND_SEND_MESSAGE = 11 class Debugger: def __init__(self): super().__init__() self.reset() self.messageHandlers = { Message.DSI: self.handleException, Message.ISI: self.handleException, Message.Program: self.handleException } def reset(self): self.connected = False self.breakpoints = [] self.trapEvents = [] self.crashEvents = [] self.threads = [] def connect(self, host): self.s = socket.socket() self.s.settimeout(4) self.s.connect((host, 1560)) self.connected = True events.Connected.emit() def handleClose(self): self.reset() events.Closed.emit() def close(self): self.sendbyte(COMMAND_CLOSE) self.s.close() self.handleClose() def read(self, addr, num): self.sendbyte(COMMAND_READ) self.sendall(struct.pack(">II", addr, num)) data = self.recvall(num) return data def write(self, addr, data): self.sendbyte(COMMAND_WRITE) self.sendall(struct.pack(">II", addr, len(data))) self.sendall(data) def writeCode(self, addr, data): self.sendbyte(COMMAND_WRITE_CODE) self.sendall(struct.pack(">II", addr, len(data))) self.sendall(data) def writeInstr(self, addr, value): self.writeCode(addr, struct.pack(">I", value)) def getModuleName(self): self.sendbyte(COMMAND_GET_MODULE_NAME) length = struct.unpack(">I", self.recvall(4))[0] return self.recvall(length).decode("ascii") + ".rpx" def getModuleList(self): self.sendbyte(COMMAND_GET_MODULE_LIST) length = struct.unpack(">I", self.recvall(4))[0] data = self.recvall(length) offset = 0 modules = [] while offset < length: module = Module(data, offset) modules.append(module) offset += 24 + len(module.name) return sorted(modules, key=lambda m: m.textAddr) def getThreadList(self): self.sendbyte(COMMAND_GET_THREAD_LIST) length = struct.unpack(">I", self.recvall(4))[0] data = self.recvall(length) offset = 0 self.threads = [] while offset < length: thread = Thread(data, offset) self.threads.append(thread) offset += 28 + thread.namelen return self.threads def getStackTrace(self, state): self.sendbyte(COMMAND_GET_STACK_TRACE) self.sendall(struct.pack(">I", state.thread)) count = struct.unpack(">I", self.recvall(4))[0] trace = struct.unpack(">%iI" %count, self.recvall(4 * count)) return trace def toggleBreakPoint(self, addr): if addr in self.breakpoints: self.breakpoints.remove(addr) else: self.breakpoints.append(addr) self.sendbyte(COMMAND_TOGGLE_BREAKPOINT) self.sendall(struct.pack(">I", addr)) events.BreakPointChanged.emit() def pokeRegisters(self, state): self.sendbyte(COMMAND_POKE_REGISTERS) self.sendall(struct.pack(">I", state.thread)) self.sendall(struct.pack(">32I32d", *state.gpr, *state.fpr)) def updateMessages(self): self.sendbyte(COMMAND_RECEIVE_MESSAGES) count = struct.unpack(">I", self.recvall(4))[0] for i in range(count): type, ptr, length, arg = struct.unpack(">IIII", self.recvall(16)) data = None if length: data = self.recvall(length) self.messageHandlers[type](Message(type, data, arg)) def sendMessage(self, message, data0=0, data1=0, data2=0): self.sendbyte(COMMAND_SEND_MESSAGE) self.sendall(struct.pack(">IIII", message, data0, data1, data2)) def handleException(self, msg): state = ExceptionState(msg) events.Exception.emit(state) def continueThread(self, state): self.sendCrashMessage(Message.Continue, state) def stepInto(self, state): self.sendCrashMessage(Message.Step, state) def stepOver(self, state): self.sendCrashMessage(Message.StepOver, state) def sendCrashMessage(self, message, state): self.sendMessage(message, state.thread) if message == Message.Continue: state.stepping = False events.Continue.emit(state) else: state.stepping = True events.Step.emit(state) def _findThread(self, handle): for thread in self.threads: if thread.handle == handle: return thread def findThread(self, handle): thread = self._findThread(handle) if not thread: self.getThreadList() thread = self._findThread(handle) return thread def sendbyte(self, byte): self.sendall(bytes([byte])) def sendall(self, data): if self.connected: try: self.s.sendall(data) except socket.error: self.handleClose() def recvall(self, num): if not self.connected: return bytes(num) try: data = b"" while len(data) < num: chunk = self.s.recv(num - len(data)) if not chunk: self.handleClose() return bytes(num) data += chunk except socket.error: self.handleClose() return bytes(num) return data debugger = Debugger() """"""""""""""""""""""""""" Custom widgets """"""""""""""""""""""""""" class HexSpinBox(QAbstractSpinBox): def __init__(self, stepSize = 1): super().__init__() self._value = 0 self.stepSize = stepSize def validate(self, text, pos): if all([char in "0123456789abcdefABCDEF" for char in text]): if not text: return QValidator.Intermediate, text.upper(), pos value = int(text, 16) if value <= 0xFFFFFFFF: self._value = value if value % self.stepSize: self._value -= value % self.stepSize return QValidator.Acceptable, text.upper(), pos return QValidator.Acceptable, text.upper(), pos return QValidator.Invalid, text.upper(), pos def stepBy(self, steps): self._value = min(max(self._value + steps * self.stepSize, 0), 0x100000000 - self.stepSize) self.lineEdit().setText("%X" %self._value) def stepEnabled(self): return QAbstractSpinBox.StepUpEnabled | QAbstractSpinBox.StepDownEnabled def setValue(self, value): self._value = value self.lineEdit().setText("%X" %self._value) def value(self): return self._value """"""""""""""""""""""""""" Memory widgets """"""""""""""""""""""""""" def format_hex(blob, offs): return "%02X" %blob[offs] def format_ascii(blob, offs): char = chr(blob[offs]) if char in string.ascii_letters + string.digits + string.punctuation + " ": return char return "?" def format_float(blob, offs): value = struct.unpack_from(">f", blob, offs)[0] if abs(value) >= 1000000 or 0 < abs(value) < 0.000001: return "%e" %value return ("%.8f" %value).rstrip("0") class MemoryViewer(QWidget): class Format: Hex = 0 Ascii = 1 Float = 2 Width = 1, 1, 4 Funcs = format_hex, format_ascii, format_float def __init__(self): super().__init__() self.layout = QGridLayout() for i in range(16): self.layout.addWidget(QLabel("%X" %i, self), 0, i + 1) self.addrLabels = [] for i in range(16): label = QLabel("%X" %(i * 0x10), self) self.layout.addWidget(label, i + 1, 0) self.addrLabels.append(label) self.dataCells = [] self.base = 0 self.format = self.Format.Hex self.updateData() self.setLayout(self.layout) events.Connected.connect(self.connected) def connected(self): self.setBase(0x10000000) def setFormat(self, format): self.format = format self.updateData() def setBase(self, base): window.mainWidget.tabWidget.memoryTab.memoryInfo.baseBox.setValue(base) self.base = base for i in range(16): self.addrLabels[i].setText("%X" %(self.base + i * 0x10)) self.updateData() def updateData(self): for cell in self.dataCells: self.layout.removeWidget(cell) cell.setParent(None) if debugger.connected: blob = debugger.read(self.base, 0x100) else: blob = b"\x00" * 0x100 width = self.Width[self.format] func = self.Funcs[self.format] for i in range(16 // width): for j in range(16): label = QLabel(func(blob, j * 0x10 + i * width), self) self.layout.addWidget(label, j + 1, i * width + 1, 1, width) self.dataCells.append(label) class MemoryInfo(QWidget): def __init__(self): super().__init__() self.dataTypeLabel = QLabel("Data type:") self.dataTypeBox = QComboBox() self.dataTypeBox.addItems(["Hex", "Ascii", "Float"]) self.dataTypeBox.currentIndexChanged.connect(self.updateDataType) self.baseLabel = QLabel("Address:") self.baseBox = HexSpinBox(0x10) self.baseButton = QPushButton("Update", self) self.baseButton.clicked.connect(self.updateMemoryBase) self.pokeAddr = HexSpinBox(4) self.pokeValue = HexSpinBox() self.pokeButton = QPushButton("Poke", self) self.pokeButton.clicked.connect(self.pokeMemory) self.layout = QGridLayout() self.layout.addWidget(self.baseLabel, 0, 0) self.layout.addWidget(self.baseBox, 0, 1) self.layout.addWidget(self.baseButton, 0, 2) self.layout.addWidget(self.pokeAddr, 1, 0) self.layout.addWidget(self.pokeValue, 1, 1) self.layout.addWidget(self.pokeButton, 1, 2) self.layout.addWidget(self.dataTypeLabel, 2, 0) self.layout.addWidget(self.dataTypeBox, 2, 1, 1, 2) self.setLayout(self.layout) def updateDataType(self, index): window.mainWidget.tabWidget.memoryTab.memoryViewer.setFormat(index) def updateMemoryBase(self): window.mainWidget.tabWidget.memoryTab.memoryViewer.setBase(self.baseBox.value()) def pokeMemory(self): debugger.write(self.pokeAddr.value(), struct.pack(">I", self.pokeValue.value())) window.mainWidget.tabWidget.memoryTab.memoryViewer.updateData() class MemoryTab(QWidget): def __init__(self): super().__init__() self.memoryInfo = MemoryInfo() self.memoryViewer = MemoryViewer() self.layout = QHBoxLayout() self.layout.addWidget(self.memoryInfo) self.layout.addWidget(self.memoryViewer) self.setLayout(self.layout) """"""""""""""""""""""""""" Disassembly widgets """"""""""""""""""""""""""" class DisassemblyWidget(QTextEdit): def __init__(self): super().__init__() self.setTextInteractionFlags(Qt.NoTextInteraction) self.setMinimumWidth(500) self.currentInstruction = None self.selectedAddress = 0 self.setBase(0) events.Closed.connect(self.updateHighlight) events.BreakPointChanged.connect(self.updateHighlight) def setCurrentInstruction(self, instr): self.currentInstruction = instr if instr is not None: self.setBase(instr - 0x20) else: self.updateHighlight() def setBase(self, base): self.base = base self.updateText() self.updateHighlight() def updateText(self): if debugger.connected: blob = debugger.read(self.base, 0x60) else: blob = b"\x00" * 0x60 text = "" for i in range(24): address = self.base + i * 4 value = struct.unpack_from(">I", blob, i * 4)[0] instr = disassemble.disassemble(value, address) text += "%08X: %08X %s\n" %(address, value, instr) self.setPlainText(text) def updateHighlight(self): selections = [] for i in range(24): address = self.base + i * 4 color = self.getColor(address) if color: cursor = self.textCursor() cursor.movePosition(QTextCursor.Down, n=i) cursor.select(QTextCursor.LineUnderCursor) format = QTextCharFormat() format.setBackground(QBrush(QColor(color))) selection = QTextEdit.ExtraSelection() selection.cursor = cursor selection.format = format selections.append(selection) self.setExtraSelections(selections) def getColor(self, addr): colors = [] if addr in debugger.breakpoints: colors.append((255, 0, 0)) if addr == self.currentInstruction: colors.append((0, 255, 0)) if addr == self.selectedAddress: colors.append((128, 128, 255)) if not colors: return None color = [sum(l)//len(colors) for l in zip(*colors)] return "#%02X%02X%02X" %tuple(color) def mousePressEvent(self, e): super().mousePressEvent(e) line = self.cursorForPosition(e.pos()).blockNumber() self.selectedAddress = self.base + line * 4 if e.button() == Qt.MidButton: debugger.toggleBreakPoint(self.selectedAddress) self.updateHighlight() class DisassemblyInfo(QWidget): def __init__(self): super().__init__() self.baseLabel = QLabel("Address:") self.baseBox = HexSpinBox(4) self.baseButton = QPushButton("Update", self) self.baseButton.clicked.connect(self.updateDisassemblyBase) self.pokeBox = HexSpinBox() self.pokeButton = QPushButton("Poke", self) self.pokeButton.clicked.connect(self.poke) self.layout = QGridLayout() self.layout.addWidget(self.baseLabel, 0, 0) self.layout.addWidget(self.baseBox, 0, 1) self.layout.addWidget(self.baseButton, 0, 2) self.layout.addWidget(self.pokeBox, 1, 0) self.layout.addWidget(self.pokeButton, 1, 1, 1, 2) self.setLayout(self.layout) self.setMinimumWidth(300) def updateDisassemblyBase(self): window.mainWidget.tabWidget.disassemblyTab.disassemblyWidget.setBase(self.baseBox.value()) def poke(self): disassembly = window.mainWidget.tabWidget.disassemblyTab.disassemblyWidget if disassembly.selectedAddress: debugger.writeInstr(disassembly.selectedAddress, self.pokeBox.value()) disassembly.updateText() class DisassemblyTab(QWidget): def __init__(self): super().__init__() self.disassemblyInfo = DisassemblyInfo() self.disassemblyWidget = DisassemblyWidget() self.layout = QHBoxLayout() self.layout.addWidget(self.disassemblyInfo) self.layout.addWidget(self.disassemblyWidget) self.setLayout(self.layout) events.Connected.connect(self.connected) def connected(self): self.disassemblyWidget.setBase(0x10000000) """"""""""""""""""""""""""" Thread widgets """"""""""""""""""""""""""" class ThreadList(QTableWidget): def __init__(self): super().__init__(0, 5) self.setHorizontalHeaderLabels(["Name", "Priority", "Core", "Stack", "Entry Point"]) self.setEditTriggers(self.NoEditTriggers) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) events.Connected.connect(self.updateThreads) def updateThreads(self): threads = debugger.getThreadList() self.setRowCount(len(threads)) for i in range(len(threads)): thread = threads[i] self.setItem(i, 0, QTableWidgetItem(thread.name)) self.setItem(i, 1, QTableWidgetItem(str(thread.priority))) self.setItem(i, 2, QTableWidgetItem(thread.core)) self.setItem(i, 3, QTableWidgetItem("0x%X - 0x%X" %(thread.stackEnd, thread.stackBase))) self.setItem(i, 4, QTableWidgetItem("0x%X" %thread.entryPoint)) class ThreadingTab(QWidget): def __init__(self): super().__init__() self.threadList = ThreadList() self.updateButton = QPushButton("Update", self) self.updateButton.clicked.connect(self.threadList.updateThreads) self.layout = QVBoxLayout() self.layout.addWidget(self.threadList) self.layout.addWidget(self.updateButton) self.setLayout(self.layout) """"""""""""""""""""""""""" Module widgets """"""""""""""""""""""""""" class ModuleList(QTableWidget): def __init__(self): super().__init__(0, 4) self.setHorizontalHeaderLabels(["Name", "Code", "Data", "Entry Point"]) self.setEditTriggers(self.NoEditTriggers) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) events.Connected.connect(self.updateModules) def updateModules(self): modules = debugger.getModuleList() self.setRowCount(len(modules)) for i in range(len(modules)): module = modules[i] textEnd = module.textAddr + module.textSize dataEnd = module.dataAddr + module.dataSize self.setItem(i, 0, QTableWidgetItem(module.name)) self.setItem(i, 1, QTableWidgetItem("%X - %X" %(module.textAddr, textEnd))) self.setItem(i, 2, QTableWidgetItem("%X - %X" %(module.dataAddr, dataEnd))) self.setItem(i, 3, QTableWidgetItem("%X" %module.entryPoint)) class ModuleTab(QWidget): def __init__(self): super().__init__() self.moduleList = ModuleList() self.updateButton = QPushButton("Update", self) self.updateButton.clicked.connect(self.moduleList.updateModules) self.layout = QVBoxLayout() self.layout.addWidget(self.moduleList) self.layout.addWidget(self.updateButton) self.setLayout(self.layout) """"""""""""""""""""""""""" Breakpoint widgets """"""""""""""""""""""""""" class BreakPointList(QListWidget): def __init__(self): super().__init__() self.itemDoubleClicked.connect(self.goToDisassembly) events.BreakPointChanged.connect(self.updateList) def updateList(self): self.clear() for bp in debugger.breakpoints: self.addItem("0x%08X" %bp) def goToDisassembly(self, item): address = debugger.breakpoints[self.row(item)] window.mainWidget.tabWidget.disassemblyTab.disassemblyWidget.setBase(address) window.mainWidget.tabWidget.setCurrentIndex(1) class BreakPointTab(QWidget): def __init__(self): super().__init__() self.list = BreakPointList() self.button = QPushButton("Remove") self.button.clicked.connect(self.removeBreakPoint) self.layout = QVBoxLayout() self.layout.addWidget(self.list) self.layout.addWidget(self.button) self.setLayout(self.layout) def removeBreakPoint(self): if self.list.currentRow() != -1: debugger.toggleBreakPoint(debugger.breakpoints[self.list.currentRow()]) """"""""""""""""""""""""""" Exception widgets """"""""""""""""""""""""""" class ExceptionThreadList(QTableWidget): itemSelected = pyqtSignal(object) def __init__(self, title): super().__init__(0, 1) self.setHorizontalHeaderLabels([title]) self.setEditTriggers(self.NoEditTriggers) self.setSelectionMode(QAbstractItemView.NoSelection) self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) self.verticalHeader().hide() self.currentItem = None self.itemDoubleClicked.connect(self.selectItem) self.threads = [] events.Closed.connect(self.handleClose) def getItem(self, state): if state.thread in self.threads: return self.item(self.threads.index(state.thread), 0) def addItem(self, state): if state.thread not in self.threads: thread = debugger.findThread(state.thread) if thread and thread.name: name = thread.name else: name = "<%08X>" item = QTableWidgetItem(name) item.state = state self.setRowCount(self.rowCount() + 1) self.setItem(self.rowCount() - 1, 0, item) self.threads.append(state.thread) return item else: item = self.getItem(state) item.state = state return item def removeItem(self, state): if state.thread in self.threads: index = self.threads.index(state.thread) item = self.item(index, 0) if item == self.currentItem: self.currentItem = None self.itemSelected.emit(None) self.removeRow(self.threads.index(state.thread)) self.threads.remove(state.thread) def selectItem(self, item): if item != self.currentItem: self.resetSelection() item.setBackground(QBrush(QColor(128, 128, 255))) self.currentItem = item self.itemSelected.emit(item) def resetSelection(self): if self.currentItem: self.currentItem.setBackground(QBrush()) self.currentItem = None def handleClose(self): self.threads = [] self.currentItem = None self.clearContents() self.setRowCount(0) class ExceptionThreads(QWidget): itemSelected = pyqtSignal(object) def __init__(self): super().__init__() self.pausedThreads = ExceptionThreadList("Paused threads") self.pausedThreads.itemSelected.connect(self.handlePauseSelect) self.crashedThreads = ExceptionThreadList("Crashed threads") self.crashedThreads.itemSelected.connect(self.handleCrashSelect) layout = QVBoxLayout(self) layout.addWidget(self.pausedThreads) layout.addWidget(self.crashedThreads) events.Exception.connect(self.handleException) events.Continue.connect(self.handleContinue) def handlePauseSelect(self, item): if item: self.crashedThreads.resetSelection() self.itemSelected.emit(item) def handleCrashSelect(self, item): if item: self.pausedThreads.resetSelection() self.itemSelected.emit(item) def handleException(self, state): if state.isFatal(): self.pausedThreads.removeItem(state) item = self.crashedThreads.addItem(state) self.crashedThreads.selectItem(item) else: self.crashedThreads.removeItem(state) item = self.pausedThreads.addItem(state) self.pausedThreads.selectItem(item) def handleContinue(self, state): self.pausedThreads.removeItem(state) self.crashedThreads.removeItem(state) class ExceptionInfo(QGroupBox): def __init__(self): super().__init__("Info") self.typeLabel = QLabel() self.layout = QVBoxLayout(self) self.layout.addWidget(self.typeLabel) def setState(self, state): self.typeLabel.setText("Type: %s" %state.exceptionName) class SpecialRegisters(QGroupBox): def __init__(self): super().__init__("Special registers") self.cr = QLabel() self.lr = QLabel() self.ctr = QLabel() self.xer = QLabel() self.srr0 = QLabel() self.srr1 = QLabel() self.ex0 = QLabel() self.ex1 = QLabel() self.userLayout = QFormLayout() self.kernelLayout = QFormLayout() self.userLayout.addRow("CR:", self.cr) self.userLayout.addRow("LR:", self.lr) self.userLayout.addRow("CTR:", self.ctr) self.userLayout.addRow("XER:", self.xer) self.kernelLayout = QFormLayout() self.kernelLayout.addRow("SRR0:", self.srr0) self.kernelLayout.addRow("SRR1:", self.srr1) self.kernelLayout.addRow("EX0:", self.ex0) self.kernelLayout.addRow("EX1:", self.ex1) self.layout = QHBoxLayout(self) self.layout.addLayout(self.userLayout) self.layout.addLayout(self.kernelLayout) def setState(self, state): self.cr.setText("%08X" %state.cr) self.lr.setText("%08X" %state.lr) self.ctr.setText("%08X" %state.ctr) self.xer.setText("%08X" %state.xer) self.srr0.setText("%08X" %state.srr0) self.srr1.setText("%08X" %state.srr1) self.ex0.setText("%08X" %state.ex0) self.ex1.setText("%08X" %state.ex1) class ExceptionInfoTab(QWidget): def __init__(self): super().__init__() self.exceptionInfo = ExceptionInfo() self.specialRegisters = SpecialRegisters() self.layout = QHBoxLayout(self) self.layout.addWidget(self.exceptionInfo) self.layout.addWidget(self.specialRegisters) def setState(self, state): self.exceptionInfo.setState(state) self.specialRegisters.setState(state) class RegisterTab(QWidget): def __init__(self): super().__init__() self.gprLabels = [] self.gprBoxes = [] self.fprLabels = [] self.fprBoxes = [] for i in range(32): self.gprLabels.append(QLabel("r%i" %i)) self.fprLabels.append(QLabel("f%i" % i)) gprBox = HexSpinBox() fprBox = QDoubleSpinBox() fprBox.setRange(float("-inf"), float("inf")) self.gprBoxes.append(gprBox) self.fprBoxes.append(fprBox) self.layout = QGridLayout(self) for i in range(32): self.layout.addWidget(self.gprLabels[i], i % 16, i // 16 * 2) self.layout.addWidget(self.gprBoxes[i], i % 16, i // 16 * 2 + 1) self.layout.addWidget(self.fprLabels[i], i % 16, i // 16 * 2 + 4) self.layout.addWidget(self.fprBoxes[i], i % 16, i // 16 * 2 + 5) self.pokeButton = QPushButton("Poke") self.resetButton = QPushButton("Reset") self.pokeButton.clicked.connect(self.pokeRegisters) self.resetButton.clicked.connect(self.updateRegisters) self.layout.addWidget(self.pokeButton, 16, 0, 1, 4) self.layout.addWidget(self.resetButton, 16, 4, 1, 4) self.setEditEnabled(False) events.Step.connect(self.handleStep) def setState(self, state): self.state = state self.updateRegisters() self.setEditEnabled(state.isBreakPoint() and not state.stepping) def handleStep(self, state): if state == self.state: self.setEditEnabled(False) def setEditEnabled(self, enabled): for i in range(32): self.gprBoxes[i].setEnabled(enabled) self.fprBoxes[i].setEnabled(enabled) self.pokeButton.setEnabled(enabled) self.resetButton.setEnabled(enabled) def updateRegisters(self): for i in range(32): self.gprBoxes[i].setValue(self.state.gpr[i]) self.fprBoxes[i].setValue(self.state.fpr[i]) def pokeRegisters(self): for i in range(32): self.state.gpr[i] = self.gprBoxes[i].value() self.state.fpr[i] = self.fprBoxes[i].value() debugger.pokeRegisters(self.state) class StackTrace(QListWidget): def setState(self, state): self.clear() stackTrace = debugger.getStackTrace(state) for address in (state.srr0, state.lr) + stackTrace: self.addItem("%X" %address) class StackTraceTab(QWidget): def __init__(self): super().__init__() self.stackTrace = StackTrace() self.disassembly = DisassemblyWidget() layout = QHBoxLayout(self) layout.addWidget(self.stackTrace) layout.addWidget(self.disassembly) self.stackTrace.itemDoubleClicked.connect(self.jumpDisassembly) events.Step.connect(self.handleStep) def handleStep(self, state): self.disassembly.setCurrentInstruction(None) def setState(self, state): self.stackTrace.setState(state) if not state.stepping: self.disassembly.setCurrentInstruction(state.srr0) else: self.disassembly.setCurrentInstruction(None) def jumpDisassembly(self, item): self.disassembly.setCurrentInstruction(int(item.text(), 16)) class ExceptionStateTabs(QTabWidget): def __init__(self): super().__init__() self.infoTab = ExceptionInfoTab() self.registerTab = RegisterTab() self.stackTab = StackTraceTab() self.addTab(self.infoTab, "General") self.addTab(self.registerTab, "Registers") self.addTab(self.stackTab, "Stack trace") def setState(self, state): self.infoTab.setState(state) self.registerTab.setState(state) self.stackTab.setState(state) class BreakpointActions(QWidget): def __init__(self): super().__init__() self.continueButton = QPushButton("Continue") self.continueButton.clicked.connect(self.continueThread) self.stepButton = QPushButton("Step") self.stepButton.clicked.connect(self.stepInto) self.stepOverButton = QPushButton("Step over") self.stepOverButton.clicked.connect(self.stepOver) layout = QHBoxLayout(self) layout.addWidget(self.continueButton) layout.addWidget(self.stepButton) layout.addWidget(self.stepOverButton) self.state = None def setState(self, state): self.state = state if state.isFatal(): self.continueButton.hide() self.stepButton.hide() self.stepOverButton.hide() else: self.continueButton.show() self.stepButton.show() self.stepOverButton.show() def continueThread(self): debugger.continueThread(self.state) def stepInto(self): debugger.stepInto(self.state) def stepOver(self): debugger.stepOver(self.state) class ExceptionStateWidget(QTabWidget): def __init__(self): super().__init__() self.exceptionTabs = ExceptionStateTabs() self.breakpointActions = BreakpointActions() layout = QVBoxLayout(self) layout.addWidget(self.exceptionTabs) layout.addWidget(self.breakpointActions) events.Step.connect(self.handleStep) def setState(self, state): self.exceptionTabs.setState(state) self.breakpointActions.setState(state) self.breakpointActions.setEnabled(not state.stepping) def handleStep(self, state): self.breakpointActions.setEnabled(False) class ExceptionTab(QSplitter): def __init__(self): super().__init__() self.threadList = ExceptionThreads() self.threadList.itemSelected.connect(self.handleItemSelected) self.stateWidget = ExceptionStateWidget() self.stateWidget.setEnabled(False) self.addWidget(self.threadList) self.addWidget(self.stateWidget) self.setSizes([100, 400]) events.Closed.connect(self.handleClose) def handleItemSelected(self, item): if item: self.stateWidget.setState(item.state) self.stateWidget.setEnabled(True) else: self.stateWidget.setEnabled(False) def handleClose(self): self.stateWidget.setEnabled(False) """"""""""""""""""""""""""" Main widgets """"""""""""""""""""""""""" class DebuggerTabs(QTabWidget): def __init__(self): super().__init__() self.memoryTab = MemoryTab() self.disassemblyTab = DisassemblyTab() self.threadingTab = ThreadingTab() self.moduleTab = ModuleTab() self.breakPointTab = BreakPointTab() self.exceptionTab = ExceptionTab() self.addTab(self.memoryTab, "Memory") self.addTab(self.disassemblyTab, "Disassembly") self.addTab(self.threadingTab, "Threads") self.addTab(self.moduleTab, "Modules") self.addTab(self.breakPointTab, "Breakpoints") self.addTab(self.exceptionTab, "Exceptions") events.Exception.connect(self.exceptionOccurred) events.Connected.connect(self.connected) events.Closed.connect(self.disconnected) def exceptionOccurred(self, state): self.setCurrentIndex(5) #Exceptions def connected(self): self.setEnabled(True) def disconnected(self): self.setEnabled(False) class StatusWidget(QWidget): def __init__(self): super().__init__() self.serverLabel = QLabel("Wii U IP:") self.serverBox = QLineEdit(self) self.serverBox.returnPressed.connect(self.connect) self.connectButton = QPushButton("Connect", self) self.connectButton.clicked.connect(self.connect) self.disconnectButton = QPushButton("Disconnect", self) self.disconnectButton.clicked.connect(debugger.close) self.disconnectButton.setEnabled(False) self.statusInfo = QLabel("Disconnected", self) self.layout = QGridLayout() self.layout.addWidget(self.serverLabel, 0, 0) self.layout.addWidget(self.serverBox, 1, 0) self.layout.addWidget(self.connectButton, 0, 1) self.layout.addWidget(self.disconnectButton, 1, 1) self.layout.addWidget(self.statusInfo, 3, 0, 1, 2) self.setLayout(self.layout) events.Connected.connect(self.connected) events.Closed.connect(self.disconnected) def connect(self): try: address = self.serverBox.text() debugger.connect(address) except: pass def connected(self): self.statusInfo.setText("Connected") self.connectButton.setEnabled(False) self.serverBox.setEnabled(False) self.disconnectButton.setEnabled(True) def disconnected(self): self.statusInfo.setText("Disconnected") self.connectButton.setEnabled(True) self.serverBox.setEnabled(True) self.disconnectButton.setEnabled(False) class MainWidget(QWidget): def __init__(self): super().__init__() self.tabWidget = DebuggerTabs() self.statusWidget = StatusWidget() self.layout = QVBoxLayout() self.layout.addWidget(self.tabWidget) self.layout.addWidget(self.statusWidget) self.tabWidget.setEnabled(False) self.setLayout(self.layout) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("DiiBugger") self.resize(1080, 720) self.mainWidget = MainWidget() self.setCentralWidget(self.mainWidget) self.timer = QTimer(self) self.timer.setInterval(100) self.timer.timeout.connect(self.updateMessages) self.timer.start() events.Connected.connect(self.updateTitle) events.Closed.connect(self.updateTitle) def updateTitle(self): if debugger.connected: name = debugger.getModuleName() self.setWindowTitle("DiiBugger - %s" %name) else: self.setWindowTitle("DiiBugger") def updateMessages(self): if debugger.connected: debugger.updateMessages() if __name__ == "__main__": app = QApplication(sys.argv) app.setFont(QFontDatabase.systemFont(QFontDatabase.FixedFont)) window = MainWindow() window.show() app.exec()