Refactor process detaching code

This commit is contained in:
TuxSH 2019-03-31 22:58:36 +02:00
parent fb800bd4c9
commit 63736d7873
7 changed files with 163 additions and 137 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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];

View File

@ -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))

View File

@ -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) ";