gdbstub_plugin/src/debugger.cpp
Maschell cda1a77ec8 WIP
2023-01-27 16:59:46 +01:00

1035 lines
29 KiB
C++

#include "debugger.h"
#include "exceptions.h"
#include "input.h"
#include "logger.h"
#include "screen.h"
#include <coreinit/cache.h>
#include <coreinit/debug.h>
#include <coreinit/dynload.h>
#include <coreinit/exception.h>
#include <coreinit/interrupts.h>
#include <coreinit/memory.h>
#include <coreinit/memorymap.h>
#include <coreinit/scheduler.h>
#include <cstdarg>
#include <cstdio>
#include <cstring>
#include <malloc.h>
#include <string>
#include <vpad/input.h>
Debugger *debugger;
bool initDebugState = false;
bool BreakPoint::isRange(uint32_t addr, uint32_t length) const {
return address >= addr && address <= addr + length - 1;
}
BreakPoint *BreakPointMgr::find(uint32_t addr, bool includeSpecial) {
BreakPoint *bp = breakpoints.find(addr);
if (!bp && includeSpecial) {
bp = special.find(addr);
}
return bp;
}
BreakPoint *BreakPointMgr::findRange(uint32_t addr, uint32_t length, int *index, bool includeSpecial) {
BreakPoint *bp = breakpoints.findRange(addr, length, index);
if (bp) {
return bp;
}
if (includeSpecial) {
int temp = *index - breakpoints.size();
bp = special.findRange(addr, length, &temp);
*index = temp + breakpoints.size();
return bp;
}
return nullptr;
}
SpecialBreakPoint *BreakPointMgr::findSpecial(uint32_t addr, OSThread *thread) {
int index = 0;
SpecialBreakPoint *bp = special.findRange(addr, 4, &index);
while (bp) {
if (bp->thread == thread) {
return bp;
}
bp = special.findRange(addr, 4, &index);
}
return nullptr;
}
bool BreakPointMgr::isCustom(uint32_t addr) {
return find(addr, false);
}
bool BreakPointMgr::isSpecial(uint32_t addr) {
return find(addr, true) && !find(addr, false);
}
bool BreakPointMgr::isSoftware(uint32_t addr) {
uint32_t instr = getInstr(addr);
uint32_t opcode = instr >> 26;
if (opcode == 3) { //twi
return true;
} else if (opcode == 31) {
return (instr & 0x7FF) == 8; //tw
}
return false;
}
void BreakPointMgr::disable(BreakPoint *bp) {
uint32_t address = bp->address;
if (bp->address) {
bp->address = 0;
if (!find(address, true)) {
KernelWriteU32(address, bp->instruction);
}
bp->instruction = 0;
}
}
void BreakPointMgr::enable(BreakPoint *bp, uint32_t addr) {
BreakPoint *other = find(addr, true);
if (other) {
bp->instruction = other->instruction;
} else {
bp->instruction = *(uint32_t *) addr;
KernelWriteU32(addr, TRAP);
}
bp->address = addr;
}
void BreakPointMgr::init() {
OSInitMutex(&mutex);
}
void BreakPointMgr::lock() {
OSLockMutex(&mutex);
}
void BreakPointMgr::unlock() {
OSUnlockMutex(&mutex);
}
void BreakPointMgr::cleanup() {
lock();
breakpoints.cleanup();
special.cleanup();
unlock();
}
void BreakPointMgr::read(void *buffer, uint32_t addr, uint32_t length) {
lock();
memcpy(buffer, (void *) addr, length);
int index = 0;
BreakPoint *bp = findRange(addr, length, &index, true);
while (bp) {
uint32_t offset = bp->address - addr;
char *bufptr = (char *) buffer + offset;
if (bp->address > addr + length - 4) {
uint32_t value = bp->instruction;
for (uint32_t i = 0; i < length - offset; i++) {
bufptr[i] = value >> 24;
value <<= 8;
}
} else {
*(uint32_t *) bufptr = bp->instruction;
}
bp = findRange(addr, length, &index, true);
}
unlock();
}
void BreakPointMgr::write(const void *buffer, uint32_t addr, uint32_t length) {
lock();
length = length & ~3;
int index = 0;
if (!findRange(addr, length, &index, true)) {
KernelWrite(addr, buffer, length);
} else {
for (uint32_t i = 0; i < length; i += 4) {
uint32_t value = *(uint32_t *) ((char *) buffer + i);
int index = 0;
BreakPoint *bp = findRange(addr + i, 4, &index, true);
if (!bp) {
KernelWriteU32(addr + i, value);
} else {
while (bp) {
bp->instruction = value;
bp = findRange(addr + i, 4, &index, true);
}
}
}
}
unlock();
}
void BreakPointMgr::toggle(uint32_t addr) {
lock();
BreakPoint *bp = find(addr, false);
if (bp) {
disable(bp);
} else {
BreakPoint *bp = breakpoints.alloc();
bp->isSpecial = false;
enable(bp, addr);
}
unlock();
}
uint32_t BreakPointMgr::getInstr(uint32_t addr) {
lock();
uint32_t instruction;
BreakPoint *bp = find(addr, true);
if (bp) {
instruction = bp->instruction;
} else {
instruction = *(uint32_t *) addr;
}
unlock();
return instruction;
}
void BreakPointMgr::clearSpecial(OSThread *thread) {
lock();
for (size_t i = 0; i < special.size(); i++) {
SpecialBreakPoint *bp = special[i];
if (bp->thread == thread) {
disable(bp);
}
}
unlock();
}
void BreakPointMgr::predictStep(ExceptionState *state, bool stepOver) {
lock();
uint32_t address = state->context.srr0;
uint32_t instruction = getInstr(address);
uint32_t target1 = address + 4;
uint32_t target2 = 0;
uint8_t opcode = instruction >> 26;
if (opcode == 18) { //Branch
bool AA = instruction & 2;
bool LK = instruction & 1;
uint32_t LI = instruction & 0x3FFFFFC;
if (LI & 0x2000000) LI -= 0x4000000;
if (!LK || !stepOver) {
if (AA) target1 = LI;
else {
target1 = address + LI;
}
}
}
else if (opcode == 16) { //Conditional branch
bool AA = instruction & 2;
bool LK = instruction & 1;
uint32_t BD = instruction & 0xFFFC;
if (BD & 0x8000) BD -= 0x10000;
if (!LK || !stepOver) {
if (AA) target2 = BD;
else {
target2 = address + BD;
}
}
}
else if (opcode == 19) { //Conditional branch to lr/ctr
uint16_t XO = (instruction >> 1) & 0x3FF;
bool LK = instruction & 1;
if (!LK || !stepOver) {
if (XO == 16) target2 = state->context.lr;
else if (XO == 528)
target2 = state->context.ctr;
}
}
SpecialBreakPoint *bp = special.alloc();
bp->isSpecial = true;
bp->thread = state->thread;
enable(bp, target1);
if (target2) {
SpecialBreakPoint *bp = special.alloc();
bp->isSpecial = true;
bp->thread = state->thread;
enable(bp, target2);
}
unlock();
}
bool ExceptionState::isBreakpoint() {
return type == PROGRAM && context.srr1 & 0x20000;
}
void ExceptionState::resume() {
OSLoadContext(&context);
}
void ExceptionMgr::init() {
OSInitMutex(&mutex);
}
void ExceptionMgr::lock() {
OSLockMutex(&mutex);
}
void ExceptionMgr::unlock() {
OSUnlockMutex(&mutex);
}
void ExceptionMgr::cleanup() {
OSMessage message;
message.message = (void *) Debugger::STEP_CONTINUE;
lock();
for (auto state : list) {
if (state->isPaused) {
OSSendMessage(&state->queue, &message, OS_MESSAGE_FLAGS_NONE);
}
}
unlock();
}
ExceptionState *ExceptionMgr::find(OSThread *thread) {
lock();
for (auto state : list) {
if (state->thread == thread) {
unlock();
return state;
}
}
unlock();
return nullptr;
}
ExceptionState *ExceptionMgr::findOrCreate(OSThread *thread) {
lock();
ExceptionState *state = find(thread);
if (!state) {
state = new ExceptionState();
state->thread = thread;
state->isPaused = false;
OSInitMessageQueue(&state->queue, &state->message, 1);
list.push_back(state);
}
unlock();
return state;
}
uint32_t StepMgr::buffer[96];
void StepMgr::init() {
OSInitMutex(&mutex);
usedMask = 0;
}
void StepMgr::lock() {
OSLockMutex(&mutex);
}
void StepMgr::unlock() {
OSUnlockMutex(&mutex);
}
uint32_t *StepMgr::alloc() {
lock();
int index = 0;
uint32_t mask = usedMask;
while (mask & 1) {
mask >>= 1;
index++;
}
usedMask |= 1 << index;
unlock();
if (index == 32) {
OSFatal("Displaced step allocation failed");
}
return &buffer[index * 3];
}
void StepMgr::free(int index) {
lock();
usedMask &= ~(1 << index);
unlock();
}
void StepMgr::branchConditional(ExceptionState *state, uint32_t instruction, uint32_t target, bool checkCtr) {
Bits<5> BO = (instruction >> 21) & 0x1F;
Bits<32> CR = state->context.cr;
uint32_t BI = (instruction >> 16) & 0x1F;
bool LK = instruction & 1;
if (LK) {
state->context.lr = state->context.srr0 + 4;
}
bool ctr_ok = true;
if (checkCtr) {
if (!BO[2]) {
state->context.ctr--;
}
ctr_ok = BO[2] || ((state->context.ctr != 0) ^ BO[3]);
}
bool cond_ok = BO[0] || (CR[BI] == BO[1]);
if (ctr_ok && cond_ok) {
state->context.srr0 = target;
}
}
void StepMgr::singleStep(ExceptionState *state, uint32_t instruction) {
uint8_t opcode = instruction >> 26;
if (opcode == 18) { //Branch
bool AA = instruction & 2;
bool LK = instruction & 1;
uint32_t LI = instruction & 0x3FFFFFC;
if (LI & 0x2000000) LI -= 0x4000000;
if (LK) {
state->context.lr = state->context.srr0 + 4;
}
if (AA) state->context.srr0 = LI;
else {
state->context.srr0 += LI;
}
return;
}
if (opcode == 16) { //Conditional branch
bool AA = instruction & 2;
uint32_t BD = instruction & 0xFFFC;
if (BD & 0x8000) BD -= 0x10000;
uint32_t target;
if (AA) target = BD;
else {
target = state->context.srr0 + BD;
}
branchConditional(state, instruction, target, true);
return;
}
if (opcode == 19) { //Conditional branch to lr/ctr
uint16_t XO = (instruction >> 1) & 0x3FF;
uint32_t target;
if (XO == 16) {
branchConditional(state, instruction, state->context.lr, true);
return;
} else if (XO == 528) {
branchConditional(state, instruction, state->context.ctr, false);
return;
}
}
uint32_t *ptr = alloc();
ptr[0] = instruction;
ptr[1] = TRAP;
ptr[2] = state->context.srr0;
DCFlushRange(ptr, 12);
ICInvalidateRange(ptr, 12);
state->context.srr0 = (uint32_t) ptr;
}
void StepMgr::handleBreakPoint(ExceptionState *state) {
uint32_t start = (uint32_t) buffer;
uint32_t end = (uint32_t) buffer + sizeof(buffer);
uint32_t addr = state->context.srr0;
if (addr >= start && addr < end) {
int offset = addr - start;
if (offset % 12 != 4) {
char buffer[100];
snprintf(buffer, 100, "Unexpected srr0 after displaced step: %08X", addr);
OSFatal(buffer);
}
int index = offset / 12;
state->context.srr0 = buffer[index * 3 + 2] + 4;
free(index);
state->resume();
}
}
void StepMgr::adjustAddress(ExceptionState *state) {
uint32_t start = (uint32_t) buffer;
uint32_t end = (uint32_t) buffer + sizeof(buffer);
uint32_t addr = state->context.srr0;
if (addr >= start && addr < end) {
int index = (addr - start) / 12;
state->context.srr0 = buffer[index * 3 + 2];
}
}
bool Debugger::checkDataRead(uint32_t addr, uint32_t length) {
uint32_t memStart, memSize;
OSGetMemBound(OS_MEM2, &memStart, &memSize);
uint32_t memEnd = memStart + memSize;
return addr >= 0x10000000 && addr + length <= memEnd;
}
Debugger::StepCommand Debugger::notifyBreak(ExceptionState *state) {
OSMessage message;
message.message = (void *) ExceptionState::PROGRAM;
message.args[0] = (uint32_t) &state->context;
message.args[1] = sizeof(OSContext);
message.args[2] = (uint32_t) state->thread;
OSSendMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_BLOCKING);
state->isPaused = true;
OSReceiveMessage(&state->queue, &message, OS_MESSAGE_FLAGS_BLOCKING);
state->isPaused = false;
return (StepCommand) (uint32_t) message.message;
}
void Debugger::resumeBreakPoint(ExceptionState *state) {
uint32_t address = state->context.srr0;
if (breakpoints.isSoftware(address)) {
state->context.srr0 += 4;
state->resume();
}
uint32_t instruction = breakpoints.getInstr(state->context.srr0);
stepper.singleStep(state, instruction);
state->resume();
}
void Debugger::processBreakPoint(ExceptionState *state) {
StepCommand command = notifyBreak(state);
if (command == STEP_INTO || command == STEP_OVER) {
breakpoints.predictStep(state, command == STEP_OVER);
}
}
void Debugger::handleBreakPoint(ExceptionState *state) {
if (firstTrap) {
firstTrap = false;
auto *screen = new Screen;
Screen::drawText(
0, 0, "Waiting for debugger connection.\n"
"Press the home button to continue without debugger.\n"
"You can still connect while the game is running.");
Screen::flip();
delete screen;
while (!connected) {
uint32_t buttons = GetInput(VPAD_BUTTON_HOME);
if (buttons) {
state->context.srr0 += 4;
state->resume();
}
}
}
stepper.handleBreakPoint(state);
if (!connected) {
handleFatalCrash(&state->context, state->type);
}
uint32_t addr = state->context.srr0;
bool trap;
breakpoints.lock();
trap = breakpoints.isCustom(addr) || breakpoints.isSoftware(addr) ||
breakpoints.findSpecial(addr, state->thread);
breakpoints.unlock();
breakpoints.clearSpecial(state->thread);
if (trap) {
processBreakPoint(state);
}
resumeBreakPoint(state);
}
void Debugger::handleCrash(ExceptionState *state) {
stepper.adjustAddress(state);
if (connected) {
OSMessage message;
message.message = (void *) state->type;
message.args[0] = (uint32_t) &state->context;
message.args[1] = sizeof(OSContext);
message.args[2] = (uint32_t) state->thread;
OSSendMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_BLOCKING);
while (true) {
OSReceiveMessage(&state->queue, &message, OS_MESSAGE_FLAGS_BLOCKING);
}
} else {
handleFatalCrash(&state->context, state->type);
}
}
void Debugger::handleFatalCrash(OSContext *context, ExceptionState::Type type) {
const char *name;
if (type == ExceptionState::DSI) {
name = "A DSI";
} else if (type == ExceptionState::ISI) {
name = "An ISI";
} else {
name = "A program";
}
DumpContext(context, name);
}
void Debugger::handleException(OSContext *context, ExceptionState::Type type) {
OSThread *thread = OSGetCurrentThread();
if (thread == serverThread) {
handleFatalCrash(context, type);
}
ExceptionState *state = exceptions.findOrCreate(thread);
memcpy(&state->context, context, sizeof(OSContext));
state->type = type;
delete context;
if (state->isBreakpoint()) {
handleBreakPoint(state);
} else {
handleCrash(state);
}
}
void Debugger::exceptionHandler(OSContext *context, ExceptionState::Type type) {
debugger->handleException(context, type);
}
BOOL Debugger::dsiHandler(OSContext *context) {
auto *info = new OSContext();
memcpy(info, context, sizeof(OSContext));
context->srr0 = (uint32_t) exceptionHandler;
context->gpr[3] = (uint32_t) info;
context->gpr[4] = ExceptionState::DSI;
return true;
}
BOOL Debugger::isiHandler(OSContext *context) {
OSContext *info = new OSContext();
memcpy(info, context, sizeof(OSContext));
context->srr0 = (uint32_t) exceptionHandler;
context->gpr[3] = (uint32_t) info;
context->gpr[4] = ExceptionState::ISI;
return true;
}
BOOL Debugger::programHandler(OSContext *context) {
OSContext *info = new OSContext();
memcpy(info, context, sizeof(OSContext));
context->srr0 = (uint32_t) exceptionHandler;
context->gpr[3] = (uint32_t) info;
context->gpr[4] = ExceptionState::PROGRAM;
return true;
}
void Debugger::cleanup() {
breakpoints.cleanup();
exceptions.cleanup();
OSMessage message;
while (OSReceiveMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_NONE))
;
}
#define LOG_DISASSEMBLY_SIZE (4096)
static char sDisassemblyBuffer[LOG_DISASSEMBLY_SIZE];
static uint32_t sDisassemblyLength = 0;
static void
disassemblyPrintCallback(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
sDisassemblyLength += vsprintf(sDisassemblyBuffer + sDisassemblyLength, fmt, args);
sDisassemblyBuffer[sDisassemblyLength] = 0;
va_end(args);
}
void eraseAllSubStr(std::string &mainStr, const std::string &toErase) {
size_t pos = std::string::npos;
// Search for the substring in string in a loop until nothing is found
while ((pos = mainStr.find(toErase)) != std::string::npos) {
// If found then erase it from string
mainStr.erase(pos, toErase.length());
}
}
//#define OSIopShell_Command_Disassemble ((void (*)(void*, uint32_t))(0x101C400 + 0x173e0))
#define __os_printf ((void (*)(char *, ...))(0x101C400 + 0x012e88))
void Debugger::mainLoop(Client *client) {
while (!stopRunning) {
uint8_t cmd;
if (!client->recvall(&cmd, 1)) {
return;
}
if (cmd == COMMAND_CLOSE) {
return;
} else if (cmd == COMMAND_READ) {
uint32_t addr, length;
if (!client->recvall(&addr, 4)) return;
if (!client->recvall(&length, 4)) return;
char *buffer = new char[length];
breakpoints.read(buffer, addr, length);
if (!client->sendall(buffer, length)) {
delete[] buffer;
return;
}
delete[] buffer;
} else if (cmd == COMMAND_DISASM) {
uint32_t addr, length;
if (!client->recvall(&addr, 4)) return;
if (!client->recvall(&length, 4)) return;
char *buffer = new char[0x40];
auto addrAsPtr = (uint32_t *) (addr & 0xfffffffc);
for (int i = 0; i < length / 4; i++) {
if (OSIsAddressValid(reinterpret_cast<uint32_t>(&addrAsPtr[i]))) {
DisassemblePPCOpcode(&addrAsPtr[i], buffer, 0x40, OSGetSymbolName, static_cast<DisassemblePPCFlags>(0x121));
disassemblyPrintCallback("0x%08x 0x%08x %s\n", &addrAsPtr[i], addrAsPtr[i], buffer);
} else {
disassemblyPrintCallback("0x%08x ?????????? ???\n", &addrAsPtr[i]);
}
}
delete[] buffer;
std::string tmpString(sDisassemblyBuffer);
eraseAllSubStr(tmpString, "(null)");
auto length_ = tmpString.length() + 1;
if (!client->sendall(&length_, 4)) {
sDisassemblyLength = 0;
return;
}
if (!client->sendall(tmpString.c_str(), length_)) {
sDisassemblyLength = 0;
return;
}
sDisassemblyLength = 0;
} else if (cmd == COMMAND_WRITE) {
uint32_t addr, length;
if (!client->recvall(&addr, 4)) return;
if (!client->recvall(&length, 4)) return;
if (!client->recvall((void *) addr, length)) return;
} else if (cmd == COMMAND_WRITE_CODE) {
uint32_t addr, length;
if (!client->recvall(&addr, 4)) return;
if (!client->recvall(&length, 4)) return;
char *buffer = new char[length];
if (!client->recvall(buffer, length)) {
delete[] buffer;
return;
}
breakpoints.write(buffer, addr, length);
delete[] buffer;
} else if (cmd == COMMAND_GET_MODULE_NAME) {
char name[0x40];
int length = 0x40;
if (OSDynLoad_GetModuleName(reinterpret_cast<OSDynLoad_Module>(-1), name, &length) != OS_DYNLOAD_OK) {
strncat(name, "ERROR", sizeof(name) - 1);
}
length = strlen(name);
if (!client->sendall(&length, 4)) return;
if (!client->sendall(name, length)) return;
} else if (cmd == COMMAND_GET_MODULE_LIST) {
int num_rpls = OSDynLoad_GetNumberOfRPLs();
if (num_rpls == 0) {
DEBUG_FUNCTION_LINE_ERR("Failed to get OSDynLoad_GetNumberOfRPLs");
continue;
}
std::vector<OSDynLoad_NotifyData> rpls;
rpls.resize(num_rpls);
bool ret = OSDynLoad_GetRPLInfo(0, num_rpls, rpls.data());
if (!ret) {
DEBUG_FUNCTION_LINE_ERR("Failed to get OSDynLoad_GetRPLInfo");
continue;
}
auto BUFFER_SIZE = 0x1000; //This should be enough
char buffer[BUFFER_SIZE];
uint32_t offset = 0;
for (auto &info : rpls) {
uint32_t namelen = 0;
if (info.name != nullptr) {
namelen = strlen(info.name);
}
if (offset + 0x18 + namelen > BUFFER_SIZE) {
break;
}
auto *infobuf = (uint32_t *) (buffer + offset);
infobuf[0] = info.textAddr;
infobuf[1] = info.textSize;
infobuf[2] = info.dataAddr;
infobuf[3] = info.dataSize;
infobuf[4] = (uint32_t) 0; // TODO: missing
infobuf[5] = namelen;
if (namelen > 0) {
memcpy(&infobuf[6], info.name, namelen);
offset += 0x18 + strlen(info.name);
}
}
if (!client->sendall(&offset, 4)) return;
if (!client->sendall(buffer, offset)) return;
} else if (cmd == COMMAND_GET_THREAD_LIST) {
int state = OSDisableInterrupts();
__OSLockScheduler(this);
auto BUFFER_SIZE = 0x1000; //This should be enough
char buffer[BUFFER_SIZE]; //This should be enough
uint32_t offset = 0;
OSThread *current = ThreadList;
while (current) {
const char *name = OSGetThreadName(current);
uint32_t namelen = 0;
if (name) {
namelen = strlen(name);
}
if (offset + 0x1C + namelen > BUFFER_SIZE) {
break;
}
int priority = current->basePriority;
int type = *((uint32_t *) current->__unk11);
if (type == 1) {
priority -= 0x20;
} else if (type == 2) {
priority -= 0x40;
}
uint32_t *infobuf = (uint32_t *) (buffer + offset);
infobuf[0] = (uint32_t) current;
infobuf[1] = current->attr & 7;
infobuf[2] = priority;
infobuf[3] = (uint32_t) current->stackStart;
infobuf[4] = (uint32_t) current->stackEnd;
infobuf[5] = (uint32_t) current->entryPoint;
infobuf[6] = namelen;
memcpy(&infobuf[7], name, namelen);
offset += 0x1C + namelen;
current = current->activeLink.next;
}
__OSUnlockScheduler(this);
OSRestoreInterrupts(state);
if (!client->sendall(&offset, 4)) return;
if (!client->sendall(buffer, offset)) return;
} else if (cmd == COMMAND_GET_STACK_TRACE) {
OSThread *thread;
if (!client->recvall(&thread, 4)) return;
ExceptionState *state = exceptions.find(thread);
if (state) {
uint32_t sp = state->context.gpr[1];
uint32_t trace[100];
int index = 0;
while (checkDataRead(sp, 4) && index < 100) {
sp = *(uint32_t *) sp;
if (!checkDataRead(sp, 4)) break;
trace[index] = *(uint32_t *) (sp + 4);
index++;
}
if (!client->sendall(&index, 4)) return;
if (!client->sendall(trace, index * 4)) return;
} else {
int index = 0;
if (!client->sendall(&index, 4)) return;
}
} else if (cmd == COMMAND_TOGGLE_BREAKPOINT) {
uint32_t address;
if (!client->recvall(&address, 4)) return;
breakpoints.toggle(address);
} else if (cmd == COMMAND_POKE_REGISTERS) {
OSThread *thread;
if (!client->recvall(&thread, 4)) return;
uint32_t gpr[32];
double fpr[32];
if (!client->recvall(gpr, 4 * 32)) return;
if (!client->recvall(fpr, 8 * 32)) return;
exceptions.lock();
ExceptionState *state = exceptions.find(thread);
if (state) {
memcpy(state->context.gpr, gpr, 4 * 32);
memcpy(state->context.fpr, fpr, 8 * 32);
}
exceptions.unlock();
} else if (cmd == COMMAND_RECEIVE_MESSAGES) {
OSMessage messages[10];
int count = 0;
while (count < 10) {
if (!OSReceiveMessage(&eventQueue, &messages[count], OS_MESSAGE_FLAGS_NONE)) {
break;
}
count++;
}
if (!client->sendall(&count, 4)) return;
for (int i = 0; i < count; i++) {
if (!client->sendall(&messages[i], sizeof(OSMessage))) return;
if (messages[i].args[0]) {
void *data = (void *) messages[i].args[0];
size_t length = messages[i].args[1];
if (!client->sendall(data, length)) return;
}
}
} else if (cmd == COMMAND_SEND_MESSAGE) {
OSMessage message;
if (!client->recvall(&message, sizeof(OSMessage))) return;
auto *thread = (OSThread *) message.args[0];
exceptions.lock();
ExceptionState *state = exceptions.find(thread);
if (state) {
OSSendMessage(&state->queue, &message, OS_MESSAGE_FLAGS_NONE);
}
exceptions.unlock();
} else {
DEBUG_FUNCTION_LINE_ERR("Recieved unknown command %d", cmd);
}
}
}
void Debugger::threadFunc() {
DEBUG_FUNCTION_LINE("Hello from debugger thread :)!");
prevDsiHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, dsiHandler);
prevIsiHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, isiHandler);
prevProgramHandler = OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, programHandler);
Server server;
Client client;
initialized = true;
while (!stopRunning) {
if (!server.init(Socket::TCP)) continue;
if (!server.bind(1560)) continue;
if (!server.accept(&client)) continue;
connected = true;
mainLoop(&client);
cleanup();
connected = false;
client.close();
server.close();
}
}
int Debugger::threadEntry(int argc, const char **argv) {
debugger->threadFunc();
return 0;
}
void Debugger::start() {
initialized = false;
connected = false;
firstTrap = true;
OSInitMessageQueue(&eventQueue, eventMessages, MESSAGE_COUNT);
breakpoints.init();
exceptions.init();
stepper.init();
serverThread = (OSThread *) memalign(0x20, sizeof(OSThread));
serverStack = (char *) memalign(0x20, STACK_SIZE);
OSCreateThread(
serverThread, threadEntry, 0, nullptr,
serverStack + STACK_SIZE, STACK_SIZE,
0, OS_THREAD_ATTRIB_AFFINITY_CPU2 | OS_THREAD_ATTRIB_DETACHED);
OSSetThreadName(serverThread, "Debug Server");
OSResumeThread(serverThread);
while (!initialized) {
OSSleepTicks(OSMillisecondsToTicks(20));
}
initDebugState = true;
DCFlushRange(&initDebugState, sizeof(initDebugState));
DEBUG_FUNCTION_LINE("Thread init done! Exit start()");
}
Debugger::~Debugger() {
stopRunning = true;
DCFlushRange(&stopRunning, sizeof(stopRunning));
OSJoinThread(serverThread, nullptr);
free(serverStack);
serverStack = nullptr;
free(serverThread);
serverThread = nullptr;
initialized = false;
connected = false;
firstTrap = true;
// Restore exceptions.
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, prevDsiHandler);
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, prevIsiHandler);
OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, prevProgramHandler);
}