mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 03:25:16 +00:00
Add memory breakpoints
RPCS3 needs to be compiled with -DHAS_MEMORY_BREAKPOINTS=ON for it to be available
This commit is contained in:
parent
783079266e
commit
eb9dbfa31a
17 changed files with 416 additions and 74 deletions
|
@ -30,6 +30,7 @@ option(USE_SYSTEM_FFMPEG "Prefer system ffmpeg instead of the prebuild one" OFF)
|
|||
option(USE_SYSTEM_OPENAL "Prefer system OpenAL instead of the prebuild one" ON)
|
||||
option(USE_SYSTEM_CURL "Prefer system Curl instead of the prebuild one" ON)
|
||||
option(USE_SYSTEM_OPENCV "Prefer system OpenCV instead of the builtin one" ON)
|
||||
option(HAS_MEMORY_BREAKPOINTS "Add support for memory breakpoints to the interpreter" OFF)
|
||||
option(USE_LTO "Use LTO for building" ON)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/buildfiles/cmake")
|
||||
|
|
|
@ -28,6 +28,10 @@ if(USE_ASAN)
|
|||
set_source_files_properties(../../Utilities/Thread.cpp PROPERTIES COMPILE_DEFINITIONS USE_ASAN)
|
||||
endif()
|
||||
|
||||
if(HAS_MEMORY_BREAKPOINTS)
|
||||
target_compile_definitions(rpcs3_emu PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS)
|
||||
endif()
|
||||
|
||||
target_link_libraries(rpcs3_emu
|
||||
PRIVATE
|
||||
3rdparty::zlib 3rdparty::yaml-cpp 3rdparty::zstd
|
||||
|
|
|
@ -26,6 +26,29 @@
|
|||
#pragma GCC diagnostic ignored "-Wuninitialized"
|
||||
#endif
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
void ppubreak(ppu_thread& ppu)
|
||||
{
|
||||
if (!g_breakpoint_handler.IsBreakOnBPM())
|
||||
return;
|
||||
|
||||
if (!ppu.state.test_and_set(cpu_flag::dbg_pause))
|
||||
{
|
||||
ppu.check_state();
|
||||
}
|
||||
}
|
||||
|
||||
#define PPU_WRITE_8(addr, value) vm::write8(addr, value, &ppu);
|
||||
#define PPU_WRITE_16(addr, value) vm::write16(addr, value, &ppu);
|
||||
#define PPU_WRITE_32(addr, value) vm::write32(addr, value, &ppu);
|
||||
#define PPU_WRITE_64(addr, value) vm::write64(addr, value, &ppu);
|
||||
#else
|
||||
#define PPU_WRITE_8(addr, value) vm::write8(addr, value);
|
||||
#define PPU_WRITE_16(addr, value) vm::write16(addr, value);
|
||||
#define PPU_WRITE_32(addr, value) vm::write32(addr, value);
|
||||
#define PPU_WRITE_64(addr, value) vm::write64(addr, value);
|
||||
#endif
|
||||
|
||||
extern bool is_debugger_present();
|
||||
|
||||
extern const ppu_decoder<ppu_itype> g_ppu_itype;
|
||||
|
@ -428,6 +451,14 @@ auto ppu_feed_data(ppu_thread& ppu, u64 addr)
|
|||
|
||||
auto value = vm::_ref<T>(vm::cast(addr));
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
if (g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_read))
|
||||
{
|
||||
debugbp_log.success("BPMR: breakpoint reading 0x%x at 0x%x", value, addr);
|
||||
ppubreak(ppu);
|
||||
}
|
||||
#endif
|
||||
|
||||
if constexpr (!((Flags == use_feed_data) || ...))
|
||||
{
|
||||
return value;
|
||||
|
@ -4274,7 +4305,7 @@ auto STVEBX()
|
|||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb];
|
||||
const u8 eb = addr & 0xf;
|
||||
vm::write8(vm::cast(addr), ppu.vr[op.vs]._u8[15 - eb]);
|
||||
PPU_WRITE_8(vm::cast(addr), ppu.vr[op.vs]._u8[15 - eb]);
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -4381,7 +4412,7 @@ auto STDX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb];
|
||||
vm::write64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
PPU_WRITE_64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -4407,7 +4438,7 @@ auto STWX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb];
|
||||
vm::write32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -4421,7 +4452,7 @@ auto STVEHX()
|
|||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = (op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb]) & ~1ULL;
|
||||
const u8 eb = (addr & 0xf) >> 1;
|
||||
vm::write16(vm::cast(addr), ppu.vr[op.vs]._u16[7 - eb]);
|
||||
PPU_WRITE_16(vm::cast(addr), ppu.vr[op.vs]._u16[7 - eb]);
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -4434,7 +4465,7 @@ auto STDUX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + ppu.gpr[op.rb];
|
||||
vm::write64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
PPU_WRITE_64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -4448,7 +4479,7 @@ auto STWUX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + ppu.gpr[op.rb];
|
||||
vm::write32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -4463,7 +4494,7 @@ auto STVEWX()
|
|||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = (op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb]) & ~3ULL;
|
||||
const u8 eb = (addr & 0xf) >> 2;
|
||||
vm::write32(vm::cast(addr), ppu.vr[op.vs]._u32[3 - eb]);
|
||||
PPU_WRITE_32(vm::cast(addr), ppu.vr[op.vs]._u32[3 - eb]);
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -4527,7 +4558,7 @@ auto STBX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb];
|
||||
vm::write8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -4639,7 +4670,7 @@ auto STBUX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + ppu.gpr[op.rb];
|
||||
vm::write8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -4883,7 +4914,7 @@ auto STHX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb];
|
||||
vm::write16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -4922,7 +4953,7 @@ auto STHUX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + ppu.gpr[op.rb];
|
||||
vm::write16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -5346,7 +5377,7 @@ auto STSWX()
|
|||
u32 count = ppu.xer.cnt & 0x7F;
|
||||
for (; count >= 4; count -= 4, addr += 4, op.rs = (op.rs + 1) & 31)
|
||||
{
|
||||
vm::write32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
}
|
||||
if (count)
|
||||
{
|
||||
|
@ -5354,7 +5385,7 @@ auto STSWX()
|
|||
for (u32 byte = 0; byte < count; byte++)
|
||||
{
|
||||
u8 byte_value = static_cast<u8>(value >> ((3 ^ byte) * 8));
|
||||
vm::write8(vm::cast(addr + byte), byte_value);
|
||||
PPU_WRITE_8(vm::cast(addr + byte), byte_value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -5434,7 +5465,7 @@ auto STSWI()
|
|||
{
|
||||
if (N > 3)
|
||||
{
|
||||
vm::write32(vm::cast(addr), static_cast<u32>(ppu.gpr[reg]));
|
||||
PPU_WRITE_32(vm::cast(addr), static_cast<u32>(ppu.gpr[reg]));
|
||||
addr += 4;
|
||||
N -= 4;
|
||||
}
|
||||
|
@ -5444,7 +5475,7 @@ auto STSWI()
|
|||
while (N > 0)
|
||||
{
|
||||
N = N - 1;
|
||||
vm::write8(vm::cast(addr), (0xFF000000 & buf) >> 24);
|
||||
PPU_WRITE_8(vm::cast(addr), (0xFF000000 & buf) >> 24);
|
||||
buf <<= 8;
|
||||
addr++;
|
||||
}
|
||||
|
@ -5678,7 +5709,7 @@ auto STFIWX()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra ? ppu.gpr[op.ra] + ppu.gpr[op.rb] : ppu.gpr[op.rb];
|
||||
vm::write32(vm::cast(addr), static_cast<u32>(std::bit_cast<u64>(ppu.fpr[op.frs])));
|
||||
PPU_WRITE_32(vm::cast(addr), static_cast<u32>(std::bit_cast<u64>(ppu.fpr[op.frs])));
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -5793,7 +5824,7 @@ auto STW()
|
|||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra || 1 ? ppu.gpr[op.ra] + op.simm16 : op.simm16;
|
||||
const u32 value = static_cast<u32>(ppu.gpr[op.rs]);
|
||||
vm::write32(vm::cast(addr), value);
|
||||
PPU_WRITE_32(vm::cast(addr), value);
|
||||
|
||||
//Insomniac engine v3 & v4 (newer R&C, Fuse, Resitance 3)
|
||||
if (value == 0xAAAAAAAA) [[unlikely]]
|
||||
|
@ -5813,7 +5844,7 @@ auto STWU()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + op.simm16;
|
||||
vm::write32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_32(vm::cast(addr), static_cast<u32>(ppu.gpr[op.rs]));
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -5827,7 +5858,7 @@ auto STB()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra || 1 ? ppu.gpr[op.ra] + op.simm16 : op.simm16;
|
||||
vm::write8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -5840,7 +5871,7 @@ auto STBU()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + op.simm16;
|
||||
vm::write8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_8(vm::cast(addr), static_cast<u8>(ppu.gpr[op.rs]));
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -5908,7 +5939,7 @@ auto STH()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = op.ra || 1 ? ppu.gpr[op.ra] + op.simm16 : op.simm16;
|
||||
vm::write16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -5921,7 +5952,7 @@ auto STHU()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + op.simm16;
|
||||
vm::write16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
PPU_WRITE_16(vm::cast(addr), static_cast<u16>(ppu.gpr[op.rs]));
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -5953,7 +5984,7 @@ auto STMW()
|
|||
u64 addr = op.ra ? ppu.gpr[op.ra] + op.simm16 : op.simm16;
|
||||
for (u32 i = op.rs; i<32; ++i, addr += 4)
|
||||
{
|
||||
vm::write32(vm::cast(addr), static_cast<u32>(ppu.gpr[i]));
|
||||
PPU_WRITE_32(vm::cast(addr), static_cast<u32>(ppu.gpr[i]));
|
||||
}
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
@ -6115,7 +6146,7 @@ auto STD()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = (op.simm16 & ~3) + (op.ra ? ppu.gpr[op.ra] : 0);
|
||||
vm::write64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
PPU_WRITE_64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
}
|
||||
|
@ -6128,7 +6159,7 @@ auto STDU()
|
|||
|
||||
static const auto exec = [](ppu_thread& ppu, ppu_opcode_t op) {
|
||||
const u64 addr = ppu.gpr[op.ra] + (op.simm16 & ~3);
|
||||
vm::write64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
PPU_WRITE_64(vm::cast(addr), ppu.gpr[op.rs]);
|
||||
ppu.gpr[op.ra] = addr;
|
||||
};
|
||||
RETURN_(ppu, op);
|
||||
|
|
|
@ -8,6 +8,17 @@
|
|||
|
||||
#include "util/to_endian.hpp"
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
#include "rpcs3qt/breakpoint_handler.h"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
LOG_CHANNEL(debugbp_log, "DebugBP");
|
||||
|
||||
class ppu_thread;
|
||||
|
||||
void ppubreak(ppu_thread& ppu);
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class shm;
|
||||
|
@ -242,9 +253,21 @@ namespace vm
|
|||
return g_base_addr[addr];
|
||||
}
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
inline void write8(u32 addr, u8 value, ppu_thread* ppu = nullptr)
|
||||
#else
|
||||
inline void write8(u32 addr, u8 value)
|
||||
#endif
|
||||
{
|
||||
g_base_addr[addr] = value;
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
|
||||
{
|
||||
debugbp_log.success("BPMW: breakpoint writing(8) 0x%x at 0x%x", value, addr);
|
||||
ppubreak(*ppu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read or write virtual memory in a safe manner, returns false on failure
|
||||
|
@ -276,9 +299,21 @@ namespace vm
|
|||
return _ref<u16>(addr);
|
||||
}
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
inline void write16(u32 addr, be_t<u16> value, ppu_thread* ppu = nullptr)
|
||||
#else
|
||||
inline void write16(u32 addr, be_t<u16> value)
|
||||
#endif
|
||||
{
|
||||
_ref<u16>(addr) = value;
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
|
||||
{
|
||||
debugbp_log.success("BPMW: breakpoint writing(16) 0x%x at 0x%x", value, addr);
|
||||
ppubreak(*ppu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const be_t<u32>& read32(u32 addr)
|
||||
|
@ -286,9 +321,21 @@ namespace vm
|
|||
return _ref<u32>(addr);
|
||||
}
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
inline void write32(u32 addr, be_t<u32> value, ppu_thread* ppu = nullptr)
|
||||
#else
|
||||
inline void write32(u32 addr, be_t<u32> value)
|
||||
#endif
|
||||
{
|
||||
_ref<u32>(addr) = value;
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
|
||||
{
|
||||
debugbp_log.success("BPMW: breakpoint writing(32) 0x%x at 0x%x", value, addr);
|
||||
ppubreak(*ppu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
inline const be_t<u64>& read64(u32 addr)
|
||||
|
@ -296,9 +343,21 @@ namespace vm
|
|||
return _ref<u64>(addr);
|
||||
}
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
inline void write64(u32 addr, be_t<u64> value, ppu_thread* ppu = nullptr)
|
||||
#else
|
||||
inline void write64(u32 addr, be_t<u64> value)
|
||||
#endif
|
||||
{
|
||||
_ref<u64>(addr) = value;
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
if (ppu && g_breakpoint_handler.HasBreakpoint(addr, breakpoint_types::bp_write))
|
||||
{
|
||||
debugbp_log.success("BPMW: breakpoint writing(64) 0x%x at 0x%x", value, addr);
|
||||
ppubreak(*ppu);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void init();
|
||||
|
|
|
@ -232,6 +232,9 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Debug\moc_custom_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_debugger_add_bp_window.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_debugger_frame.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
@ -511,6 +514,9 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Release\moc_custom_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_debugger_add_bp_window.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_debugger_frame.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
@ -845,6 +851,7 @@
|
|||
<ClCompile Include="headless_application.cpp" />
|
||||
<ClCompile Include="rpcs3qt\auto_pause_settings_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\cg_disasm_window.cpp" />
|
||||
<ClCompile Include="rpcs3qt\debugger_add_bp_window.cpp" />
|
||||
<ClCompile Include="rpcs3qt\debugger_frame.cpp" />
|
||||
<ClCompile Include="rpcs3qt\emu_settings.cpp" />
|
||||
<ClCompile Include="rpcs3qt\game_list_frame.cpp" />
|
||||
|
@ -960,6 +967,16 @@
|
|||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\debugger_add_bp_window.h">
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing debugger_add_bp_window.h...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing debugger_add_bp_window.h...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath);$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\debugger_frame.h">
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing debugger_frame.h...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
|
|
|
@ -213,6 +213,12 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Release\moc_cg_disasm_window.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_debugger_add_bp_window.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_debugger_add_bp_window.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_debugger_frame.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
|
@ -396,6 +402,9 @@
|
|||
<ClCompile Include="rpcs3qt\cg_disasm_window.cpp">
|
||||
<Filter>Gui\dev tools</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\debugger_add_bp_window.cpp">
|
||||
<Filter>Gui\debugger</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\debugger_frame.cpp">
|
||||
<Filter>Gui\debugger</Filter>
|
||||
</ClCompile>
|
||||
|
|
|
@ -13,6 +13,7 @@ add_library(rpcs3_ui STATIC
|
|||
curl_handle.cpp
|
||||
custom_dialog.cpp
|
||||
custom_table_widget_item.cpp
|
||||
debugger_add_bp_window.cpp
|
||||
debugger_frame.cpp
|
||||
debugger_list.cpp
|
||||
downloader.cpp
|
||||
|
@ -128,6 +129,10 @@ add_library(rpcs3_ui STATIC
|
|||
"../resources.qrc"
|
||||
)
|
||||
|
||||
if(HAS_MEMORY_BREAKPOINTS)
|
||||
target_compile_definitions(rpcs3_ui PRIVATE RPCS3_HAS_MEMORY_BREAKPOINTS)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(rpcs3_ui PUBLIC "../windows.qrc")
|
||||
target_compile_definitions(rpcs3_ui PRIVATE UNICODE _UNICODE)
|
||||
|
|
|
@ -2,29 +2,53 @@
|
|||
|
||||
extern bool ppu_breakpoint(u32 loc, bool is_adding);
|
||||
|
||||
bool breakpoint_handler::HasBreakpoint(u32 loc) const
|
||||
bool breakpoint_handler::IsBreakOnBPM() const
|
||||
{
|
||||
return m_breakpoints.contains(loc);
|
||||
return m_break_on_bpm;
|
||||
}
|
||||
|
||||
bool breakpoint_handler::AddBreakpoint(u32 loc)
|
||||
void breakpoint_handler::SetBreakOnBPM(bool break_on_bpm)
|
||||
{
|
||||
if (!ppu_breakpoint(loc, true))
|
||||
m_break_on_bpm = break_on_bpm;
|
||||
}
|
||||
|
||||
bool breakpoint_handler::HasBreakpoint(u32 loc, bs_t<breakpoint_types> type)
|
||||
{
|
||||
std::lock_guard lock(mutex_breakpoints);
|
||||
|
||||
return m_breakpoints.contains(loc) && ((m_breakpoints.at(loc) & type) == type);
|
||||
}
|
||||
|
||||
bool breakpoint_handler::AddBreakpoint(u32 loc, bs_t<breakpoint_types> type)
|
||||
{
|
||||
std::lock_guard lock(mutex_breakpoints);
|
||||
|
||||
if ((type & breakpoint_types::bp_exec) && !ppu_breakpoint(loc, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ensure(m_breakpoints.insert(loc).second);
|
||||
return true;
|
||||
return m_breakpoints.insert({loc, type}).second;
|
||||
}
|
||||
|
||||
bool breakpoint_handler::RemoveBreakpoint(u32 loc)
|
||||
{
|
||||
std::lock_guard lock(mutex_breakpoints);
|
||||
|
||||
bs_t<breakpoint_types> bp_type{};
|
||||
if (m_breakpoints.contains(loc))
|
||||
{
|
||||
bp_type = m_breakpoints.at(loc);
|
||||
}
|
||||
|
||||
if (m_breakpoints.erase(loc) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ensure(ppu_breakpoint(loc, false));
|
||||
if (bp_type & breakpoint_types::bp_exec)
|
||||
{
|
||||
ensure(ppu_breakpoint(loc, false));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include <set>
|
||||
#include "Utilities/bit_set.h"
|
||||
#include <map>
|
||||
#include "Utilities/mutex.h"
|
||||
|
||||
enum class breakpoint_types
|
||||
{
|
||||
bp_read = 0x1,
|
||||
bp_write = 0x2,
|
||||
bp_exec = 0x4,
|
||||
__bitset_enum_max
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -20,16 +23,19 @@ public:
|
|||
breakpoint_handler() = default;
|
||||
~breakpoint_handler() = default;
|
||||
|
||||
bool IsBreakOnBPM() const;
|
||||
void SetBreakOnBPM(bool break_on_bpm);
|
||||
|
||||
/**
|
||||
* Returns true iff breakpoint exists at loc.
|
||||
* TODO: Add arg for flags, gameid, and maybe even thread if it should be thread local breakpoint.... breakpoint struct is probably what'll happen
|
||||
*/
|
||||
bool HasBreakpoint(u32 loc) const;
|
||||
bool HasBreakpoint(u32 loc, bs_t<breakpoint_types> type);
|
||||
|
||||
/**
|
||||
* Returns true if added successfully. TODO: flags
|
||||
*/
|
||||
bool AddBreakpoint(u32 loc);
|
||||
bool AddBreakpoint(u32 loc, bs_t<breakpoint_types> type);
|
||||
|
||||
/**
|
||||
* Returns true if removed breakpoint at loc successfully.
|
||||
|
@ -39,5 +45,9 @@ public:
|
|||
private:
|
||||
// TODO : generalize to hold multiple games and handle flags.Probably do : std::map<std::string (gameid), std::set<breakpoint>>.
|
||||
// Although, externally, they'll only be accessed by loc (I think) so a map of maps may also do?
|
||||
std::set<u32> m_breakpoints; //! Holds all breakpoints.
|
||||
shared_mutex mutex_breakpoints;
|
||||
std::map<u32, bs_t<breakpoint_types>> m_breakpoints; //! Holds all breakpoints.
|
||||
bool m_break_on_bpm = false;
|
||||
};
|
||||
|
||||
extern breakpoint_handler g_breakpoint_handler;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Emu/CPU/CPUDisAsm.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "rpcs3qt/debugger_add_bp_window.h"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
|
@ -72,19 +73,35 @@ void breakpoint_list::RemoveBreakpoint(u32 addr)
|
|||
}
|
||||
}
|
||||
|
||||
bool breakpoint_list::AddBreakpoint(u32 pc)
|
||||
bool breakpoint_list::AddBreakpoint(u32 pc, bs_t<breakpoint_types> type)
|
||||
{
|
||||
if (!m_ppu_breakpoint_handler->AddBreakpoint(pc))
|
||||
if (!m_ppu_breakpoint_handler->AddBreakpoint(pc, type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_disasm->disasm(pc);
|
||||
QString breakpoint_item_text;
|
||||
|
||||
QString text = QString::fromStdString(m_disasm->last_opcode);
|
||||
text.remove(10, 13);
|
||||
if (type == breakpoint_types::bp_exec)
|
||||
{
|
||||
m_disasm->disasm(m_disasm->dump_pc = pc);
|
||||
breakpoint_item_text = QString::fromStdString(m_disasm->last_opcode);
|
||||
breakpoint_item_text.remove(10, 13);
|
||||
}
|
||||
else if (type == breakpoint_types::bp_read)
|
||||
{
|
||||
breakpoint_item_text = QString("BPMR: 0x%1").arg(pc, 8, 16, QChar('0'));
|
||||
}
|
||||
else if (type == breakpoint_types::bp_write)
|
||||
{
|
||||
breakpoint_item_text = QString("BPMW: 0x%1").arg(pc, 8, 16, QChar('0'));
|
||||
}
|
||||
else if (type == (breakpoint_types::bp_read + breakpoint_types::bp_write))
|
||||
{
|
||||
breakpoint_item_text = QString("BPMRW: 0x%1").arg(pc, 8, 16, QChar('0'));
|
||||
}
|
||||
|
||||
QListWidgetItem* breakpoint_item = new QListWidgetItem(text);
|
||||
QListWidgetItem* breakpoint_item = new QListWidgetItem(breakpoint_item_text);
|
||||
breakpoint_item->setForeground(m_text_color_bp);
|
||||
breakpoint_item->setBackground(m_color_bp);
|
||||
breakpoint_item->setData(Qt::UserRole, pc);
|
||||
|
@ -96,8 +113,8 @@ bool breakpoint_list::AddBreakpoint(u32 pc)
|
|||
}
|
||||
|
||||
/**
|
||||
* If breakpoint exists, we remove it, else add new one. Yeah, it'd be nicer from a code logic to have it be set/reset. But, that logic has to happen somewhere anyhow.
|
||||
*/
|
||||
* If breakpoint exists, we remove it, else add new one. Yeah, it'd be nicer from a code logic to have it be set/reset. But, that logic has to happen somewhere anyhow.
|
||||
*/
|
||||
void breakpoint_list::HandleBreakpointRequest(u32 loc, bool only_add)
|
||||
{
|
||||
const auto cpu = m_disasm ? m_disasm->get_cpu() : nullptr;
|
||||
|
@ -168,7 +185,7 @@ void breakpoint_list::HandleBreakpointRequest(u32 loc, bool only_add)
|
|||
return;
|
||||
}
|
||||
|
||||
if (m_ppu_breakpoint_handler->HasBreakpoint(loc))
|
||||
if (m_ppu_breakpoint_handler->HasBreakpoint(loc, breakpoint_types::bp_exec))
|
||||
{
|
||||
if (!only_add)
|
||||
{
|
||||
|
@ -177,7 +194,7 @@ void breakpoint_list::HandleBreakpointRequest(u32 loc, bool only_add)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (!AddBreakpoint(loc))
|
||||
if (!AddBreakpoint(loc, breakpoint_types::bp_exec))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Unknown error while setting breakpoint!"), tr("Failed to set breakpoints."));
|
||||
return;
|
||||
|
@ -194,13 +211,8 @@ void breakpoint_list::OnBreakpointListDoubleClicked()
|
|||
}
|
||||
}
|
||||
|
||||
void breakpoint_list::OnBreakpointListRightClicked(const QPoint &pos)
|
||||
void breakpoint_list::OnBreakpointListRightClicked(const QPoint& pos)
|
||||
{
|
||||
if (!itemAt(pos))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_context_menu = new QMenu();
|
||||
|
||||
if (selectedItems().count() == 1)
|
||||
|
@ -215,7 +227,28 @@ void breakpoint_list::OnBreakpointListRightClicked(const QPoint &pos)
|
|||
m_context_menu->addSeparator();
|
||||
}
|
||||
|
||||
m_context_menu->addAction(m_delete_action);
|
||||
if (selectedItems().count() >= 1)
|
||||
{
|
||||
m_context_menu->addAction(m_delete_action);
|
||||
}
|
||||
|
||||
QAction* m_addbp = new QAction(tr("Add Breakpoint"), this);
|
||||
connect(m_addbp, &QAction::triggered, this, [this]
|
||||
{
|
||||
debugger_add_bp_window dlg(this, this);
|
||||
dlg.exec();
|
||||
});
|
||||
m_context_menu->addAction(m_addbp);
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
QAction* m_tglbpmbreak = new QAction(m_ppu_breakpoint_handler->IsBreakOnBPM() ? tr("Disable BPM") : tr("Enable BPM"), this);
|
||||
connect(m_tglbpmbreak, &QAction::triggered, [this]
|
||||
{
|
||||
m_ppu_breakpoint_handler->SetBreakOnBPM(!m_ppu_breakpoint_handler->IsBreakOnBPM());
|
||||
});
|
||||
m_context_menu->addAction(m_tglbpmbreak);
|
||||
#endif
|
||||
|
||||
m_context_menu->exec(viewport()->mapToGlobal(pos));
|
||||
m_context_menu->deleteLater();
|
||||
m_context_menu = nullptr;
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
#include <QListWidget>
|
||||
|
||||
#include "breakpoint_handler.h"
|
||||
|
||||
class CPUDisAsm;
|
||||
class cpu_thread;
|
||||
class breakpoint_handler;
|
||||
|
||||
class breakpoint_list : public QListWidget
|
||||
{
|
||||
|
@ -16,8 +17,8 @@ public:
|
|||
breakpoint_list(QWidget* parent, breakpoint_handler* handler);
|
||||
void UpdateCPUData(std::shared_ptr<CPUDisAsm> disasm);
|
||||
void ClearBreakpoints();
|
||||
bool AddBreakpoint(u32 pc);
|
||||
void RemoveBreakpoint(u32 addr);
|
||||
bool AddBreakpoint(u32 pc, bs_t<breakpoint_types> type);
|
||||
|
||||
QColor m_text_color_bp;
|
||||
QColor m_color_bp;
|
||||
|
@ -36,7 +37,7 @@ private Q_SLOTS:
|
|||
void OnBreakpointListDelete();
|
||||
|
||||
private:
|
||||
breakpoint_handler* m_ppu_breakpoint_handler;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler = nullptr;
|
||||
QMenu* m_context_menu = nullptr;
|
||||
QAction* m_delete_action;
|
||||
std::shared_ptr<CPUDisAsm> m_disasm = nullptr;
|
||||
|
|
116
rpcs3/rpcs3qt/debugger_add_bp_window.cpp
Normal file
116
rpcs3/rpcs3qt/debugger_add_bp_window.cpp
Normal file
|
@ -0,0 +1,116 @@
|
|||
#include "debugger_add_bp_window.h"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Utilities/StrUtil.h"
|
||||
#include "breakpoint_handler.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLineEdit>
|
||||
#include <QLabel>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QMessageBox>
|
||||
|
||||
debugger_add_bp_window::debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
ensure(bp_list);
|
||||
|
||||
setWindowTitle(tr("Add a breakpoint"));
|
||||
setModal(true);
|
||||
|
||||
QVBoxLayout* vbox_panel = new QVBoxLayout();
|
||||
|
||||
QHBoxLayout* hbox_top = new QHBoxLayout();
|
||||
QLabel* l_address = new QLabel(tr("Address"));
|
||||
QLineEdit* t_address = new QLineEdit();
|
||||
t_address->setPlaceholderText(tr("Address here"));
|
||||
t_address->setFocus();
|
||||
|
||||
hbox_top->addWidget(l_address);
|
||||
hbox_top->addWidget(t_address);
|
||||
vbox_panel->addLayout(hbox_top);
|
||||
|
||||
QHBoxLayout* hbox_bot = new QHBoxLayout();
|
||||
QComboBox* co_bptype = new QComboBox(this);
|
||||
QStringList qstr_breakpoint_types;
|
||||
|
||||
qstr_breakpoint_types
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
<< tr("Memory Read")
|
||||
<< tr("Memory Write")
|
||||
<< tr("Memory Read&Write")
|
||||
#endif
|
||||
<< tr("Execution");
|
||||
|
||||
co_bptype->addItems(qstr_breakpoint_types);
|
||||
|
||||
hbox_bot->addWidget(co_bptype);
|
||||
vbox_panel->addLayout(hbox_bot);
|
||||
|
||||
QHBoxLayout* hbox_buttons = new QHBoxLayout();
|
||||
QDialogButtonBox* button_box = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
|
||||
button_box->button(QDialogButtonBox::Ok)->setText(tr("Add"));
|
||||
|
||||
hbox_buttons->addWidget(button_box);
|
||||
vbox_panel->addLayout(hbox_buttons);
|
||||
|
||||
setLayout(vbox_panel);
|
||||
|
||||
connect(button_box, &QDialogButtonBox::accepted, this, [=, this]
|
||||
{
|
||||
const std::string str_address = t_address->text().toStdString();
|
||||
|
||||
if (str_address.empty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Add BP error"), tr("Address is empty!"));
|
||||
return;
|
||||
}
|
||||
|
||||
// We always want hex
|
||||
const std::string parsed_string = (!str_address.starts_with("0x") && !str_address.starts_with("0X")) ? fmt::format("0x%s", str_address) : str_address;
|
||||
u64 parsed_address = 0;
|
||||
|
||||
// We don't accept 0
|
||||
if (!try_to_uint64(&parsed_address, parsed_string, 1, 0xFF'FF'FF'FF))
|
||||
{
|
||||
QMessageBox::warning(this, tr("Add BP error"), tr("Address is invalid!"));
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 address = static_cast<u32>(parsed_address);
|
||||
bs_t<breakpoint_types> bp_t{};
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
switch (co_bptype->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
bp_t = breakpoint_types::bp_read;
|
||||
break;
|
||||
case 1:
|
||||
bp_t = breakpoint_types::bp_write;
|
||||
break;
|
||||
case 2:
|
||||
bp_t = breakpoint_types::bp_read + breakpoint_types::bp_write;
|
||||
break;
|
||||
case 3:
|
||||
bp_t = breakpoint_types::bp_exec;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#else
|
||||
bp_t = breakpoint_types::bp_exec;
|
||||
#endif
|
||||
|
||||
if (bp_t)
|
||||
bp_list->AddBreakpoint(address, bp_t);
|
||||
|
||||
QDialog::accept();
|
||||
});
|
||||
|
||||
connect(button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
|
||||
move(QCursor::pos());
|
||||
}
|
13
rpcs3/rpcs3qt/debugger_add_bp_window.h
Normal file
13
rpcs3/rpcs3qt/debugger_add_bp_window.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "breakpoint_list.h"
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class debugger_add_bp_window : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit debugger_add_bp_window(breakpoint_list* bp_list, QWidget* parent = nullptr);
|
||||
};
|
|
@ -34,6 +34,7 @@
|
|||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
#include "rpcs3qt/debugger_add_bp_window.h"
|
||||
#include "util/asm.hpp"
|
||||
|
||||
constexpr auto qstr = QString::fromStdString;
|
||||
|
@ -43,6 +44,9 @@ constexpr auto s_pause_flags = cpu_flag::dbg_pause + cpu_flag::dbg_global_pause;
|
|||
extern atomic_t<bool> g_debugger_pause_all_threads_on_bp;
|
||||
|
||||
extern const ppu_decoder<ppu_itype> g_ppu_itype;
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
breakpoint_handler g_breakpoint_handler = breakpoint_handler();
|
||||
#endif
|
||||
|
||||
extern bool is_using_interpreter(thread_class t_class);
|
||||
|
||||
|
@ -66,7 +70,12 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidg
|
|||
QHBoxLayout* hbox_b_main = new QHBoxLayout();
|
||||
hbox_b_main->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
#ifdef RPCS3_HAS_MEMORY_BREAKPOINTS
|
||||
m_ppu_breakpoint_handler = &g_breakpoint_handler;
|
||||
#else
|
||||
m_ppu_breakpoint_handler = new breakpoint_handler();
|
||||
#endif
|
||||
|
||||
m_breakpoint_list = new breakpoint_list(this, m_ppu_breakpoint_handler);
|
||||
|
||||
m_debugger_list = new debugger_list(this, m_gui_settings, m_ppu_breakpoint_handler);
|
||||
|
@ -90,6 +99,7 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidg
|
|||
m_go_to_pc = new QPushButton(tr("Go To PC"), this);
|
||||
m_btn_step = new QPushButton(tr("Step"), this);
|
||||
m_btn_step_over = new QPushButton(tr("Step Over"), this);
|
||||
m_btn_add_bp = new QPushButton(tr("Add BP"), this);
|
||||
m_btn_run = new QPushButton(RunString, this);
|
||||
|
||||
EnableButtons(false);
|
||||
|
@ -100,6 +110,7 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidg
|
|||
hbox_b_main->addWidget(m_go_to_pc);
|
||||
hbox_b_main->addWidget(m_btn_step);
|
||||
hbox_b_main->addWidget(m_btn_step_over);
|
||||
hbox_b_main->addWidget(m_btn_add_bp);
|
||||
hbox_b_main->addWidget(m_btn_run);
|
||||
hbox_b_main->addWidget(m_choice_units);
|
||||
hbox_b_main->addStretch();
|
||||
|
@ -152,6 +163,12 @@ debugger_frame::debugger_frame(std::shared_ptr<gui_settings> gui_settings, QWidg
|
|||
connect(m_btn_step, &QAbstractButton::clicked, this, &debugger_frame::DoStep);
|
||||
connect(m_btn_step_over, &QAbstractButton::clicked, [this]() { DoStep(true); });
|
||||
|
||||
connect(m_btn_add_bp, &QAbstractButton::clicked, this, [this]
|
||||
{
|
||||
debugger_add_bp_window dlg(m_breakpoint_list, this);
|
||||
dlg.exec();
|
||||
});
|
||||
|
||||
connect(m_btn_run, &QAbstractButton::clicked, this, &debugger_frame::RunBtnPress);
|
||||
|
||||
connect(m_choice_units->lineEdit(), &QLineEdit::editingFinished, [&]
|
||||
|
@ -1588,7 +1605,7 @@ void debugger_frame::DoStep(bool step_over)
|
|||
|
||||
// Set breakpoint on next instruction
|
||||
const u32 next_instruction_pc = current_instruction_pc + 4;
|
||||
m_ppu_breakpoint_handler->AddBreakpoint(next_instruction_pc);
|
||||
m_ppu_breakpoint_handler->AddBreakpoint(next_instruction_pc, breakpoint_types::bp_exec);
|
||||
|
||||
// Undefine previous step over breakpoint if it hasn't been already
|
||||
// This can happen when the user steps over a branch that doesn't return to itself
|
||||
|
@ -1680,6 +1697,7 @@ void debugger_frame::EnableButtons(bool enable)
|
|||
|
||||
m_go_to_addr->setEnabled(enable);
|
||||
m_go_to_pc->setEnabled(enable);
|
||||
m_btn_add_bp->setEnabled(enable);
|
||||
m_btn_step->setEnabled(step);
|
||||
m_btn_step_over->setEnabled(step);
|
||||
m_btn_run->setEnabled(enable);
|
||||
|
|
|
@ -46,20 +46,21 @@ class debugger_frame : public custom_dock_widget
|
|||
const QString RunString = tr("Run");
|
||||
const QString PauseString = tr("Pause");
|
||||
|
||||
debugger_list* m_debugger_list;
|
||||
QSplitter* m_right_splitter;
|
||||
debugger_list* m_debugger_list = nullptr;
|
||||
QSplitter* m_right_splitter = nullptr;
|
||||
QFont m_mono;
|
||||
QPlainTextEdit* m_misc_state;
|
||||
QPlainTextEdit* m_regs;
|
||||
QPushButton* m_go_to_addr;
|
||||
QPushButton* m_go_to_pc;
|
||||
QPushButton* m_btn_step;
|
||||
QPushButton* m_btn_step_over;
|
||||
QPushButton* m_btn_run;
|
||||
QPlainTextEdit* m_misc_state = nullptr;
|
||||
QPlainTextEdit* m_regs = nullptr;
|
||||
QPushButton* m_go_to_addr = nullptr;
|
||||
QPushButton* m_go_to_pc = nullptr;
|
||||
QPushButton* m_btn_step = nullptr;
|
||||
QPushButton* m_btn_step_over = nullptr;
|
||||
QPushButton* m_btn_add_bp = nullptr;
|
||||
QPushButton* m_btn_run = nullptr;
|
||||
|
||||
QComboBox* m_choice_units;
|
||||
QTimer* m_update;
|
||||
QSplitter* m_splitter;
|
||||
QComboBox* m_choice_units = nullptr;
|
||||
QTimer* m_update = nullptr;
|
||||
QSplitter* m_splitter = nullptr;
|
||||
|
||||
u64 m_threads_created = -1;
|
||||
u64 m_threads_deleted = -1;
|
||||
|
@ -83,9 +84,9 @@ class debugger_frame : public custom_dock_widget
|
|||
u32 m_spu_disasm_pc = 0;
|
||||
bool m_is_spu_disasm_mode = false;
|
||||
|
||||
breakpoint_list* m_breakpoint_list;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler;
|
||||
call_stack_list* m_call_stack_list;
|
||||
breakpoint_list* m_breakpoint_list = nullptr;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler = nullptr;
|
||||
call_stack_list* m_call_stack_list = nullptr;
|
||||
instruction_editor_dialog* m_inst_editor = nullptr;
|
||||
register_editor_dialog* m_reg_editor = nullptr;
|
||||
QDialog* m_goto_dialog = nullptr;
|
||||
|
|
|
@ -157,7 +157,7 @@ void debugger_list::ShowAddress(u32 addr, bool select_addr, bool direct)
|
|||
{
|
||||
case thread_class::ppu:
|
||||
{
|
||||
return m_ppu_breakpoint_handler->HasBreakpoint(pc);
|
||||
return m_ppu_breakpoint_handler->HasBreakpoint(pc, breakpoint_types::bp_exec);
|
||||
}
|
||||
case thread_class::spu:
|
||||
{
|
||||
|
|
|
@ -56,7 +56,7 @@ private:
|
|||
|
||||
std::shared_ptr<gui_settings> m_gui_settings;
|
||||
|
||||
breakpoint_handler* m_ppu_breakpoint_handler;
|
||||
breakpoint_handler* m_ppu_breakpoint_handler = nullptr;
|
||||
cpu_thread* m_cpu = nullptr;
|
||||
std::shared_ptr<CPUDisAsm> m_disasm;
|
||||
QDialog* m_cmd_detail = nullptr;
|
||||
|
|
Loading…
Add table
Reference in a new issue