Files
archived-pcsx2/pcsx2/DebugTools/Breakpoints.h

188 lines
5.6 KiB
C++

// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-2.0+
#pragma once
#include <algorithm>
#include <iterator>
#include <vector>
#include "DebugInterface.h"
#include "common/Pcsx2Types.h"
struct BreakPointCond
{
DebugInterface* debug = nullptr;
PostfixExpression expression;
std::string expressionString;
u32 Evaluate()
{
u64 result;
std::string error;
if (!debug->parseExpression(expression, result, error) || result == 0)
return 0;
return 1;
}
};
struct BreakPoint
{
u32 addr = 0;
bool enabled = false;
bool temporary = false;
bool stepping = false;
bool hasCond = false;
BreakPointCond cond;
BreakPointCpu cpu;
std::string description;
bool operator==(const BreakPoint& other) const
{
return addr == other.addr;
}
bool operator<(const BreakPoint& other) const
{
return addr < other.addr;
}
};
enum MemCheckCondition
{
MEMCHECK_READ = 0x01,
MEMCHECK_WRITE = 0x02,
MEMCHECK_WRITE_ONCHANGE = 0x04,
MEMCHECK_READWRITE = 0x03,
MEMCHECK_INVALID = 0x08, // Invalid condition, used by the CSV parser to know if the line is for a memcheck
};
enum MemCheckResult
{
MEMCHECK_IGNORE = 0x00,
MEMCHECK_LOG = 0x01,
MEMCHECK_BREAK = 0x02,
MEMCHECK_BOTH = 0x03,
};
struct MemCheck
{
MemCheck();
u32 start;
u32 end;
bool hasCond;
BreakPointCond cond;
MemCheckCondition memCond;
MemCheckResult result;
BreakPointCpu cpu;
std::string description;
u32 numHits;
u32 lastPC;
u32 lastAddr;
int lastSize;
void Action(u32 addr, bool write, int size, u32 pc);
void JitBefore(u32 addr, bool write, int size, u32 pc);
void JitCleanup();
void Log(u32 addr, bool write, int size, u32 pc);
bool operator==(const MemCheck& other) const
{
return start == other.start && end == other.end;
}
};
// BreakPoints cannot overlap, only one is allowed per address.
// MemChecks can overlap, as long as their ends are different.
// WARNING: MemChecks are not used in the interpreter or HLE currently.
class CBreakPoints
{
public:
static const size_t INVALID_BREAKPOINT = -1;
static const size_t INVALID_MEMCHECK = -1;
static bool IsAddressBreakPoint(BreakPointCpu cpu, u32 addr);
static bool IsAddressBreakPoint(BreakPointCpu cpu, u32 addr, bool* enabled);
static bool IsTempBreakPoint(BreakPointCpu cpu, u32 addr);
static bool IsSteppingBreakPoint(BreakPointCpu cpu, u32 addr);
static void AddBreakPoint(BreakPointCpu cpu, u32 addr, bool temp = false, bool enabled = true, bool stepping = false);
static void RemoveBreakPoint(BreakPointCpu cpu, u32 addr);
static void ChangeBreakPoint(BreakPointCpu cpu, u32 addr, bool enable);
static void ClearAllBreakPoints();
static void ClearTemporaryBreakPoints();
// Makes a copy. Temporary breakpoints can't have conditions.
static void ChangeBreakPointAddCond(BreakPointCpu cpu, u32 addr, const BreakPointCond& cond);
static void ChangeBreakPointRemoveCond(BreakPointCpu cpu, u32 addr);
static BreakPointCond* GetBreakPointCondition(BreakPointCpu cpu, u32 addr);
static void ChangeBreakPointDescription(BreakPointCpu cpu, u32 addr, const std::string& description);
static void AddMemCheck(BreakPointCpu cpu, u32 start, u32 end, MemCheckCondition cond, MemCheckResult result);
static void RemoveMemCheck(BreakPointCpu cpu, u32 start, u32 end);
static void ChangeMemCheck(BreakPointCpu cpu, u32 start, u32 end, MemCheckCondition cond, MemCheckResult result);
static void ChangeMemCheckRemoveCond(BreakPointCpu cpu, u32 start, u32 end);
static void ChangeMemCheckAddCond(BreakPointCpu cpu, u32 start, u32 end, const BreakPointCond& cond);
static void ChangeMemCheckDescription(BreakPointCpu cpu, u32 start, u32 end, const std::string& description);
static void ClearAllMemChecks();
static void SetSkipFirst(BreakPointCpu cpu, u32 pc);
static u32 CheckSkipFirst(BreakPointCpu cpu, u32 pc);
static void ClearSkipFirst(BreakPointCpu cpu = BREAKPOINT_IOP_AND_EE);
static void CommitClearSkipFirst(BreakPointCpu cpu);
// Includes uncached addresses.
static const std::vector<MemCheck> GetMemCheckRanges();
static const std::vector<MemCheck> GetMemChecks(BreakPointCpu cpu);
static const std::vector<BreakPoint> GetBreakpoints(BreakPointCpu cpu, bool includeTemp);
// Returns count of all non-temporary breakpoints
static size_t GetNumBreakpoints()
{
return std::count_if(breakPoints_.begin(), breakPoints_.end(), [](BreakPoint& bp) { return !bp.temporary; });
}
static size_t GetNumMemchecks() { return memChecks_.size(); }
static void Update(BreakPointCpu cpu = BREAKPOINT_IOP_AND_EE, u32 addr = 0);
static void SetBreakpointTriggered(bool triggered, BreakPointCpu cpu = BreakPointCpu::BREAKPOINT_IOP_AND_EE)
{
breakpointTriggered_ = triggered;
breakpointTriggeredCpu_ = cpu;
};
static bool GetBreakpointTriggered() { return breakpointTriggered_; };
static BreakPointCpu GetBreakpointTriggeredCpu() { return breakpointTriggeredCpu_; };
static bool GetCorePaused() { return corePaused; };
static void SetCorePaused(bool b) { corePaused = b; };
private:
static size_t FindBreakpoint(BreakPointCpu cpu, u32 addr, bool matchTemp = false, bool temp = false);
// Finds exactly, not using a range check.
static size_t FindMemCheck(BreakPointCpu cpu, u32 start, u32 end);
static std::vector<BreakPoint> breakPoints_;
static u32 breakSkipFirstAtEE_;
static bool pendingClearSkipFirstAtEE_;
static u32 breakSkipFirstAtIop_;
static bool pendingClearSkipFirstAtIop_;
static bool breakpointTriggered_;
static BreakPointCpu breakpointTriggeredCpu_;
static bool corePaused;
static std::vector<MemCheck> memChecks_;
static std::vector<MemCheck*> cleanupMemChecks_;
};
// called from the dynarec
u32 standardizeBreakpointAddress(u32 addr);