Refactor process detaching code
This commit is contained in:
parent
fb800bd4c9
commit
63736d7873
@ -72,7 +72,7 @@ typedef enum GDBState
|
||||
GDB_STATE_CONNECTED,
|
||||
GDB_STATE_NOACK_SENT,
|
||||
GDB_STATE_NOACK,
|
||||
GDB_STATE_CLOSING
|
||||
GDB_STATE_DETACHING
|
||||
} GDBState;
|
||||
|
||||
typedef struct ThreadInfo
|
||||
@ -81,9 +81,12 @@ typedef struct ThreadInfo
|
||||
u32 tls;
|
||||
} ThreadInfo;
|
||||
|
||||
struct GDBServer;
|
||||
|
||||
typedef struct GDBContext
|
||||
{
|
||||
sock_ctx super;
|
||||
struct GDBServer *parent;
|
||||
|
||||
RecursiveLock lock;
|
||||
u16 localPort;
|
||||
@ -98,7 +101,7 @@ typedef struct GDBContext
|
||||
u32 currentThreadId, selectedThreadId, selectedThreadIdForContinuing;
|
||||
u32 totalNbCreatedThreads;
|
||||
|
||||
Handle clientAcceptedEvent, continuedEvent;
|
||||
Handle processAttachedEvent, continuedEvent;
|
||||
Handle eventToWaitFor;
|
||||
|
||||
bool catchThreadEvents;
|
||||
@ -130,4 +133,8 @@ typedef int (*GDBCommandHandler)(GDBContext *ctx);
|
||||
|
||||
void GDB_InitializeContext(GDBContext *ctx);
|
||||
void GDB_FinalizeContext(GDBContext *ctx);
|
||||
|
||||
Result GDB_AttachToProcess(GDBContext *ctx);
|
||||
void GDB_DetachFromProcess(GDBContext *ctx);
|
||||
|
||||
GDB_DECLARE_HANDLER(Unsupported);
|
||||
|
@ -27,6 +27,12 @@
|
||||
#include "gdb.h"
|
||||
#include "gdb/net.h"
|
||||
|
||||
#include "gdb/debug.h"
|
||||
|
||||
#include "gdb/watchpoints.h"
|
||||
#include "gdb/breakpoints.h"
|
||||
#include "gdb/stop_point.h"
|
||||
|
||||
void GDB_InitializeContext(GDBContext *ctx)
|
||||
{
|
||||
memset(ctx, 0, sizeof(GDBContext));
|
||||
@ -35,9 +41,9 @@ void GDB_InitializeContext(GDBContext *ctx)
|
||||
RecursiveLock_Lock(&ctx->lock);
|
||||
|
||||
svcCreateEvent(&ctx->continuedEvent, RESET_ONESHOT);
|
||||
svcCreateEvent(&ctx->clientAcceptedEvent, RESET_STICKY);
|
||||
svcCreateEvent(&ctx->processAttachedEvent, RESET_STICKY);
|
||||
|
||||
ctx->eventToWaitFor = ctx->clientAcceptedEvent;
|
||||
ctx->eventToWaitFor = ctx->processAttachedEvent;
|
||||
ctx->continueFlags = (DebugFlags)(DBG_SIGNAL_FAULT_EXCEPTION_EVENTS | DBG_INHIBIT_USER_CPU_EXCEPTION_HANDLERS);
|
||||
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
@ -47,14 +53,134 @@ void GDB_FinalizeContext(GDBContext *ctx)
|
||||
{
|
||||
RecursiveLock_Lock(&ctx->lock);
|
||||
|
||||
svcClearEvent(ctx->clientAcceptedEvent);
|
||||
svcClearEvent(ctx->processAttachedEvent);
|
||||
|
||||
svcCloseHandle(ctx->clientAcceptedEvent);
|
||||
svcCloseHandle(ctx->processAttachedEvent);
|
||||
svcCloseHandle(ctx->continuedEvent);
|
||||
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
Result GDB_AttachToProcess(GDBContext *ctx)
|
||||
{
|
||||
Result r;
|
||||
|
||||
// Two cases: attached during execution, or started attached
|
||||
// The second case will have, after RunQueuedProcess: attach process, debugger break, attach thread (with creator = 0)
|
||||
|
||||
if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START))
|
||||
svcDebugActiveProcess(&ctx->debug, ctx->pid);
|
||||
else
|
||||
{
|
||||
r = 0;
|
||||
}
|
||||
if(R_SUCCEEDED(r))
|
||||
{
|
||||
// Note: ctx->pid will be (re)set while processing 'attach process'
|
||||
DebugEventInfo *info = &ctx->latestDebugEvent;
|
||||
ctx->state = GDB_STATE_CONNECTED;
|
||||
ctx->processExited = ctx->processEnded = false;
|
||||
ctx->latestSentPacketSize = 0;
|
||||
if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START))
|
||||
{
|
||||
while(R_SUCCEEDED(svcGetProcessDebugEvent(info, ctx->debug)) &&
|
||||
info->type != DBGEVENT_EXCEPTION &&
|
||||
info->exception.type != EXCEVENT_ATTACH_BREAK)
|
||||
{
|
||||
GDB_PreprocessDebugEvent(ctx, info);
|
||||
svcContinueDebugEvent(ctx->debug, ctx->continueFlags);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Attach process, debugger break
|
||||
for(u32 i = 0; i < 2; i++)
|
||||
{
|
||||
if (R_FAILED(r = svcGetProcessDebugEvent(info, ctx->debug)))
|
||||
return r;
|
||||
GDB_PreprocessDebugEvent(ctx, info);
|
||||
if (R_FAILED(r = svcContinueDebugEvent(ctx->debug, ctx->continueFlags)))
|
||||
return r;
|
||||
}
|
||||
|
||||
if(R_FAILED(r = svcWaitSynchronization(ctx->debug, -1LL)))
|
||||
return r;
|
||||
if (R_FAILED(r = svcGetProcessDebugEvent(info, ctx->debug)))
|
||||
return r;
|
||||
// Attach thread
|
||||
GDB_PreprocessDebugEvent(ctx, info);
|
||||
}
|
||||
}
|
||||
else
|
||||
return r;
|
||||
|
||||
return svcSignalEvent(ctx->processAttachedEvent);
|
||||
}
|
||||
|
||||
void GDB_DetachFromProcess(GDBContext *ctx)
|
||||
{
|
||||
DebugEventInfo dummy;
|
||||
for(u32 i = 0; i < ctx->nbBreakpoints; i++)
|
||||
{
|
||||
if(!ctx->breakpoints[i].persistent)
|
||||
GDB_DisableBreakpointById(ctx, i);
|
||||
}
|
||||
memset(&ctx->breakpoints, 0, sizeof(ctx->breakpoints));
|
||||
ctx->nbBreakpoints = 0;
|
||||
|
||||
for(u32 i = 0; i < ctx->nbWatchpoints; i++)
|
||||
{
|
||||
GDB_RemoveWatchpoint(ctx, ctx->watchpoints[i], WATCHPOINT_DISABLED);
|
||||
ctx->watchpoints[i] = 0;
|
||||
}
|
||||
ctx->nbWatchpoints = 0;
|
||||
|
||||
svcKernelSetState(0x10002, ctx->pid, false);
|
||||
memset(ctx->svcMask, 0, 32);
|
||||
|
||||
memset(ctx->memoryOsInfoXmlData, 0, sizeof(ctx->memoryOsInfoXmlData));
|
||||
memset(ctx->processesOsInfoXmlData, 0, sizeof(ctx->processesOsInfoXmlData));
|
||||
memset(ctx->threadListData, 0, sizeof(ctx->threadListData));
|
||||
ctx->threadListDataPos = 0;
|
||||
|
||||
svcClearEvent(ctx->processAttachedEvent);
|
||||
ctx->eventToWaitFor = ctx->processAttachedEvent;
|
||||
|
||||
//svcSignalEvent(server->statusUpdated);
|
||||
|
||||
/*
|
||||
There's a possibility of a race condition with a possible user exception handler, but you shouldn't
|
||||
use 'kill' on APPLICATION titles in the first place (reboot hanging because the debugger is still running, etc).
|
||||
*/
|
||||
|
||||
ctx->continueFlags = (DebugFlags)0;
|
||||
|
||||
while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug)));
|
||||
while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags)));
|
||||
if(ctx->flags & GDB_FLAG_TERMINATE_PROCESS)
|
||||
{
|
||||
svcTerminateDebugProcess(ctx->debug);
|
||||
ctx->processEnded = true;
|
||||
ctx->processExited = false;
|
||||
}
|
||||
|
||||
while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug)));
|
||||
while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags)));
|
||||
|
||||
svcCloseHandle(ctx->debug);
|
||||
ctx->debug = 0;
|
||||
|
||||
|
||||
ctx->eventToWaitFor = ctx->processAttachedEvent;
|
||||
ctx->continueFlags = (DebugFlags)(DBG_SIGNAL_FAULT_EXCEPTION_EVENTS | DBG_INHIBIT_USER_CPU_EXCEPTION_HANDLERS);
|
||||
ctx->pid = 0;
|
||||
ctx->currentThreadId = ctx->selectedThreadId = ctx->selectedThreadIdForContinuing = 0;
|
||||
ctx->nbThreads = 0;
|
||||
ctx->totalNbCreatedThreads = 0;
|
||||
memset(ctx->threadInfos, 0, sizeof(ctx->threadInfos));
|
||||
ctx->catchThreadEvents = false;
|
||||
}
|
||||
|
||||
GDB_DECLARE_HANDLER(Unsupported)
|
||||
{
|
||||
return GDB_ReplyEmpty(ctx);
|
||||
|
@ -43,13 +43,13 @@
|
||||
|
||||
GDB_DECLARE_HANDLER(Detach)
|
||||
{
|
||||
ctx->state = GDB_STATE_CLOSING;
|
||||
ctx->state = GDB_STATE_DETACHING;
|
||||
return GDB_ReplyOk(ctx);
|
||||
}
|
||||
|
||||
GDB_DECLARE_HANDLER(Kill)
|
||||
{
|
||||
ctx->state = GDB_STATE_CLOSING;
|
||||
ctx->state = GDB_STATE_DETACHING;
|
||||
ctx->flags |= GDB_FLAG_TERMINATE_PROCESS;
|
||||
return 0;
|
||||
}
|
||||
@ -480,7 +480,7 @@ int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info)
|
||||
*/
|
||||
int GDB_HandleDebugEvents(GDBContext *ctx)
|
||||
{
|
||||
if(ctx->state == GDB_STATE_CLOSING)
|
||||
if(ctx->state == GDB_STATE_DETACHING)
|
||||
return -1;
|
||||
|
||||
DebugEventInfo info;
|
||||
|
@ -42,11 +42,13 @@ void GDB_RunMonitor(GDBServer *server)
|
||||
|
||||
do
|
||||
{
|
||||
GDB_LockAllContexts(server);
|
||||
for(int i = 0; i < MAX_DEBUG; i++)
|
||||
{
|
||||
GDBContext *ctx = &server->ctxs[i];
|
||||
handles[3 + i] = ctx->eventToWaitFor;
|
||||
}
|
||||
GDB_UnlockAllContexts(server);
|
||||
|
||||
s32 idx = -1;
|
||||
r = svcWaitSynchronizationN(&idx, handles, 3 + MAX_DEBUG, false, -1LL);
|
||||
@ -60,15 +62,15 @@ void GDB_RunMonitor(GDBServer *server)
|
||||
GDBContext *ctx = &server->ctxs[idx - 3];
|
||||
|
||||
RecursiveLock_Lock(&ctx->lock);
|
||||
if(ctx->state == GDB_STATE_DISCONNECTED || ctx->state == GDB_STATE_CLOSING)
|
||||
if(ctx->state == GDB_STATE_DISCONNECTED || ctx->state == GDB_STATE_DETACHING)
|
||||
{
|
||||
svcClearEvent(ctx->clientAcceptedEvent);
|
||||
ctx->eventToWaitFor = ctx->clientAcceptedEvent;
|
||||
svcClearEvent(ctx->processAttachedEvent);
|
||||
ctx->eventToWaitFor = ctx->processAttachedEvent;
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(ctx->eventToWaitFor == ctx->clientAcceptedEvent)
|
||||
if(ctx->eventToWaitFor == ctx->processAttachedEvent)
|
||||
ctx->eventToWaitFor = ctx->continuedEvent;
|
||||
else if(ctx->eventToWaitFor == ctx->continuedEvent)
|
||||
ctx->eventToWaitFor = ctx->debug;
|
||||
@ -82,8 +84,8 @@ void GDB_RunMonitor(GDBServer *server)
|
||||
while(GDB_HandleDebugEvents(ctx) != -1) // until we've got all the remaining debug events
|
||||
svcSleepThread(1 * 1000 * 1000LL); // sleep just in case
|
||||
|
||||
svcClearEvent(ctx->clientAcceptedEvent);
|
||||
ctx->eventToWaitFor = ctx->clientAcceptedEvent;
|
||||
svcClearEvent(ctx->processAttachedEvent);
|
||||
ctx->eventToWaitFor = ctx->processAttachedEvent;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,7 +291,7 @@ int GDB_SendStreamData(GDBContext *ctx, const char *streamData, u32 offset, u32
|
||||
|
||||
int GDB_SendDebugString(GDBContext *ctx, const char *fmt, ...) // unsecure
|
||||
{
|
||||
if(ctx->state == GDB_STATE_CLOSING || !(ctx->flags & GDB_FLAG_PROCESS_CONTINUING))
|
||||
if(ctx->state == GDB_STATE_DETACHING || !(ctx->flags & GDB_FLAG_PROCESS_CONTINUING))
|
||||
return 0;
|
||||
|
||||
char formatted[(GDB_BUF_LEN - 1) / 2 + 1];
|
||||
|
@ -154,98 +154,22 @@ GDBContext *GDB_SelectAvailableContext(GDBServer *server, u16 minPort, u16 maxPo
|
||||
|
||||
int GDB_AcceptClient(GDBContext *ctx)
|
||||
{
|
||||
RecursiveLock_Lock(&ctx->lock);
|
||||
Result r;
|
||||
|
||||
// Two cases: attached during execution, or started attached
|
||||
// The second case will have, after RunQueuedProcess: attach process, debugger break, attach thread (with creator = 0)
|
||||
|
||||
if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START))
|
||||
svcDebugActiveProcess(&ctx->debug, ctx->pid);
|
||||
else
|
||||
{
|
||||
r = 0;
|
||||
}
|
||||
if(R_SUCCEEDED(r))
|
||||
{
|
||||
// Note: ctx->pid will be (re)set while processing 'attach process'
|
||||
DebugEventInfo *info = &ctx->latestDebugEvent;
|
||||
ctx->state = GDB_STATE_CONNECTED;
|
||||
ctx->processExited = ctx->processEnded = false;
|
||||
ctx->latestSentPacketSize = 0;
|
||||
if (!(ctx->flags & GDB_FLAG_ATTACHED_AT_START))
|
||||
{
|
||||
while(R_SUCCEEDED(svcGetProcessDebugEvent(info, ctx->debug)) &&
|
||||
info->type != DBGEVENT_EXCEPTION &&
|
||||
info->exception.type != EXCEVENT_ATTACH_BREAK)
|
||||
{
|
||||
GDB_PreprocessDebugEvent(ctx, info);
|
||||
svcContinueDebugEvent(ctx->debug, ctx->continueFlags);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Attach process, debugger break
|
||||
for(u32 i = 0; i < 2; i++)
|
||||
{
|
||||
if (R_FAILED(svcGetProcessDebugEvent(info, ctx->debug)))
|
||||
return -1;
|
||||
GDB_PreprocessDebugEvent(ctx, info);
|
||||
if (R_FAILED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags)))
|
||||
return -1;
|
||||
}
|
||||
|
||||
svcWaitSynchronization(ctx->debug, -1LL);
|
||||
if (R_FAILED(svcGetProcessDebugEvent(info, ctx->debug)))
|
||||
return -1; //svcBreak(0);
|
||||
// Attach thread
|
||||
GDB_PreprocessDebugEvent(ctx, info);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
svcSignalEvent(ctx->clientAcceptedEvent);
|
||||
RecursiveLock_Lock(&ctx->lock);
|
||||
r = GDB_AttachToProcess(ctx);
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
|
||||
return 0;
|
||||
return R_SUCCEEDED(r) ? 0 : -1;
|
||||
}
|
||||
|
||||
int GDB_CloseClient(GDBContext *ctx)
|
||||
{
|
||||
RecursiveLock_Lock(&ctx->lock);
|
||||
|
||||
for(u32 i = 0; i < ctx->nbBreakpoints; i++)
|
||||
{
|
||||
if(!ctx->breakpoints[i].persistent)
|
||||
GDB_DisableBreakpointById(ctx, i);
|
||||
}
|
||||
memset(&ctx->breakpoints, 0, sizeof(ctx->breakpoints));
|
||||
ctx->nbBreakpoints = 0;
|
||||
|
||||
for(u32 i = 0; i < ctx->nbWatchpoints; i++)
|
||||
{
|
||||
GDB_RemoveWatchpoint(ctx, ctx->watchpoints[i], WATCHPOINT_DISABLED);
|
||||
ctx->watchpoints[i] = 0;
|
||||
}
|
||||
ctx->nbWatchpoints = 0;
|
||||
|
||||
svcKernelSetState(0x10002, ctx->pid, false);
|
||||
memset(ctx->svcMask, 0, 32);
|
||||
|
||||
memset(ctx->memoryOsInfoXmlData, 0, sizeof(ctx->memoryOsInfoXmlData));
|
||||
memset(ctx->processesOsInfoXmlData, 0, sizeof(ctx->processesOsInfoXmlData));
|
||||
memset(ctx->threadListData, 0, sizeof(ctx->threadListData));
|
||||
ctx->threadListDataPos = 0;
|
||||
|
||||
svcClearEvent(ctx->clientAcceptedEvent);
|
||||
ctx->eventToWaitFor = ctx->clientAcceptedEvent;
|
||||
|
||||
ctx->localPort = 0;
|
||||
svcSignalEvent(ctx->parent->statusUpdated); // note: monitor will be waiting for lock
|
||||
GDB_DetachFromProcess(ctx);
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -266,6 +190,7 @@ GDBContext *GDB_GetClient(GDBServer *server, u16 port)
|
||||
{
|
||||
ctx->flags |= GDB_FLAG_USED;
|
||||
ctx->state = GDB_STATE_CONNECTED;
|
||||
ctx->parent = server;
|
||||
}
|
||||
|
||||
GDB_UnlockAllContexts(server);
|
||||
@ -274,46 +199,12 @@ GDBContext *GDB_GetClient(GDBServer *server, u16 port)
|
||||
|
||||
void GDB_ReleaseClient(GDBServer *server, GDBContext *ctx)
|
||||
{
|
||||
DebugEventInfo dummy;
|
||||
|
||||
svcSignalEvent(server->statusUpdated);
|
||||
|
||||
(void)server;
|
||||
RecursiveLock_Lock(&ctx->lock);
|
||||
|
||||
/*
|
||||
There's a possibility of a race condition with a possible user exception handler, but you shouldn't
|
||||
use 'kill' on APPLICATION titles in the first place (reboot hanging because the debugger is still running, etc).
|
||||
*/
|
||||
|
||||
ctx->continueFlags = (DebugFlags)0;
|
||||
|
||||
while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug)));
|
||||
while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags)));
|
||||
if(ctx->flags & GDB_FLAG_TERMINATE_PROCESS)
|
||||
{
|
||||
svcTerminateDebugProcess(ctx->debug);
|
||||
ctx->processEnded = true;
|
||||
ctx->processExited = false;
|
||||
}
|
||||
|
||||
while(R_SUCCEEDED(svcGetProcessDebugEvent(&dummy, ctx->debug)));
|
||||
while(R_SUCCEEDED(svcContinueDebugEvent(ctx->debug, ctx->continueFlags)));
|
||||
|
||||
svcCloseHandle(ctx->debug);
|
||||
ctx->debug = 0;
|
||||
|
||||
ctx->localPort = 0;
|
||||
ctx->enableExternalMemoryAccess = false;
|
||||
ctx->flags = (GDBFlags)0;
|
||||
ctx->state = GDB_STATE_DISCONNECTED;
|
||||
|
||||
ctx->eventToWaitFor = ctx->clientAcceptedEvent;
|
||||
ctx->continueFlags = (DebugFlags)(DBG_SIGNAL_FAULT_EXCEPTION_EVENTS | DBG_INHIBIT_USER_CPU_EXCEPTION_HANDLERS);
|
||||
ctx->pid = 0;
|
||||
ctx->currentThreadId = ctx->selectedThreadId = ctx->selectedThreadIdForContinuing = 0;
|
||||
ctx->nbThreads = 0;
|
||||
ctx->totalNbCreatedThreads = 0;
|
||||
memset(ctx->threadInfos, 0, sizeof(ctx->threadInfos));
|
||||
ctx->catchThreadEvents = false;
|
||||
ctx->enableExternalMemoryAccess = false;
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
@ -384,7 +275,7 @@ int GDB_DoPacket(GDBContext *ctx)
|
||||
ret = 0;
|
||||
|
||||
RecursiveLock_Unlock(&ctx->lock);
|
||||
if(ctx->state == GDB_STATE_CLOSING)
|
||||
if(ctx->state == GDB_STATE_DETACHING)
|
||||
return -1;
|
||||
|
||||
if((oldFlags & GDB_FLAG_PROCESS_CONTINUING) && !(ctx->flags & GDB_FLAG_PROCESS_CONTINUING))
|
||||
|
@ -71,7 +71,7 @@ static inline int ProcessListMenu_FormatInfoLine(char *out, const ProcessInfo *i
|
||||
|
||||
else if(gdbServer.super.running && id < MAX_DEBUG)
|
||||
{
|
||||
if(gdbServer.ctxs[id].state >= GDB_STATE_CONNECTED && gdbServer.ctxs[id].state < GDB_STATE_CLOSING)
|
||||
if(gdbServer.ctxs[id].state >= GDB_STATE_CONNECTED && gdbServer.ctxs[id].state < GDB_STATE_DETACHING)
|
||||
{
|
||||
u8 *addr = (u8 *)&gdbServer.ctxs[id].super.addr_in.sin_addr;
|
||||
checkbox = "(A) ";
|
||||
|
Reference in New Issue
Block a user