mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-23 04:41:12 +00:00
thermosphere: impl. debug event dispatching, vStopped, "?"
This commit is contained in:
parent
0e47f7f46b
commit
984f6776c6
8 changed files with 301 additions and 162 deletions
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
#include "gdb/debug.h"
|
#include "gdb/debug.h"
|
||||||
|
|
||||||
|
GDBContext g_gdbContext = { 0 };
|
||||||
|
|
||||||
typedef struct DebugManager {
|
typedef struct DebugManager {
|
||||||
DebugEventInfo debugEventInfos[MAX_CORE];
|
DebugEventInfo debugEventInfos[MAX_CORE];
|
||||||
|
|
||||||
|
@ -32,8 +34,6 @@ typedef struct DebugManager {
|
||||||
atomic_uint singleStepCoreList;
|
atomic_uint singleStepCoreList;
|
||||||
atomic_uint eventsSentList;
|
atomic_uint eventsSentList;
|
||||||
Barrier pauseBarrier;
|
Barrier pauseBarrier;
|
||||||
|
|
||||||
atomic_bool nonStop;
|
|
||||||
} DebugManager;
|
} DebugManager;
|
||||||
|
|
||||||
static DebugManager g_debugManager = { 0 };
|
static DebugManager g_debugManager = { 0 };
|
||||||
|
@ -114,6 +114,17 @@ void debugManagerUnpauseCores(u32 coreList, u32 singleStepList)
|
||||||
__sev();
|
__sev();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 debugManagerGetPausedCoreList(void)
|
||||||
|
{
|
||||||
|
return atomic_load(&g_debugManager.pausedCoreList);
|
||||||
|
}
|
||||||
|
|
||||||
|
const DebugEventInfo *debugManagerMarkAndGetCoreDebugEvent(u32 coreId)
|
||||||
|
{
|
||||||
|
g_debugManager.debugEventInfos[coreId].handled = true;
|
||||||
|
return &g_debugManager.debugEventInfos[coreId];
|
||||||
|
}
|
||||||
|
|
||||||
void debugManagerReportEvent(DebugEventType type, ...)
|
void debugManagerReportEvent(DebugEventType type, ...)
|
||||||
{
|
{
|
||||||
u64 flags = maskIrq();
|
u64 flags = maskIrq();
|
||||||
|
@ -141,6 +152,11 @@ void debugManagerReportEvent(DebugEventType type, ...)
|
||||||
|
|
||||||
// Now, pause ourselves and try to signal we have a debug event
|
// Now, pause ourselves and try to signal we have a debug event
|
||||||
debugManagerDoPauseCores(BIT(coreId));
|
debugManagerDoPauseCores(BIT(coreId));
|
||||||
// TODO gdb enter leave functions
|
|
||||||
|
exceptionEnterInterruptibleHypervisorCode();
|
||||||
|
unmaskIrq();
|
||||||
|
|
||||||
|
GDB_TrySignalDebugEvent(&g_gdbContext, info);
|
||||||
|
|
||||||
restoreInterruptFlags(flags);
|
restoreInterruptFlags(flags);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
|
#include "gdb/context.h"
|
||||||
|
|
||||||
|
extern GDBContext g_gdbContext;
|
||||||
|
|
||||||
typedef enum DebugEventType {
|
typedef enum DebugEventType {
|
||||||
DBGEVENT_DEBUGGER_BREAK = 0,
|
DBGEVENT_DEBUGGER_BREAK = 0,
|
||||||
|
@ -34,6 +37,7 @@ typedef struct OutputStringDebugEventInfo {
|
||||||
|
|
||||||
typedef struct DebugEventInfo {
|
typedef struct DebugEventInfo {
|
||||||
DebugEventType type;
|
DebugEventType type;
|
||||||
|
bool handled;
|
||||||
u32 coreId;
|
u32 coreId;
|
||||||
ExceptionStackFrame *frame;
|
ExceptionStackFrame *frame;
|
||||||
union {
|
union {
|
||||||
|
@ -51,3 +55,12 @@ void debugManagerHandlePause(void);
|
||||||
// "Unpause" doesn't synchronize, it just makes sure the core resumes & that "pause" can be called again.
|
// "Unpause" doesn't synchronize, it just makes sure the core resumes & that "pause" can be called again.
|
||||||
void debugManagerPauseCores(u32 coreList);
|
void debugManagerPauseCores(u32 coreList);
|
||||||
void debugManagerUnpauseCores(u32 coreList, u32 singleStepList);
|
void debugManagerUnpauseCores(u32 coreList, u32 singleStepList);
|
||||||
|
|
||||||
|
u32 debugManagerGetPausedCoreList(void);
|
||||||
|
|
||||||
|
const DebugEventInfo *debugManagerMarkAndGetCoreDebugEvent(u32 coreId);
|
||||||
|
|
||||||
|
static inline bool debugManagerIsCorePaused(u32 coreId)
|
||||||
|
{
|
||||||
|
return (debugManagerGetPausedCoreList() & BIT(coreId)) != 0;
|
||||||
|
}
|
||||||
|
|
|
@ -61,6 +61,8 @@ typedef enum GDBState
|
||||||
GDB_STATE_DETACHING,
|
GDB_STATE_DETACHING,
|
||||||
} GDBState;
|
} GDBState;
|
||||||
|
|
||||||
|
struct DebugEventInfo;
|
||||||
|
|
||||||
typedef struct GDBContext {
|
typedef struct GDBContext {
|
||||||
// No need for a lock, it's in the transport interface layer...
|
// No need for a lock, it's in the transport interface layer...
|
||||||
|
|
||||||
|
@ -69,13 +71,20 @@ typedef struct GDBContext {
|
||||||
GDBState state;
|
GDBState state;
|
||||||
bool noAckSent;
|
bool noAckSent;
|
||||||
|
|
||||||
|
u32 attachedCoreList;
|
||||||
|
|
||||||
u32 currentThreadId;
|
u32 currentThreadId;
|
||||||
u32 selectedThreadId;
|
u32 selectedThreadId;
|
||||||
u32 selectedThreadIdForContinuing;
|
u32 selectedThreadIdForContinuing;
|
||||||
|
|
||||||
|
u32 sentDebugEventCoreList;
|
||||||
|
|
||||||
|
bool sendOwnDebugEventAllowed;
|
||||||
|
|
||||||
bool catchThreadEvents;
|
bool catchThreadEvents;
|
||||||
bool processEnded, processExited;
|
bool processEnded, processExited;
|
||||||
|
|
||||||
//DebugEventInfo latestDebugEvent; FIXME
|
const struct DebugEventInfo *lastDebugEvent;
|
||||||
|
|
||||||
uintptr_t currentHioRequestTargetAddr;
|
uintptr_t currentHioRequestTargetAddr;
|
||||||
PackedGdbHioRequest currentHioRequest;
|
PackedGdbHioRequest currentHioRequest;
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#define _GNU_SOURCE // for strchrnul
|
#define _GNU_SOURCE // for strchrnul
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "../exceptions.h"
|
#include "../debug_manager.h"
|
||||||
#include "../watchpoints.h"
|
#include "../watchpoints.h"
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
@ -19,17 +19,246 @@
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "hio.h"
|
#include "hio.h"
|
||||||
#include "watchpoints.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
|
static int GDB_ParseExceptionFrame(char *out, const DebugEventInfo *info, int sig)
|
||||||
|
{
|
||||||
|
u32 coreId = info->coreId;
|
||||||
|
ExceptionStackFrame *frame = info->frame;
|
||||||
|
|
||||||
|
int n = sprintf(out, "T%02xthread:%lx;core:%lx;", sig, 1 + coreId, coreId);
|
||||||
|
|
||||||
|
// Dump the GPRs & sp & pc & cpsr (cpsr is 32-bit in the xml desc)
|
||||||
|
// For performance reasons, we don't include the FPU registers here
|
||||||
|
for (u32 i = 0; i < 31; i++) {
|
||||||
|
n += sprintf(out + n, "%x:%016lx;", i, __builtin_bswap64(readFrameRegister(frame, i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
n += sprintf(
|
||||||
|
out + n,
|
||||||
|
"1f:%016lx;20:%016lx;21:%08x",
|
||||||
|
__builtin_bswap64(*exceptionGetSpPtr(frame)),
|
||||||
|
__builitin_bswap32((u32)frame->spsr_el2)
|
||||||
|
);
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info, bool asNotification)
|
||||||
|
{
|
||||||
|
char *buf = ctx->buffer + 1;
|
||||||
|
int n;
|
||||||
|
bool invalid = false;
|
||||||
|
|
||||||
|
buf[0] = 0;
|
||||||
|
|
||||||
|
if (asNotification) {
|
||||||
|
strcpy(buf, "Stopped:");
|
||||||
|
}
|
||||||
|
|
||||||
|
n = strlen(buf);
|
||||||
|
|
||||||
|
// Even if the info is invalid:
|
||||||
|
ctx->lastDebugEvent = info;
|
||||||
|
ctx->sentDebugEventCoreList |= BIT(info->coreId);
|
||||||
|
|
||||||
|
switch(info->type) {
|
||||||
|
case DBGEVENT_DEBUGGER_BREAK: {
|
||||||
|
strcat(buf, "S02");
|
||||||
|
}
|
||||||
|
|
||||||
|
case DBGEVENT_CORE_ON: {
|
||||||
|
if (ctx->catchThreadEvents) {
|
||||||
|
ctx->currentThreadId = info->coreId + 1; // FIXME?
|
||||||
|
strcat(buf, "T05create:;");
|
||||||
|
} else {
|
||||||
|
invalid = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DBGEVENT_CORE_OFF: {
|
||||||
|
if(ctx->catchThreadEvents) {
|
||||||
|
sprintf(buf, "w0;%x", info->coreId + 1);
|
||||||
|
} else {
|
||||||
|
invalid = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DBGEVENT_EXIT: {
|
||||||
|
// exited (no error / unhandled exception), SIGTERM (process terminated) * 2
|
||||||
|
static const char *processExitReplies[] = { "W00", "X0f" };
|
||||||
|
strcat(buf, processExitReplies[ctx->processExited ? 0 : 1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DBGEVENT_EXCEPTION: {
|
||||||
|
ExceptionClass ec = info->frame->esr_el2.ec;
|
||||||
|
ctx->currentThreadId = info->coreId + 1; // FIXME?
|
||||||
|
|
||||||
|
// Aside from stage 2 translation faults and other pre-handled exceptions,
|
||||||
|
// the only notable exceptions we get are stop point/single step events from the debugee (basically classes 0x3x)
|
||||||
|
switch(ec) {
|
||||||
|
case Exception_BreakpointLowerEl: {
|
||||||
|
n += GDB_ParseExceptionFrame(buf + n, ctx, SIGTRAP);
|
||||||
|
strcat(buf, "hwbreak:;");
|
||||||
|
}
|
||||||
|
|
||||||
|
case Exception_WatchpointLowerEl: {
|
||||||
|
static const char *kinds[] = { "", "r", "", "a" };
|
||||||
|
// Note: exception info doesn't provide us with the access size. Use 1.
|
||||||
|
bool wnr = (info->frame->esr_el2.iss & BIT(6)) != 0;
|
||||||
|
WatchpointLoadStoreControl dr = wnr ? WatchpointLoadStoreControl_Store : WatchpointLoadStoreControl_Load;
|
||||||
|
DebugControlRegister cr = retrieveSplitWatchpointConfig(info->frame->far_el2, 1, dr, false);
|
||||||
|
if (!cr.enabled) {
|
||||||
|
DEBUG("GDB: oops, unhandled watchpoint for core id %u, far=%016lx\n", info->coreId, info->frame->far_el2);
|
||||||
|
} else {
|
||||||
|
n += GDB_ParseExceptionFrame(buf + n, ctx, SIGTRAP);
|
||||||
|
sprintf(buf + n, "%swatch:%016lx;", kinds[cr.lsc], info->frame->far_el2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: we don't really support 32-bit sw breakpoints, we'll still report them
|
||||||
|
// if the guest has inserted some of them manually...
|
||||||
|
case Exception_SoftwareBreakpointA64:
|
||||||
|
case Exception_SoftwareBreakpointA32: {
|
||||||
|
n += GDB_ParseExceptionFrame(buf + n, ctx, SIGTRAP);
|
||||||
|
strcat(buf, "swbreak:;");
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
invalid = true;
|
||||||
|
DEBUG("GDB: oops, unhandled exception for core id %u\n", info->coreId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case DBGEVENT_OUTPUT_STRING: {
|
||||||
|
if (!(ctx->flags & GDB_FLAG_NONSTOP)) {
|
||||||
|
uintptr_t addr = info->outputString.address;
|
||||||
|
size_t remaining = info->outputString.size;
|
||||||
|
size_t sent = 0;
|
||||||
|
size_t total = 0;
|
||||||
|
while (remaining > 0) {
|
||||||
|
size_t pending = (GDB_BUF_LEN - 1) / 2;
|
||||||
|
pending = pending < remaining ? pending : remaining;
|
||||||
|
|
||||||
|
int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending);
|
||||||
|
if(res < 0 || res != 5 + 2 * pending)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sent += pending;
|
||||||
|
remaining -= pending;
|
||||||
|
total += res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)total;
|
||||||
|
} else {
|
||||||
|
invalid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: HIO
|
||||||
|
|
||||||
|
default: {
|
||||||
|
invalid = true;
|
||||||
|
DEBUG("GDB: unknown exception type %u, core id %u\n", (u32)info->type, info->coreId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalid) {
|
||||||
|
return 0;
|
||||||
|
} else if (asNotification) {
|
||||||
|
return GDB_SendNotificationPacket(ctx, buf, strlen(buf));
|
||||||
|
} else {
|
||||||
|
return GDB_SendPacket(ctx, buf, strlen(buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Since we can't select particular threads to continue (and that's uncompliant behavior):
|
Non-stop mode:
|
||||||
- if we continue the current thread, continue all threads
|
-> %Stop:<info>
|
||||||
- otherwise, leaves all threads stopped but make the client believe it's continuing
|
<- $vStopped
|
||||||
|
-> $<info>
|
||||||
|
<- vStopped, etc.
|
||||||
|
-> $OK
|
||||||
|
If we're the first to try to send a notification, send it.
|
||||||
|
Otherwise don't, the core which will handle the GDB packets then will see the changes.
|
||||||
|
|
||||||
|
GDB can also send the "?" packet. This aborts the current notfication/vStopped sequence,
|
||||||
|
and asks to resend the events for each stopped core, no matter if already sent before.
|
||||||
|
|
||||||
|
Full-stop mode (default):
|
||||||
|
|
||||||
|
If we lose the race, we have to wait until we're continued to send the remaining events...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
int GDB_TrySignalDebugEvent(GDBContext *ctx, DebugEventInfo *info)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
// Acquire the gdb lock/disable rx irq. We most likely block here.
|
||||||
|
GDB_AcquireContext(ctx);
|
||||||
|
|
||||||
|
// Are we still paused & has the packet not been handled & are we allowed to send on our own?
|
||||||
|
|
||||||
|
if (ctx->sendOwnDebugEventAllowed && !info->handled && debugManagerIsCorePaused(info->coreId)) {
|
||||||
|
bool nonStop = (ctx->flags & GDB_FLAG_NONSTOP) != 0;
|
||||||
|
info->handled = true;
|
||||||
|
|
||||||
|
// Full-stop mode: stop other cores
|
||||||
|
if (!nonStop) {
|
||||||
|
debugManagerPauseCores(ctx->attachedCoreList & ~BIT(info->coreId));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->sendOwnDebugEventAllowed = false;
|
||||||
|
ret = GDB_SendStopReply(ctx, info, nonStop);
|
||||||
|
}
|
||||||
|
|
||||||
|
GDB_ReleaseContext(ctx);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
GDB_DECLARE_VERBOSE_HANDLER(Stopped)
|
||||||
|
{
|
||||||
|
u32 coreList = debugManagerGetPausedCoreList() & ctx->attachedCoreList;
|
||||||
|
u32 remaining = coreList & ~ctx->sentDebugEventCoreList;
|
||||||
|
|
||||||
|
if (remaining != 0) {
|
||||||
|
// Send one more debug event (marking it as handled)
|
||||||
|
u32 coreId = __builtin_ctz(remaining);
|
||||||
|
DebugEventInfo *info = debugManagerMarkAndGetCoreDebugEvent(coreId);
|
||||||
|
|
||||||
|
ctx->sendOwnDebugEventAllowed = false;
|
||||||
|
return GDB_SendStopReply(ctx, info, false);
|
||||||
|
} else {
|
||||||
|
// vStopped sequenced finished
|
||||||
|
ctx->sendOwnDebugEventAllowed = true;
|
||||||
|
return GDB_ReplyOk(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GDB_DECLARE_HANDLER(GetStopReason)
|
||||||
|
{
|
||||||
|
bool nonStop = (ctx->flags & GDB_FLAG_NONSTOP) != 0;
|
||||||
|
if (!nonStop) {
|
||||||
|
// Full-stop:
|
||||||
|
return GDB_SendStopReply(ctx, &ctx->lastDebugEvent, true);
|
||||||
|
} else {
|
||||||
|
// Non-stop, start new vStopped sequence
|
||||||
|
ctx->sentDebugEventCoreList = 0;
|
||||||
|
ctx->sendOwnDebugEventAllowed = false;
|
||||||
|
return GDB_HandleVerboseStopped(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GDB_DECLARE_HANDLER(Detach)
|
GDB_DECLARE_HANDLER(Detach)
|
||||||
{
|
{
|
||||||
ctx->state = GDB_STATE_DETACHING;
|
ctx->state = GDB_STATE_DETACHING;
|
||||||
|
@ -143,151 +372,3 @@ GDB_DECLARE_VERBOSE_HANDLER(Continue)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GDB_DECLARE_HANDLER(GetStopReason)
|
|
||||||
{
|
|
||||||
if (ctx->processEnded && ctx->processExited) {
|
|
||||||
return GDB_SendPacket(ctx, "W00", 3);
|
|
||||||
} else if (ctx->processEnded && !ctx->processExited) {
|
|
||||||
return GDB_SendPacket(ctx, "X0f", 3);
|
|
||||||
} else if (!GDB_IsAttached(ctx)) {
|
|
||||||
return GDB_SendPacket(ctx, "W00", 3);
|
|
||||||
} else {
|
|
||||||
// TODO //return GDB_SendStopReply(ctx, &ctx->latestDebugEvent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int GDB_ParseExceptionFrame(char *out, const DebugEventInfo *info, int sig)
|
|
||||||
{
|
|
||||||
u32 coreId = info->coreId;
|
|
||||||
ExceptionStackFrame *frame = info->frame;
|
|
||||||
|
|
||||||
int n = sprintf(out, "T%02xthread:%lx;core:%lx;", sig, 1 + coreId, coreId);
|
|
||||||
|
|
||||||
// Dump the GPRs & sp & pc & cpsr (cpsr is 32-bit in the xml desc)
|
|
||||||
// For performance reasons, we don't include the FPU registers here
|
|
||||||
for (u32 i = 0; i < 31; i++) {
|
|
||||||
n += sprintf(out + n, "%x:%016lx;", i, __builtin_bswap64(readFrameRegister(frame, i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
n += sprintf(
|
|
||||||
out + n,
|
|
||||||
"1f:%016lx;20:%016lx;21:%08x",
|
|
||||||
__builtin_bswap64(*exceptionGetSpPtr(frame)),
|
|
||||||
__builitin_bswap32((u32)frame->spsr_el2)
|
|
||||||
);
|
|
||||||
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info)
|
|
||||||
{
|
|
||||||
char buffer[GDB_BUF_LEN + 1];
|
|
||||||
int n;
|
|
||||||
|
|
||||||
buffer[0] = 0;
|
|
||||||
|
|
||||||
switch(info->type) {
|
|
||||||
case DBGEVENT_DEBUGGER_BREAK: {
|
|
||||||
strcpy(buffer, "S02");
|
|
||||||
}
|
|
||||||
|
|
||||||
case DBGEVENT_CORE_ON: {
|
|
||||||
if (ctx->catchThreadEvents) {
|
|
||||||
ctx->currentThreadId = info->coreId + 1; // FIXME?
|
|
||||||
strcpy(buffer, "T05create:;");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case DBGEVENT_CORE_OFF: {
|
|
||||||
if(ctx->catchThreadEvents) {
|
|
||||||
sprintf(buffer, "w0;%x", info->coreId + 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case DBGEVENT_EXIT: {
|
|
||||||
// exited (no error / unhandled exception), SIGTERM (process terminated) * 2
|
|
||||||
static const char *processExitReplies[] = { "W00", "X0f" };
|
|
||||||
strcpy(buffer, processExitReplies[ctx->processExited ? 0 : 1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case DBGEVENT_EXCEPTION: {
|
|
||||||
ExceptionClass ec = info->frame->esr_el2.ec;
|
|
||||||
ctx->currentThreadId = info->coreId + 1; // FIXME?
|
|
||||||
|
|
||||||
// Aside from stage 2 translation faults and other pre-handled exceptions,
|
|
||||||
// the only notable exceptions we get are stop point/single step events from the debugee (basically classes 0x3x)
|
|
||||||
switch(ec) {
|
|
||||||
case Exception_BreakpointLowerEl: {
|
|
||||||
n = GDB_ParseExceptionFrame(buffer, ctx, SIGTRAP);
|
|
||||||
strcat(buffer + n, "hwbreak:;");
|
|
||||||
}
|
|
||||||
|
|
||||||
case Exception_WatchpointLowerEl: {
|
|
||||||
static const char *kinds[] = { "", "r", "", "a" };
|
|
||||||
// Note: exception info doesn't provide us with the access size. Use 1.
|
|
||||||
bool wnr = (info->frame->esr_el2.iss & BIT(6)) != 0;
|
|
||||||
WatchpointLoadStoreControl dr = wnr ? WatchpointLoadStoreControl_Store : WatchpointLoadStoreControl_Load;
|
|
||||||
DebugControlRegister cr = retrieveSplitWatchpointConfig(info->frame->far_el2, 1, dr, false);
|
|
||||||
if (!cr.enabled) {
|
|
||||||
DEBUG("GDB: oops, unhandled watchpoint for core id %u, far=%016lx\n", info->coreId, info->frame->far_el2);
|
|
||||||
} else {
|
|
||||||
n = GDB_ParseExceptionFrame(buffer, ctx, SIGTRAP);
|
|
||||||
sprintf(buffer + n, "%swatch:%016lx;", kinds[cr.lsc], info->frame->far_el2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: we don't really support 32-bit sw breakpoints, we'll still report them
|
|
||||||
// if the guest has inserted some of them manually...
|
|
||||||
case Exception_SoftwareBreakpointA64:
|
|
||||||
case Exception_SoftwareBreakpointA32: {
|
|
||||||
n = GDB_ParseExceptionFrame(buffer, ctx, SIGTRAP);
|
|
||||||
strcat(buffer + n, "swbreak:;");
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
DEBUG("GDB: oops, unhandled exception for core id %u\n", info->coreId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case DBGEVENT_OUTPUT_STRING: {
|
|
||||||
uintptr_t addr = info->outputString.address;
|
|
||||||
size_t remaining = info->outputString.size;
|
|
||||||
size_t sent = 0;
|
|
||||||
size_t total = 0;
|
|
||||||
while (remaining > 0) {
|
|
||||||
size_t pending = (GDB_BUF_LEN - 1) / 2;
|
|
||||||
pending = pending < remaining ? pending : remaining;
|
|
||||||
|
|
||||||
int res = GDB_SendMemory(ctx, "O", 1, addr + sent, pending);
|
|
||||||
if(res < 0 || res != 5 + 2 * pending)
|
|
||||||
break;
|
|
||||||
|
|
||||||
sent += pending;
|
|
||||||
remaining -= pending;
|
|
||||||
total += res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int)total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: HIO
|
|
||||||
|
|
||||||
default: {
|
|
||||||
DEBUG("GDB: unknown exception type %u, core id %u\n", (u32)info->type, info->coreId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer[0] == 0) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return GDB_SendPacket(ctx, buffer, strlen(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,6 +11,12 @@
|
||||||
#include "../core_ctx.h"
|
#include "../core_ctx.h"
|
||||||
#include "../debug_manager.h"
|
#include "../debug_manager.h"
|
||||||
|
|
||||||
|
void GDB_ContinueExecution(GDBContext *ctx);
|
||||||
|
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info, bool asNotification);
|
||||||
|
int GDB_TrySignalDebugEvent(GDBContext *ctx, DebugEventInfo *info);
|
||||||
|
|
||||||
|
GDB_DECLARE_VERBOSE_HANDLER(Stopped);
|
||||||
|
|
||||||
GDB_DECLARE_HANDLER(Detach);
|
GDB_DECLARE_HANDLER(Detach);
|
||||||
GDB_DECLARE_HANDLER(Kill);
|
GDB_DECLARE_HANDLER(Kill);
|
||||||
GDB_DECLARE_HANDLER(Break);
|
GDB_DECLARE_HANDLER(Break);
|
||||||
|
@ -18,7 +24,4 @@ GDB_DECLARE_HANDLER(Continue);
|
||||||
GDB_DECLARE_VERBOSE_HANDLER(Continue);
|
GDB_DECLARE_VERBOSE_HANDLER(Continue);
|
||||||
GDB_DECLARE_HANDLER(GetStopReason);
|
GDB_DECLARE_HANDLER(GetStopReason);
|
||||||
|
|
||||||
void GDB_ContinueExecution(GDBContext *ctx);
|
|
||||||
int GDB_SendStopReply(GDBContext *ctx, const DebugEventInfo *info);
|
|
||||||
int GDB_HandleDebugEvents(GDBContext *ctx);
|
|
||||||
//void GDB_BreakProcessAndSinkDebugEvents(GDBContext *ctx, DebugFlags flags);
|
//void GDB_BreakProcessAndSinkDebugEvents(GDBContext *ctx, DebugFlags flags);
|
||||||
|
|
|
@ -295,6 +295,21 @@ int GDB_SendHexPacket(GDBContext *ctx, const void *packetData, size_t len)
|
||||||
return GDB_DoSendPacket(ctx, 4 + 2 * len);
|
return GDB_DoSendPacket(ctx, 4 + 2 * len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GDB_SendNotificationPacket(GDBContext *ctx, const char *packetData, size_t len)
|
||||||
|
{
|
||||||
|
if (packetData != ctx->buffer + 1) {
|
||||||
|
memmove(ctx->buffer + 1, packetData, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->buffer[0] = '%';
|
||||||
|
|
||||||
|
char *checksumLoc = ctx->buffer + len + 1;
|
||||||
|
*checksumLoc++ = '#';
|
||||||
|
|
||||||
|
hexItoa(GDB_ComputeChecksum(ctx->buffer + 1, len), checksumLoc, 2, false);
|
||||||
|
return GDB_DoSendPacket(ctx, 4 + len);
|
||||||
|
}
|
||||||
|
|
||||||
int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast)
|
int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast)
|
||||||
{
|
{
|
||||||
// GDB_BUF_LEN does not include the usual %#<1-byte checksum>
|
// GDB_BUF_LEN does not include the usual %#<1-byte checksum>
|
||||||
|
|
|
@ -24,6 +24,7 @@ int GDB_ReceivePacket(GDBContext *ctx);
|
||||||
int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len);
|
int GDB_SendPacket(GDBContext *ctx, const char *packetData, size_t len);
|
||||||
int GDB_SendFormattedPacket(GDBContext *ctx, const char *packetDataFmt, ...);
|
int GDB_SendFormattedPacket(GDBContext *ctx, const char *packetDataFmt, ...);
|
||||||
int GDB_SendHexPacket(GDBContext *ctx, const void *packetData, size_t len);
|
int GDB_SendHexPacket(GDBContext *ctx, const void *packetData, size_t len);
|
||||||
|
int GDB_SendNotificationPacket(GDBContext *ctx, const char *packetData, size_t len);
|
||||||
int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast);
|
int GDB_SendStreamData(GDBContext *ctx, const char *streamData, size_t offset, size_t length, size_t totalSize, bool forceEmptyLast);
|
||||||
int GDB_ReplyEmpty(GDBContext *ctx);
|
int GDB_ReplyEmpty(GDBContext *ctx);
|
||||||
int GDB_ReplyOk(GDBContext *ctx);
|
int GDB_ReplyOk(GDBContext *ctx);
|
||||||
|
|
|
@ -50,7 +50,7 @@ GDB_DECLARE_HANDLER(IsThreadAlive)
|
||||||
return GDB_ReplyErrno(ctx, EILSEQ);
|
return GDB_ReplyErrno(ctx, EILSEQ);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 coreMask = getActiveCoreMask();
|
u32 coreMask = ctx->attachedCoreList;
|
||||||
return (coreMask & BIT(threadId)) != 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, ESRCH);
|
return (coreMask & BIT(threadId)) != 0 ? GDB_ReplyOk(ctx) : GDB_ReplyErrno(ctx, ESRCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ GDB_DECLARE_QUERY_HANDLER(fThreadInfo)
|
||||||
int n = 1;
|
int n = 1;
|
||||||
buf[0] = 'm';
|
buf[0] = 'm';
|
||||||
|
|
||||||
u32 coreMask = getActiveCoreMask();
|
u32 coreMask = ctx->attachedCoreList;
|
||||||
|
|
||||||
FOREACH_BIT (tmp, coreId, coreMask) {
|
FOREACH_BIT (tmp, coreId, coreMask) {
|
||||||
n += sprintf(buf + n, "%x,", 1 + coreId);
|
n += sprintf(buf + n, "%x,", 1 + coreId);
|
||||||
|
@ -86,6 +86,7 @@ GDB_DECLARE_QUERY_HANDLER(fThreadInfo)
|
||||||
GDB_DECLARE_QUERY_HANDLER(sThreadInfo)
|
GDB_DECLARE_QUERY_HANDLER(sThreadInfo)
|
||||||
{
|
{
|
||||||
// We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) in fThreadInfo
|
// We have made our GDB packet big enough to list all the thread ids (coreIds + 1 for each coreId) in fThreadInfo
|
||||||
|
// Note: we assume GDB doesn't accept notifications during the sequence transfer...
|
||||||
return GDB_SendPacket(ctx, "m", 1);
|
return GDB_SendPacket(ctx, "m", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue