diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h index 6283ff0a4eee..2efb54e1ce7f 100644 --- a/gdb/nat/windows-nat.h +++ b/gdb/nat/windows-nat.h @@ -205,6 +205,13 @@ struct windows_process_info virtual bool handle_access_violation (const EXCEPTION_RECORD *rec) = 0; + /* Fill in the thread's CONTEXT/WOW64_CONTEXT, if it wasn't filled + in yet. + + This function must be supplied by the embedding application. */ + + virtual void fill_thread_context (windows_thread_info *th) = 0; + handle_exception_result handle_exception (DEBUG_EVENT ¤t_event, struct target_waitstatus *ourstatus, bool debug_exceptions); diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index bdabe67ded6f..e11cc9528c6c 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -114,6 +114,7 @@ struct windows_per_inferior : public windows_process_info bool handle_access_violation (const EXCEPTION_RECORD *rec) override; void invalidate_context (windows_thread_info *th); + void fill_thread_context (windows_thread_info *th) override; void continue_one_thread (windows_thread_info *th, windows_continue_flags cont_flags); @@ -747,17 +748,10 @@ windows_fetch_one_register (struct regcache *regcache, } void -windows_nat_target::fetch_registers (struct regcache *regcache, int r) +windows_per_inferior::fill_thread_context (windows_thread_info *th) { - windows_thread_info *th = windows_process.find_thread (regcache->ptid ()); - - /* Check if TH exists. Windows sometimes uses a non-existent - thread id in its events. */ - if (th == NULL) - return; - #ifdef __x86_64__ - if (windows_process.wow64_process) + if (wow64_process) { if (th->wow64_context.ContextFlags == 0) { @@ -774,6 +768,19 @@ windows_nat_target::fetch_registers (struct regcache *regcache, int r) CHECK (GetThreadContext (th->h, &th->context)); } } +} + +void +windows_nat_target::fetch_registers (struct regcache *regcache, int r) +{ + windows_thread_info *th = windows_process.find_thread (regcache->ptid ()); + + /* Check if TH exists. Windows sometimes uses a non-existent + thread id in its events. */ + if (th == nullptr) + return; + + windows_process.fill_thread_context (th); if (r < 0) for (r = 0; r < gdbarch_num_regs (regcache->arch()); r++) @@ -1292,36 +1299,83 @@ windows_per_inferior::continue_one_thread (windows_thread_info *th, DWORD &context_flags_ref = (wow64_process ? th->wow64_context.ContextFlags : th->context.ContextFlags); + const DWORD64 dr6 = (wow64_process + ? th->wow64_context.Dr6 + : th->context.Dr6); #else DWORD &context_flags_ref = th->context.ContextFlags; + const DWORD dr6 = th->context.Dr6; #endif if (th->debug_registers_changed) { - context_flags_ref |= CONTEXT_DEBUG_REGISTERS; -#ifdef __x86_64__ - if (wow64_process) + windows_process.fill_thread_context (th); + + gdb_assert ((context_flags_ref & CONTEXT_DEBUG_REGISTERS) != 0); + + /* Check whether the thread has Dr6 set indicating a + watchpoint hit, and we haven't seen the watchpoint event + yet (reported as + EXCEPTION_SINGLE_STEP/STATUS_WX86_SINGLE_STEP). In that + case, don't change the debug registers. Changing debug + registers, even if to the same values, makes the kernel + clear Dr6. The result would be we would lose the + unreported watchpoint hit. */ + if ((dr6 & ~DR6_CLEAR_VALUE) != 0) { - th->wow64_context.Dr0 = state->dr_mirror[0]; - th->wow64_context.Dr1 = state->dr_mirror[1]; - th->wow64_context.Dr2 = state->dr_mirror[2]; - th->wow64_context.Dr3 = state->dr_mirror[3]; - th->wow64_context.Dr6 = DR6_CLEAR_VALUE; - th->wow64_context.Dr7 = state->dr_control_mirror; + if (th->last_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT + && (th->last_event.u.Exception.ExceptionRecord.ExceptionCode + == EXCEPTION_SINGLE_STEP + || (th->last_event.u.Exception.ExceptionRecord.ExceptionCode + == STATUS_WX86_SINGLE_STEP))) + { + DEBUG_EVENTS ("0x%x already reported watchpoint", th->tid); + } + else + { + DEBUG_EVENTS ("0x%x last reported something else (0x%x)", + th->tid, + th->last_event.dwDebugEventCode); + + /* Don't touch debug registers. Let the pending + watchpoint event be reported instead. We will + update the debug registers later when the thread + is re-resumed by the core after the watchpoint + event. */ + context_flags_ref &= ~CONTEXT_DEBUG_REGISTERS; + } } else -#endif + DEBUG_EVENTS ("0x%x has no dr6 set", th->tid); + + if ((context_flags_ref & CONTEXT_DEBUG_REGISTERS) != 0) { - th->context.Dr0 = state->dr_mirror[0]; - th->context.Dr1 = state->dr_mirror[1]; - th->context.Dr2 = state->dr_mirror[2]; - th->context.Dr3 = state->dr_mirror[3]; - th->context.Dr6 = DR6_CLEAR_VALUE; - th->context.Dr7 = state->dr_control_mirror; - } + DEBUG_EVENTS ("0x%x changing dregs", th->tid); +#ifdef __x86_64__ + if (wow64_process) + { + th->wow64_context.Dr0 = state->dr_mirror[0]; + th->wow64_context.Dr1 = state->dr_mirror[1]; + th->wow64_context.Dr2 = state->dr_mirror[2]; + th->wow64_context.Dr3 = state->dr_mirror[3]; + th->wow64_context.Dr6 = DR6_CLEAR_VALUE; + th->wow64_context.Dr7 = state->dr_control_mirror; + } + else +#endif + { + th->context.Dr0 = state->dr_mirror[0]; + th->context.Dr1 = state->dr_mirror[1]; + th->context.Dr2 = state->dr_mirror[2]; + th->context.Dr3 = state->dr_mirror[3]; + th->context.Dr6 = DR6_CLEAR_VALUE; + th->context.Dr7 = state->dr_control_mirror; + } - th->debug_registers_changed = false; + th->debug_registers_changed = false; + } } + if (context_flags_ref != 0) { DWORD ec = 0; diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc index 004bf94c83a9..65b01dc97ac6 100644 --- a/gdbserver/win32-low.cc +++ b/gdbserver/win32-low.cc @@ -141,6 +141,14 @@ win32_require_context (windows_thread_info *th) /* See nat/windows-nat.h. */ +void +gdbserver_windows_process::fill_thread_context (windows_thread_info *th) +{ + win32_require_context (th); +} + +/* See nat/windows-nat.h. */ + windows_thread_info * gdbserver_windows_process::find_thread (ptid_t ptid) { diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h index e99e47ea8295..ea2a9b4c5b67 100644 --- a/gdbserver/win32-low.h +++ b/gdbserver/win32-low.h @@ -181,6 +181,8 @@ struct gdbserver_windows_process : public windows_nat::windows_process_info void handle_unload_dll (const DEBUG_EVENT ¤t_event) override; bool handle_access_violation (const EXCEPTION_RECORD *rec) override; + void fill_thread_context (windows_nat::windows_thread_info *th) override; + int attaching = 0; /* A status that hasn't been reported to the core yet, and so