GE Debugger: Add conditions to cmd breakpoints.

This commit is contained in:
Unknown W. Brackets 2022-09-05 16:46:16 -07:00
parent 0b30b723bb
commit f14e49a373
8 changed files with 155 additions and 23 deletions

View file

@ -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:

View file

@ -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;
}

View file

@ -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);

View file

@ -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));

View file

@ -47,6 +47,7 @@ protected:
private:
bool RowValuesChanged(int row);
void SetCmdValue(u32 op);
void PromptBreakpointCond(const TabStateRow &info);
const TabStateRow *rows_;
int rowCount_;

View file

@ -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;

View file

@ -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 {

View file

@ -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