mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
GE Debugger: Add conditions to cmd breakpoints.
This commit is contained in:
parent
0b30b723bb
commit
f14e49a373
8 changed files with 155 additions and 23 deletions
|
@ -29,6 +29,8 @@ enum class GEReferenceIndex : uint32_t {
|
|||
PC,
|
||||
STALL,
|
||||
BFLAG,
|
||||
OP,
|
||||
DATA,
|
||||
|
||||
BONE_MATRIX = 0x200,
|
||||
WORLD_MATRIX = 0x260,
|
||||
|
@ -52,6 +54,8 @@ static constexpr ReferenceName referenceNames[] = {
|
|||
{ GEReferenceIndex::STALL, "stall" },
|
||||
{ GEReferenceIndex::BFLAG, "bflag" },
|
||||
{ GEReferenceIndex::BFLAG, "boundflag" },
|
||||
{ GEReferenceIndex::OP, "op" },
|
||||
{ GEReferenceIndex::DATA, "data" },
|
||||
};
|
||||
|
||||
class GEExpressionFunctions : public IExpressionFunctions {
|
||||
|
@ -181,6 +185,17 @@ uint32_t GEExpressionFunctions::getReferenceValue(uint32_t referenceIndex) {
|
|||
return list.bboxResult ? 1 : 0;
|
||||
}
|
||||
return 0;
|
||||
case GEReferenceIndex::OP:
|
||||
// TODO: Support fields under this as per the cmd format?
|
||||
if (gpu_->GetCurrentDisplayList(list)) {
|
||||
return Memory::Read_U32(list.pc);
|
||||
}
|
||||
return 0;
|
||||
case GEReferenceIndex::DATA:
|
||||
if (gpu_->GetCurrentDisplayList(list)) {
|
||||
return Memory::Read_U32(list.pc) & 0x00FFFFFF;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case GEReferenceIndex::BONE_MATRIX:
|
||||
case GEReferenceIndex::WORLD_MATRIX:
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
|
@ -30,14 +30,15 @@
|
|||
namespace GPUBreakpoints {
|
||||
|
||||
struct BreakpointInfo {
|
||||
bool isConditional;
|
||||
bool isConditional = false;
|
||||
PostfixExpression expression;
|
||||
std::string expressionString;
|
||||
};
|
||||
|
||||
static std::mutex breaksLock;
|
||||
static bool breakCmds[256];
|
||||
static std::map<u32, BreakpointInfo> breakPCs;
|
||||
static BreakpointInfo breakCmdsInfo[256];
|
||||
static std::unordered_map<u32, BreakpointInfo> breakPCs;
|
||||
static std::set<u32> breakTextures;
|
||||
static std::set<u32> breakRenderTargets;
|
||||
// Small optimization to avoid a lock/lookup for the common case.
|
||||
|
@ -185,8 +186,24 @@ static bool HitAddressBreakpoint(u32 pc) {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool HitOpBreakpoint(u32 op) {
|
||||
u8 cmd = op >> 24;
|
||||
if (!IsCmdBreakpoint(cmd))
|
||||
return false;
|
||||
|
||||
if (breakCmdsInfo[cmd].isConditional) {
|
||||
std::lock_guard<std::mutex> guard(breaksLock);
|
||||
u32 result = 1;
|
||||
if (!GPUDebugExecExpression(gpuDebug, breakCmdsInfo[cmd].expression, result))
|
||||
return false;
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsBreakpoint(u32 pc, u32 op) {
|
||||
if (HitAddressBreakpoint(pc) || IsOpBreakpoint(op)) {
|
||||
if (HitAddressBreakpoint(pc) || HitOpBreakpoint(op)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -308,7 +325,7 @@ void AddAddressBreakpoint(u32 addr, bool temp) {
|
|||
} else {
|
||||
// Remove the temporary marking.
|
||||
breakPCsTemp.erase(addr);
|
||||
breakPCs[addr].isConditional = false;
|
||||
breakPCs.insert(std::make_pair(addr, BreakpointInfo{}));
|
||||
}
|
||||
|
||||
breakPCsCount = breakPCs.size();
|
||||
|
@ -320,12 +337,16 @@ void AddCmdBreakpoint(u8 cmd, bool temp) {
|
|||
if (!breakCmds[cmd]) {
|
||||
breakCmdsTemp[cmd] = true;
|
||||
breakCmds[cmd] = true;
|
||||
breakCmdsInfo[cmd].isConditional = false;
|
||||
}
|
||||
// Ignore adding a temp breakpoint when a normal one exists.
|
||||
} else {
|
||||
// This is no longer temporary.
|
||||
breakCmdsTemp[cmd] = false;
|
||||
breakCmds[cmd] = true;
|
||||
if (!breakCmds[cmd]) {
|
||||
breakCmds[cmd] = true;
|
||||
breakCmdsInfo[cmd].isConditional = false;
|
||||
}
|
||||
}
|
||||
notifyBreakpoints(true);
|
||||
}
|
||||
|
@ -432,7 +453,7 @@ static bool SetupCond(BreakpointInfo &bp, const std::string &expression, std::st
|
|||
bp.isConditional = true;
|
||||
bp.expressionString = expression;
|
||||
} else {
|
||||
bp.isConditional = false;
|
||||
// Don't change if it failed.
|
||||
if (error)
|
||||
*error = getExpressionError();
|
||||
success = false;
|
||||
|
@ -463,6 +484,25 @@ bool GetAddressBreakpointCond(u32 addr, std::string *expression) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *error) {
|
||||
// Must have one in the first place, make sure it's not temporary.
|
||||
AddCmdBreakpoint(cmd);
|
||||
|
||||
std::lock_guard<std::mutex> guard(breaksLock);
|
||||
return SetupCond(breakCmdsInfo[cmd], expression, error);
|
||||
}
|
||||
|
||||
bool GetCmdBreakpointCond(u8 cmd, std::string *expression) {
|
||||
if (breakCmds[cmd] && breakCmdsInfo[cmd].isConditional) {
|
||||
if (expression) {
|
||||
std::lock_guard<std::mutex> guard(breaksLock);
|
||||
*expression = breakCmdsInfo[cmd].expressionString;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UpdateLastTexture(u32 addr) {
|
||||
lastTexture = addr;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ namespace GPUBreakpoints {
|
|||
|
||||
bool SetAddressBreakpointCond(u32 addr, const std::string &expression, std::string *error);
|
||||
bool GetAddressBreakpointCond(u32 addr, std::string *expression);
|
||||
bool SetCmdBreakpointCond(u8 cmd, const std::string &expression, std::string *error);
|
||||
bool GetCmdBreakpointCond(u8 cmd, std::string *expression);
|
||||
|
||||
void UpdateLastTexture(u32 addr);
|
||||
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
#include <commctrl.h>
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#include "Common/Data/Text/Parsers.h"
|
||||
#include "Common/Log.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Windows/resource.h"
|
||||
#include "Windows/InputBox.h"
|
||||
|
@ -900,7 +901,13 @@ void CtrlStateValues::OnDoubleClick(int row, int column) {
|
|||
const auto info = rows_[row];
|
||||
|
||||
if (column == STATEVALUES_COL_BREAKPOINT) {
|
||||
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
|
||||
bool proceed = true;
|
||||
if (IsCmdBreakpoint(info.cmd)) {
|
||||
int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
|
||||
proceed = ret == IDYES;
|
||||
}
|
||||
if (proceed)
|
||||
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -960,6 +967,7 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) {
|
|||
|
||||
HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_STATE);
|
||||
SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE);
|
||||
EnableMenuItem(subMenu, ID_GEDBG_SETCOND, GPUBreakpoints::IsCmdBreakpoint(info.cmd) ? MF_ENABLED : MF_GRAYED);
|
||||
|
||||
// Ehh, kinda ugly.
|
||||
if (!watchList.empty() && rows_ == &watchList[0]) {
|
||||
|
@ -975,8 +983,19 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) {
|
|||
|
||||
switch (TriggerContextMenu(ContextMenuID::GEDBG_STATE, GetHandle(), ContextPoint::FromClient(point)))
|
||||
{
|
||||
case ID_DISASM_TOGGLEBREAKPOINT:
|
||||
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
|
||||
case ID_DISASM_TOGGLEBREAKPOINT: {
|
||||
bool proceed = true;
|
||||
if (IsCmdBreakpoint(info.cmd)) {
|
||||
int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
|
||||
proceed = ret == IDYES;
|
||||
}
|
||||
if (proceed)
|
||||
SetItemState(row, ToggleBreakpoint(info) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case ID_GEDBG_SETCOND:
|
||||
PromptBreakpointCond(info);
|
||||
break;
|
||||
|
||||
case ID_DISASM_COPYINSTRUCTIONHEX: {
|
||||
|
@ -1043,6 +1062,24 @@ bool CtrlStateValues::RowValuesChanged(int row) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void CtrlStateValues::PromptBreakpointCond(const TabStateRow &info) {
|
||||
std::string expression;
|
||||
GPUBreakpoints::GetCmdBreakpointCond(info.cmd, &expression);
|
||||
if (!InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Expression", expression, expression))
|
||||
return;
|
||||
|
||||
std::string error;
|
||||
if (!GPUBreakpoints::SetCmdBreakpointCond(info.cmd, expression, &error)) {
|
||||
MessageBox(GetHandle(), ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);
|
||||
} else {
|
||||
if (info.otherCmd)
|
||||
GPUBreakpoints::SetCmdBreakpointCond(info.otherCmd, expression, &error);
|
||||
if (info.otherCmd2)
|
||||
GPUBreakpoints::SetCmdBreakpointCond(info.otherCmd2, expression, &error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TabStateValues::TabStateValues(const TabStateRow *rows, int rowCount, LPCSTR dialogID, HINSTANCE _hInstance, HWND _hParent)
|
||||
: Dialog(dialogID, _hInstance, _hParent) {
|
||||
values = new CtrlStateValues(rows, rowCount, GetDlgItem(m_hDlg, IDC_GEDBG_VALUES));
|
||||
|
|
|
@ -47,6 +47,7 @@ protected:
|
|||
private:
|
||||
bool RowValuesChanged(int row);
|
||||
void SetCmdValue(u32 op);
|
||||
void PromptBreakpointCond(const TabStateRow &info);
|
||||
|
||||
const TabStateRow *rows_;
|
||||
int rowCount_;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#include <commctrl.h>
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#include "Common/StringUtils.h"
|
||||
#include "Core/System.h"
|
||||
#include "Windows/resource.h"
|
||||
|
@ -550,23 +551,50 @@ static constexpr MatrixCmdPair matrixCmds[] = {
|
|||
{ MATRIXLIST_ROW_COUNT, GE_CMD_NOP },
|
||||
};
|
||||
|
||||
void CtrlMatrixList::ToggleBreakpoint(int row) {
|
||||
static const MatrixCmdPair *FindCmdPair(int row) {
|
||||
for (int i = 0; i < ARRAY_SIZE(matrixCmds) - 1; ++i) {
|
||||
if (row < matrixCmds[i].row || row >= matrixCmds[i + 1].row)
|
||||
continue;
|
||||
|
||||
// Okay, this command is in range. Toggle the actual breakpoint.
|
||||
auto &info = matrixCmds[i];
|
||||
bool state = !GPUBreakpoints::IsCmdBreakpoint(info.cmd);
|
||||
if (state)
|
||||
GPUBreakpoints::AddCmdBreakpoint(info.cmd);
|
||||
else
|
||||
GPUBreakpoints::RemoveCmdBreakpoint(info.cmd);
|
||||
|
||||
for (int r = matrixCmds[i].row; r < matrixCmds[i + 1].row; ++r) {
|
||||
SetItemState(r, state ? 1 : 0);
|
||||
}
|
||||
return &matrixCmds[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CtrlMatrixList::ToggleBreakpoint(int row) {
|
||||
const MatrixCmdPair *info = FindCmdPair(row);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
// Okay, this command is in range. Toggle the actual breakpoint.
|
||||
bool state = !GPUBreakpoints::IsCmdBreakpoint(info->cmd);
|
||||
if (state) {
|
||||
GPUBreakpoints::AddCmdBreakpoint(info->cmd);
|
||||
} else {
|
||||
int ret = MessageBox(GetHandle(), L"This breakpoint has a custom condition.\nDo you want to remove it?", L"Confirmation", MB_YESNO);
|
||||
if (ret != IDYES)
|
||||
return;
|
||||
GPUBreakpoints::RemoveCmdBreakpoint(info->cmd);
|
||||
}
|
||||
|
||||
for (int r = info->row; r < (info + 1)->row; ++r) {
|
||||
SetItemState(r, state ? 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
void CtrlMatrixList::PromptBreakpointCond(int row) {
|
||||
const MatrixCmdPair *info = FindCmdPair(row);
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
std::string expression;
|
||||
GPUBreakpoints::GetCmdBreakpointCond(info->cmd, &expression);
|
||||
if (!InputBox_GetString(GetModuleHandle(NULL), GetHandle(), L"Expression", expression, expression))
|
||||
return;
|
||||
|
||||
std::string error;
|
||||
if (!GPUBreakpoints::SetCmdBreakpointCond(info->cmd, expression, &error))
|
||||
MessageBox(GetHandle(), ConvertUTF8ToWString(error).c_str(), L"Invalid expression", MB_OK | MB_ICONEXCLAMATION);
|
||||
}
|
||||
|
||||
void CtrlMatrixList::OnDoubleClick(int row, int column) {
|
||||
|
@ -624,18 +652,24 @@ void CtrlMatrixList::OnDoubleClick(int row, int column) {
|
|||
void CtrlMatrixList::OnRightClick(int row, int column, const POINT &point) {
|
||||
if (row >= GetRowCount())
|
||||
return;
|
||||
const MatrixCmdPair *info = FindCmdPair(row);
|
||||
|
||||
POINT screenPt(point);
|
||||
ClientToScreen(GetHandle(), &screenPt);
|
||||
|
||||
HMENU subMenu = GetContextMenu(ContextMenuID::GEDBG_MATRIX);
|
||||
SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE);
|
||||
EnableMenuItem(subMenu, ID_GEDBG_SETCOND, info && GPUBreakpoints::IsCmdBreakpoint(info->cmd) ? MF_ENABLED : MF_GRAYED);
|
||||
|
||||
switch (TriggerContextMenu(ContextMenuID::GEDBG_MATRIX, GetHandle(), ContextPoint::FromClient(point))) {
|
||||
case ID_DISASM_TOGGLEBREAKPOINT:
|
||||
ToggleBreakpoint(row);
|
||||
break;
|
||||
|
||||
case ID_GEDBG_SETCOND:
|
||||
PromptBreakpointCond(row);
|
||||
break;
|
||||
|
||||
case ID_DISASM_COPYINSTRUCTIONDISASM:
|
||||
{
|
||||
float val;
|
||||
|
|
|
@ -90,6 +90,7 @@ private:
|
|||
bool GetValue(const GPUgstate &state, int row, int col, float &val);
|
||||
bool ColChanged(const GPUgstate &lastState, const GPUgstate &state, int row, int col);
|
||||
void ToggleBreakpoint(int row);
|
||||
void PromptBreakpointCond(int row);
|
||||
};
|
||||
|
||||
class TabMatrices : public Dialog {
|
||||
|
|
|
@ -778,6 +778,7 @@ BEGIN
|
|||
MENUITEM "Copy Entire Tab (Formatted)",ID_GEDBG_COPYALL
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "Toggle Breakpoint", ID_DISASM_TOGGLEBREAKPOINT
|
||||
MENUITEM "Set Breakpoint Condition", ID_GEDBG_SETCOND
|
||||
MENUITEM "Add Watch", ID_GEDBG_WATCH
|
||||
END
|
||||
POPUP "gepreviewoptions"
|
||||
|
@ -793,6 +794,7 @@ BEGIN
|
|||
MENUITEM "Copy Entire Tab (Formatted)",ID_GEDBG_COPYALL
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "Toggle Breakpoint", ID_DISASM_TOGGLEBREAKPOINT
|
||||
MENUITEM "Set Breakpoint Condition", ID_GEDBG_SETCOND
|
||||
END
|
||||
POPUP "getaboptions"
|
||||
BEGIN
|
||||
|
|
Loading…
Add table
Reference in a new issue