winedbg: Add support for hardware watchpoints.

Signed-off-by: Rémi Bernon <rbernon@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Rémi Bernon 2020-04-04 11:30:14 +02:00 committed by Alexandre Julliard
parent 40f1c6693b
commit 729a2462fb
1 changed files with 186 additions and 0 deletions

View File

@ -67,6 +67,17 @@
WINE_DEFAULT_DEBUG_CHANNEL(winedbg);
struct gdb_xpoint
{
struct list entry;
int pid;
int tid;
enum be_xpoint_type type;
void *addr;
int size;
unsigned long value;
};
struct gdb_context
{
/* gdb information */
@ -86,6 +97,7 @@ struct gdb_context
/* generic GDB thread information */
int exec_tid; /* tid used in step & continue */
int other_tid; /* tid to be used in any other operation */
struct list xpoint_list;
/* current Win32 trap env */
DEBUG_EVENT de;
DWORD de_reply;
@ -98,6 +110,64 @@ struct gdb_context
BOOL no_ack_mode;
};
static void gdbctx_delete_xpoint(struct gdb_context *gdbctx, struct dbg_thread *thread,
dbg_ctx_t *ctx, struct gdb_xpoint *x)
{
struct dbg_process *process = thread->process;
struct backend_cpu *cpu = process->be_cpu;
if (!cpu->remove_Xpoint(process->handle, process->process_io, ctx, x->type, x->addr, x->value, x->size))
ERR("%04x:%04x: Couldn't remove breakpoint at:%p/%x type:%d\n", process->pid, thread->tid, x->addr, x->size, x->type);
list_remove(&x->entry);
HeapFree(GetProcessHeap(), 0, x);
}
static void gdbctx_insert_xpoint(struct gdb_context *gdbctx, struct dbg_thread *thread,
dbg_ctx_t *ctx, enum be_xpoint_type type, void *addr, int size)
{
struct dbg_process *process = thread->process;
struct backend_cpu *cpu = process->be_cpu;
struct gdb_xpoint *x;
unsigned long value;
if (!cpu->insert_Xpoint(process->handle, process->process_io, ctx, type, addr, &value, size))
{
ERR("%04x:%04x: Couldn't insert breakpoint at:%p/%x type:%d\n", process->pid, thread->tid, addr, size, type);
return;
}
if (!(x = HeapAlloc(GetProcessHeap(), 0, sizeof(struct gdb_xpoint))))
{
ERR("%04x:%04x: Couldn't allocate memory for breakpoint at:%p/%x type:%d\n", process->pid, thread->tid, addr, size, type);
return;
}
x->pid = process->pid;
x->tid = thread->tid;
x->type = type;
x->addr = addr;
x->size = size;
x->value = value;
list_add_head(&gdbctx->xpoint_list, &x->entry);
}
static struct gdb_xpoint *gdb_find_xpoint(struct gdb_context *gdbctx, struct dbg_thread *thread,
enum be_xpoint_type type, void *addr, int size)
{
struct gdb_xpoint *x;
LIST_FOR_EACH_ENTRY(x, &gdbctx->xpoint_list, struct gdb_xpoint, entry)
{
if (thread && (x->pid != thread->process->pid || x->tid != thread->tid))
continue;
if (x->type == type && x->addr == addr && x->size == size)
return x;
}
return NULL;
}
static BOOL tgt_process_gdbproxy_read(HANDLE hProcess, const void* addr,
void* buffer, SIZE_T len, SIZE_T* rlen)
{
@ -775,6 +845,34 @@ static inline void packet_reply_register_hex_to(struct gdb_context* gdbctx, dbg_
* =============================================== *
*/
static void packet_reply_status_xpoints(struct gdb_context* gdbctx, struct dbg_thread *thread,
dbg_ctx_t *ctx)
{
struct dbg_process *process = thread->process;
struct backend_cpu *cpu = process->be_cpu;
struct gdb_xpoint *x;
LIST_FOR_EACH_ENTRY(x, &gdbctx->xpoint_list, struct gdb_xpoint, entry)
{
if (x->pid != process->pid || x->tid != thread->tid)
continue;
if (!cpu->is_watchpoint_set(ctx, x->value))
continue;
if (x->type == be_xpoint_watch_write)
{
packet_reply_add(gdbctx, "watch:");
packet_reply_val(gdbctx, (unsigned long)x->addr, sizeof(x->addr));
packet_reply_add(gdbctx, ";");
}
if (x->type == be_xpoint_watch_read)
{
packet_reply_add(gdbctx, "rwatch:");
packet_reply_val(gdbctx, (unsigned long)x->addr, sizeof(x->addr));
packet_reply_add(gdbctx, ";");
}
}
}
static enum packet_return packet_reply_status(struct gdb_context* gdbctx)
{
struct dbg_process *process = gdbctx->process;
@ -800,6 +898,7 @@ static enum packet_return packet_reply_status(struct gdb_context* gdbctx)
packet_reply_add(gdbctx, "thread:");
packet_reply_val(gdbctx, gdbctx->de.dwThreadId, 4);
packet_reply_add(gdbctx, ";");
packet_reply_status_xpoints(gdbctx, thread, &ctx);
for (i = 0; i < backend->gdb_num_regs; i++)
{
@ -924,6 +1023,90 @@ static enum packet_return packet_continue_signal(struct gdb_context* gdbctx)
return packet_reply_status(gdbctx);
}
static enum packet_return packet_delete_breakpoint(struct gdb_context* gdbctx)
{
struct dbg_process *process = gdbctx->process;
struct dbg_thread *thread;
struct backend_cpu *cpu;
struct gdb_xpoint *x;
dbg_ctx_t ctx;
char type;
void *addr;
int size;
if (!process) return packet_error;
if (!(cpu = process->be_cpu)) return packet_error;
if (sscanf(gdbctx->in_packet, "%c,%p,%x", &type, &addr, &size) < 3)
return packet_error;
if (type == '0')
return packet_error;
LIST_FOR_EACH_ENTRY(thread, &process->threads, struct dbg_thread, entry)
{
if (!cpu->get_context(thread->handle, &ctx))
continue;
if ((type == '1') && (x = gdb_find_xpoint(gdbctx, thread, be_xpoint_watch_exec, addr, size)))
gdbctx_delete_xpoint(gdbctx, thread, &ctx, x);
if ((type == '2' || type == '4') && (x = gdb_find_xpoint(gdbctx, thread, be_xpoint_watch_read, addr, size)))
gdbctx_delete_xpoint(gdbctx, thread, &ctx, x);
if ((type == '3' || type == '4') && (x = gdb_find_xpoint(gdbctx, thread, be_xpoint_watch_write, addr, size)))
gdbctx_delete_xpoint(gdbctx, thread, &ctx, x);
cpu->set_context(thread->handle, &ctx);
}
while ((type == '1') && (x = gdb_find_xpoint(gdbctx, NULL, be_xpoint_watch_exec, addr, size)))
gdbctx_delete_xpoint(gdbctx, NULL, NULL, x);
while ((type == '2' || type == '4') && (x = gdb_find_xpoint(gdbctx, NULL, be_xpoint_watch_read, addr, size)))
gdbctx_delete_xpoint(gdbctx, NULL, NULL, x);
while ((type == '3' || type == '4') && (x = gdb_find_xpoint(gdbctx, NULL, be_xpoint_watch_write, addr, size)))
gdbctx_delete_xpoint(gdbctx, NULL, NULL, x);
return packet_ok;
}
static enum packet_return packet_insert_breakpoint(struct gdb_context* gdbctx)
{
struct dbg_process *process = gdbctx->process;
struct dbg_thread *thread;
struct backend_cpu *cpu;
dbg_ctx_t ctx;
char type;
void *addr;
int size;
if (!process) return packet_error;
if (!(cpu = process->be_cpu)) return packet_error;
if (memchr(gdbctx->in_packet, ';', gdbctx->in_packet_len))
{
FIXME("breakpoint commands not supported\n");
return packet_error;
}
if (sscanf(gdbctx->in_packet, "%c,%p,%x", &type, &addr, &size) < 3)
return packet_error;
if (type == '0')
return packet_error;
LIST_FOR_EACH_ENTRY(thread, &process->threads, struct dbg_thread, entry)
{
if (!cpu->get_context(thread->handle, &ctx))
continue;
if (type == '1')
gdbctx_insert_xpoint(gdbctx, thread, &ctx, be_xpoint_watch_exec, addr, size);
if (type == '2' || type == '4')
gdbctx_insert_xpoint(gdbctx, thread, &ctx, be_xpoint_watch_read, addr, size);
if (type == '3' || type == '4')
gdbctx_insert_xpoint(gdbctx, thread, &ctx, be_xpoint_watch_write, addr, size);
cpu->set_context(thread->handle, &ctx);
}
return packet_ok;
}
static enum packet_return packet_detach(struct gdb_context* gdbctx)
{
detach_debuggee(gdbctx, FALSE);
@ -1815,6 +1998,8 @@ static struct packet_entry packet_entries[] =
{'s', packet_step},
{'T', packet_thread_alive},
{'v', packet_verbose},
{'z', packet_delete_breakpoint},
{'Z', packet_insert_breakpoint},
};
static BOOL extract_packets(struct gdb_context* gdbctx)
@ -2069,6 +2254,7 @@ static BOOL gdb_init_context(struct gdb_context* gdbctx, unsigned flags, unsigne
gdbctx->exec_tid = -1;
gdbctx->other_tid = -1;
list_init(&gdbctx->xpoint_list);
gdbctx->last_sig = 0;
gdbctx->in_trap = FALSE;
gdbctx->process = NULL;