mirror of
https://github.com/cemu-project/Cemu.git
synced 2024-11-25 02:26:54 +01:00
GDBStub: Support watchpoints on linux (#1030)
* GDBStub: Support watchpoints on linux * GDBStub: Use `TCP_NODELAY`
This commit is contained in:
parent
bab1616565
commit
4405116324
@ -40,6 +40,7 @@ add_library(CemuCafe
|
||||
HW/Espresso/Debugger/DebugSymbolStorage.h
|
||||
HW/Espresso/Debugger/GDBStub.h
|
||||
HW/Espresso/Debugger/GDBStub.cpp
|
||||
HW/Espresso/Debugger/GDBBreakpoints.cpp
|
||||
HW/Espresso/Debugger/GDBBreakpoints.h
|
||||
HW/Espresso/EspressoISA.h
|
||||
HW/Espresso/Interpreter/PPCInterpreterALU.hpp
|
||||
|
304
src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp
Normal file
304
src/Cafe/HW/Espresso/Debugger/GDBBreakpoints.cpp
Normal file
@ -0,0 +1,304 @@
|
||||
#include "GDBBreakpoints.h"
|
||||
#include "Debugger.h"
|
||||
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
||||
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_LINUX
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/user.h>
|
||||
|
||||
DRType _GetDR(pid_t tid, int drIndex)
|
||||
{
|
||||
size_t drOffset = offsetof(struct user, u_debugreg) + drIndex * sizeof(user::u_debugreg[0]);
|
||||
|
||||
long v;
|
||||
v = ptrace(PTRACE_PEEKUSER, tid, drOffset, nullptr);
|
||||
if (v == -1)
|
||||
perror("ptrace(PTRACE_PEEKUSER)");
|
||||
|
||||
return (DRType)v;
|
||||
}
|
||||
|
||||
void _SetDR(pid_t tid, int drIndex, DRType newValue)
|
||||
{
|
||||
size_t drOffset = offsetof(struct user, u_debugreg) + drIndex * sizeof(user::u_debugreg[0]);
|
||||
|
||||
long rc = ptrace(PTRACE_POKEUSER, tid, drOffset, newValue);
|
||||
if (rc == -1)
|
||||
perror("ptrace(PTRACE_POKEUSER)");
|
||||
}
|
||||
|
||||
DRType _ReadDR6()
|
||||
{
|
||||
pid_t tid = gettid();
|
||||
|
||||
// linux doesn't let us attach to the current thread / threads in the current thread group
|
||||
// we have to create a child process which then modifies the debug registers and quits
|
||||
pid_t child = fork();
|
||||
if (child == -1)
|
||||
{
|
||||
perror("fork");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (child == 0)
|
||||
{
|
||||
if (ptrace(PTRACE_ATTACH, tid, nullptr, nullptr))
|
||||
{
|
||||
perror("attach");
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
waitpid(tid, NULL, 0);
|
||||
|
||||
uint64_t dr6 = _GetDR(tid, 6);
|
||||
|
||||
if (ptrace(PTRACE_DETACH, tid, nullptr, nullptr))
|
||||
perror("detach");
|
||||
|
||||
// since the status code only uses the lower 8 bits, we have to discard the rest of DR6
|
||||
// this should be fine though, since the lower 4 bits of DR6 contain all the bp conditions
|
||||
_exit(dr6 & 0xff);
|
||||
}
|
||||
|
||||
// wait for child process
|
||||
int wstatus;
|
||||
waitpid(child, &wstatus, 0);
|
||||
|
||||
return (DRType)WEXITSTATUS(wstatus);
|
||||
}
|
||||
#endif
|
||||
|
||||
GDBServer::ExecutionBreakpoint::ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason)
|
||||
: m_address(address), m_removedAfterInterrupt(false), m_reason(std::move(reason))
|
||||
{
|
||||
if (type == BreakpointType::BP_SINGLE)
|
||||
{
|
||||
this->m_pauseThreads = true;
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
this->m_deleteAfterAnyInterrupt = false;
|
||||
this->m_pauseOnNextInterrupt = false;
|
||||
this->m_visible = visible;
|
||||
}
|
||||
else if (type == BreakpointType::BP_PERSISTENT)
|
||||
{
|
||||
this->m_pauseThreads = true;
|
||||
this->m_restoreAfterInterrupt = true;
|
||||
this->m_deleteAfterAnyInterrupt = false;
|
||||
this->m_pauseOnNextInterrupt = false;
|
||||
this->m_visible = visible;
|
||||
}
|
||||
else if (type == BreakpointType::BP_RESTORE_POINT)
|
||||
{
|
||||
this->m_pauseThreads = false;
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
this->m_deleteAfterAnyInterrupt = false;
|
||||
this->m_pauseOnNextInterrupt = false;
|
||||
this->m_visible = false;
|
||||
}
|
||||
else if (type == BreakpointType::BP_STEP_POINT)
|
||||
{
|
||||
this->m_pauseThreads = false;
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
this->m_deleteAfterAnyInterrupt = true;
|
||||
this->m_pauseOnNextInterrupt = true;
|
||||
this->m_visible = false;
|
||||
}
|
||||
|
||||
this->m_origOpCode = memory_readU32(address);
|
||||
memory_writeU32(address, DEBUGGER_BP_T_GDBSTUB_TW);
|
||||
PPCRecompiler_invalidateRange(address, address + 4);
|
||||
}
|
||||
|
||||
GDBServer::ExecutionBreakpoint::~ExecutionBreakpoint()
|
||||
{
|
||||
memory_writeU32(this->m_address, this->m_origOpCode);
|
||||
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
|
||||
}
|
||||
|
||||
uint32 GDBServer::ExecutionBreakpoint::GetVisibleOpCode() const
|
||||
{
|
||||
if (this->m_visible)
|
||||
return memory_readU32(this->m_address);
|
||||
else
|
||||
return this->m_origOpCode;
|
||||
}
|
||||
|
||||
void GDBServer::ExecutionBreakpoint::RemoveTemporarily()
|
||||
{
|
||||
memory_writeU32(this->m_address, this->m_origOpCode);
|
||||
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
|
||||
this->m_restoreAfterInterrupt = true;
|
||||
}
|
||||
|
||||
void GDBServer::ExecutionBreakpoint::Restore()
|
||||
{
|
||||
memory_writeU32(this->m_address, DEBUGGER_BP_T_GDBSTUB_TW);
|
||||
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
}
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
#if BOOST_OS_LINUX
|
||||
std::vector<pid_t>& OSGetSchedulerThreadIds();
|
||||
#endif
|
||||
|
||||
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
|
||||
}
|
||||
|
||||
GDBServer::AccessBreakpoint::AccessBreakpoint(MPTR address, AccessPointType type)
|
||||
: m_address(address), m_type(type)
|
||||
{
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
|
||||
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
|
||||
{
|
||||
HANDLE hThread = (HANDLE)hThreadNH;
|
||||
CONTEXT ctx{};
|
||||
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
SuspendThread(hThread);
|
||||
GetThreadContext(hThread, &ctx);
|
||||
|
||||
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
|
||||
ctx.Dr2 = (DWORD64)memory_getPointerFromVirtualOffset(address);
|
||||
ctx.Dr3 = (DWORD64)memory_getPointerFromVirtualOffset(address);
|
||||
// breakpoint 2
|
||||
SetBits(ctx.Dr7, 4, 1, 1); // breakpoint #3 enabled: true
|
||||
SetBits(ctx.Dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
|
||||
SetBits(ctx.Dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
|
||||
// breakpoint 3
|
||||
SetBits(ctx.Dr7, 6, 1, 1); // breakpoint #4 enabled: true
|
||||
SetBits(ctx.Dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
|
||||
SetBits(ctx.Dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
|
||||
|
||||
SetThreadContext(hThread, &ctx);
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX
|
||||
// linux doesn't let us attach to threads which are in the same thread group as our current thread
|
||||
// we have to create a child process which then modifies the debug registers and quits
|
||||
pid_t child = fork();
|
||||
if (child == -1)
|
||||
{
|
||||
perror("fork");
|
||||
return;
|
||||
}
|
||||
|
||||
if (child == 0)
|
||||
{
|
||||
for (pid_t tid : coreinit::OSGetSchedulerThreadIds())
|
||||
{
|
||||
long rc = ptrace(PTRACE_ATTACH, tid, nullptr, nullptr);
|
||||
if (rc == -1)
|
||||
perror("ptrace(PTRACE_ATTACH)");
|
||||
|
||||
waitpid(tid, nullptr, 0);
|
||||
|
||||
DRType dr7 = _GetDR(tid, 7);
|
||||
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
|
||||
DRType dr2 = (uint64)memory_getPointerFromVirtualOffset(address);
|
||||
DRType dr3 = (uint64)memory_getPointerFromVirtualOffset(address);
|
||||
// breakpoint 2
|
||||
SetBits(dr7, 4, 1, 1); // breakpoint #3 enabled: true
|
||||
SetBits(dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
|
||||
SetBits(dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
|
||||
// breakpoint 3
|
||||
SetBits(dr7, 6, 1, 1); // breakpoint #4 enabled: true
|
||||
SetBits(dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
|
||||
SetBits(dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
|
||||
|
||||
_SetDR(tid, 2, dr2);
|
||||
_SetDR(tid, 3, dr3);
|
||||
_SetDR(tid, 7, dr7);
|
||||
|
||||
rc = ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
|
||||
if (rc == -1)
|
||||
perror("ptrace(PTRACE_DETACH)");
|
||||
}
|
||||
|
||||
// exit child process
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
// wait for child process
|
||||
waitpid(child, nullptr, 0);
|
||||
#else
|
||||
cemuLog_log(LogType::Force, "Debugger read/write breakpoints are not supported on non-x86 CPUs yet.");
|
||||
#endif
|
||||
}
|
||||
|
||||
GDBServer::AccessBreakpoint::~AccessBreakpoint()
|
||||
{
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
|
||||
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
|
||||
{
|
||||
HANDLE hThread = (HANDLE)hThreadNH;
|
||||
CONTEXT ctx{};
|
||||
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
SuspendThread(hThread);
|
||||
GetThreadContext(hThread, &ctx);
|
||||
|
||||
// reset BP 2/3 to zero
|
||||
ctx.Dr2 = (DWORD64)0;
|
||||
ctx.Dr3 = (DWORD64)0;
|
||||
// breakpoint 2
|
||||
SetBits(ctx.Dr7, 4, 1, 0);
|
||||
SetBits(ctx.Dr7, 24, 2, 0);
|
||||
SetBits(ctx.Dr7, 26, 2, 0);
|
||||
// breakpoint 3
|
||||
SetBits(ctx.Dr7, 6, 1, 0);
|
||||
SetBits(ctx.Dr7, 28, 2, 0);
|
||||
SetBits(ctx.Dr7, 30, 2, 0);
|
||||
SetThreadContext(hThread, &ctx);
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX
|
||||
// linux doesn't let us attach to threads which are in the same thread group as our current thread
|
||||
// we have to create a child process which then modifies the debug registers and quits
|
||||
pid_t child = fork();
|
||||
if (child == -1)
|
||||
{
|
||||
perror("fork");
|
||||
return;
|
||||
}
|
||||
|
||||
if (child == 0)
|
||||
{
|
||||
for (pid_t tid : coreinit::OSGetSchedulerThreadIds())
|
||||
{
|
||||
long rc = ptrace(PTRACE_ATTACH, tid, nullptr, nullptr);
|
||||
if (rc == -1)
|
||||
perror("ptrace(PTRACE_ATTACH)");
|
||||
|
||||
waitpid(tid, nullptr, 0);
|
||||
|
||||
DRType dr7 = _GetDR(tid, 7);
|
||||
// reset BP 2/3 to zero
|
||||
DRType dr2 = 0;
|
||||
DRType dr3 = 0;
|
||||
// breakpoint 2
|
||||
SetBits(dr7, 4, 1, 0);
|
||||
SetBits(dr7, 24, 2, 0);
|
||||
SetBits(dr7, 26, 2, 0);
|
||||
// breakpoint 3
|
||||
SetBits(dr7, 6, 1, 0);
|
||||
SetBits(dr7, 28, 2, 0);
|
||||
SetBits(dr7, 30, 2, 0);
|
||||
|
||||
_SetDR(tid, 2, dr2);
|
||||
_SetDR(tid, 3, dr3);
|
||||
_SetDR(tid, 7, dr7);
|
||||
|
||||
rc = ptrace(PTRACE_DETACH, tid, nullptr, nullptr);
|
||||
if (rc == -1)
|
||||
perror("ptrace(PTRACE_DETACH)");
|
||||
}
|
||||
|
||||
// exit child process
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
// wait for child process
|
||||
waitpid(child, nullptr, 0);
|
||||
#endif
|
||||
}
|
@ -1,33 +1,18 @@
|
||||
#pragma once
|
||||
#include "GDBStub.h"
|
||||
#include <utility>
|
||||
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/user.h>
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_LINUX
|
||||
#include <sys/types.h>
|
||||
|
||||
// helpers for accessing debug register
|
||||
typedef unsigned long DRType;
|
||||
|
||||
DRType _GetDR(pid_t tid, int drIndex)
|
||||
{
|
||||
unsigned long v;
|
||||
v = ptrace (PTRACE_PEEKUSER, tid, offsetof (struct user, u_debugreg[drIndex]), 0);
|
||||
return (DRType)v;
|
||||
}
|
||||
|
||||
void _SetDR(pid_t tid, int drIndex, DRType newValue)
|
||||
{
|
||||
unsigned long v = newValue;
|
||||
ptrace (PTRACE_POKEUSER, tid, offsetof (struct user, u_debugreg[drIndex]), v);
|
||||
}
|
||||
|
||||
DRType _GetDR(pid_t tid, int drIndex);
|
||||
void _SetDR(pid_t tid, int drIndex, DRType newValue);
|
||||
DRType _ReadDR6();
|
||||
#endif
|
||||
|
||||
namespace coreinit
|
||||
{
|
||||
std::vector<std::thread::native_handle_type>& OSGetSchedulerThreads();
|
||||
}
|
||||
|
||||
enum class BreakpointType
|
||||
{
|
||||
BP_SINGLE,
|
||||
@ -38,59 +23,10 @@ enum class BreakpointType
|
||||
|
||||
class GDBServer::ExecutionBreakpoint {
|
||||
public:
|
||||
ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason)
|
||||
: m_address(address), m_removedAfterInterrupt(false), m_reason(std::move(reason))
|
||||
{
|
||||
if (type == BreakpointType::BP_SINGLE)
|
||||
{
|
||||
this->m_pauseThreads = true;
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
this->m_deleteAfterAnyInterrupt = false;
|
||||
this->m_pauseOnNextInterrupt = false;
|
||||
this->m_visible = visible;
|
||||
}
|
||||
else if (type == BreakpointType::BP_PERSISTENT)
|
||||
{
|
||||
this->m_pauseThreads = true;
|
||||
this->m_restoreAfterInterrupt = true;
|
||||
this->m_deleteAfterAnyInterrupt = false;
|
||||
this->m_pauseOnNextInterrupt = false;
|
||||
this->m_visible = visible;
|
||||
}
|
||||
else if (type == BreakpointType::BP_RESTORE_POINT)
|
||||
{
|
||||
this->m_pauseThreads = false;
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
this->m_deleteAfterAnyInterrupt = false;
|
||||
this->m_pauseOnNextInterrupt = false;
|
||||
this->m_visible = false;
|
||||
}
|
||||
else if (type == BreakpointType::BP_STEP_POINT)
|
||||
{
|
||||
this->m_pauseThreads = false;
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
this->m_deleteAfterAnyInterrupt = true;
|
||||
this->m_pauseOnNextInterrupt = true;
|
||||
this->m_visible = false;
|
||||
}
|
||||
ExecutionBreakpoint(MPTR address, BreakpointType type, bool visible, std::string reason);
|
||||
~ExecutionBreakpoint();
|
||||
|
||||
this->m_origOpCode = memory_readU32(address);
|
||||
memory_writeU32(address, DEBUGGER_BP_T_GDBSTUB_TW);
|
||||
PPCRecompiler_invalidateRange(address, address + 4);
|
||||
};
|
||||
~ExecutionBreakpoint()
|
||||
{
|
||||
memory_writeU32(this->m_address, this->m_origOpCode);
|
||||
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
|
||||
};
|
||||
|
||||
[[nodiscard]] uint32 GetVisibleOpCode() const
|
||||
{
|
||||
if (this->m_visible)
|
||||
return memory_readU32(this->m_address);
|
||||
else
|
||||
return this->m_origOpCode;
|
||||
};
|
||||
[[nodiscard]] uint32 GetVisibleOpCode() const;
|
||||
[[nodiscard]] bool ShouldBreakThreads() const
|
||||
{
|
||||
return this->m_pauseThreads;
|
||||
@ -118,18 +54,8 @@ public:
|
||||
return m_reason;
|
||||
};
|
||||
|
||||
void RemoveTemporarily()
|
||||
{
|
||||
memory_writeU32(this->m_address, this->m_origOpCode);
|
||||
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
|
||||
this->m_restoreAfterInterrupt = true;
|
||||
};
|
||||
void Restore()
|
||||
{
|
||||
memory_writeU32(this->m_address, DEBUGGER_BP_T_GDBSTUB_TW);
|
||||
PPCRecompiler_invalidateRange(this->m_address, this->m_address + 4);
|
||||
this->m_restoreAfterInterrupt = false;
|
||||
};
|
||||
void RemoveTemporarily();
|
||||
void Restore();
|
||||
void PauseOnNextInterrupt()
|
||||
{
|
||||
this->m_pauseOnNextInterrupt = true;
|
||||
@ -162,115 +88,8 @@ enum class AccessPointType
|
||||
|
||||
class GDBServer::AccessBreakpoint {
|
||||
public:
|
||||
AccessBreakpoint(MPTR address, AccessPointType type)
|
||||
: m_address(address), m_type(type)
|
||||
{
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
|
||||
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
|
||||
{
|
||||
HANDLE hThread = (HANDLE)hThreadNH;
|
||||
CONTEXT ctx{};
|
||||
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
SuspendThread(hThread);
|
||||
GetThreadContext(hThread, &ctx);
|
||||
|
||||
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
|
||||
ctx.Dr2 = (DWORD64)memory_getPointerFromVirtualOffset(address);
|
||||
ctx.Dr3 = (DWORD64)memory_getPointerFromVirtualOffset(address);
|
||||
// breakpoint 2
|
||||
SetBits(ctx.Dr7, 4, 1, 1); // breakpoint #3 enabled: true
|
||||
SetBits(ctx.Dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
|
||||
SetBits(ctx.Dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
|
||||
// breakpoint 3
|
||||
SetBits(ctx.Dr7, 6, 1, 1); // breakpoint #4 enabled: true
|
||||
SetBits(ctx.Dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
|
||||
SetBits(ctx.Dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
|
||||
|
||||
SetThreadContext(hThread, &ctx);
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
// todo: port the following code to all unix platforms, they seem to differ quite a bit
|
||||
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE
|
||||
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
|
||||
{
|
||||
pid_t pid = (pid_t)(uintptr_t)hThreadNH;
|
||||
ptrace(PTRACE_ATTACH, pid, nullptr, nullptr);
|
||||
waitpid(pid, nullptr, 0);
|
||||
|
||||
DRType dr7 = _GetDR(pid, 7);
|
||||
// use BP 2/3 for gdb stub since cemu's internal debugger uses BP 0/1 already
|
||||
DRType dr2 = (uint64)memory_getPointerFromVirtualOffset(address);
|
||||
DRType dr3 = (uint64)memory_getPointerFromVirtualOffset(address);
|
||||
// breakpoint 2
|
||||
SetBits(dr7, 4, 1, 1); // breakpoint #3 enabled: true
|
||||
SetBits(dr7, 24, 2, 1); // breakpoint #3 condition: 1 (write)
|
||||
SetBits(dr7, 26, 2, 3); // breakpoint #3 length: 3 (4 bytes)
|
||||
// breakpoint 3
|
||||
SetBits(dr7, 6, 1, 1); // breakpoint #4 enabled: true
|
||||
SetBits(dr7, 28, 2, 3); // breakpoint #4 condition: 3 (read & write)
|
||||
SetBits(dr7, 30, 2, 3); // breakpoint #4 length: 3 (4 bytes)
|
||||
|
||||
_SetDR(pid, 2, dr2);
|
||||
_SetDR(pid, 3, dr3);
|
||||
_SetDR(pid, 7, dr7);
|
||||
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
|
||||
}
|
||||
#else
|
||||
cemuLog_log(LogType::Force, "Debugger read/write breakpoints are not supported on non-x86 CPUs yet.");
|
||||
#endif
|
||||
};
|
||||
~AccessBreakpoint()
|
||||
{
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_WINDOWS
|
||||
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
|
||||
{
|
||||
HANDLE hThread = (HANDLE)hThreadNH;
|
||||
CONTEXT ctx{};
|
||||
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
|
||||
SuspendThread(hThread);
|
||||
GetThreadContext(hThread, &ctx);
|
||||
|
||||
// reset BP 2/3 to zero
|
||||
ctx.Dr2 = (DWORD64)0;
|
||||
ctx.Dr3 = (DWORD64)0;
|
||||
// breakpoint 2
|
||||
SetBits(ctx.Dr7, 4, 1, 0);
|
||||
SetBits(ctx.Dr7, 24, 2, 0);
|
||||
SetBits(ctx.Dr7, 26, 2, 0);
|
||||
// breakpoint 3
|
||||
SetBits(ctx.Dr7, 6, 1, 0);
|
||||
SetBits(ctx.Dr7, 28, 2, 0);
|
||||
SetBits(ctx.Dr7, 30, 2, 0);
|
||||
SetThreadContext(hThread, &ctx);
|
||||
ResumeThread(hThread);
|
||||
}
|
||||
#elif defined(ARCH_X86_64) && BOOST_OS_LINUX && FALSE
|
||||
for (auto& hThreadNH : coreinit::OSGetSchedulerThreads())
|
||||
{
|
||||
pid_t pid = (pid_t)(uintptr_t)hThreadNH;
|
||||
ptrace(PTRACE_ATTACH, pid, nullptr, nullptr);
|
||||
waitpid(pid, nullptr, 0);
|
||||
|
||||
DRType dr7 = _GetDR(pid, 7);
|
||||
// reset BP 2/3 to zero
|
||||
DRType dr2 = 0;
|
||||
DRType dr3 = 0;
|
||||
// breakpoint 2
|
||||
SetBits(dr7, 4, 1, 0);
|
||||
SetBits(dr7, 24, 2, 0);
|
||||
SetBits(dr7, 26, 2, 0);
|
||||
// breakpoint 3
|
||||
SetBits(dr7, 6, 1, 0);
|
||||
SetBits(dr7, 28, 2, 0);
|
||||
SetBits(dr7, 30, 2, 0);
|
||||
|
||||
_SetDR(pid, 2, dr2);
|
||||
_SetDR(pid, 3, dr3);
|
||||
_SetDR(pid, 7, dr7);
|
||||
ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
AccessBreakpoint(MPTR address, AccessPointType type);
|
||||
~AccessBreakpoint();
|
||||
|
||||
MPTR GetAddress() const
|
||||
{
|
||||
|
@ -263,6 +263,14 @@ bool GDBServer::Initialize()
|
||||
return false;
|
||||
}
|
||||
|
||||
int nodelayEnabled = TRUE;
|
||||
if (setsockopt(m_server_socket, IPPROTO_TCP, TCP_NODELAY, (char*)&nodelayEnabled, sizeof(nodelayEnabled)) == SOCKET_ERROR)
|
||||
{
|
||||
closesocket(m_server_socket);
|
||||
m_server_socket = INVALID_SOCKET;
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(&m_server_addr, 0, sizeof(m_server_addr));
|
||||
m_server_addr.sin_family = AF_INET;
|
||||
m_server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
|
||||
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
|
||||
#include "Cafe/OS/libs/snd_core/ax.h"
|
||||
#include "Cafe/HW/Espresso/Debugger/GDBStub.h"
|
||||
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
|
||||
#include "Cafe/HW/Espresso/Recompiler/PPCRecompiler.h"
|
||||
|
||||
@ -1153,6 +1154,18 @@ namespace coreinit
|
||||
}
|
||||
}
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
std::vector<pid_t> g_schedulerThreadIds;
|
||||
|
||||
std::vector<pid_t>& OSGetSchedulerThreadIds()
|
||||
{
|
||||
return g_schedulerThreadIds;
|
||||
}
|
||||
#endif
|
||||
|
||||
void OSSchedulerCoreEmulationThread(void* _assignedCoreIndex)
|
||||
{
|
||||
SetThreadName(fmt::format("OSSchedulerThread[core={}]", (uintptr_t)_assignedCoreIndex).c_str());
|
||||
@ -1160,6 +1173,19 @@ namespace coreinit
|
||||
#if defined(ARCH_X86_64)
|
||||
_mm_setcsr(_mm_getcsr() | 0x8000); // flush denormals to zero
|
||||
#endif
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
if (g_gdbstub)
|
||||
{
|
||||
// need to allow the GDBStub to attach to our thread
|
||||
prctl(PR_SET_DUMPABLE, (unsigned long)1);
|
||||
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
|
||||
}
|
||||
|
||||
pid_t tid = gettid();
|
||||
g_schedulerThreadIds.emplace_back(tid);
|
||||
#endif
|
||||
|
||||
t_schedulerFiber = Fiber::PrepareCurrentThread();
|
||||
|
||||
// create scheduler idle fiber and switch to it
|
||||
@ -1211,6 +1237,9 @@ namespace coreinit
|
||||
threadItr.join();
|
||||
sSchedulerThreads.clear();
|
||||
g_schedulerThreadHandles.clear();
|
||||
#if BOOST_OS_LINUX
|
||||
g_schedulerThreadIds.clear();
|
||||
#endif
|
||||
// clean up all fibers
|
||||
for (auto& it : g_idleLoopFiber)
|
||||
{
|
||||
|
@ -6,6 +6,9 @@
|
||||
#include "util/helpers/StringHelpers.h"
|
||||
#include "ExceptionHandler.h"
|
||||
|
||||
#include "Cafe/HW/Espresso/Debugger/GDBStub.h"
|
||||
#include "Cafe/HW/Espresso/Debugger/GDBBreakpoints.h"
|
||||
|
||||
#if BOOST_OS_LINUX
|
||||
#include "ELFSymbolTable.h"
|
||||
#endif
|
||||
@ -61,6 +64,16 @@ void DemangleAndPrintBacktrace(char** backtrace, size_t size)
|
||||
// handle signals that would dump core, print stacktrace and then dump depending on config
|
||||
void handlerDumpingSignal(int sig, siginfo_t *info, void *context)
|
||||
{
|
||||
#if defined(ARCH_X86_64) && BOOST_OS_LINUX
|
||||
// Check for hardware breakpoints
|
||||
if (info->si_signo == SIGTRAP && info->si_code == TRAP_HWBKPT)
|
||||
{
|
||||
uint64 dr6 = _ReadDR6();
|
||||
g_gdbstub->HandleAccessException(dr6);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(!CrashLog_Create())
|
||||
return; // give up if crashlog was already created
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user