#include "Windows/Debugger/Debugger_Lists.h" #include "Common/CommonWindows.h" #include #include #include "Windows/Debugger/DebuggerShared.h" #include "Windows/Debugger/CtrlDisAsmView.h" #include "Windows/resource.h" #include "Windows/main.h" #include "BreakpointWindow.h" #include "../../Core/HLE/sceKernelThread.h" #include "util/text/utf8.h" static const int numCPUs = 1; enum { TL_NAME, TL_PROGRAMCOUNTER, TL_ENTRYPOINT, TL_PRIORITY, TL_STATE, TL_WAITTYPE, TL_COLUMNCOUNT }; enum { BPL_ENABLED, BPL_TYPE, BPL_OFFSET, BPL_SIZELABEL, BPL_OPCODE, BPL_CONDITION, BPL_HITS, BPL_COLUMNCOUNT }; enum { SF_ENTRY, SF_ENTRYNAME, SF_CURPC, SF_CUROPCODE, SF_CURSP, SF_FRAMESIZE, SF_COLUMNCOUNT }; enum { ML_NAME, ML_ADDRESS, ML_SIZE, ML_ACTIVE, ML_COLUMNCOUNT }; GenericListViewColumn threadColumns[TL_COLUMNCOUNT] = { { L"Name", 0.20f }, { L"PC", 0.15f }, { L"Entry Point", 0.15f }, { L"Priority", 0.15f }, { L"State", 0.15f }, { L"Wait type", 0.20f } }; GenericListViewDef threadListDef = { threadColumns, ARRAY_SIZE(threadColumns), NULL, false }; GenericListViewColumn breakpointColumns[BPL_COLUMNCOUNT] = { { L"", 0.03f }, // enabled { L"Type", 0.15f }, { L"Offset", 0.12f }, { L"Size/Label", 0.20f }, { L"Opcode", 0.28f }, { L"Condition", 0.17f }, { L"Hits", 0.05f }, }; GenericListViewDef breakpointListDef = { breakpointColumns, ARRAY_SIZE(breakpointColumns), NULL, true }; GenericListViewColumn stackTraceColumns[SF_COLUMNCOUNT] = { { L"Entry", 0.12f }, { L"Name", 0.24f }, { L"PC", 0.12f }, { L"Opcode", 0.28f }, { L"SP", 0.12f }, { L"Frame Size", 0.12f } }; GenericListViewDef stackTraceListDef = { stackTraceColumns, ARRAY_SIZE(stackTraceColumns), NULL, false }; GenericListViewColumn moduleListColumns[ML_COLUMNCOUNT] = { { L"Name", 0.25f }, { L"Address", 0.25f }, { L"Size", 0.25f }, { L"Active", 0.25f }, }; GenericListViewDef moduleListDef = { moduleListColumns, ARRAY_SIZE(moduleListColumns), NULL, false }; const int POPUP_SUBMENU_ID_BREAKPOINTLIST = 5; const int POPUP_SUBMENU_ID_THREADLIST = 6; const int POPUP_SUBMENU_ID_NEWBREAKPOINT = 7; // // CtrlThreadList // CtrlThreadList::CtrlThreadList(HWND hwnd): GenericListControl(hwnd,threadListDef) { Update(); } bool CtrlThreadList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue) { switch (msg) { case WM_KEYDOWN: if (wParam == VK_TAB) { SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0); returnValue = 0; return true; } break; case WM_GETDLGCODE: if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN) { if (wParam == VK_TAB) { returnValue = DLGC_WANTMESSAGE; return true; } } break; } return false; } void CtrlThreadList::showMenu(int itemIndex, const POINT &pt) { auto threadInfo = threads[itemIndex]; // Can't do it, sorry. Needs to not be running. if (Core_IsActive()) return; POINT screenPt(pt); ClientToScreen(GetHandle(), &screenPt); HMENU subMenu = GetSubMenu(g_hPopupMenus, POPUP_SUBMENU_ID_THREADLIST); switch (threadInfo.status) { case THREADSTATUS_DEAD: case THREADSTATUS_DORMANT: case THREADSTATUS_RUNNING: EnableMenuItem(subMenu, ID_DISASM_THREAD_FORCERUN, MF_BYCOMMAND | MF_DISABLED); EnableMenuItem(subMenu, ID_DISASM_THREAD_KILL, MF_BYCOMMAND | MF_DISABLED); break; case THREADSTATUS_READY: EnableMenuItem(subMenu, ID_DISASM_THREAD_FORCERUN, MF_BYCOMMAND | MF_DISABLED); EnableMenuItem(subMenu, ID_DISASM_THREAD_KILL, MF_BYCOMMAND | MF_ENABLED); break; case THREADSTATUS_SUSPEND: case THREADSTATUS_WAIT: case THREADSTATUS_WAITSUSPEND: default: EnableMenuItem(subMenu, ID_DISASM_THREAD_FORCERUN, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(subMenu, ID_DISASM_THREAD_KILL, MF_BYCOMMAND | MF_ENABLED); break; } switch (TrackPopupMenuEx(subMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, screenPt.x, screenPt.y, GetHandle(), 0)) { case ID_DISASM_THREAD_FORCERUN: __KernelResumeThreadFromWait(threadInfo.id, 0); reloadThreads(); break; case ID_DISASM_THREAD_KILL: sceKernelTerminateThread(threadInfo.id); reloadThreads(); break; } } void CtrlThreadList::GetColumnText(wchar_t* dest, int row, int col) { if (row < 0 || row >= (int)threads.size()) { return; } switch (col) { case TL_NAME: wcscpy(dest, ConvertUTF8ToWString(threads[row].name).c_str()); break; case TL_PROGRAMCOUNTER: switch (threads[row].status) { case THREADSTATUS_DORMANT: case THREADSTATUS_DEAD: wcscpy(dest, L"N/A"); break; default: wsprintf(dest, L"0x%08X",threads[row].curPC); break; }; break; case TL_ENTRYPOINT: wsprintf(dest,L"0x%08X",threads[row].entrypoint); break; case TL_PRIORITY: wsprintf(dest,L"%d",threads[row].priority); break; case TL_STATE: switch (threads[row].status) { case THREADSTATUS_RUNNING: wcscpy(dest,L"Running"); break; case THREADSTATUS_READY: wcscpy(dest,L"Ready"); break; case THREADSTATUS_WAIT: wcscpy(dest,L"Waiting"); break; case THREADSTATUS_SUSPEND: wcscpy(dest,L"Suspended"); break; case THREADSTATUS_DORMANT: wcscpy(dest,L"Dormant"); break; case THREADSTATUS_DEAD: wcscpy(dest,L"Dead"); break; case THREADSTATUS_WAITSUSPEND: wcscpy(dest,L"Waiting/Suspended"); break; default: wcscpy(dest,L"Invalid"); break; } break; case TL_WAITTYPE: wcscpy(dest, ConvertUTF8ToWString(getWaitTypeName(threads[row].waitType)).c_str()); break; } } void CtrlThreadList::OnDoubleClick(int itemIndex, int column) { u32 address; switch (threads[itemIndex].status) { case THREADSTATUS_DORMANT: case THREADSTATUS_DEAD: address = threads[itemIndex].entrypoint; break; default: address = threads[itemIndex].curPC; break; } SendMessage(GetParent(GetHandle()),WM_DEB_GOTOWPARAM,address,0); } void CtrlThreadList::OnRightClick(int itemIndex, int column, const POINT& point) { showMenu(itemIndex,point); } void CtrlThreadList::reloadThreads() { threads = GetThreadsInfo(); Update(); } const char* CtrlThreadList::getCurrentThreadName() { for (size_t i = 0; i < threads.size(); i++) { if (threads[i].isCurrent) return threads[i].name; } return "N/A"; } // // CtrlBreakpointList // CtrlBreakpointList::CtrlBreakpointList(HWND hwnd, DebugInterface* cpu, CtrlDisAsmView* disasm) : GenericListControl(hwnd,breakpointListDef),cpu(cpu),disasm(disasm) { SetSendInvalidRows(true); Update(); } bool CtrlBreakpointList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue) { switch(msg) { case WM_KEYDOWN: returnValue = 0; if(wParam == VK_RETURN) { int index = GetSelectedIndex(); editBreakpoint(index); return true; } else if (wParam == VK_DELETE) { int index = GetSelectedIndex(); removeBreakpoint(index); return true; } else if (wParam == VK_TAB) { SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0); return true; } else if (wParam == VK_SPACE) { int index = GetSelectedIndex(); toggleEnabled(index); return true; } break; case WM_GETDLGCODE: if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN) { if (wParam == VK_TAB || wParam == VK_RETURN) { returnValue = DLGC_WANTMESSAGE; return true; } } break; } return false; } void CtrlBreakpointList::reloadBreakpoints() { // Update the items we're displaying from the debugger. displayedBreakPoints_ = CBreakPoints::GetBreakpoints(); displayedMemChecks_= CBreakPoints::GetMemChecks(); Update(); for (int i = 0; i < GetRowCount(); i++) { bool isMemory; int index = getBreakpointIndex(i, isMemory); if (index < 0) continue; if (isMemory) SetCheckState(i, displayedMemChecks_[index].IsEnabled()); else SetCheckState(i, displayedBreakPoints_[index].IsEnabled()); } } void CtrlBreakpointList::editBreakpoint(int itemIndex) { bool isMemory; int index = getBreakpointIndex(itemIndex, isMemory); if (index == -1) return; BreakpointWindow win(GetHandle(),cpu); if (isMemory) { auto mem = displayedMemChecks_[index]; win.loadFromMemcheck(mem); if (win.exec()) { CBreakPoints::RemoveMemCheck(mem.start,mem.end); win.addBreakpoint(); } } else { auto bp = displayedBreakPoints_[index]; win.loadFromBreakpoint(bp); if (win.exec()) { CBreakPoints::RemoveBreakPoint(bp.addr); win.addBreakpoint(); } } } void CtrlBreakpointList::toggleEnabled(int itemIndex) { bool isMemory; int index = getBreakpointIndex(itemIndex, isMemory); if (index == -1) return; if (isMemory) { MemCheck mcPrev = displayedMemChecks_[index]; CBreakPoints::ChangeMemCheck(mcPrev.start, mcPrev.end, mcPrev.cond, BreakAction(mcPrev.result ^ BREAK_ACTION_PAUSE)); } else { BreakPoint bpPrev = displayedBreakPoints_[index]; CBreakPoints::ChangeBreakPoint(bpPrev.addr, BreakAction(bpPrev.result ^ BREAK_ACTION_PAUSE)); } } void CtrlBreakpointList::gotoBreakpointAddress(int itemIndex) { bool isMemory; int index = getBreakpointIndex(itemIndex,isMemory); if (index == -1) return; if (isMemory) { u32 address = displayedMemChecks_[index].start; for (int i=0; iGoto(address); } else { u32 address = displayedBreakPoints_[index].addr; for (int i=0; iGoto(address); } } void CtrlBreakpointList::removeBreakpoint(int itemIndex) { bool isMemory; int index = getBreakpointIndex(itemIndex,isMemory); if (index == -1) return; if (isMemory) { auto mc = displayedMemChecks_[index]; CBreakPoints::RemoveMemCheck(mc.start, mc.end); } else { u32 address = displayedBreakPoints_[index].addr; CBreakPoints::RemoveBreakPoint(address); } } int CtrlBreakpointList::getTotalBreakpointCount() { int count = (int)CBreakPoints::GetMemChecks().size(); for (size_t i = 0; i < CBreakPoints::GetBreakpoints().size(); i++) { if (!displayedBreakPoints_[i].temporary) count++; } return count; } int CtrlBreakpointList::getBreakpointIndex(int itemIndex, bool& isMemory) { // memory breakpoints first if (itemIndex < (int)displayedMemChecks_.size()) { isMemory = true; return itemIndex; } itemIndex -= (int)displayedMemChecks_.size(); size_t i = 0; while (i < displayedBreakPoints_.size()) { if (displayedBreakPoints_[i].temporary) { i++; continue; } // the index is 0 when there are no more breakpoints to skip if (itemIndex == 0) { isMemory = false; return (int)i; } i++; itemIndex--; } return -1; } void CtrlBreakpointList::GetColumnText(wchar_t* dest, int row, int col) { if (!PSP_IsInited()) { return; } bool isMemory; int index = getBreakpointIndex(row,isMemory); if (index == -1) return; switch (col) { case BPL_TYPE: { if (isMemory) { switch ((int)displayedMemChecks_[index].cond) { case MEMCHECK_READ: wcscpy(dest,L"Read"); break; case MEMCHECK_WRITE: wcscpy(dest,L"Write"); break; case MEMCHECK_READWRITE: wcscpy(dest,L"Read/Write"); break; case MEMCHECK_WRITE | MEMCHECK_WRITE_ONCHANGE: wcscpy(dest,L"Write Change"); break; case MEMCHECK_READWRITE | MEMCHECK_WRITE_ONCHANGE: wcscpy(dest,L"Read/Write Change"); break; } } else { wcscpy(dest,L"Execute"); } } break; case BPL_OFFSET: { if (isMemory) { wsprintf(dest,L"0x%08X",displayedMemChecks_[index].start); } else { wsprintf(dest,L"0x%08X",displayedBreakPoints_[index].addr); } } break; case BPL_SIZELABEL: { if (isMemory) { auto mc = displayedMemChecks_[index]; if (mc.end == 0) wsprintf(dest,L"0x%08X",1); else wsprintf(dest,L"0x%08X",mc.end-mc.start); } else { const std::string sym = g_symbolMap->GetLabelString(displayedBreakPoints_[index].addr); if (!sym.empty()) { std::wstring s = ConvertUTF8ToWString(sym); wcscpy(dest,s.c_str()); } else { wcscpy(dest,L"-"); } } } break; case BPL_OPCODE: { if (isMemory) { wcscpy(dest,L"-"); } else { char temp[256]; disasm->getOpcodeText(displayedBreakPoints_[index].addr, temp, sizeof(temp)); std::wstring s = ConvertUTF8ToWString(temp); wcscpy(dest,s.c_str()); } } break; case BPL_CONDITION: { if (isMemory || displayedBreakPoints_[index].hasCond == false) { wcscpy(dest,L"-"); } else { std::wstring s = ConvertUTF8ToWString(displayedBreakPoints_[index].cond.expressionString); wcscpy(dest,s.c_str()); } } break; case BPL_HITS: { if (isMemory) { wsprintf(dest,L"%d",displayedMemChecks_[index].numHits); } else { wsprintf(dest,L"-"); } } break; case BPL_ENABLED: { wsprintf(dest,L"\xFFFE"); } break; } } void CtrlBreakpointList::OnDoubleClick(int itemIndex, int column) { gotoBreakpointAddress(itemIndex); } void CtrlBreakpointList::OnRightClick(int itemIndex, int column, const POINT& point) { showBreakpointMenu(itemIndex,point); } void CtrlBreakpointList::OnToggle(int item, bool newValue) { toggleEnabled(item); } void CtrlBreakpointList::showBreakpointMenu(int itemIndex, const POINT &pt) { POINT screenPt(pt); ClientToScreen(GetHandle(), &screenPt); bool isMemory; int index = getBreakpointIndex(itemIndex, isMemory); if (index == -1) { HMENU subMenu = GetSubMenu(g_hPopupMenus, POPUP_SUBMENU_ID_NEWBREAKPOINT); switch (TrackPopupMenuEx(subMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, screenPt.x, screenPt.y, GetHandle(), 0)) { case ID_DISASM_ADDNEWBREAKPOINT: { BreakpointWindow bpw(GetHandle(),cpu); if (bpw.exec()) bpw.addBreakpoint(); } break; } } else { MemCheck mcPrev; BreakPoint bpPrev; if (isMemory) { mcPrev = displayedMemChecks_[index]; } else { bpPrev = displayedBreakPoints_[index]; } HMENU subMenu = GetSubMenu(g_hPopupMenus, POPUP_SUBMENU_ID_BREAKPOINTLIST); if (isMemory) { CheckMenuItem(subMenu, ID_DISASM_DISABLEBREAKPOINT, MF_BYCOMMAND | (mcPrev.IsEnabled() ? MF_CHECKED : MF_UNCHECKED)); } else { CheckMenuItem(subMenu, ID_DISASM_DISABLEBREAKPOINT, MF_BYCOMMAND | (bpPrev.IsEnabled() ? MF_CHECKED : MF_UNCHECKED)); } switch (TrackPopupMenuEx(subMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, screenPt.x, screenPt.y, GetHandle(), 0)) { case ID_DISASM_DISABLEBREAKPOINT: if (isMemory) { CBreakPoints::ChangeMemCheck(mcPrev.start, mcPrev.end, mcPrev.cond, BreakAction(mcPrev.result ^ BREAK_ACTION_PAUSE)); } else { CBreakPoints::ChangeBreakPoint(bpPrev.addr, BreakAction(bpPrev.result ^ BREAK_ACTION_PAUSE)); } break; case ID_DISASM_EDITBREAKPOINT: editBreakpoint(itemIndex); break; case ID_DISASM_ADDNEWBREAKPOINT: { BreakpointWindow bpw(GetHandle(),cpu); if (bpw.exec()) bpw.addBreakpoint(); } break; } } } // // CtrlStackTraceView // CtrlStackTraceView::CtrlStackTraceView(HWND hwnd, DebugInterface* cpu, CtrlDisAsmView* disasm) : GenericListControl(hwnd,stackTraceListDef),cpu(cpu),disasm(disasm) { Update(); } bool CtrlStackTraceView::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue) { switch(msg) { case WM_KEYDOWN: if (wParam == VK_TAB) { returnValue = 0; SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0); return true; } break; case WM_GETDLGCODE: if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN) { if (wParam == VK_TAB || wParam == VK_RETURN) { returnValue = DLGC_WANTMESSAGE; return true; } } break; } return false; } void CtrlStackTraceView::GetColumnText(wchar_t* dest, int row, int col) { if (row < 0 || row >= (int)frames.size()) { return; } switch (col) { case SF_ENTRY: wsprintf(dest,L"%08X",frames[row].entry); break; case SF_ENTRYNAME: { const std::string sym = g_symbolMap->GetLabelString(frames[row].entry); if (!sym.empty()) { wcscpy(dest, ConvertUTF8ToWString(sym).c_str()); } else { wcscpy(dest,L"-"); } } break; case SF_CURPC: wsprintf(dest,L"%08X",frames[row].pc); break; case SF_CUROPCODE: { char temp[512]; disasm->getOpcodeText(frames[row].pc, temp, sizeof(temp)); wcscpy(dest, ConvertUTF8ToWString(temp).c_str()); } break; case SF_CURSP: wsprintf(dest,L"%08X",frames[row].sp); break; case SF_FRAMESIZE: wsprintf(dest,L"%08X",frames[row].stackSize); break; } } void CtrlStackTraceView::OnDoubleClick(int itemIndex, int column) { SendMessage(GetParent(GetHandle()),WM_DEB_GOTOWPARAM,frames[itemIndex].pc,0); } void CtrlStackTraceView::loadStackTrace() { auto threads = GetThreadsInfo(); u32 entry = 0, stackTop = 0; for (size_t i = 0; i < threads.size(); i++) { if (threads[i].isCurrent) { entry = threads[i].entrypoint; stackTop = threads[i].initialStack; break; } } if (entry != 0) { frames = MIPSStackWalk::Walk(cpu->GetPC(),cpu->GetRegValue(0,31),cpu->GetRegValue(0,29),entry,stackTop); } else { frames.clear(); } Update(); } // // CtrlModuleList // CtrlModuleList::CtrlModuleList(HWND hwnd, DebugInterface* cpu) : GenericListControl(hwnd,moduleListDef),cpu(cpu) { Update(); } bool CtrlModuleList::WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue) { switch(msg) { case WM_KEYDOWN: if (wParam == VK_TAB) { returnValue = 0; SendMessage(GetParent(GetHandle()),WM_DEB_TABPRESSED,0,0); return true; } break; case WM_GETDLGCODE: if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN) { if (wParam == VK_TAB || wParam == VK_RETURN) { returnValue = DLGC_WANTMESSAGE; return true; } } break; } return false; } void CtrlModuleList::GetColumnText(wchar_t* dest, int row, int col) { if (row < 0 || row >= (int)modules.size()) { return; } switch (col) { case ML_NAME: wcscpy(dest,ConvertUTF8ToWString(modules[row].name).c_str()); break; case ML_ADDRESS: wsprintf(dest,L"%08X",modules[row].address); break; case ML_SIZE: wsprintf(dest,L"%08X",modules[row].size); break; case ML_ACTIVE: wcscpy(dest,modules[row].active ? L"true" : L"false"); break; } } void CtrlModuleList::OnDoubleClick(int itemIndex, int column) { SendMessage(GetParent(GetHandle()),WM_DEB_GOTOWPARAM,modules[itemIndex].address,0); } void CtrlModuleList::loadModules() { if (g_symbolMap) { modules = g_symbolMap->getAllModules(); } else { modules.clear(); } Update(); }