Skip to content

Commit

Permalink
GE Debugger: Add conditions to cmd breakpoints.
Browse files Browse the repository at this point in the history
  • Loading branch information
unknownbrackets committed Sep 6, 2022
1 parent 0b30b72 commit f14e49a
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 22 deletions.
15 changes: 15 additions & 0 deletions GPU/Common/GPUDebugInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ enum class GEReferenceIndex : uint32_t {
PC,
STALL,
BFLAG,
OP,
DATA,

BONE_MATRIX = 0x200,
WORLD_MATRIX = 0x260,
Expand All @@ -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 {
Expand Down Expand Up @@ -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:
Expand Down
54 changes: 47 additions & 7 deletions GPU/Debugger/Breakpoints.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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.
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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();
Expand All @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 2 additions & 0 deletions GPU/Debugger/Breakpoints.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
45 changes: 41 additions & 4 deletions Windows/GEDebugger/TabState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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]) {
Expand All @@ -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: {
Expand Down Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions Windows/GEDebugger/TabState.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class CtrlStateValues: public GenericListControl {
private:
bool RowValuesChanged(int row);
void SetCmdValue(u32 op);
void PromptBreakpointCond(const TabStateRow &info);

const TabStateRow *rows_;
int rowCount_;
Expand Down
56 changes: 45 additions & 11 deletions Windows/GEDebugger/TabVertices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -550,25 +551,52 @@ 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);
return &matrixCmds[i];
}
return nullptr;
}

for (int r = matrixCmds[i].row; r < matrixCmds[i + 1].row; ++r) {
SetItemState(r, state ? 1 : 0);
}
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) {
if (row >= GetRowCount())
return;
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions Windows/GEDebugger/TabVertices.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class CtrlMatrixList: public GenericListControl {
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 {
Expand Down
2 changes: 2 additions & 0 deletions Windows/ppsspp.rc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down

0 comments on commit f14e49a

Please sign in to comment.