* [pushed 00/28] Windows non-stop prep
@ 2026-04-24 20:44 Pedro Alves
2026-04-24 20:44 ` [pushed 01/28] Windows gdb+gdbserver: New find_thread, replaces thread_rec(DONT_INVALIDATE_CONTEXT) Pedro Alves
` (27 more replies)
0 siblings, 28 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches
I've now merged all the patches from the Windows non-stop v2 series,
from here:
https://inbox.sourceware.org/gdb-patches/20250519132308.3553663-1-pedro@palves.net/
... up to patch 34. Those which hadn't been merged yet, that is.
They had all been approved last year by Tromey. Thank you very much,
Tromey!
Many of the patches here didn't apply cleanly on current master, due
to pervasive thread pointer <=> reference changes that had gone in
meanwhile, and also due to code in windows-nat.c having moved to other
files for the Aarch64 port. But other than that, the patches are
largely the same.
I'll handle the rest of the series, including the actual non-stop
bits, soon.
Pedro Alves (28):
Windows gdb+gdbserver: New find_thread, replaces
thread_rec(DONT_INVALIDATE_CONTEXT)
Windows gdb: handle_output_debug_string return type
Windows gdb: Eliminate reload_context
Windows gdb+gdbserver: Eliminate thread_rec(INVALIDATE_CONTEXT) calls
Windows gdb+gdbserver: Eliminate DONT_SUSPEND
Windows gdb+gdbserver: Eliminate windows_process_info::thread_rec
Windows gdb: Simplify windows_nat_target::wait
Windows gdb+gdbserver: Move suspending thread to when returning event
Windows gdb: Introduce continue_last_debug_event_main_thread
Windows gdb: Introduce windows_continue_flags
Windows gdb: Factor code out of windows_nat_target::windows_continue
Windows gdb: Pending stop and current_event
Windows gdb+gdbserver: Elim desired_stop_thread_id / rework
pending_stops
Windows gdb+gdbserver: Introduce get_last_debug_event_ptid
Windows gdb: Can't pass signal to thread other than last stopped
thread
Windows gdbserver: Fix scheduler-locking
Windows gdb: Enable "set scheduler-locking on"
Windows gdbserver: Eliminate soft-interrupt mechanism
Windows gdb+gdbserver: Make current_event per-thread state
Windows gdb+gdbserver: Make last_sig per-thread state
Windows gdb+gdbserver: Make siginfo_er per-thread state
Add backpointer from windows_thread_info to windows_process_info
Windows gdb+gdbserver: Share $_siginfo reading code
Windows gdb+gdbserver: Eliminate struct pending_stop
Windows gdb: Change serial_event management
Windows gdb: cygwin_set_dr => windows_set_dr, etc.
Windows gdb: Move debug macros to windows-nat.h
Windows gdb: Avoid writing debug registers if watchpoint hit pending
gdb/aarch64-windows-nat.c | 26 +-
gdb/nat/windows-nat.c | 130 +++++-----
gdb/nat/windows-nat.h | 142 +++++------
gdb/windows-nat.c | 485 ++++++++++++++++++--------------------
gdb/windows-nat.h | 69 +++++-
gdb/x86-windows-nat.c | 117 ++++++---
gdbserver/win32-low.cc | 398 ++++++++++++++-----------------
gdbserver/win32-low.h | 19 +-
8 files changed, 710 insertions(+), 676 deletions(-)
base-commit: a6602675786257f53094ff1df58e24a70c01c2b9
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 01/28] Windows gdb+gdbserver: New find_thread, replaces thread_rec(DONT_INVALIDATE_CONTEXT)
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 02/28] Windows gdb: handle_output_debug_string return type Pedro Alves
` (26 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
The goal of the next few patches is to eliminate thread_rec
completely. This is the first patch in that effort.
thread_rec(DONT_INVALIDATE_CONTEXT) is really just a thread lookup
with no side effects, so this adds a find_thread function that lets
you do that.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ie486badce00e234b10caa478b066c34537103e3f
---
gdb/nat/windows-nat.c | 5 ++--
gdb/nat/windows-nat.h | 7 +++--
gdb/windows-nat.c | 68 +++++++++++++++++++++---------------------
gdb/windows-nat.h | 1 +
gdb/x86-windows-nat.c | 5 ++--
gdbserver/win32-low.cc | 32 +++++++++++---------
gdbserver/win32-low.h | 1 +
7 files changed, 62 insertions(+), 57 deletions(-)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index a74bab75238..cefd335f652 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -315,9 +315,8 @@ windows_process_info::handle_ms_vc_exception (const EXCEPTION_RECORD *rec)
if (named_thread_id == (DWORD) -1)
named_thread_id = current_event.dwThreadId;
- named_thread = thread_rec (ptid_t (current_event.dwProcessId,
- named_thread_id, 0),
- DONT_INVALIDATE_CONTEXT);
+ named_thread = find_thread (ptid_t (current_event.dwProcessId,
+ named_thread_id, 0));
if (named_thread != NULL)
{
int thread_name_len;
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index f4b3669df1b..32f055a7a52 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -111,9 +111,6 @@ struct windows_thread_info
/* Possible values to pass to 'thread_rec'. */
enum thread_disposition_type
{
- /* Do not invalidate the thread's context, and do not suspend the
- thread. */
- DONT_INVALIDATE_CONTEXT,
/* Invalidate the context, but do not suspend the thread. */
DONT_SUSPEND,
/* Invalidate the context and suspend the thread. */
@@ -184,6 +181,10 @@ struct windows_process_info
bool ignore_first_breakpoint = false;
#endif
+ /* Find a thread record given a thread id.
+
+ This function must be supplied by the embedding application. */
+ virtual windows_thread_info *find_thread (ptid_t ptid) = 0;
/* Find a thread record given a thread id. THREAD_DISPOSITION
controls whether the thread is suspended, and whether the context
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 65dfc0c1800..46385ff87c5 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -264,34 +264,38 @@ windows_nat_target::wait_for_debug_event_main_thread (DEBUG_EVENT *event)
/* See nat/windows-nat.h. */
windows_thread_info *
-windows_per_inferior::thread_rec
- (ptid_t ptid, thread_disposition_type disposition)
+windows_per_inferior::find_thread (ptid_t ptid)
{
for (auto &th : thread_list)
if (th->tid == ptid.lwp ())
- {
- if (!th->suspended)
- {
- switch (disposition)
- {
- case DONT_INVALIDATE_CONTEXT:
- /* Nothing. */
- break;
- case INVALIDATE_CONTEXT:
- if (ptid.lwp () != current_event.dwThreadId)
- th->suspend ();
- th->reload_context = true;
- break;
- case DONT_SUSPEND:
- th->reload_context = true;
- th->suspended = -1;
- break;
- }
- }
- return th.get ();
- }
+ return th.get ();
+ return nullptr;
+}
+
+/* See nat/windows-nat.h. */
- return NULL;
+windows_thread_info *
+windows_per_inferior::thread_rec (ptid_t ptid,
+ thread_disposition_type disposition)
+{
+ windows_thread_info *th = find_thread (ptid);
+
+ if (th != nullptr && !th->suspended)
+ {
+ switch (disposition)
+ {
+ case INVALIDATE_CONTEXT:
+ if (ptid.lwp () != current_event.dwThreadId)
+ th->suspend ();
+ th->reload_context = true;
+ break;
+ case DONT_SUSPEND:
+ th->reload_context = true;
+ th->suspended = -1;
+ break;
+ }
+ }
+ return th;
}
/* Add a thread to the thread list.
@@ -310,7 +314,7 @@ windows_nat_target::add_thread (ptid_t ptid, HANDLE h, void *tlb,
gdb_assert (ptid.lwp () != 0);
- if ((th = windows_process->thread_rec (ptid, DONT_INVALIDATE_CONTEXT)))
+ if ((th = windows_process->find_thread (ptid)))
return th;
CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb;
@@ -434,8 +438,7 @@ windows_nat_target::store_registers (struct regcache *regcache, int r)
bool
windows_nat_target::stopped_by_sw_breakpoint ()
{
- windows_thread_info *th
- = windows_process->thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process->find_thread (inferior_ptid);
return th->stopped_at_software_breakpoint;
}
@@ -843,7 +846,7 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
ptid.pid (), (unsigned) ptid.lwp (), step, sig);
/* Get context for currently selected thread. */
- th = windows_process->thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT);
+ th = windows_process->find_thread (inferior_ptid);
if (th != nullptr && step)
{
/* Single step by setting t bit. */
@@ -2510,7 +2513,7 @@ windows_nat_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr)
{
windows_thread_info *th;
- th = windows_process->thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
+ th = windows_process->find_thread (ptid);
if (th == NULL)
return false;
@@ -2531,9 +2534,7 @@ windows_nat_target::get_ada_task_ptid (long lwp, ULONGEST thread)
const char *
windows_nat_target::thread_name (struct thread_info *thr)
{
- windows_thread_info *th
- = windows_process->thread_rec (thr->ptid,
- DONT_INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process->find_thread (thr->ptid);
return th->thread_name ();
}
@@ -2634,8 +2635,7 @@ windows_nat_target::thread_alive (ptid_t ptid)
{
gdb_assert (ptid.lwp () != 0);
- windows_thread_info *th
- = windows_process->thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process->find_thread (ptid);
return WaitForSingleObject (th->h, 0) != WAIT_OBJECT_0;
}
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index 4368a29b2bb..10c144bf4c6 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -47,6 +47,7 @@ struct windows_solib
struct windows_per_inferior : public windows_nat::windows_process_info
{
+ windows_thread_info *find_thread (ptid_t ptid) override;
windows_thread_info *thread_rec (ptid_t ptid,
thread_disposition_type disposition) override;
int handle_output_debug_string (struct target_waitstatus *ourstatus) override;
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index c7be192a894..5a82a2e0e36 100644
--- a/gdb/x86-windows-nat.c
+++ b/gdb/x86-windows-nat.c
@@ -307,8 +307,7 @@ cygwin_set_dr7 (unsigned long val)
static CORE_ADDR
cygwin_get_dr (int i)
{
- windows_thread_info *th
- = windows_process->thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process->find_thread (inferior_ptid);
return windows_process->with_context (th, [&] (auto *context) -> CORE_ADDR
{
@@ -443,7 +442,7 @@ display_selectors (const char * args, int from_tty)
}
windows_thread_info *current_windows_thread
- = windows_process->thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT);
+ = windows_process->find_thread (inferior_ptid);
if (!args)
{
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index adb4c08b980..d2b76b5d6c9 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -118,6 +118,17 @@ win32_require_context (windows_thread_info *th)
/* See nat/windows-nat.h. */
+windows_thread_info *
+gdbserver_windows_process::find_thread (ptid_t ptid)
+{
+ thread_info *thread = find_thread_ptid (ptid);
+ if (thread == nullptr)
+ return nullptr;
+ return static_cast<windows_thread_info *> (thread->target_data ());
+}
+
+/* See nat/windows-nat.h. */
+
windows_thread_info *
gdbserver_windows_process::thread_rec
(ptid_t ptid, thread_disposition_type disposition)
@@ -127,8 +138,7 @@ gdbserver_windows_process::thread_rec
return NULL;
auto th = static_cast<windows_thread_info *> (thread->target_data ());
- if (disposition != DONT_INVALIDATE_CONTEXT)
- win32_require_context (th);
+ win32_require_context (th);
return th;
}
@@ -139,7 +149,7 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
windows_thread_info *th;
ptid_t ptid = ptid_t (pid, tid, 0);
- if ((th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT)))
+ if ((th = windows_process.find_thread (ptid)))
return th;
CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb;
@@ -787,7 +797,7 @@ win32_process_target::resume (thread_resume *resume_info, size_t n)
/* Get context for the currently selected thread. */
ptid = debug_event_ptid (&windows_process.current_event);
- th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
+ th = windows_process.find_thread (ptid);
if (th)
{
win32_prepare_to_resume (th);
@@ -940,8 +950,7 @@ maybe_adjust_pc ()
child_fetch_inferior_registers (regcache, -1);
windows_thread_info *th
- = windows_process.thread_rec (current_thread->id,
- DONT_INVALIDATE_CONTEXT);
+ = windows_process.find_thread (current_thread->id);
th->stopped_at_software_breakpoint = false;
if (windows_process.current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT
@@ -1339,8 +1348,7 @@ win32_process_target::supports_get_tib_address ()
int
win32_process_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr)
{
- windows_thread_info *th;
- th = windows_process.thread_rec (ptid, DONT_INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process.find_thread (ptid);
if (th == NULL)
return 0;
if (addr != NULL)
@@ -1360,9 +1368,7 @@ win32_process_target::sw_breakpoint_from_kind (int kind, int *size)
bool
win32_process_target::stopped_by_sw_breakpoint ()
{
- windows_thread_info *th
- = windows_process.thread_rec (current_thread->id,
- DONT_INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process.find_thread (current_thread->id);
return th == nullptr ? false : th->stopped_at_software_breakpoint;
}
@@ -1387,9 +1393,7 @@ win32_process_target::write_pc (struct regcache *regcache, CORE_ADDR pc)
const char *
win32_process_target::thread_name (ptid_t thread)
{
- windows_thread_info *th
- = windows_process.thread_rec (current_thread->id,
- DONT_INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process.find_thread (current_thread->id);
return th->thread_name ();
}
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index 0259105ba11..edd7c757f09 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -178,6 +178,7 @@ class win32_process_target : public process_stratum_target
struct gdbserver_windows_process : public windows_nat::windows_process_info
{
+ windows_nat::windows_thread_info *find_thread (ptid_t ptid) override;
windows_nat::windows_thread_info *thread_rec
(ptid_t ptid,
windows_nat::thread_disposition_type disposition) override;
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 02/28] Windows gdb: handle_output_debug_string return type
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
2026-04-24 20:44 ` [pushed 01/28] Windows gdb+gdbserver: New find_thread, replaces thread_rec(DONT_INVALIDATE_CONTEXT) Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 03/28] Windows gdb: Eliminate reload_context Pedro Alves
` (25 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
handle_output_debug_string returns a Windows thread id, so it should
return a DWORD instead of an int.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Icbd071a1a37de8a0fc8918bd13254a8d40311e32
---
gdb/nat/windows-nat.h | 2 +-
gdb/windows-nat.c | 14 +++++++-------
gdb/windows-nat.h | 2 +-
gdbserver/win32-low.cc | 2 +-
gdbserver/win32-low.h | 2 +-
5 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 32f055a7a52..97c8c6cc43d 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -202,7 +202,7 @@ struct windows_process_info
a Cygwin signal. Otherwise just print the string as a warning.
This function must be supplied by the embedding application. */
- virtual int handle_output_debug_string (struct target_waitstatus *ourstatus) = 0;
+ virtual DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) = 0;
/* Handle a DLL load event.
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 46385ff87c5..ff23791e5a8 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -627,11 +627,11 @@ signal_event_command (const char *args, int from_tty)
/* See nat/windows-nat.h. */
-int
+DWORD
windows_per_inferior::handle_output_debug_string
(struct target_waitstatus *ourstatus)
{
- int retval = 0;
+ DWORD thread_id = 0;
gdb::unique_xmalloc_ptr<char> s
= (target_read_string
@@ -672,19 +672,19 @@ windows_per_inferior::handle_output_debug_string
if (gotasig)
{
ourstatus->set_stopped (gotasig);
- retval = strtoul (p, &p, 0);
- if (!retval)
- retval = current_event.dwThreadId;
+ thread_id = strtoul (p, &p, 0);
+ if (thread_id == 0)
+ thread_id = current_event.dwThreadId;
else
x = (LPCVOID) (uintptr_t) strtoull (p, NULL, 0);
}
DEBUG_EVENTS ("gdb: cygwin signal %d, thread 0x%x, CONTEXT @ %p",
- gotasig, retval, x);
+ gotasig, thread_id, x);
}
#endif
- return retval;
+ return thread_id;
}
/* See nat/windows-nat.h. */
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index 10c144bf4c6..a686c5b6679 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -50,7 +50,7 @@ struct windows_per_inferior : public windows_nat::windows_process_info
windows_thread_info *find_thread (ptid_t ptid) override;
windows_thread_info *thread_rec (ptid_t ptid,
thread_disposition_type disposition) override;
- int handle_output_debug_string (struct target_waitstatus *ourstatus) override;
+ DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) override;
void handle_load_dll (const char *dll_name, LPVOID base) override;
void handle_unload_dll () override;
bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index d2b76b5d6c9..d6d7acf599d 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -608,7 +608,7 @@ win32_process_target::attach (unsigned long pid)
/* See nat/windows-nat.h. */
-int
+DWORD
gdbserver_windows_process::handle_output_debug_string
(struct target_waitstatus *ourstatus)
{
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index edd7c757f09..48ad111ad1a 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -182,7 +182,7 @@ struct gdbserver_windows_process : public windows_nat::windows_process_info
windows_nat::windows_thread_info *thread_rec
(ptid_t ptid,
windows_nat::thread_disposition_type disposition) override;
- int handle_output_debug_string (struct target_waitstatus *ourstatus) override;
+ DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) override;
void handle_load_dll (const char *dll_name, LPVOID base) override;
void handle_unload_dll () override;
bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 03/28] Windows gdb: Eliminate reload_context
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
2026-04-24 20:44 ` [pushed 01/28] Windows gdb+gdbserver: New find_thread, replaces thread_rec(DONT_INVALIDATE_CONTEXT) Pedro Alves
2026-04-24 20:44 ` [pushed 02/28] Windows gdb: handle_output_debug_string return type Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 04/28] Windows gdb+gdbserver: Eliminate thread_rec(INVALIDATE_CONTEXT) calls Pedro Alves
` (24 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
We don't need reload_context, because we can get the same information
out of th->context.ContextFlags. If ContextFlags is zero, then we
need to fetch the context out of the inferior thread. This is what
gdbserver does too.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ied566037c81383414c46c77713bdd1aec6377b23
---
gdb/aarch64-windows-nat.c | 18 ++++++++++++++++--
gdb/nat/windows-nat.h | 4 ----
gdb/windows-nat.c | 16 ++++------------
gdb/windows-nat.h | 3 +++
gdb/x86-windows-nat.c | 21 ++++++++++++++++++---
5 files changed, 41 insertions(+), 21 deletions(-)
diff --git a/gdb/aarch64-windows-nat.c b/gdb/aarch64-windows-nat.c
index 4ae2c758f15..c375bbf2ddd 100644
--- a/gdb/aarch64-windows-nat.c
+++ b/gdb/aarch64-windows-nat.c
@@ -29,6 +29,8 @@ using namespace windows_nat;
struct aarch64_windows_per_inferior : public windows_per_inferior
{
aarch64_debug_reg_state dr_state;
+
+ void invalidate_thread_context (windows_thread_info *th) override;
};
struct aarch64_windows_nat_target final
@@ -181,8 +183,20 @@ aarch64_windows_nat_target::fill_thread_context (windows_thread_info *th)
{
CONTEXT *context = &th->context;
- context->ContextFlags = WindowsContext<decltype(context)>::all;
- CHECK (get_thread_context (th->h, context));
+ if (context->ContextFlags == 0)
+ {
+ context->ContextFlags = WindowsContext<decltype(context)>::all;
+ CHECK (get_thread_context (th->h, context));
+ }
+}
+
+/* See windows-nat.h. */
+
+void
+aarch64_windows_per_inferior::invalidate_thread_context (windows_thread_info *th)
+{
+ CONTEXT *context = &th->context;
+ context->ContextFlags = 0;
}
/* See windows-nat.h. */
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 97c8c6cc43d..24ef6cc3f9c 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -90,10 +90,6 @@ struct windows_thread_info
the thread. */
bool debug_registers_changed = false;
- /* Nonzero if CONTEXT is invalidated and must be re-read from the
- inferior thread. */
- bool reload_context = false;
-
/* True if this thread is currently stopped at a software
breakpoint. This is used to offset the PC when needed. */
bool stopped_at_software_breakpoint = false;
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index ff23791e5a8..e8df5e053e6 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -287,11 +287,11 @@ windows_per_inferior::thread_rec (ptid_t ptid,
case INVALIDATE_CONTEXT:
if (ptid.lwp () != current_event.dwThreadId)
th->suspend ();
- th->reload_context = true;
+ invalidate_thread_context (th);
break;
case DONT_SUSPEND:
- th->reload_context = true;
th->suspended = -1;
+ invalidate_thread_context (th);
break;
}
}
@@ -400,12 +400,7 @@ windows_nat_target::fetch_registers (struct regcache *regcache, int r)
if (th == NULL)
return;
- if (th->reload_context)
- {
- fill_thread_context (th);
-
- th->reload_context = false;
- }
+ fill_thread_context (th);
if (r < 0)
for (r = 0; r < gdbarch_num_regs (regcache->arch()); r++)
@@ -938,10 +933,7 @@ windows_nat_target::get_windows_debug_event
*ourstatus = stop->status;
ptid_t ptid (windows_process->current_event.dwProcessId, thread_id);
- windows_thread_info *th
- = windows_process->thread_rec (ptid, INVALIDATE_CONTEXT);
- th->reload_context = true;
-
+ windows_process->thread_rec (ptid, INVALIDATE_CONTEXT);
return ptid;
}
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index a686c5b6679..aa90dfd37d3 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -55,6 +55,9 @@ struct windows_per_inferior : public windows_nat::windows_process_info
void handle_unload_dll () override;
bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
+ /* Invalidate the thread context. */
+ virtual void invalidate_thread_context (windows_thread_info *th) = 0;
+
int windows_initialization_done = 0;
std::vector<std::unique_ptr<windows_thread_info>> thread_list;
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index 5a82a2e0e36..d76b89b186a 100644
--- a/gdb/x86-windows-nat.c
+++ b/gdb/x86-windows-nat.c
@@ -47,6 +47,8 @@ struct x86_windows_per_inferior : public windows_per_inferior
/* The function to use in order to determine whether a register is
a segment register or not. */
segment_register_p_ftype *segment_register_p = nullptr;
+
+ void invalidate_thread_context (windows_thread_info *th) override;
};
struct x86_windows_nat_target final : public x86_nat_target<windows_nat_target>
@@ -107,8 +109,22 @@ x86_windows_nat_target::fill_thread_context (windows_thread_info *th)
{
x86_windows_process.with_context (th, [&] (auto *context)
{
- context->ContextFlags = WindowsContext<decltype(context)>::all;
- CHECK (get_thread_context (th->h, context));
+ if (context->ContextFlags == 0)
+ {
+ context->ContextFlags = WindowsContext<decltype(context)>::all;
+ CHECK (get_thread_context (th->h, context));
+ }
+ });
+}
+
+/* See nat/windows-nat.h. */
+
+void
+x86_windows_per_inferior::invalidate_thread_context (windows_thread_info *th)
+{
+ x86_windows_process.with_context (th, [&] (auto *context)
+ {
+ context->ContextFlags = 0;
});
}
@@ -170,7 +186,6 @@ x86_windows_nat_target::fetch_one_register (struct regcache *regcache,
windows_thread_info *th, int r)
{
gdb_assert (r >= 0);
- gdb_assert (!th->reload_context);
char *context_ptr = x86_windows_process.with_context (th, [] (auto *context)
{
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 04/28] Windows gdb+gdbserver: Eliminate thread_rec(INVALIDATE_CONTEXT) calls
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (2 preceding siblings ...)
2026-04-24 20:44 ` [pushed 03/28] Windows gdb: Eliminate reload_context Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 05/28] Windows gdb+gdbserver: Eliminate DONT_SUSPEND Pedro Alves
` (23 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
Replace thread_rec(INVALIDATE_CONTEXT) calls with find_thread, and
invalidate_context / suspend calls in the spots that might need those.
I don't know why does the INVALIDATE_CONTEXT implementation in GDB
avoid suspending the event thread:
case INVALIDATE_CONTEXT:
if (ptid.lwp () != current_event.dwThreadId)
th->suspend ();
Checks for a global "current_event" get in the way of non-stop support
later in the series, as each thread will have its own "last debug
event". Regardless, it should be fine to suspend the event thread.
As a data point, the GDBserver implementation always suspends. So
this patch does not try to avoid suspending the event thread on the
native side either.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I8d2f0a749d23329956e62362a7007189902dddb5
---
gdb/aarch64-windows-nat.c | 2 ++
gdb/nat/windows-nat.h | 2 --
gdb/windows-nat.c | 21 +++++++--------------
gdb/x86-windows-nat.c | 2 ++
gdbserver/win32-low.cc | 10 ++++------
5 files changed, 15 insertions(+), 22 deletions(-)
diff --git a/gdb/aarch64-windows-nat.c b/gdb/aarch64-windows-nat.c
index c375bbf2ddd..d43ee6f13fe 100644
--- a/gdb/aarch64-windows-nat.c
+++ b/gdb/aarch64-windows-nat.c
@@ -185,6 +185,7 @@ aarch64_windows_nat_target::fill_thread_context (windows_thread_info *th)
if (context->ContextFlags == 0)
{
+ th->suspend ();
context->ContextFlags = WindowsContext<decltype(context)>::all;
CHECK (get_thread_context (th->h, context));
}
@@ -285,6 +286,7 @@ aarch64_windows_nat_target::store_one_register (const struct regcache *regcache,
windows_thread_info *th, int r)
{
gdb_assert (r >= 0);
+ gdb_assert (th->context.ContextFlags != 0);
char *context_ptr = (char *) &th->context;
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 24ef6cc3f9c..1a564fbf11b 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -109,8 +109,6 @@ enum thread_disposition_type
{
/* Invalidate the context, but do not suspend the thread. */
DONT_SUSPEND,
- /* Invalidate the context and suspend the thread. */
- INVALIDATE_CONTEXT
};
/* A single pending stop. See "pending_stops" for more
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index e8df5e053e6..ca9a197d64e 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -284,11 +284,6 @@ windows_per_inferior::thread_rec (ptid_t ptid,
{
switch (disposition)
{
- case INVALIDATE_CONTEXT:
- if (ptid.lwp () != current_event.dwThreadId)
- th->suspend ();
- invalidate_thread_context (th);
- break;
case DONT_SUSPEND:
th->suspended = -1;
invalidate_thread_context (th);
@@ -392,8 +387,7 @@ windows_nat_target::delete_thread (ptid_t ptid, DWORD exit_code,
void
windows_nat_target::fetch_registers (struct regcache *regcache, int r)
{
- windows_thread_info *th
- = windows_process->thread_rec (regcache->ptid (), INVALIDATE_CONTEXT);
+ 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. */
@@ -415,8 +409,7 @@ windows_nat_target::fetch_registers (struct regcache *regcache, int r)
void
windows_nat_target::store_registers (struct regcache *regcache, int r)
{
- windows_thread_info *th
- = windows_process->thread_rec (regcache->ptid (), INVALIDATE_CONTEXT);
+ 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. */
@@ -933,7 +926,9 @@ windows_nat_target::get_windows_debug_event
*ourstatus = stop->status;
ptid_t ptid (windows_process->current_event.dwProcessId, thread_id);
- windows_process->thread_rec (ptid, INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process->find_thread (ptid);
+ if (th != nullptr)
+ windows_process->invalidate_thread_context (th);
return ptid;
}
@@ -1151,8 +1146,7 @@ windows_nat_target::get_windows_debug_event
&& windows_process->windows_initialization_done)
{
ptid_t ptid = ptid_t (current_event->dwProcessId, thread_id, 0);
- windows_thread_info *th
- = windows_process->thread_rec (ptid, INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process->find_thread (ptid);
th->stopped_at_software_breakpoint = true;
th->pc_adjusted = false;
}
@@ -1190,8 +1184,7 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
if (ourstatus->kind () != TARGET_WAITKIND_EXITED
&& ourstatus->kind () != TARGET_WAITKIND_SIGNALLED)
{
- windows_thread_info *th
- = windows_process->thread_rec (result, INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process->find_thread (result);
if (th != nullptr)
{
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index d76b89b186a..5b0f7066fba 100644
--- a/gdb/x86-windows-nat.c
+++ b/gdb/x86-windows-nat.c
@@ -111,6 +111,7 @@ x86_windows_nat_target::fill_thread_context (windows_thread_info *th)
{
if (context->ContextFlags == 0)
{
+ th->suspend ();
context->ContextFlags = WindowsContext<decltype(context)>::all;
CHECK (get_thread_context (th->h, context));
}
@@ -254,6 +255,7 @@ x86_windows_nat_target::store_one_register (const struct regcache *regcache,
char *context_ptr = x86_windows_process.with_context (th, [] (auto *context)
{
+ gdb_assert (context->ContextFlags != 0);
return (char *) context;
});
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index d6d7acf599d..180fabe0df9 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -432,9 +432,8 @@ static void
child_fetch_inferior_registers (struct regcache *regcache, int r)
{
int regno;
- windows_thread_info *th
- = windows_process.thread_rec (current_thread->id,
- INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process.find_thread (current_thread->id);
+ win32_require_context (th);
if (r == -1 || r > NUM_REGS)
child_fetch_inferior_registers (regcache, NUM_REGS);
else
@@ -448,9 +447,8 @@ static void
child_store_inferior_registers (struct regcache *regcache, int r)
{
int regno;
- windows_thread_info *th
- = windows_process.thread_rec (current_thread->id,
- INVALIDATE_CONTEXT);
+ windows_thread_info *th = windows_process.find_thread (current_thread->id);
+ win32_require_context (th);
if (r == -1 || r == 0 || r > NUM_REGS)
child_store_inferior_registers (regcache, NUM_REGS);
else
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 05/28] Windows gdb+gdbserver: Eliminate DONT_SUSPEND
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (3 preceding siblings ...)
2026-04-24 20:44 ` [pushed 04/28] Windows gdb+gdbserver: Eliminate thread_rec(INVALIDATE_CONTEXT) calls Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 06/28] Windows gdb+gdbserver: Eliminate windows_process_info::thread_rec Pedro Alves
` (22 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
There's a single call to thread_rec(DONT_SUSPEND), in
windows_process_info::handle_exception.
In GDB, the windows-nat.c thread_rec implementation avoids actually
calling SuspendThread on the event thread by doing:
th->suspended = -1;
I am not exactly sure why, but it kind of looks like it is done as an
optimization, avoiding a SuspendThread call? It is probably done for
the same reason as the code touched in the previous patch avoided
suspending the event thread.
This however gets in the way of non-stop mode, which will really want
to SuspendThread the event thread for DBG_REPLY_LATER.
In gdbserver's thread_rec implementation DONT_SUSPEND is ignored, and
thread_rec actually always suspends, which really suggests that
SuspendThread on the event thread is really not a problem. I really
can't imagine why it would be.
DONT_SUSPEND invalidates the thread's context, but there is no need to
invalidate the context when we get an event for a thread, because we
invalidate it when we previously resumed the thread.
So, we can just remove the thread_rec call from
windows_process_info::handle_exception. That's what this patch does.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I0f328542bda6d8268814ca1ee4ae7a478098ecf2
---
gdb/nat/windows-nat.c | 4 ----
gdb/nat/windows-nat.h | 2 --
gdb/windows-nat.c | 11 -----------
3 files changed, 17 deletions(-)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index cefd335f652..16d4acae569 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -356,10 +356,6 @@ windows_process_info::handle_exception (struct target_waitstatus *ourstatus,
memcpy (&siginfo_er, rec, sizeof siginfo_er);
- /* Record the context of the current thread. */
- thread_rec (ptid_t (current_event.dwProcessId, current_event.dwThreadId, 0),
- DONT_SUSPEND);
-
last_sig = GDB_SIGNAL_0;
switch (code)
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 1a564fbf11b..2cc665fb53c 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -107,8 +107,6 @@ struct windows_thread_info
/* Possible values to pass to 'thread_rec'. */
enum thread_disposition_type
{
- /* Invalidate the context, but do not suspend the thread. */
- DONT_SUSPEND,
};
/* A single pending stop. See "pending_stops" for more
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index ca9a197d64e..aae4de42a73 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -279,17 +279,6 @@ windows_per_inferior::thread_rec (ptid_t ptid,
thread_disposition_type disposition)
{
windows_thread_info *th = find_thread (ptid);
-
- if (th != nullptr && !th->suspended)
- {
- switch (disposition)
- {
- case DONT_SUSPEND:
- th->suspended = -1;
- invalidate_thread_context (th);
- break;
- }
- }
return th;
}
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 06/28] Windows gdb+gdbserver: Eliminate windows_process_info::thread_rec
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (4 preceding siblings ...)
2026-04-24 20:44 ` [pushed 05/28] Windows gdb+gdbserver: Eliminate DONT_SUSPEND Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 07/28] Windows gdb: Simplify windows_nat_target::wait Pedro Alves
` (21 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
After the previous patches, thread_rec is no longer called anywhere.
Delete it.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ib14e5807fc427e1c3c4a393a9ea7b36b6047a2d7
---
gdb/nat/windows-nat.h | 13 -------------
gdb/windows-nat.c | 8 --------
gdb/windows-nat.h | 3 ---
gdbserver/win32-low.cc | 15 ---------------
gdbserver/win32-low.h | 3 ---
5 files changed, 42 deletions(-)
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 2cc665fb53c..8920d445838 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -104,11 +104,6 @@ struct windows_thread_info
};
-/* Possible values to pass to 'thread_rec'. */
-enum thread_disposition_type
-{
-};
-
/* A single pending stop. See "pending_stops" for more
information. */
struct pending_stop
@@ -178,14 +173,6 @@ struct windows_process_info
This function must be supplied by the embedding application. */
virtual windows_thread_info *find_thread (ptid_t ptid) = 0;
- /* Find a thread record given a thread id. THREAD_DISPOSITION
- controls whether the thread is suspended, and whether the context
- is invalidated.
-
- This function must be supplied by the embedding application. */
- virtual windows_thread_info *thread_rec (ptid_t ptid,
- thread_disposition_type disposition) = 0;
-
/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. Updates
OURSTATUS and returns the thread id if this represents a thread
change (this is specific to Cygwin), otherwise 0.
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index aae4de42a73..2dd372ae7b2 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -274,14 +274,6 @@ windows_per_inferior::find_thread (ptid_t ptid)
/* See nat/windows-nat.h. */
-windows_thread_info *
-windows_per_inferior::thread_rec (ptid_t ptid,
- thread_disposition_type disposition)
-{
- windows_thread_info *th = find_thread (ptid);
- return th;
-}
-
/* Add a thread to the thread list.
PTID is the ptid of the thread to be added.
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index aa90dfd37d3..2907d97d655 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -27,7 +27,6 @@
#include "ser-event.h"
using windows_nat::windows_thread_info;
-using windows_nat::thread_disposition_type;
/* A pointer to a function that should return non-zero iff REGNUM
corresponds to one of the segment registers. */
@@ -48,8 +47,6 @@ struct windows_solib
struct windows_per_inferior : public windows_nat::windows_process_info
{
windows_thread_info *find_thread (ptid_t ptid) override;
- windows_thread_info *thread_rec (ptid_t ptid,
- thread_disposition_type disposition) override;
DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) override;
void handle_load_dll (const char *dll_name, LPVOID base) override;
void handle_unload_dll () override;
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 180fabe0df9..bf55183f705 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -127,21 +127,6 @@ gdbserver_windows_process::find_thread (ptid_t ptid)
return static_cast<windows_thread_info *> (thread->target_data ());
}
-/* See nat/windows-nat.h. */
-
-windows_thread_info *
-gdbserver_windows_process::thread_rec
- (ptid_t ptid, thread_disposition_type disposition)
-{
- thread_info *thread = find_thread_ptid (ptid);
- if (thread == NULL)
- return NULL;
-
- auto th = static_cast<windows_thread_info *> (thread->target_data ());
- win32_require_context (th);
- return th;
-}
-
/* Add a thread to the thread list. */
static windows_thread_info *
child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index 48ad111ad1a..402254fdad5 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -179,9 +179,6 @@ class win32_process_target : public process_stratum_target
struct gdbserver_windows_process : public windows_nat::windows_process_info
{
windows_nat::windows_thread_info *find_thread (ptid_t ptid) override;
- windows_nat::windows_thread_info *thread_rec
- (ptid_t ptid,
- windows_nat::thread_disposition_type disposition) override;
DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) override;
void handle_load_dll (const char *dll_name, LPVOID base) override;
void handle_unload_dll () override;
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 07/28] Windows gdb: Simplify windows_nat_target::wait
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (5 preceding siblings ...)
2026-04-24 20:44 ` [pushed 06/28] Windows gdb+gdbserver: Eliminate windows_process_info::thread_rec Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-26 12:52 ` Hannes Domani
2026-04-24 20:44 ` [pushed 08/28] Windows gdb+gdbserver: Move suspending thread to when returning event Pedro Alves
` (20 subsequent siblings)
27 siblings, 1 reply; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
The logic in windows_nat_target::wait, where we decide what to do
depending on the result from get_windows_debug_event is harder to
grasp than it looks.
It is not easy to tell what should happen when in async mode
get_windows_debug_event returns that there's no event to process.
And then, if get_windows_debug_event returns null_ptid /
TARGET_WAITKIND_SPURIOUS, then we need to issue a ContinueDebugEvent.
There's also this comment in windows_nat_target::wait, which we're not
really implementing today:
~~~~
/* We loop when we get a non-standard exception rather than return
with a SPURIOUS because resume can try and step or modify things,
which needs a current_thread->h. But some of these exceptions mark
the birth or death of threads, which mean that the current thread
isn't necessarily what you think it is. */
~~~~
This patch changes things a bit so that the code is more obvious:
- look at the status kind, instead of ptid_t.
- add an explicit early return case for no-event.
- add an explicit case for TARGET_WAITKIND_SPURIOUS.
- with those, we no longer need to handle the case of find_thread not
finding a thread, so we can drop one indentation level.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I76c41762e1f893a7ff23465856ccf6a44af1f0e7
commit-id:aff7fc4a
---
gdb/windows-nat.c | 39 +++++++++++++++++++++++++--------------
1 file changed, 25 insertions(+), 14 deletions(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 2dd372ae7b2..30b07221b62 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -1139,7 +1139,10 @@ windows_nat_target::get_windows_debug_event
}
if (thread_id == 0)
- return null_ptid;
+ {
+ ourstatus->set_ignore ();
+ return null_ptid;
+ }
return ptid_t (windows_process->current_event.dwProcessId, thread_id, 0);
}
@@ -1160,25 +1163,33 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
{
ptid_t result = get_windows_debug_event (pid, ourstatus, options);
- if (result != null_ptid)
+ if ((options & TARGET_WNOHANG) != 0
+ && ourstatus->kind () == TARGET_WAITKIND_IGNORE)
+ return result;
+
+ if (ourstatus->kind () == TARGET_WAITKIND_SPURIOUS)
+ {
+ CHECK (windows_continue (DBG_CONTINUE,
+ windows_process->desired_stop_thread_id, 0));
+ }
+ else if (ourstatus->kind () != TARGET_WAITKIND_IGNORE)
{
if (ourstatus->kind () != TARGET_WAITKIND_EXITED
- && ourstatus->kind () != TARGET_WAITKIND_SIGNALLED)
+ && ourstatus->kind () != TARGET_WAITKIND_SIGNALLED)
{
windows_thread_info *th = windows_process->find_thread (result);
- if (th != nullptr)
+ th->stopped_at_software_breakpoint = false;
+ if (windows_process->current_event.dwDebugEventCode
+ == EXCEPTION_DEBUG_EVENT
+ && ((windows_process->current_event.u.Exception.ExceptionRecord.ExceptionCode
+ == EXCEPTION_BREAKPOINT)
+ || (windows_process->current_event.u.Exception.ExceptionRecord.ExceptionCode
+ == STATUS_WX86_BREAKPOINT))
+ && windows_process->windows_initialization_done)
{
- th->stopped_at_software_breakpoint = false;
- if (windows_process->current_event.dwDebugEventCode
- == EXCEPTION_DEBUG_EVENT
- && is_sw_breakpoint (&windows_process->current_event
- .u.Exception.ExceptionRecord)
- && windows_process->windows_initialization_done)
- {
- th->stopped_at_software_breakpoint = true;
- th->pc_adjusted = false;
- }
+ th->stopped_at_software_breakpoint = true;
+ th->pc_adjusted = false;
}
}
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 08/28] Windows gdb+gdbserver: Move suspending thread to when returning event
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (6 preceding siblings ...)
2026-04-24 20:44 ` [pushed 07/28] Windows gdb: Simplify windows_nat_target::wait Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 09/28] Windows gdb: Introduce continue_last_debug_event_main_thread Pedro Alves
` (19 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
The current code suspends a thread just before calling
GetThreadContext. You can only call GetThreadContext if the thread is
suspended. But, after WaitForDebugEvent, all threads are implicitly
suspended. So I don't think we even needed to call SuspendThread
explictly at all before our GetThreadContext calls.
However, suspending threads when we're about to present a stop to gdb
simplifies adding non-stop support later. This way, the windows
SuspendThread state corresponds to whether a thread is suspended or
resumed from the core's perspective. Curiously, I noticed that Wine's
winedbg does something similar:
https://github.com/wine-mirror/wine/blob/234943344f7495d1e072338f0e06fa2d5cbf0aa1/programs/winedbg/gdbproxy.c#L651
This makes it much easier to reason about a thread's suspend state,
and simplifies adding non-stop mode later on.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ifd6889a8afc041fad33cd1c4500e38941da6781b
commit-id:c4d2c92e
---
gdb/aarch64-windows-nat.c | 1 -
gdb/windows-nat.c | 11 +++++------
gdb/x86-windows-nat.c | 1 -
gdbserver/win32-low.cc | 5 +++++
4 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/gdb/aarch64-windows-nat.c b/gdb/aarch64-windows-nat.c
index d43ee6f13fe..871531bb93a 100644
--- a/gdb/aarch64-windows-nat.c
+++ b/gdb/aarch64-windows-nat.c
@@ -185,7 +185,6 @@ aarch64_windows_nat_target::fill_thread_context (windows_thread_info *th)
if (context->ContextFlags == 0)
{
- th->suspend ();
context->ContextFlags = WindowsContext<decltype(context)>::all;
CHECK (get_thread_context (th->h, context));
}
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 30b07221b62..468f451595d 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -710,12 +710,6 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
th->resume ();
}
- else
- {
- /* When single-stepping a specific thread, other threads must
- be suspended. */
- th->suspend ();
- }
std::optional<unsigned> err;
do_synchronously ([&] ()
@@ -1191,6 +1185,11 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
th->stopped_at_software_breakpoint = true;
th->pc_adjusted = false;
}
+
+ /* All-stop, suspend all threads until they are
+ explicitly resumed. */
+ for (auto &thr : windows_process.thread_list)
+ thr->suspend ();
}
return result;
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index 5b0f7066fba..baa6b969e99 100644
--- a/gdb/x86-windows-nat.c
+++ b/gdb/x86-windows-nat.c
@@ -111,7 +111,6 @@ x86_windows_nat_target::fill_thread_context (windows_thread_info *th)
{
if (context->ContextFlags == 0)
{
- th->suspend ();
context->ContextFlags = WindowsContext<decltype(context)>::all;
CHECK (get_thread_context (th->h, context));
}
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index bf55183f705..d78de17ff19 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -1188,6 +1188,11 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
OUTMSG2 (("Child Stopped with signal = %d \n",
ourstatus->sig ()));
maybe_adjust_pc ();
+
+ /* All-stop, suspend all threads until they are explicitly
+ resumed. */
+ for_each_thread (suspend_one_thread);
+
return debug_event_ptid (&windows_process.current_event);
}
default:
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 09/28] Windows gdb: Introduce continue_last_debug_event_main_thread
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (7 preceding siblings ...)
2026-04-24 20:44 ` [pushed 08/28] Windows gdb+gdbserver: Move suspending thread to when returning event Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 10/28] Windows gdb: Introduce windows_continue_flags Pedro Alves
` (18 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
We have code using do_synchronously to call continue_last_debug_event,
and later patches in the series would need to add the same code in few
more places. Factor it out to a continue_last_debug_event_main_thread
function so these other places in future patches can just call it.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I945e668d2b3daeb9de968219925a7b3c7c7ce9ed
commit-id:ee04461a
---
gdb/windows-nat.c | 43 ++++++++++++++++++++++++++-----------------
gdb/windows-nat.h | 6 ++++++
2 files changed, 32 insertions(+), 17 deletions(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 468f451595d..174b54eda78 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -261,6 +261,28 @@ windows_nat_target::wait_for_debug_event_main_thread (DEBUG_EVENT *event)
m_continued = false;
}
+void
+windows_nat_target::continue_last_debug_event_main_thread
+ (const char *context_str, DWORD continue_status, bool last_call)
+{
+ std::optional<unsigned> err;
+ do_synchronously ([&] ()
+ {
+ if (!continue_last_debug_event (continue_status, debug_events))
+ err = (unsigned) GetLastError ();
+
+ /* On the last call, do not block waiting for an event that will
+ never come. */
+ return !last_call;
+ });
+ if (err.has_value ())
+ throw_winerror_with_name (string_printf (_("ContinueDebugEvent failed: %s"),
+ context_str).c_str (),
+ *err);
+
+ m_continued = !last_call;
+}
+
/* See nat/windows-nat.h. */
windows_thread_info *
@@ -711,22 +733,9 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
th->resume ();
}
- std::optional<unsigned> err;
- do_synchronously ([&] ()
- {
- if (!continue_last_debug_event (continue_status, debug_events))
- err = (unsigned) GetLastError ();
- /* On the last call, do not block waiting for an event that will
- never come. */
- return !last_call;
- });
-
- if (err.has_value ())
- throw_winerror_with_name (_("Failed to resume program execution"
- " - ContinueDebugEvent failed"),
- *err);
-
- m_continued = !last_call;
+ continue_last_debug_event_main_thread
+ (_("Failed to resume program execution"), continue_status,
+ last_call);
return TRUE;
}
@@ -1188,7 +1197,7 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
/* All-stop, suspend all threads until they are
explicitly resumed. */
- for (auto &thr : windows_process.thread_list)
+ for (auto &thr : windows_process->thread_list)
thr->suspend ();
}
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index 2907d97d655..8866408567c 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -249,6 +249,12 @@ struct windows_nat_target : public inf_child_target
needed. */
void wait_for_debug_event_main_thread (DEBUG_EVENT *event);
+ /* This continues the last debug event, dispatching to the worker
+ thread as needed. */
+ void continue_last_debug_event_main_thread (const char *context_str,
+ DWORD continue_status,
+ bool last_call = false);
+
/* Force the process_thread thread to return from WaitForDebugEvent.
PROCESS_ALIVE is set to false if the inferior process exits while
we're trying to break out the process_thread thread. This can
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 10/28] Windows gdb: Introduce windows_continue_flags
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (8 preceding siblings ...)
2026-04-24 20:44 ` [pushed 09/28] Windows gdb: Introduce continue_last_debug_event_main_thread Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 11/28] Windows gdb: Factor code out of windows_nat_target::windows_continue Pedro Alves
` (17 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
windows_continue already has two boolean parameters:
(..., int killed, bool last_call = false)
A patch later in the series would need a third. Instead, convert
windows_continue to use an optional enum-flags parameter instead of
multiple booleans.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I17c4d8a12b662190f972c380f838cb3317bd2e1e
commit-id:e669e7de
---
gdb/windows-nat.c | 25 ++++++++++++-------------
gdb/windows-nat.h | 20 ++++++++++++++++++--
2 files changed, 30 insertions(+), 15 deletions(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 174b54eda78..170b837e930 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -705,14 +705,12 @@ windows_per_inferior::handle_access_violation
}
/* Resume thread specified by ID, or all artificially suspended
- threads, if we are continuing execution. KILLED non-zero means we
- have killed the inferior, so we should ignore weird errors due to
- threads shutting down. LAST_CALL is true if we expect this to be
- the last call to continue the inferior -- we are either mourning it
- or detaching. */
+ threads, if we are continuing execution. See description of
+ windows_continue_flags for CONT_FLAGS. */
+
BOOL
windows_nat_target::windows_continue (DWORD continue_status, int id,
- int killed, bool last_call)
+ windows_continue_flags cont_flags)
{
windows_process->desired_stop_thread_id = id;
@@ -728,6 +726,7 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
for (auto &th : windows_process->thread_list)
if (id == -1 || id == (int) th->tid)
{
+ bool killed = (cont_flags & WCONT_KILLED) != 0;
thread_context_continue (th.get (), killed);
th->resume ();
@@ -735,7 +734,7 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
continue_last_debug_event_main_thread
(_("Failed to resume program execution"), continue_status,
- last_call);
+ cont_flags & WCONT_LAST_CALL);
return TRUE;
}
@@ -832,9 +831,9 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
Otherwise complain. */
if (resume_all)
- windows_continue (continue_status, -1, 0);
+ windows_continue (continue_status, -1);
else
- windows_continue (continue_status, ptid.lwp (), 0);
+ windows_continue (continue_status, ptid.lwp ());
}
/* Interrupt the inferior. */
@@ -1538,7 +1537,7 @@ windows_nat_target::detach (inferior *inf, int from_tty)
if (m_continued)
break_out_process_thread (process_alive);
- windows_continue (DBG_CONTINUE, -1, 0, true);
+ windows_continue (DBG_CONTINUE, -1, WCONT_LAST_CALL);
std::optional<unsigned> err;
if (process_alive)
@@ -2286,13 +2285,13 @@ windows_nat_target::create_inferior (const char *exec_file,
do_initial_windows_stuff (pi.dwProcessId, 0);
- /* windows_continue (DBG_CONTINUE, -1, 0); */
+ /* windows_continue (DBG_CONTINUE, -1); */
}
void
windows_nat_target::mourn_inferior ()
{
- (void) windows_continue (DBG_CONTINUE, -1, 0, true);
+ windows_continue (DBG_CONTINUE, -1, WCONT_LAST_CALL);
cleanup_windows_arch ();
if (windows_process->open_process_used)
{
@@ -2350,7 +2349,7 @@ windows_nat_target::kill ()
for (;;)
{
- if (!windows_continue (DBG_CONTINUE, -1, 1))
+ if (!windows_continue (DBG_CONTINUE, -1, WCONT_KILLED))
break;
wait_for_debug_event_main_thread (&windows_process->current_event);
if (windows_process->current_event.dwDebugEventCode
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index 8866408567c..c4f3b2423b6 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -44,6 +44,22 @@ struct windows_solib
std::string name;
};
+/* Flags that can be passed to windows_continue. */
+
+enum windows_continue_flag
+ {
+ /* This means we have killed the inferior, so windows_continue
+ should ignore weird errors due to threads shutting down. */
+ WCONT_KILLED = 1,
+
+ /* This means we expect this windows_continue call to be the last
+ call to continue the inferior -- we are either mourning it or
+ detaching. */
+ WCONT_LAST_CALL = 2,
+ };
+
+DEF_ENUM_FLAGS_TYPE (windows_continue_flag, windows_continue_flags);
+
struct windows_per_inferior : public windows_nat::windows_process_info
{
windows_thread_info *find_thread (ptid_t ptid) override;
@@ -227,8 +243,8 @@ struct windows_nat_target : public inf_child_target
void delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p);
DWORD fake_create_process ();
- BOOL windows_continue (DWORD continue_status, int id, int killed,
- bool last_call = false);
+ BOOL windows_continue (DWORD continue_status, int id,
+ windows_continue_flags cont_flags = 0);
/* Helper function to start process_thread. */
static DWORD WINAPI process_thread_starter (LPVOID self);
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 11/28] Windows gdb: Factor code out of windows_nat_target::windows_continue
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (9 preceding siblings ...)
2026-04-24 20:44 ` [pushed 10/28] Windows gdb: Introduce windows_continue_flags Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 12/28] Windows gdb: Pending stop and current_event Pedro Alves
` (16 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
This factors some code out of windows_nat_target::windows_continue
into a new windows_continue_one function. This will make the
following patch easier to read (as well as the resulting code itself).
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I14a0386b1b8b03015e86273060af173b5130e375
---
gdb/windows-nat.c | 16 ++++++++++------
gdb/windows-nat.h | 3 +++
2 files changed, 13 insertions(+), 6 deletions(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 170b837e930..2ecb8a105c0 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -704,6 +704,15 @@ windows_per_inferior::handle_access_violation
return false;
}
+void
+windows_nat_target::continue_one_thread (windows_thread_info *th,
+ windows_continue_flags cont_flags)
+{
+ bool killed = (cont_flags & WCONT_KILLED) != 0;
+ thread_context_continue (th, killed);
+ th->resume ();
+}
+
/* Resume thread specified by ID, or all artificially suspended
threads, if we are continuing execution. See description of
windows_continue_flags for CONT_FLAGS. */
@@ -725,12 +734,7 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
for (auto &th : windows_process->thread_list)
if (id == -1 || id == (int) th->tid)
- {
- bool killed = (cont_flags & WCONT_KILLED) != 0;
- thread_context_continue (th.get (), killed);
-
- th->resume ();
- }
+ continue_one_thread (th.get (), cont_flags);
continue_last_debug_event_main_thread
(_("Failed to resume program execution"), continue_status,
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index c4f3b2423b6..221ce58a887 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -243,6 +243,9 @@ struct windows_nat_target : public inf_child_target
void delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p);
DWORD fake_create_process ();
+ void continue_one_thread (windows_thread_info *th,
+ windows_continue_flags cont_flags);
+
BOOL windows_continue (DWORD continue_status, int id,
windows_continue_flags cont_flags = 0);
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 12/28] Windows gdb: Pending stop and current_event
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (10 preceding siblings ...)
2026-04-24 20:44 ` [pushed 11/28] Windows gdb: Factor code out of windows_nat_target::windows_continue Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 13/28] Windows gdb+gdbserver: Elim desired_stop_thread_id / rework pending_stops Pedro Alves
` (15 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
I noticed that windows_nat_target::get_windows_debug_event does not
copy the event recorded in pending stop to
windows_process.current_event. This seems like an oversight. The
equivalent code in gdbserver/win32-low.cc does copy it.
This change will become moot later in the series, but I figure its
still clearer to correct the buglet as preparatory patch.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ic8935d854cf67a3a3c4edcbc1a1e8957b800d907
---
gdb/windows-nat.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 2ecb8a105c0..89b7f103dc3 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -911,6 +911,7 @@ windows_nat_target::get_windows_debug_event
{
thread_id = stop->thread_id;
*ourstatus = stop->status;
+ windows_process->current_event = stop->event;
ptid_t ptid (windows_process->current_event.dwProcessId, thread_id);
windows_thread_info *th = windows_process->find_thread (ptid);
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 13/28] Windows gdb+gdbserver: Elim desired_stop_thread_id / rework pending_stops
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (11 preceding siblings ...)
2026-04-24 20:44 ` [pushed 12/28] Windows gdb: Pending stop and current_event Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 14/28] Windows gdb+gdbserver: Introduce get_last_debug_event_ptid Pedro Alves
` (14 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
windows_process.desired_stop_thread_id doesn't work for non-stop, as
in that case every thread will have its own independent
WaitForDebugEvent event.
Instead, detect whether we have been reported a stop that was not
supposed to be reported by simply checking whether the thread that is
reporting the event is suspended. This is now easilly possible since
each thread's suspend state is kept in sync with whether infrun wants
the thread executing or not.
windows_process.desired_stop_thread_id was also used as thread to pass
to windows_continue. However, we don't really need that either.
windows_continue is used to update the thread's registers, unsuspend
them, and then finally call ContinueDebugEvent. In most cases, we
only need the ContinueDebugEvent step, so we can convert the
windows_continue calls to continue_last_debug_event_main_thread calls.
The exception is when we see a thread creation event -- in that case,
we need to update the debug registers of the new thread. We can use
continue_one_thread for that.
Since the pending stop is now stored in windows_thread_info,
get_windows_debug_event needs to avoid reaching the bottom code if
there's no thread associated with the event anymore (i.e.,
EXIT_THREAD_DEBUG_EVENT / EXIT_PROCESS_DEBUG_EVENT).
I considered whether it would be possible to keep the pending_stop
handling code shared in gdb/nat/windows-nat.c, in this patch and
throughout the series, but I conclused that it isn't worth it, until
gdbserver is taught about async and non-stop as well.
The pending_stop struct will eventually be eliminated later down the
series.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ib7c8e8d16edc0900b7c411976c5d70cf93931c1c
commit-id:0b93b3f0
---
gdb/nat/windows-nat.c | 51 ------------------
gdb/nat/windows-nat.h | 71 ++++++++++---------------
gdb/windows-nat.c | 116 +++++++++++++++++++++++------------------
gdbserver/win32-low.cc | 55 +++++++++++--------
4 files changed, 127 insertions(+), 166 deletions(-)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 16d4acae569..cf173295517 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -642,57 +642,6 @@ windows_process_info::add_all_dlls ()
/* See nat/windows-nat.h. */
-bool
-windows_process_info::matching_pending_stop (bool debug_events)
-{
- /* If there are pending stops, and we might plausibly hit one of
- them, we don't want to actually continue the inferior -- we just
- want to report the stop. In this case, we just pretend to
- continue. See the comment by the definition of "pending_stops"
- for details on why this is needed. */
- for (const auto &item : pending_stops)
- {
- if (desired_stop_thread_id == -1
- || desired_stop_thread_id == item.thread_id)
- {
- DEBUG_EVENTS ("pending stop anticipated, desired=0x%x, item=0x%x",
- desired_stop_thread_id, item.thread_id);
- return true;
- }
- }
-
- return false;
-}
-
-/* See nat/windows-nat.h. */
-
-std::optional<pending_stop>
-windows_process_info::fetch_pending_stop (bool debug_events)
-{
- std::optional<pending_stop> result;
- for (auto iter = pending_stops.begin ();
- iter != pending_stops.end ();
- ++iter)
- {
- if (desired_stop_thread_id == -1
- || desired_stop_thread_id == iter->thread_id)
- {
- result = *iter;
- current_event = iter->event;
-
- DEBUG_EVENTS ("pending stop found in 0x%x (desired=0x%x)",
- iter->thread_id, desired_stop_thread_id);
-
- pending_stops.erase (iter);
- break;
- }
- }
-
- return result;
-}
-
-/* See nat/windows-nat.h. */
-
BOOL
continue_last_debug_event (DWORD continue_status, bool debug_events)
{
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 8920d445838..075d3858350 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -38,6 +38,21 @@
namespace windows_nat
{
+/* Info about a potential pending stop. Each thread holds one of
+ these. See "windows_thread_info::pending_stop" for more
+ information. */
+struct pending_stop
+{
+ /* The target waitstatus we computed. TARGET_WAITKIND_IGNORE if the
+ thread does not have a pending stop. */
+ target_waitstatus status;
+
+ /* The event. A few fields of this can be referenced after a stop,
+ and it seemed simplest to store the entire event. */
+ DEBUG_EVENT event;
+};
+
+
/* Thread information structure used to track extra information about
each thread. */
struct windows_thread_info
@@ -77,6 +92,18 @@ struct windows_thread_info
was not. */
int suspended = 0;
+/* Info about a potential pending stop.
+
+ Sometimes, Windows will report a stop on a thread that has been
+ ostensibly suspended. We believe what happens here is that two
+ threads hit a breakpoint simultaneously, and the Windows kernel
+ queues the stop events. However, this can result in the strange
+ effect of trying to single step thread A -- leaving all other
+ threads suspended -- and then seeing a stop in thread B. To handle
+ this scenario, we queue all such "pending" stops here, and then
+ process them once the step has completed. See PR gdb/22992. */
+ struct pending_stop pending_stop {};
+
/* The context of the thread, including any manipulations. */
union
{
@@ -103,22 +130,6 @@ struct windows_thread_info
gdb::unique_xmalloc_ptr<char> name;
};
-
-/* A single pending stop. See "pending_stops" for more
- information. */
-struct pending_stop
-{
- /* The thread id. */
- DWORD thread_id;
-
- /* The target waitstatus we computed. */
- target_waitstatus status;
-
- /* The event. A few fields of this can be referenced after a stop,
- and it seemed simplest to store the entire event. */
- DEBUG_EVENT event;
-};
-
enum handle_exception_result
{
HANDLE_EXCEPTION_UNHANDLED = 0,
@@ -142,22 +153,6 @@ struct windows_process_info
stop. */
DEBUG_EVENT current_event {};
- /* The ID of the thread for which we anticipate a stop event.
- Normally this is -1, meaning we'll accept an event in any
- thread. */
- DWORD desired_stop_thread_id = -1;
-
- /* A vector of pending stops. Sometimes, Windows will report a stop
- on a thread that has been ostensibly suspended. We believe what
- happens here is that two threads hit a breakpoint simultaneously,
- and the Windows kernel queues the stop events. However, this can
- result in the strange effect of trying to single step thread A --
- leaving all other threads suspended -- and then seeing a stop in
- thread B. To handle this scenario, we queue all such "pending"
- stops here, and then process them once the step has completed. See
- PR gdb/22992. */
- std::vector<pending_stop> pending_stops;
-
/* Contents of $_siginfo */
EXCEPTION_RECORD siginfo_er {};
@@ -223,18 +218,6 @@ struct windows_process_info
void add_all_dlls ();
- /* Return true if there is a pending stop matching
- desired_stop_thread_id. If DEBUG_EVENTS is true, logging will be
- enabled. */
-
- bool matching_pending_stop (bool debug_events);
-
- /* See if a pending stop matches DESIRED_STOP_THREAD_ID. If so,
- remove it from the list of pending stops, set 'current_event', and
- return it. Otherwise, return an empty optional. */
-
- std::optional<pending_stop> fetch_pending_stop (bool debug_events);
-
const char *pid_to_exec_file (int);
template<typename Function>
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 89b7f103dc3..2a61953f439 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -721,15 +721,22 @@ BOOL
windows_nat_target::windows_continue (DWORD continue_status, int id,
windows_continue_flags cont_flags)
{
- windows_process->desired_stop_thread_id = id;
-
- if (windows_process->matching_pending_stop (debug_events))
+ for (auto &th : windows_process->thread_list)
{
- /* There's no need to really continue, because there's already
- another event pending. However, we do need to inform the
- event loop of this. */
- serial_event_set (m_wait_event);
- return TRUE;
+ if ((id == -1 || id == (int) th->tid)
+ && !th->suspended
+ && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ {
+ DEBUG_EVENTS ("got matching pending stop event "
+ "for 0x%x, not resuming",
+ th->tid);
+
+ /* There's no need to really continue, because there's already
+ another event pending. However, we do need to inform the
+ event loop of this. */
+ serial_event_set (m_wait_event);
+ return TRUE;
+ }
}
for (auto &th : windows_process->thread_list)
@@ -903,21 +910,24 @@ windows_nat_target::get_windows_debug_event
DWORD thread_id = 0;
/* If there is a relevant pending stop, report it now. See the
- comment by the definition of "pending_stops" for details on why
- this is needed. */
- std::optional<pending_stop> stop
- = windows_process->fetch_pending_stop (debug_events);
- if (stop.has_value ())
- {
- thread_id = stop->thread_id;
- *ourstatus = stop->status;
- windows_process->current_event = stop->event;
-
- ptid_t ptid (windows_process->current_event.dwProcessId, thread_id);
- windows_thread_info *th = windows_process->find_thread (ptid);
- if (th != nullptr)
- windows_process->invalidate_thread_context (th);
- return ptid;
+ comment by the definition of "windows_thread_info::pending_stop"
+ for details on why this is needed. */
+ for (auto &th : windows_process->thread_list)
+ {
+ if (!th->suspended
+ && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ {
+ DEBUG_EVENTS ("reporting pending event for 0x%x", th->tid);
+
+ thread_id = th->tid;
+ *ourstatus = th->pending_stop.status;
+ th->pending_stop.status.set_ignore ();
+ windows_process->current_event = th->pending_stop.event;
+
+ ptid_t ptid (windows_process->process_id, thread_id);
+ windows_process->invalidate_thread_context (th.get ());
+ return ptid;
+ }
}
windows_process->last_sig = GDB_SIGNAL_0;
@@ -959,12 +969,18 @@ windows_nat_target::get_windows_debug_event
}
/* Record the existence of this thread. */
thread_id = current_event->dwThreadId;
- add_thread
- (ptid_t (current_event->dwProcessId, current_event->dwThreadId, 0),
- current_event->u.CreateThread.hThread,
- current_event->u.CreateThread.lpThreadLocalBase,
- false /* main_thread_p */);
+ {
+ windows_thread_info *th
+ = (add_thread
+ (ptid_t (current_event->dwProcessId, current_event->dwThreadId, 0),
+ current_event->u.CreateThread.hThread,
+ current_event->u.CreateThread.lpThreadLocalBase,
+ false /* main_thread_p */));
+
+ /* This updates debug registers if necessary. */
+ continue_one_thread (th, 0);
+ }
break;
case EXIT_THREAD_DEBUG_EVENT:
@@ -976,6 +992,7 @@ windows_nat_target::get_windows_debug_event
current_event->dwThreadId, 0),
current_event->u.ExitThread.dwExitCode,
false /* main_thread_p */);
+ thread_id = 0;
break;
case CREATE_PROCESS_DEBUG_EVENT:
@@ -1026,8 +1043,7 @@ windows_nat_target::get_windows_debug_event
ourstatus->set_exited (exit_status);
else
ourstatus->set_signalled (gdb_signal_from_host (exit_signal));
-
- thread_id = current_event->dwThreadId;
+ return ptid_t (current_event->dwProcessId);
}
break;
@@ -1117,40 +1133,40 @@ windows_nat_target::get_windows_debug_event
if (!thread_id || windows_process->saw_create != 1)
{
- CHECK (windows_continue (continue_status,
- windows_process->desired_stop_thread_id, 0));
+ continue_last_debug_event_main_thread
+ (_("Failed to resume program execution"), continue_status);
+ ourstatus->set_ignore ();
+ return null_ptid;
}
- else if (windows_process->desired_stop_thread_id != -1
- && windows_process->desired_stop_thread_id != thread_id)
+
+ const ptid_t ptid = ptid_t (current_event->dwProcessId, thread_id, 0);
+ windows_thread_info *th = windows_process->find_thread (ptid);
+
+ if (th->suspended)
{
/* Pending stop. See the comment by the definition of
"pending_stops" for details on why this is needed. */
DEBUG_EVENTS ("get_windows_debug_event - "
- "unexpected stop in 0x%x (expecting 0x%x)",
- thread_id, windows_process->desired_stop_thread_id);
+ "unexpected stop in suspended thread 0x%x",
+ thread_id);
if (current_event->dwDebugEventCode == EXCEPTION_DEBUG_EVENT
&& is_sw_breakpoint (¤t_event->u.Exception.ExceptionRecord)
&& windows_process->windows_initialization_done)
{
- ptid_t ptid = ptid_t (current_event->dwProcessId, thread_id, 0);
- windows_thread_info *th = windows_process->find_thread (ptid);
th->stopped_at_software_breakpoint = true;
th->pc_adjusted = false;
}
- windows_process->pending_stops.push_back
- ({thread_id, *ourstatus, windows_process->current_event});
- thread_id = 0;
- CHECK (windows_continue (continue_status,
- windows_process->desired_stop_thread_id, 0));
- }
-
- if (thread_id == 0)
- {
+ th->pending_stop.status = *ourstatus;
+ th->pending_stop.event = *current_event;
ourstatus->set_ignore ();
+
+ continue_last_debug_event_main_thread
+ (_("Failed to resume program execution"), continue_status);
return null_ptid;
}
- return ptid_t (windows_process->current_event.dwProcessId, thread_id, 0);
+
+ return ptid;
}
/* Wait for interesting events to occur in the target process. */
@@ -1176,8 +1192,8 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
if (ourstatus->kind () == TARGET_WAITKIND_SPURIOUS)
{
- CHECK (windows_continue (DBG_CONTINUE,
- windows_process->desired_stop_thread_id, 0));
+ continue_last_debug_event_main_thread
+ (_("Failed to resume program execution"), DBG_CONTINUE);
}
else if (ourstatus->kind () != TARGET_WAITKIND_IGNORE)
{
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index d78de17ff19..3f684dd2bf6 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -397,9 +397,15 @@ continue_one_thread (thread_info *thread, int thread_id)
static BOOL
child_continue (DWORD continue_status, int thread_id)
{
- windows_process.desired_stop_thread_id = thread_id;
- if (windows_process.matching_pending_stop (debug_threads))
- return TRUE;
+ process_info *proc = find_process_pid (windows_process.process_id);
+ for (thread_info &thread : proc->thread_list ())
+ {
+ auto *th = static_cast<windows_thread_info *> (thread.target_data ());
+ if ((thread_id == -1 || thread_id == (int) th->tid)
+ && !th->suspended
+ && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ return TRUE;
+ }
/* The inferior will only continue after the ContinueDebugEvent
call. */
@@ -974,15 +980,21 @@ get_child_debug_event (DWORD *continue_status,
windows_process.attaching = 0;
{
- std::optional<pending_stop> stop
- = windows_process.fetch_pending_stop (debug_threads);
- if (stop.has_value ())
+ process_info *proc = find_process_pid (windows_process.process_id);
+ for (thread_info &thread : proc->thread_list ())
{
- *ourstatus = stop->status;
- windows_process.current_event = stop->event;
- ptid = debug_event_ptid (&windows_process.current_event);
- switch_to_thread (find_thread_ptid (ptid));
- return 1;
+ auto *th = static_cast<windows_thread_info *> (thread.target_data ());
+
+ if (!th->suspended
+ && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ {
+ *ourstatus = th->pending_stop.status;
+ th->pending_stop.status.set_ignore ();
+ windows_process.current_event = th->pending_stop.event;
+ ptid = debug_event_ptid (&windows_process.current_event);
+ switch_to_thread (find_thread_ptid (ptid));
+ return 1;
+ }
}
/* Keep the wait time low enough for comfortable remote
@@ -1074,7 +1086,7 @@ get_child_debug_event (DWORD *continue_status,
else
ourstatus->set_signalled (gdb_signal_from_host (exit_signal));
}
- child_continue (DBG_CONTINUE, windows_process.desired_stop_thread_id);
+ continue_last_debug_event (DBG_CONTINUE, debug_threads);
break;
case LOAD_DLL_DEBUG_EVENT:
@@ -1131,17 +1143,19 @@ get_child_debug_event (DWORD *continue_status,
ptid = debug_event_ptid (&windows_process.current_event);
- if (windows_process.desired_stop_thread_id != -1
- && windows_process.desired_stop_thread_id != ptid.lwp ())
+ windows_thread_info *th = windows_process.find_thread (ptid);
+
+ if (th != nullptr && th->suspended)
{
/* Pending stop. See the comment by the definition of
- "pending_stops" for details on why this is needed. */
+ "windows_thread_info::pending_stop" for details on why this
+ is needed. */
OUTMSG2 (("get_windows_debug_event - "
- "unexpected stop in 0x%lx (expecting 0x%x)\n",
- ptid.lwp (), windows_process.desired_stop_thread_id));
+ "unexpected stop in suspended thread 0x%x\n",
+ th->tid));
maybe_adjust_pc ();
- windows_process.pending_stops.push_back
- ({(DWORD) ptid.lwp (), *ourstatus, *current_event});
+ th->pending_stop.status = *ourstatus;
+ th->pending_stop.event = *current_event;
ourstatus->set_spurious ();
}
else
@@ -1201,8 +1215,7 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
[[fallthrough]];
case TARGET_WAITKIND_SPURIOUS:
/* do nothing, just continue */
- child_continue (continue_status,
- windows_process.desired_stop_thread_id);
+ continue_last_debug_event (continue_status, debug_threads);
break;
}
}
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 14/28] Windows gdb+gdbserver: Introduce get_last_debug_event_ptid
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (12 preceding siblings ...)
2026-04-24 20:44 ` [pushed 13/28] Windows gdb+gdbserver: Elim desired_stop_thread_id / rework pending_stops Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 15/28] Windows gdb: Can't pass signal to thread other than last stopped thread Pedro Alves
` (13 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
This will be used in subsequent patches to avoid using
DBG_EXCEPTION_NOT_HANDLED on the wrong thread.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I32915623b5036fb902f9830ce2d6f0b1ccf1f5cf
commit-id:1cf51152
---
gdb/nat/windows-nat.c | 8 ++++++++
gdb/nat/windows-nat.h | 5 +++++
2 files changed, 13 insertions(+)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index cf173295517..11151fdc28a 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -642,6 +642,14 @@ windows_process_info::add_all_dlls ()
/* See nat/windows-nat.h. */
+ptid_t
+get_last_debug_event_ptid ()
+{
+ return ptid_t (last_wait_event.dwProcessId, last_wait_event.dwThreadId, 0);
+}
+
+/* See nat/windows-nat.h. */
+
BOOL
continue_last_debug_event (DWORD continue_status, bool debug_events)
{
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 075d3858350..8d97ecc4cb6 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -278,6 +278,11 @@ struct windows_process_info
extern BOOL continue_last_debug_event (DWORD continue_status,
bool debug_events);
+/* Return the ptid_t of the thread that the last waited-for event was
+ for. */
+
+extern ptid_t get_last_debug_event_ptid ();
+
/* A simple wrapper for WaitForDebugEvent that also sets the internal
'last_wait_event' on success. */
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 15/28] Windows gdb: Can't pass signal to thread other than last stopped thread
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (13 preceding siblings ...)
2026-04-24 20:44 ` [pushed 14/28] Windows gdb+gdbserver: Introduce get_last_debug_event_ptid Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 16/28] Windows gdbserver: Fix scheduler-locking Pedro Alves
` (12 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
Passing a signal to a thread other than the one that last reported an
event will be later possible with DBG_REPLY_LATER and the Windows
backend working in non-stop mode.
With an all-stop backend that isn't possible, so at least don't
incorrectly consider passing DBG_EXCEPTION_NOT_HANDLED if the thread
that we're going to call ContinueDebugEvent for is not the one that
the user issued "signal SIG" on.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I27092ecfbf0904ebce02dff07d9104d22f3d8f0e
commit-id:30c8d0ce
---
gdb/windows-nat.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 2a61953f439..614d85ac8f7 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -790,10 +790,21 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
if (sig != GDB_SIGNAL_0)
{
- if (windows_process->current_event.dwDebugEventCode
+ /* Note it is OK to call get_last_debug_event_ptid() from the
+ main thread here, because we know the process_thread thread
+ isn't waiting for an event at this point, so there's no data
+ race. */
+ if (inferior_ptid != get_last_debug_event_ptid ())
+ {
+ /* ContinueDebugEvent will be for a different thread. */
+ DEBUG_EXCEPT ("Cannot continue with signal %d here. "
+ "Not last-event thread", sig);
+ }
+ else if (windows_process->current_event.dwDebugEventCode
!= EXCEPTION_DEBUG_EVENT)
{
- DEBUG_EXCEPT ("Cannot continue with signal %d here.", sig);
+ DEBUG_EXCEPT ("Cannot continue with signal %d here. "
+ "Not stopped for EXCEPTION_DEBUG_EVENT", sig);
}
else if (sig == windows_process->last_sig)
continue_status = DBG_EXCEPTION_NOT_HANDLED;
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 16/28] Windows gdbserver: Fix scheduler-locking
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (14 preceding siblings ...)
2026-04-24 20:44 ` [pushed 15/28] Windows gdb: Can't pass signal to thread other than last stopped thread Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 17/28] Windows gdb: Enable "set scheduler-locking on" Pedro Alves
` (11 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
This rewrites win32_process_target::resume such that scheduler-locking
is implemented properly.
It also uses the new get_last_debug_event_ptid function to avoid
considering passing a signal to the wrong thread, like done for the
native side in a previous patch.
Note this code/comment being removed:
- /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make
- the Windows resume code do the right thing for thread switching. */
- tid = windows_process.current_event.dwThreadId;
This meant that scheduler-locking currently is broken badly unless you
stay in the thread that last reported an event. If you switch to a
different thread from the one that last reported an event and step,
you get a spurious SIGTRAP in the thread that last reported a stop,
not the one that you tried to step:
(gdb) t 1
[Switching to thread 1 (Thread 3908)]
#0 0x00007fffc768d6e4 in ntdll!ZwDelayExecution () from target:C:/Windows/SYSTEM32/ntdll.dll
(gdb) set scheduler-locking on
(gdb) set disassemble-next-line on
(gdb) frame
#0 0x00007fffc768d6e4 in ntdll!ZwDelayExecution () from target:C:/Windows/SYSTEM32/ntdll.dll
=> 0x00007fffc768d6e4 <ntdll!ZwDelayExecution+20>: c3 ret
(gdb) si
Thread 3 received signal SIGTRAP, Trace/breakpoint trap.
[Switching to Thread 2660]
0x00007fffc4e4e92e in KERNELBASE!EncodeRemotePointer () from target:C:/Windows/System32/KernelBase.dll
=> 0x00007fffc4e4e92e <KERNELBASE!EncodeRemotePointer+8254>: eb 78 jmp 0x7fffc4e4e9a8 <KERNELBASE!EncodeRemotePointer+8376>
(gdb)
Note how we switched to thread 1, stepped, and GDBserver still stepped
thread 3... This is fixed by this patch. We now get:
(gdb) info threads
Id Target Id Frame
1 Thread 920 0x00007ffe0372d6e4 in ntdll!ZwDelayExecution () from target:C:/Windows/SYSTEM32/ntdll.dll
2 Thread 8528 0x00007ffe03730ad4 in ntdll!ZwWaitForWorkViaWorkerFactory () from target:C:/Windows/SYSTEM32/ntdll.dll
3 Thread 3128 0x00007ffe03730ad4 in ntdll!ZwWaitForWorkViaWorkerFactory () from target:C:/Windows/SYSTEM32/ntdll.dll
* 4 Thread 7164 0x00007ffe0102e929 in KERNELBASE!EncodeRemotePointer () from target:C:/Windows/System32/KernelBase.dll
5 Thread 8348 0x00007ffe0372d6e4 in ntdll!ZwDelayExecution () from target:C:/Windows/SYSTEM32/ntdll.dll
6 Thread 2064 0x00007ffe0372d6e4 in ntdll!ZwDelayExecution () from target:C:/Windows/SYSTEM32/ntdll.dll
(gdb) t 1
[Switching to thread 1 (Thread 920)]
#0 0x00007ffe0372d6e4 in ntdll!ZwDelayExecution () from target:C:/Windows/SYSTEM32/ntdll.dll
(gdb) set scheduler-locking on
(gdb) si
0x00007ffe0372d6e4 in ntdll!ZwDelayExecution () from target:C:/Windows/SYSTEM32/ntdll.dll
(gdb) si
0x00007ffe00f9b44e in SleepEx () from target:C:/Windows/System32/KernelBase.dll
(gdb) si
0x00007ffe00f9b453 in SleepEx () from target:C:/Windows/System32/KernelBase.dll
I.e., we kept stepping the right thread, thread 1.
Note we stopped again at 0x00007ffe0372d6e4 the first time (same PC
the thread already was at before the first stepi) because the thread
had been stopped at a syscall, so that's normal:
(gdb) disassemble
Dump of assembler code for function ntdll!ZwDelayExecution:
0x00007ffe0372d6d0 <+0>: mov %rcx,%r10
0x00007ffe0372d6d3 <+3>: mov $0x34,%eax
0x00007ffe0372d6d8 <+8>: testb $0x1,0x7ffe0308
0x00007ffe0372d6e0 <+16>: jne 0x7ffe0372d6e5 <ntdll!ZwDelayExecution+21>
0x00007ffe0372d6e2 <+18>: syscall
=> 0x00007ffe0372d6e4 <+20>: ret
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I44f4fe4cb98592517569c6716b9d189f42db25a0
commit-id:2a7b7d8e
---
gdbserver/win32-low.cc | 158 ++++++++++++++++++++++-------------------
1 file changed, 85 insertions(+), 73 deletions(-)
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 3f684dd2bf6..be01940d158 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -394,19 +394,13 @@ continue_one_thread (thread_info *thread, int thread_id)
}
}
+/* Helper for win32_process_target::kill. Resume all suspended
+ threads that match THREAD_ID, and continue the last debug event
+ with CONTINUE_STATUS. */
+
static BOOL
-child_continue (DWORD continue_status, int thread_id)
+child_continue_for_kill (DWORD continue_status, int thread_id)
{
- process_info *proc = find_process_pid (windows_process.process_id);
- for (thread_info &thread : proc->thread_list ())
- {
- auto *th = static_cast<windows_thread_info *> (thread.target_data ());
- if ((thread_id == -1 || thread_id == (int) th->tid)
- && !th->suspended
- && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
- return TRUE;
- }
-
/* The inferior will only continue after the ContinueDebugEvent
call. */
for_each_thread ([&] (thread_info *thread)
@@ -665,7 +659,7 @@ win32_process_target::kill (process_info *process)
TerminateProcess (windows_process.handle, 137);
for (;;)
{
- if (!child_continue (DBG_CONTINUE, -1))
+ if (!child_continue_for_kill (DBG_CONTINUE, -1))
break;
if (!wait_for_debug_event (&windows_process.current_event, INFINITE))
break;
@@ -732,90 +726,108 @@ win32_process_target::thread_alive (ptid_t ptid)
return find_thread_ptid (ptid) != NULL;
}
-/* Resume the inferior process. RESUME_INFO describes how we want
- to resume. */
-void
-win32_process_target::resume (thread_resume *resume_info, size_t n)
-{
- DWORD tid;
- enum gdb_signal sig;
- int step;
- windows_thread_info *th;
- DWORD continue_status = DBG_CONTINUE;
- ptid_t ptid;
-
- /* This handles the very limited set of resume packets that GDB can
- currently produce. */
-
- if (n == 1 && resume_info[0].thread == minus_one_ptid)
- tid = -1;
- else if (n > 1)
- tid = -1;
- else
- /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make
- the Windows resume code do the right thing for thread switching. */
- tid = windows_process.current_event.dwThreadId;
+/* Helper for win32_process_target::resume. Resume THREAD, with
+ signal SIG, and stepping if STEP. If resuming the thread that last
+ reported an event, sets CONTINUE_STATUS appropriately to the status
+ that should be passed to ContinueDebugEvent. */
- if (resume_info[0].thread != minus_one_ptid)
- {
- sig = gdb_signal_from_host (resume_info[0].sig);
- step = resume_info[0].kind == resume_step;
- }
- else
- {
- sig = GDB_SIGNAL_0;
- step = 0;
- }
+static void
+resume_one_thread (thread_info *thread, bool step, gdb_signal sig,
+ DWORD *continue_status)
+{
+ auto *th = static_cast<windows_thread_info *> (thread->target_data ());
if (sig != GDB_SIGNAL_0)
{
- if (windows_process.current_event.dwDebugEventCode
- != EXCEPTION_DEBUG_EVENT)
+ /* Allow continuing with the same signal that interrupted us.
+ Otherwise complain. */
+ if (thread->id != get_last_debug_event_ptid ())
+ {
+ /* ContinueDebugEvent will be for a different thread. */
+ OUTMSG (("Cannot continue with signal %d here. "
+ "Not last-event thread", sig));
+ }
+ else if (windows_process.current_event.dwDebugEventCode
+ != EXCEPTION_DEBUG_EVENT)
{
- OUTMSG (("Cannot continue with signal %s here.\n",
+ OUTMSG (("Cannot continue with signal %s here. "
+ "Not stopped for EXCEPTION_DEBUG_EVENT.\n",
gdb_signal_to_string (sig)));
}
else if (sig == windows_process.last_sig)
- continue_status = DBG_EXCEPTION_NOT_HANDLED;
+ *continue_status = DBG_EXCEPTION_NOT_HANDLED;
else
OUTMSG (("Can only continue with received signal %s.\n",
gdb_signal_to_string (windows_process.last_sig)));
}
- windows_process.last_sig = GDB_SIGNAL_0;
+ win32_prepare_to_resume (th);
- /* Get context for the currently selected thread. */
- ptid = debug_event_ptid (&windows_process.current_event);
- th = windows_process.find_thread (ptid);
- if (th)
+ DWORD *context_flags = windows_process.context_flags_ptr (th);
+ if (*context_flags)
{
- win32_prepare_to_resume (th);
+ /* Move register values from the inferior into the thread
+ context structure. */
+ regcache_invalidate ();
- DWORD *context_flags = windows_process.context_flags_ptr (th);
- if (*context_flags)
+ if (step)
{
- /* Move register values from the inferior into the thread
- context structure. */
- regcache_invalidate ();
+ if (the_low_target.single_step != NULL)
+ (*the_low_target.single_step) (th);
+ else
+ error ("Single stepping is not supported "
+ "in this configuration.\n");
+ }
+
+ win32_set_thread_context (th);
+ *context_flags = 0;
+ }
+
+ continue_one_thread (thread, th->tid);
+}
+
+/* Resume the inferior process. RESUME_INFO describes how we want
+ to resume. */
+void
+win32_process_target::resume (thread_resume *resume_info, size_t n)
+{
+ DWORD continue_status = DBG_CONTINUE;
+ bool any_pending = false;
+
+ for_each_thread ([&] (thread_info *thread)
+ {
+ auto *th = static_cast<windows_thread_info *> (thread->target_data ());
- if (step)
+ for (int ndx = 0; ndx < n; ndx++)
+ {
+ thread_resume &r = resume_info[ndx];
+ ptid_t ptid = r.thread;
+ gdb_signal sig = gdb_signal_from_host (r.sig);
+ bool step = r.kind == resume_step;
+
+ if (ptid == minus_one_ptid || ptid == thread->id
+ /* Handle both 'pPID' and 'pPID.-1' as meaning 'all threads
+ of PID'. */
+ || (ptid.pid () == thread->id.pid ()
+ && (ptid.is_pid () || ptid.lwp () == -1)))
{
- if (the_low_target.single_step != NULL)
- (*the_low_target.single_step) (th);
- else
- error ("Single stepping is not supported "
- "in this configuration.\n");
- }
+ /* Ignore (wildcard) resume requests for already-resumed
+ threads. */
+ if (!th->suspended)
+ continue;
- win32_set_thread_context (th);
- *context_flags = 0;
+ resume_one_thread (thread, step, sig, &continue_status);
+ break;
+ }
}
- }
- /* Allow continuing with the same signal that interrupted us.
- Otherwise complain. */
+ if (!th->suspended
+ && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ any_pending = true;
+ });
- child_continue (continue_status, tid);
+ if (!any_pending)
+ continue_last_debug_event (continue_status, debug_threads);
}
/* See nat/windows-nat.h. */
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 17/28] Windows gdb: Enable "set scheduler-locking on"
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (15 preceding siblings ...)
2026-04-24 20:44 ` [pushed 16/28] Windows gdbserver: Fix scheduler-locking Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-27 12:57 ` [pushed] Mention Windows scheduler-locking in NEWS (Re: [pushed 17/28] Windows gdb: Enable "set scheduler-locking on") Pedro Alves
2026-04-24 20:44 ` [pushed 18/28] Windows gdbserver: Eliminate soft-interrupt mechanism Pedro Alves
` (10 subsequent siblings)
27 siblings, 1 reply; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
Surprisingly (to me), enabling scheduler locking on Windows currently
fails:
(gdb)
set scheduler-locking on
Target 'native' cannot support this command.
The backend itself does support scheduler-locking. This patch
implements windows_nat_target::get_thread_control_capabilities so that
the core knows schedlocking works for this target.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ie762d3768fd70e4ac398c8bcc03c3213bfa26a6a
---
gdb/windows-nat.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index 221ce58a887..0f586cd195e 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -115,6 +115,9 @@ struct windows_nat_target : public inf_child_target
void close () override;
+ thread_control_capabilities get_thread_control_capabilities () override
+ { return tc_schedlock; }
+
void attach (const char *, int) override;
bool attach_no_wait () override
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 18/28] Windows gdbserver: Eliminate soft-interrupt mechanism
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (16 preceding siblings ...)
2026-04-24 20:44 ` [pushed 17/28] Windows gdb: Enable "set scheduler-locking on" Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:44 ` [pushed 19/28] Windows gdb+gdbserver: Make current_event per-thread state Pedro Alves
` (9 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
I noticed that faked_breakpoint is write only. And then I hacked
win32_process_target::request_interrupt to force it to stop threads
using the soft_interrupt_requested mechanism (which suspends threads,
and then fakes a breakpoint event in the main thread), and saw that it
no longer works -- gdbserver crashes accessing a NULL current_thread,
because fake_breakpoint_event does not switch to a thread.
This code was originally added for Windows CE, as neither
GenerateConsoleCtrlEvent nor DebugBreakProcess worked there. Windows
CE support has since been removed.
We nowadays require Windows XP or later, and XP has DebugBreakProcess.
The soft_interrupt_requested mechanism has other problems, like for
example faking the event in the main thread, even if that thread was
previously stopped, due to scheduler-locking.
A following patch will add a similar mechanism stopping all threads
with SuspendThread to native GDB, for non-stop mode, which doesn't
have these problems. It's different enough from this old code that I
think we should just rip the old code out, and reimplement it from
scratch (based on gdb's version) when we need it.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I89e98233a9c40c6dcba7c8e1dacee08603843fb1
---
gdbserver/win32-low.cc | 39 +--------------------------------------
gdbserver/win32-low.h | 8 --------
2 files changed, 1 insertion(+), 46 deletions(-)
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index be01940d158..5caec960082 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -289,8 +289,6 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
windows_process.process_id = pid;
windows_process.main_thread_id = 0;
- windows_process.soft_interrupt_requested = 0;
- windows_process.faked_breakpoint = 0;
windows_process.open_process_used = true;
memset (&windows_process.current_event, 0,
@@ -407,7 +405,6 @@ child_continue_for_kill (DWORD continue_status, int thread_id)
{
continue_one_thread (thread, thread_id);
});
- windows_process.faked_breakpoint = 0;
return continue_last_debug_event (continue_status, debug_threads);
}
@@ -907,30 +904,6 @@ suspend_one_thread (thread_info *thread)
th->suspend ();
}
-static void
-fake_breakpoint_event (void)
-{
- OUTMSG2(("fake_breakpoint_event\n"));
-
- windows_process.faked_breakpoint = 1;
-
- memset (&windows_process.current_event, 0,
- sizeof (windows_process.current_event));
- windows_process.current_event.dwThreadId = windows_process.main_thread_id;
- windows_process.current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
- windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
- = EXCEPTION_BREAKPOINT;
-
- /* On aarch64, hardware breakpoints also get EXCEPTION_BREAKPOINT,
- but they can be recognized with ExceptionInformation. */
- windows_process.current_event.u.Exception.ExceptionRecord.NumberParameters
- = 1;
- windows_process.current_event.u.Exception.ExceptionRecord
- .ExceptionInformation[0] = 0;
-
- for_each_thread (suspend_one_thread);
-}
-
/* See nat/windows-nat.h. */
bool
@@ -983,13 +956,6 @@ get_child_debug_event (DWORD *continue_status,
DEBUG_EVENT *current_event = &windows_process.current_event;
- if (windows_process.soft_interrupt_requested)
- {
- windows_process.soft_interrupt_requested = 0;
- fake_breakpoint_event ();
- goto gotevent;
- }
-
windows_process.attaching = 0;
{
process_info *proc = find_process_pid (windows_process.process_id);
@@ -1030,8 +996,6 @@ get_child_debug_event (DWORD *continue_status,
}
}
- gotevent:
-
switch (current_event->dwDebugEventCode)
{
case CREATE_THREAD_DEBUG_EVENT:
@@ -1285,8 +1249,7 @@ win32_process_target::request_interrupt ()
if (DebugBreakProcess (windows_process.handle))
return;
- /* Last resort, suspend all threads manually. */
- windows_process.soft_interrupt_requested = 1;
+ OUTMSG2 (("Could not interrupt.\n"));
}
bool
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index 402254fdad5..9c672bfce1a 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -191,14 +191,6 @@ struct gdbserver_windows_process : public windows_nat::windows_process_info
debug event off the win32 API. */
struct target_waitstatus cached_status;
- /* Non zero if an interrupt request is to be satisfied by suspending
- all threads. */
- int soft_interrupt_requested = 0;
-
- /* Non zero if the inferior is stopped in a simulated breakpoint done
- by suspending all the threads. */
- int faked_breakpoint = 0;
-
/* True if current_process_handle needs to be closed. */
bool open_process_used = false;
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 19/28] Windows gdb+gdbserver: Make current_event per-thread state
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (17 preceding siblings ...)
2026-04-24 20:44 ` [pushed 18/28] Windows gdbserver: Eliminate soft-interrupt mechanism Pedro Alves
@ 2026-04-24 20:44 ` Pedro Alves
2026-04-24 20:45 ` [pushed 20/28] Windows gdb+gdbserver: Make last_sig " Pedro Alves
` (8 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
With non-stop mode, each thread is controlled independently of the
others, and each thread has its own independent reason for its last
stop.
Thus, any thread-specific state that is currently per-process must be
converted to per-thread state.
This patch converts windows_process_info::current_event, moving it to
windows_thread_info instead, renamed to last_event.
Since each thread will have its own copy of its last Windows debug
event, we no longer need the same information stored in struct
pending_stop.
Since windows_process.current_event no longer exists, we need to pass
the current event as parameter to a number of methods.
This adjusts both native gdb and gdbserver.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ice09a5d932c912210608d5af25e1898f823e3c99
commit-id:ca7e9182
---
gdb/nat/windows-nat.c | 13 +++---
gdb/nat/windows-nat.h | 24 ++++++-----
gdb/windows-nat.c | 90 +++++++++++++++++++++++-------------------
gdb/windows-nat.h | 10 +++--
gdbserver/win32-low.cc | 68 +++++++++++++++----------------
gdbserver/win32-low.h | 5 ++-
6 files changed, 112 insertions(+), 98 deletions(-)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 11151fdc28a..4adca4afa64 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -300,8 +300,10 @@ get_image_name (HANDLE h, void *address, int unicode)
/* See nat/windows-nat.h. */
bool
-windows_process_info::handle_ms_vc_exception (const EXCEPTION_RECORD *rec)
+windows_process_info::handle_ms_vc_exception (const DEBUG_EVENT ¤t_event)
{
+ const EXCEPTION_RECORD *rec = ¤t_event.u.Exception.ExceptionRecord;
+
if (rec->NumberParameters >= 3
&& (rec->ExceptionInformation[0] & 0xffffffff) == 0x1000)
{
@@ -342,7 +344,8 @@ windows_process_info::handle_ms_vc_exception (const EXCEPTION_RECORD *rec)
#define MS_VC_EXCEPTION 0x406d1388
handle_exception_result
-windows_process_info::handle_exception (struct target_waitstatus *ourstatus,
+windows_process_info::handle_exception (DEBUG_EVENT ¤t_event,
+ struct target_waitstatus *ourstatus,
bool debug_exceptions)
{
#define DEBUG_EXCEPTION_SIMPLE(x) if (debug_exceptions) \
@@ -470,7 +473,7 @@ windows_process_info::handle_exception (struct target_waitstatus *ourstatus,
break;
case MS_VC_EXCEPTION:
DEBUG_EXCEPTION_SIMPLE ("MS_VC_EXCEPTION");
- if (handle_ms_vc_exception (rec))
+ if (handle_ms_vc_exception (current_event))
{
ourstatus->set_stopped (GDB_SIGNAL_TRAP);
result = HANDLE_EXCEPTION_IGNORED;
@@ -610,11 +613,11 @@ windows_process_info::add_dll (LPVOID load_addr)
/* See nat/windows-nat.h. */
void
-windows_process_info::dll_loaded_event ()
+windows_process_info::dll_loaded_event (const DEBUG_EVENT ¤t_event)
{
gdb_assert (current_event.dwDebugEventCode == LOAD_DLL_DEBUG_EVENT);
- LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
+ const LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
const char *dll_name;
/* Try getting the DLL name via the lpImageName field of the event.
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 8d97ecc4cb6..4710ec39130 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -46,10 +46,6 @@ struct pending_stop
/* The target waitstatus we computed. TARGET_WAITKIND_IGNORE if the
thread does not have a pending stop. */
target_waitstatus status;
-
- /* The event. A few fields of this can be referenced after a stop,
- and it seemed simplest to store the entire event. */
- DEBUG_EVENT event;
};
@@ -104,6 +100,10 @@ struct windows_thread_info
process them once the step has completed. See PR gdb/22992. */
struct pending_stop pending_stop {};
+ /* The last Windows event returned by WaitForDebugEvent for this
+ thread. */
+ DEBUG_EVENT last_event {};
+
/* The context of the thread, including any manipulations. */
union
{
@@ -149,10 +149,6 @@ struct windows_process_info
DWORD main_thread_id = 0;
enum gdb_signal last_sig = GDB_SIGNAL_0;
- /* The current debug event from WaitForDebugEvent or from a pending
- stop. */
- DEBUG_EVENT current_event {};
-
/* Contents of $_siginfo */
EXCEPTION_RECORD siginfo_er {};
@@ -176,7 +172,8 @@ struct windows_process_info
a Cygwin signal. Otherwise just print the string as a warning.
This function must be supplied by the embedding application. */
- virtual DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) = 0;
+ virtual DWORD handle_output_debug_string (const DEBUG_EVENT ¤t_event,
+ struct target_waitstatus *ourstatus) = 0;
/* Handle a DLL load event.
@@ -197,7 +194,7 @@ struct windows_process_info
This function must be supplied by the embedding application. */
- virtual void handle_unload_dll () = 0;
+ virtual void handle_unload_dll (const DEBUG_EVENT ¤t_event) = 0;
/* When EXCEPTION_ACCESS_VIOLATION is processed, we give the embedding
application a chance to change it to be considered "unhandled".
@@ -207,11 +204,12 @@ struct windows_process_info
virtual bool handle_access_violation (const EXCEPTION_RECORD *rec) = 0;
handle_exception_result handle_exception
- (struct target_waitstatus *ourstatus, bool debug_exceptions);
+ (DEBUG_EVENT ¤t_event,
+ struct target_waitstatus *ourstatus, bool debug_exceptions);
/* Call to indicate that a DLL was loaded. */
- void dll_loaded_event ();
+ void dll_loaded_event (const DEBUG_EVENT ¤t_event);
/* Iterate over all DLLs currently mapped by our inferior, and
add them to our list of solibs. */
@@ -247,7 +245,7 @@ struct windows_process_info
Return true if the exception was handled; return false otherwise. */
- bool handle_ms_vc_exception (const EXCEPTION_RECORD *rec);
+ bool handle_ms_vc_exception (const DEBUG_EVENT ¤t_event);
/* Iterate over all DLLs currently mapped by our inferior, looking for
a DLL which is loaded at LOAD_ADDR. If found, add the DLL to our
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 614d85ac8f7..64c53b53934 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -557,7 +557,7 @@ windows_per_inferior::handle_load_dll (const char *dll_name, LPVOID base)
/* See nat/windows-nat.h. */
void
-windows_per_inferior::handle_unload_dll ()
+windows_per_inferior::handle_unload_dll (const DEBUG_EVENT ¤t_event)
{
LPVOID lpBaseOfDll = current_event.u.UnloadDll.lpBaseOfDll;
@@ -620,7 +620,8 @@ signal_event_command (const char *args, int from_tty)
DWORD
windows_per_inferior::handle_output_debug_string
- (struct target_waitstatus *ourstatus)
+ (const DEBUG_EVENT ¤t_event,
+ struct target_waitstatus *ourstatus)
{
DWORD thread_id = 0;
@@ -753,11 +754,11 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
/* Called in pathological case where Windows fails to send a
CREATE_PROCESS_DEBUG_EVENT after an attach. */
DWORD
-windows_nat_target::fake_create_process ()
+windows_nat_target::fake_create_process (const DEBUG_EVENT ¤t_event)
{
windows_process->handle
= OpenProcess (PROCESS_ALL_ACCESS, FALSE,
- windows_process->current_event.dwProcessId);
+ current_event.dwProcessId);
if (windows_process->handle != NULL)
windows_process->open_process_used = 1;
else
@@ -766,12 +767,11 @@ windows_nat_target::fake_create_process ()
throw_winerror_with_name (_("OpenProcess call failed"), err);
/* We can not debug anything in that case. */
}
- add_thread (ptid_t (windows_process->current_event.dwProcessId,
- windows_process->current_event.dwThreadId, 0),
- windows_process->current_event.u.CreateThread.hThread,
- windows_process->current_event.u.CreateThread.lpThreadLocalBase,
+ add_thread (ptid_t (current_event.dwProcessId, current_event.dwThreadId, 0),
+ current_event.u.CreateThread.hThread,
+ current_event.u.CreateThread.lpThreadLocalBase,
true /* main_thread_p */);
- return windows_process->current_event.dwThreadId;
+ return current_event.dwThreadId;
}
void
@@ -788,6 +788,13 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
if (resume_all)
ptid = inferior_ptid;
+ DEBUG_EXEC ("pid=%d, tid=0x%x, step=%d, sig=%d",
+ ptid.pid (), (unsigned) ptid.lwp (), step, sig);
+
+ /* Get currently selected thread. */
+ th = windows_process->find_thread (inferior_ptid);
+ gdb_assert (th != nullptr);
+
if (sig != GDB_SIGNAL_0)
{
/* Note it is OK to call get_last_debug_event_ptid() from the
@@ -800,8 +807,7 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
DEBUG_EXCEPT ("Cannot continue with signal %d here. "
"Not last-event thread", sig);
}
- else if (windows_process->current_event.dwDebugEventCode
- != EXCEPTION_DEBUG_EVENT)
+ else if (th->last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
{
DEBUG_EXCEPT ("Cannot continue with signal %d here. "
"Not stopped for EXCEPTION_DEBUG_EVENT", sig);
@@ -818,7 +824,7 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
for (const xlate_exception &x : xlate)
if (x.us == sig)
{
- current_event.u.Exception.ExceptionRecord.ExceptionCode
+ th->last_event.u.Exception.ExceptionRecord.ExceptionCode
= x.them;
continue_status = DBG_EXCEPTION_NOT_HANDLED;
break;
@@ -835,12 +841,8 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
windows_process->last_sig = GDB_SIGNAL_0;
- DEBUG_EXEC ("pid=%d, tid=0x%x, step=%d, sig=%d",
- ptid.pid (), (unsigned) ptid.lwp (), step, sig);
-
/* Get context for currently selected thread. */
- th = windows_process->find_thread (inferior_ptid);
- if (th != nullptr && step)
+ if (step)
{
/* Single step by setting t bit. */
regcache *regcache = get_thread_regcache (inferior_thread ());
@@ -915,7 +917,8 @@ windows_nat_target::stop (ptid_t ptid)
ptid_t
windows_nat_target::get_windows_debug_event
- (int pid, struct target_waitstatus *ourstatus, target_wait_flags options)
+ (int pid, struct target_waitstatus *ourstatus, target_wait_flags options,
+ DEBUG_EVENT *current_event)
{
DWORD continue_status, event_code;
DWORD thread_id = 0;
@@ -933,7 +936,7 @@ windows_nat_target::get_windows_debug_event
thread_id = th->tid;
*ourstatus = th->pending_stop.status;
th->pending_stop.status.set_ignore ();
- windows_process->current_event = th->pending_stop.event;
+ *current_event = th->last_event;
ptid_t ptid (windows_process->process_id, thread_id);
windows_process->invalidate_thread_context (th.get ());
@@ -942,7 +945,6 @@ windows_nat_target::get_windows_debug_event
}
windows_process->last_sig = GDB_SIGNAL_0;
- DEBUG_EVENT *current_event = &windows_process->current_event;
if ((options & TARGET_WNOHANG) != 0 && !m_debug_event_pending)
{
@@ -950,11 +952,11 @@ windows_nat_target::get_windows_debug_event
return minus_one_ptid;
}
- wait_for_debug_event_main_thread (&windows_process->current_event);
+ wait_for_debug_event_main_thread (current_event);
continue_status = DBG_CONTINUE;
- event_code = windows_process->current_event.dwDebugEventCode;
+ event_code = current_event->dwDebugEventCode;
ourstatus->set_spurious ();
switch (event_code)
@@ -972,7 +974,7 @@ windows_nat_target::get_windows_debug_event
/* Kludge around a Windows bug where first event is a create
thread event. Caused when attached process does not have
a main thread. */
- thread_id = fake_create_process ();
+ thread_id = fake_create_process (*current_event);
if (thread_id)
windows_process->saw_create++;
}
@@ -1069,7 +1071,7 @@ windows_nat_target::get_windows_debug_event
break;
try
{
- windows_process->dll_loaded_event ();
+ windows_process->dll_loaded_event (*current_event);
}
catch (const gdb_exception &ex)
{
@@ -1089,7 +1091,7 @@ windows_nat_target::get_windows_debug_event
break;
try
{
- windows_process->handle_unload_dll ();
+ windows_process->handle_unload_dll (*current_event);
}
catch (const gdb_exception &ex)
{
@@ -1106,7 +1108,8 @@ windows_nat_target::get_windows_debug_event
"EXCEPTION_DEBUG_EVENT");
if (windows_process->saw_create != 1)
break;
- switch (windows_process->handle_exception (ourstatus, debug_exceptions))
+ switch (windows_process->handle_exception (*current_event,
+ ourstatus, debug_exceptions))
{
case HANDLE_EXCEPTION_UNHANDLED:
default:
@@ -1128,7 +1131,8 @@ windows_nat_target::get_windows_debug_event
"OUTPUT_DEBUG_STRING_EVENT");
if (windows_process->saw_create != 1)
break;
- thread_id = windows_process->handle_output_debug_string (ourstatus);
+ thread_id = windows_process->handle_output_debug_string (*current_event,
+ ourstatus);
break;
default:
@@ -1153,6 +1157,8 @@ windows_nat_target::get_windows_debug_event
const ptid_t ptid = ptid_t (current_event->dwProcessId, thread_id, 0);
windows_thread_info *th = windows_process->find_thread (ptid);
+ th->last_event = *current_event;
+
if (th->suspended)
{
/* Pending stop. See the comment by the definition of
@@ -1168,8 +1174,8 @@ windows_nat_target::get_windows_debug_event
th->stopped_at_software_breakpoint = true;
th->pc_adjusted = false;
}
+
th->pending_stop.status = *ourstatus;
- th->pending_stop.event = *current_event;
ourstatus->set_ignore ();
continue_last_debug_event_main_thread
@@ -1195,7 +1201,10 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
while (1)
{
- ptid_t result = get_windows_debug_event (pid, ourstatus, options);
+ DEBUG_EVENT current_event;
+
+ ptid_t result = get_windows_debug_event (pid, ourstatus, options,
+ ¤t_event);
if ((options & TARGET_WNOHANG) != 0
&& ourstatus->kind () == TARGET_WAITKIND_IGNORE)
@@ -1214,11 +1223,11 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
windows_thread_info *th = windows_process->find_thread (result);
th->stopped_at_software_breakpoint = false;
- if (windows_process->current_event.dwDebugEventCode
+ if (current_event.dwDebugEventCode
== EXCEPTION_DEBUG_EVENT
- && ((windows_process->current_event.u.Exception.ExceptionRecord.ExceptionCode
+ && ((current_event.u.Exception.ExceptionRecord.ExceptionCode
== EXCEPTION_BREAKPOINT)
- || (windows_process->current_event.u.Exception.ExceptionRecord.ExceptionCode
+ || (current_event.u.Exception.ExceptionRecord.ExceptionCode
== STATUS_WX86_BREAKPOINT))
&& windows_process->windows_initialization_done)
{
@@ -1261,8 +1270,6 @@ windows_nat_target::do_initial_windows_stuff (DWORD pid, bool attaching)
windows_process->cygwin_load_end = 0;
#endif
windows_process->process_id = pid;
- memset (&windows_process->current_event, 0,
- sizeof (windows_process->current_event));
inf = current_inferior ();
if (!inf->target_is_pushed (this))
inf->push_target (this);
@@ -1292,7 +1299,10 @@ windows_nat_target::do_initial_windows_stuff (DWORD pid, bool attaching)
&& status.kind () != TARGET_WAITKIND_SPURIOUS)
break;
- this->resume (minus_one_ptid, 0, GDB_SIGNAL_0);
+ /* Don't use windows_nat_target::resume here because that
+ assumes that inferior_ptid points at a valid thread, and we
+ haven't switched to any thread yet. */
+ windows_continue (DBG_CONTINUE, -1);
}
switch_to_thread (this->find_thread (last_ptid));
@@ -1575,7 +1585,7 @@ windows_nat_target::detach (inferior *inf, int from_tty)
if (process_alive)
do_synchronously ([&] ()
{
- if (!DebugActiveProcessStop (windows_process->current_event.dwProcessId))
+ if (!DebugActiveProcessStop (windows_process->process_id))
err = (unsigned) GetLastError ();
else
DebugSetProcessKillOnExit (FALSE);
@@ -1586,7 +1596,7 @@ windows_nat_target::detach (inferior *inf, int from_tty)
{
std::string msg
= string_printf (_("Can't detach process %u"),
- (unsigned) windows_process->current_event.dwProcessId);
+ windows_process->process_id);
throw_winerror_with_name (msg.c_str (), *err);
}
@@ -2383,9 +2393,9 @@ windows_nat_target::kill ()
{
if (!windows_continue (DBG_CONTINUE, -1, WCONT_KILLED))
break;
- wait_for_debug_event_main_thread (&windows_process->current_event);
- if (windows_process->current_event.dwDebugEventCode
- == EXIT_PROCESS_DEBUG_EVENT)
+ DEBUG_EVENT current_event;
+ wait_for_debug_event_main_thread (¤t_event);
+ if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
break;
}
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index 0f586cd195e..15a177214f2 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -63,9 +63,10 @@ DEF_ENUM_FLAGS_TYPE (windows_continue_flag, windows_continue_flags);
struct windows_per_inferior : public windows_nat::windows_process_info
{
windows_thread_info *find_thread (ptid_t ptid) override;
- DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) override;
+ DWORD handle_output_debug_string (const DEBUG_EVENT ¤t_event,
+ struct target_waitstatus *ourstatus) override;
void handle_load_dll (const char *dll_name, LPVOID base) override;
- void handle_unload_dll () override;
+ void handle_unload_dll (const DEBUG_EVENT ¤t_event) override;
bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
/* Invalidate the thread context. */
@@ -172,7 +173,8 @@ struct windows_nat_target : public inf_child_target
const char *thread_name (struct thread_info *) override;
ptid_t get_windows_debug_event (int pid, struct target_waitstatus *ourstatus,
- target_wait_flags options);
+ target_wait_flags options,
+ DEBUG_EVENT *current_event);
void do_initial_windows_stuff (DWORD pid, bool attaching);
@@ -244,7 +246,7 @@ struct windows_nat_target : public inf_child_target
windows_thread_info *add_thread (ptid_t ptid, HANDLE h, void *tlb,
bool main_thread_p);
void delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p);
- DWORD fake_create_process ();
+ DWORD fake_create_process (const DEBUG_EVENT ¤t_event);
void continue_one_thread (windows_thread_info *th,
windows_continue_flags cont_flags);
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 5caec960082..aa484fcda4a 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -291,9 +291,6 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
windows_process.open_process_used = true;
- memset (&windows_process.current_event, 0,
- sizeof (windows_process.current_event));
-
#ifdef __x86_64__
BOOL wow64;
if (!IsWow64Process (proch, &wow64))
@@ -590,7 +587,8 @@ win32_process_target::attach (unsigned long pid)
DWORD
gdbserver_windows_process::handle_output_debug_string
- (struct target_waitstatus *ourstatus)
+ (const DEBUG_EVENT ¤t_event,
+ struct target_waitstatus *ourstatus)
{
#define READ_BUFFER_LEN 1024
CORE_ADDR addr;
@@ -658,14 +656,13 @@ win32_process_target::kill (process_info *process)
{
if (!child_continue_for_kill (DBG_CONTINUE, -1))
break;
- if (!wait_for_debug_event (&windows_process.current_event, INFINITE))
+ DEBUG_EVENT current_event;
+ if (!wait_for_debug_event (¤t_event, INFINITE))
break;
- if (windows_process.current_event.dwDebugEventCode
- == EXIT_PROCESS_DEBUG_EVENT)
+ if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
break;
- else if (windows_process.current_event.dwDebugEventCode
- == OUTPUT_DEBUG_STRING_EVENT)
- windows_process.handle_output_debug_string (nullptr);
+ else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
+ windows_process.handle_output_debug_string (current_event, nullptr);
}
win32_clear_process ();
@@ -744,8 +741,7 @@ resume_one_thread (thread_info *thread, bool step, gdb_signal sig,
OUTMSG (("Cannot continue with signal %d here. "
"Not last-event thread", sig));
}
- else if (windows_process.current_event.dwDebugEventCode
- != EXCEPTION_DEBUG_EVENT)
+ else if (th->last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
{
OUTMSG (("Cannot continue with signal %s here. "
"Not stopped for EXCEPTION_DEBUG_EVENT.\n",
@@ -884,7 +880,7 @@ gdbserver_windows_process::handle_load_dll (const char *name, LPVOID base)
/* See nat/windows-nat.h. */
void
-gdbserver_windows_process::handle_unload_dll ()
+gdbserver_windows_process::handle_unload_dll (const DEBUG_EVENT ¤t_event)
{
CORE_ADDR load_addr =
(CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll;
@@ -918,7 +914,7 @@ gdbserver_windows_process::handle_access_violation
PC. */
static void
-maybe_adjust_pc ()
+maybe_adjust_pc (const DEBUG_EVENT ¤t_event)
{
regcache *regcache = get_thread_regcache (current_thread);
child_fetch_inferior_registers (regcache, -1);
@@ -927,8 +923,8 @@ maybe_adjust_pc ()
= windows_process.find_thread (current_thread->id);
th->stopped_at_software_breakpoint = false;
- if (windows_process.current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT
- && (*the_low_target.is_sw_breakpoint) (&windows_process.current_event
+ if (current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT
+ && (*the_low_target.is_sw_breakpoint) (¤t_event
.u.Exception.ExceptionRecord)
&& windows_process.child_initialization_done)
{
@@ -943,7 +939,8 @@ maybe_adjust_pc ()
static int
get_child_debug_event (DWORD *continue_status,
- struct target_waitstatus *ourstatus)
+ struct target_waitstatus *ourstatus,
+ DEBUG_EVENT *current_event)
{
ptid_t ptid;
@@ -954,8 +951,6 @@ get_child_debug_event (DWORD *continue_status,
/* Check if GDB sent us an interrupt request. */
check_remote_input_interrupt_request ();
- DEBUG_EVENT *current_event = &windows_process.current_event;
-
windows_process.attaching = 0;
{
process_info *proc = find_process_pid (windows_process.process_id);
@@ -968,8 +963,8 @@ get_child_debug_event (DWORD *continue_status,
{
*ourstatus = th->pending_stop.status;
th->pending_stop.status.set_ignore ();
- windows_process.current_event = th->pending_stop.event;
- ptid = debug_event_ptid (&windows_process.current_event);
+ *current_event = th->last_event;
+ ptid = debug_event_ptid (current_event);
switch_to_thread (find_thread_ptid (ptid));
return 1;
}
@@ -978,7 +973,7 @@ get_child_debug_event (DWORD *continue_status,
/* Keep the wait time low enough for comfortable remote
interruption, but high enough so gdbserver doesn't become a
bottleneck. */
- if (!wait_for_debug_event (&windows_process.current_event, 250))
+ if (!wait_for_debug_event (current_event, 250))
{
DWORD e = GetLastError();
@@ -1073,7 +1068,7 @@ get_child_debug_event (DWORD *continue_status,
CloseHandle (current_event->u.LoadDll.hFile);
if (! windows_process.child_initialization_done)
break;
- windows_process.dll_loaded_event ();
+ windows_process.dll_loaded_event (*current_event);
ourstatus->set_loaded ();
break;
@@ -1085,7 +1080,7 @@ get_child_debug_event (DWORD *continue_status,
(unsigned) current_event->dwThreadId));
if (! windows_process.child_initialization_done)
break;
- windows_process.handle_unload_dll ();
+ windows_process.handle_unload_dll (*current_event);
ourstatus->set_loaded ();
break;
@@ -1094,7 +1089,8 @@ get_child_debug_event (DWORD *continue_status,
"for pid=%u tid=%x\n",
(unsigned) current_event->dwProcessId,
(unsigned) current_event->dwThreadId));
- if (windows_process.handle_exception (ourstatus, debug_threads)
+ if (windows_process.handle_exception (*current_event,
+ ourstatus, debug_threads)
== HANDLE_EXCEPTION_UNHANDLED)
*continue_status = DBG_EXCEPTION_NOT_HANDLED;
break;
@@ -1105,7 +1101,7 @@ get_child_debug_event (DWORD *continue_status,
"for pid=%u tid=%x\n",
(unsigned) current_event->dwProcessId,
(unsigned) current_event->dwThreadId));
- windows_process.handle_output_debug_string (nullptr);
+ windows_process.handle_output_debug_string (*current_event, nullptr);
break;
default:
@@ -1117,10 +1113,12 @@ get_child_debug_event (DWORD *continue_status,
break;
}
- ptid = debug_event_ptid (&windows_process.current_event);
+ ptid = debug_event_ptid (current_event);
windows_thread_info *th = windows_process.find_thread (ptid);
+ th->last_event = *current_event;
+
if (th != nullptr && th->suspended)
{
/* Pending stop. See the comment by the definition of
@@ -1129,9 +1127,8 @@ get_child_debug_event (DWORD *continue_status,
OUTMSG2 (("get_windows_debug_event - "
"unexpected stop in suspended thread 0x%x\n",
th->tid));
- maybe_adjust_pc ();
+ maybe_adjust_pc (*current_event);
th->pending_stop.status = *ourstatus;
- th->pending_stop.event = *current_event;
ourstatus->set_spurious ();
}
else
@@ -1155,13 +1152,16 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
fails). Report it now. */
*ourstatus = windows_process.cached_status;
windows_process.cached_status.set_ignore ();
- return debug_event_ptid (&windows_process.current_event);
+ return ptid_t (windows_process.process_id,
+ windows_process.main_thread_id, 0);
}
while (1)
{
DWORD continue_status;
- if (!get_child_debug_event (&continue_status, ourstatus))
+ DEBUG_EVENT current_event;
+ if (!get_child_debug_event (&continue_status, ourstatus,
+ ¤t_event))
continue;
switch (ourstatus->kind ())
@@ -1170,20 +1170,20 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus,
OUTMSG2 (("Child exited with retcode = %x\n",
ourstatus->exit_status ()));
win32_clear_process ();
- return ptid_t (windows_process.current_event.dwProcessId);
+ return ptid_t (windows_process.process_id);
case TARGET_WAITKIND_STOPPED:
case TARGET_WAITKIND_SIGNALLED:
case TARGET_WAITKIND_LOADED:
{
OUTMSG2 (("Child Stopped with signal = %d \n",
ourstatus->sig ()));
- maybe_adjust_pc ();
+ maybe_adjust_pc (current_event);
/* All-stop, suspend all threads until they are explicitly
resumed. */
for_each_thread (suspend_one_thread);
- return debug_event_ptid (&windows_process.current_event);
+ return debug_event_ptid (¤t_event);
}
default:
OUTMSG (("Ignoring unknown internal event, %d\n",
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index 9c672bfce1a..01a904043eb 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -179,9 +179,10 @@ class win32_process_target : public process_stratum_target
struct gdbserver_windows_process : public windows_nat::windows_process_info
{
windows_nat::windows_thread_info *find_thread (ptid_t ptid) override;
- DWORD handle_output_debug_string (struct target_waitstatus *ourstatus) override;
+ DWORD handle_output_debug_string (const DEBUG_EVENT ¤t_event,
+ struct target_waitstatus *ourstatus) override;
void handle_load_dll (const char *dll_name, LPVOID base) override;
- void handle_unload_dll () override;
+ void handle_unload_dll (const DEBUG_EVENT ¤t_event) override;
bool handle_access_violation (const EXCEPTION_RECORD *rec) override;
int attaching = 0;
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 20/28] Windows gdb+gdbserver: Make last_sig per-thread state
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (18 preceding siblings ...)
2026-04-24 20:44 ` [pushed 19/28] Windows gdb+gdbserver: Make current_event per-thread state Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 21/28] Windows gdb+gdbserver: Make siginfo_er " Pedro Alves
` (7 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
With non-stop mode, each thread is controlled independently of the
others, and each thread has its own independent reason for its last
stop.
Thus, any thread-specific state that is currently per-process must be
converted to per-thread state.
This patch converts windows_process_info::last_sig to per-thread
state, moving it to windows_thread_info instead.
This adjusts both native gdb and gdbserver.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: Ie8c673a819be445753d967afd3a6084565648448
commit-id:d7dd0d0e
---
gdb/nat/windows-nat.c | 8 +++++---
gdb/nat/windows-nat.h | 5 ++++-
gdb/windows-nat.c | 10 +++-------
gdbserver/win32-low.cc | 7 +++----
4 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 4adca4afa64..7eff99170fc 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -359,8 +359,6 @@ windows_process_info::handle_exception (DEBUG_EVENT ¤t_event,
memcpy (&siginfo_er, rec, sizeof siginfo_er);
- last_sig = GDB_SIGNAL_0;
-
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
@@ -494,7 +492,11 @@ windows_process_info::handle_exception (DEBUG_EVENT ¤t_event,
}
if (ourstatus->kind () == TARGET_WAITKIND_STOPPED)
- last_sig = ourstatus->sig ();
+ {
+ ptid_t ptid (current_event.dwProcessId, current_event.dwThreadId, 0);
+ windows_thread_info *th = find_thread (ptid);
+ th->last_sig = ourstatus->sig ();
+ }
return result;
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 4710ec39130..8555085c9f7 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -104,6 +104,10 @@ struct windows_thread_info
thread. */
DEBUG_EVENT last_event {};
+ /* The last signal reported for this thread, extracted out of
+ last_event. */
+ enum gdb_signal last_sig = GDB_SIGNAL_0;
+
/* The context of the thread, including any manipulations. */
union
{
@@ -147,7 +151,6 @@ struct windows_process_info
HANDLE handle = 0;
DWORD process_id = 0;
DWORD main_thread_id = 0;
- enum gdb_signal last_sig = GDB_SIGNAL_0;
/* Contents of $_siginfo */
EXCEPTION_RECORD siginfo_er {};
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 64c53b53934..b5e80139c26 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -712,6 +712,7 @@ windows_nat_target::continue_one_thread (windows_thread_info *th,
bool killed = (cont_flags & WCONT_KILLED) != 0;
thread_context_continue (th, killed);
th->resume ();
+ th->last_sig = GDB_SIGNAL_0;
}
/* Resume thread specified by ID, or all artificially suspended
@@ -812,7 +813,7 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
DEBUG_EXCEPT ("Cannot continue with signal %d here. "
"Not stopped for EXCEPTION_DEBUG_EVENT", sig);
}
- else if (sig == windows_process->last_sig)
+ else if (sig == th->last_sig)
continue_status = DBG_EXCEPTION_NOT_HANDLED;
else
#if 0
@@ -836,11 +837,9 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig)
}
#endif
DEBUG_EXCEPT ("Can only continue with received signal %d.",
- windows_process->last_sig);
+ th->last_sig);
}
- windows_process->last_sig = GDB_SIGNAL_0;
-
/* Get context for currently selected thread. */
if (step)
{
@@ -944,8 +943,6 @@ windows_nat_target::get_windows_debug_event
}
}
- windows_process->last_sig = GDB_SIGNAL_0;
-
if ((options & TARGET_WNOHANG) != 0 && !m_debug_event_pending)
{
ourstatus->set_ignore ();
@@ -1263,7 +1260,6 @@ windows_nat_target::do_initial_windows_stuff (DWORD pid, bool attaching)
initialize_windows_arch (attaching);
- windows_process->last_sig = GDB_SIGNAL_0;
windows_process->open_process_used = 0;
#ifdef __CYGWIN__
windows_process->cygwin_load_start = 0;
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index aa484fcda4a..5e09779082c 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -284,7 +284,6 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
{
struct process_info *proc;
- windows_process.last_sig = GDB_SIGNAL_0;
windows_process.handle = proch;
windows_process.process_id = pid;
windows_process.main_thread_id = 0;
@@ -385,6 +384,7 @@ continue_one_thread (thread_info *thread, int thread_id)
}
th->resume ();
+ th->last_sig = GDB_SIGNAL_0;
}
}
}
@@ -747,11 +747,11 @@ resume_one_thread (thread_info *thread, bool step, gdb_signal sig,
"Not stopped for EXCEPTION_DEBUG_EVENT.\n",
gdb_signal_to_string (sig)));
}
- else if (sig == windows_process.last_sig)
+ else if (sig == th->last_sig)
*continue_status = DBG_EXCEPTION_NOT_HANDLED;
else
OUTMSG (("Can only continue with received signal %s.\n",
- gdb_signal_to_string (windows_process.last_sig)));
+ gdb_signal_to_string (th->last_sig)));
}
win32_prepare_to_resume (th);
@@ -944,7 +944,6 @@ get_child_debug_event (DWORD *continue_status,
{
ptid_t ptid;
- windows_process.last_sig = GDB_SIGNAL_0;
ourstatus->set_spurious ();
*continue_status = DBG_CONTINUE;
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 21/28] Windows gdb+gdbserver: Make siginfo_er per-thread state
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (19 preceding siblings ...)
2026-04-24 20:45 ` [pushed 20/28] Windows gdb+gdbserver: Make last_sig " Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 22/28] Add backpointer from windows_thread_info to windows_process_info Pedro Alves
` (6 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
With non-stop mode support, each thread has its own "last event", and
so printing $_siginfo should print the siginfo of the selected thread.
Likewise, with all-stop and scheduler-locking.
This patch reworks the siginfo functions in gdb/windows-nat.c and
gdbserver/win32-low.cc to reuse the exception record already saved
within each thread's 'last_event' field.
Here's an example of what you'll see after the whole non-stop series:
(gdb) thread apply all p -pretty -- $_siginfo
Thread 3 (Thread 2612.0x1470):
$1 = {
ExceptionCode = DBG_CONTROL_C,
ExceptionFlags = 0,
ExceptionRecord = 0x0,
ExceptionAddress = 0x7ffd0583e929 <KERNELBASE!EncodeRemotePointer+8249>,
NumberParameters = 0,
{
ExceptionInformation = {0 <repeats 15 times>},
AccessViolationInformation = {
Type = READ_ACCESS_VIOLATION,
Address = 0x0
}
}
}
Thread 2 (Thread 2612.0x1704):
$2 = {
ExceptionCode = SINGLE_STEP,
ExceptionFlags = 0,
ExceptionRecord = 0x0,
ExceptionAddress = 0x7ffd080ad6e4 <ntdll!ZwDelayExecution+20>,
NumberParameters = 0,
{
ExceptionInformation = {0 <repeats 15 times>},
AccessViolationInformation = {
Type = READ_ACCESS_VIOLATION,
Address = 0x0
}
}
}
Thread 1 (Thread 2612.0x434):
$3 = {
ExceptionCode = BREAKPOINT,
ExceptionFlags = 0,
ExceptionRecord = 0x0,
ExceptionAddress = 0x7ff6f691174c <main+185>,
NumberParameters = 1,
{
ExceptionInformation = {0 <repeats 15 times>},
AccessViolationInformation = {
Type = READ_ACCESS_VIOLATION,
Address = 0x0
}
}
}
(gdb)
This was in non-stop mode, and the program originally had two threads.
Thread 1 stopped for a breakpoint, then thread 2 was manually
interrupted/paused and then single-stepped. And then I typed Ctrl-C
in the inferior's terminal, which made Windows inject thread 3 in the
inferior, and report a DBG_CONTROL_C exception for it.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I5d4f1b62f59e8aef3606642c6524df2362b0fb7d
commit-id:e0f75dea
---
gdb/nat/windows-nat.c | 2 --
gdb/nat/windows-nat.h | 3 ---
gdb/windows-nat.c | 33 ++++++++++++++++-----------------
gdbserver/win32-low.cc | 27 ++++++++++++++-------------
4 files changed, 30 insertions(+), 35 deletions(-)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 7eff99170fc..1f8967dcd8d 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -357,8 +357,6 @@ windows_process_info::handle_exception (DEBUG_EVENT ¤t_event,
DWORD code = rec->ExceptionCode;
handle_exception_result result = HANDLE_EXCEPTION_HANDLED;
- memcpy (&siginfo_er, rec, sizeof siginfo_er);
-
switch (code)
{
case EXCEPTION_ACCESS_VIOLATION:
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 8555085c9f7..152410580f0 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -152,9 +152,6 @@ struct windows_process_info
DWORD process_id = 0;
DWORD main_thread_id = 0;
- /* Contents of $_siginfo */
- EXCEPTION_RECORD siginfo_er {};
-
#ifdef __x86_64__
/* The target is a WOW64 process */
bool wow64_process = false;
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index b5e80139c26..1593524f900 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2336,7 +2336,6 @@ windows_nat_target::mourn_inferior ()
CHECK (CloseHandle (windows_process->handle));
windows_process->open_process_used = 0;
}
- windows_process->siginfo_er.ExceptionCode = 0;
inf_child_target::mourn_inferior ();
}
@@ -2453,8 +2452,15 @@ static enum target_xfer_status
windows_xfer_siginfo (gdb_byte *readbuf, ULONGEST offset, ULONGEST len,
ULONGEST *xfered_len)
{
- char *buf = (char *) &windows_process->siginfo_er;
- size_t bufsize = sizeof (windows_process->siginfo_er);
+ windows_thread_info *th = windows_process->find_thread (inferior_ptid);
+
+ if (th->last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
+ return TARGET_XFER_E_IO;
+
+ EXCEPTION_RECORD &er = th->last_event.u.Exception.ExceptionRecord;
+
+ char *buf = (char *) &er;
+ size_t bufsize = sizeof (er);
#ifdef __x86_64__
EXCEPTION_RECORD32 er32;
@@ -2463,23 +2469,16 @@ windows_xfer_siginfo (gdb_byte *readbuf, ULONGEST offset, ULONGEST len,
buf = (char *) &er32;
bufsize = sizeof (er32);
- er32.ExceptionCode = windows_process->siginfo_er.ExceptionCode;
- er32.ExceptionFlags = windows_process->siginfo_er.ExceptionFlags;
- er32.ExceptionRecord
- = (uintptr_t) windows_process->siginfo_er.ExceptionRecord;
- er32.ExceptionAddress
- = (uintptr_t) windows_process->siginfo_er.ExceptionAddress;
- er32.NumberParameters = windows_process->siginfo_er.NumberParameters;
- int i;
- for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
- er32.ExceptionInformation[i]
- = windows_process->siginfo_er.ExceptionInformation[i];
+ er32.ExceptionCode = er.ExceptionCode;
+ er32.ExceptionFlags = er.ExceptionFlags;
+ er32.ExceptionRecord = (uintptr_t) er.ExceptionRecord;
+ er32.ExceptionAddress = (uintptr_t) er.ExceptionAddress;
+ er32.NumberParameters = er.NumberParameters;
+ for (int i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
+ er32.ExceptionInformation[i] = er.ExceptionInformation[i];
}
#endif
- if (windows_process->siginfo_er.ExceptionCode == 0)
- return TARGET_XFER_E_IO;
-
if (readbuf == nullptr)
return TARGET_XFER_E_IO;
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 5e09779082c..cc4a63319ef 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -643,7 +643,6 @@ win32_clear_process ()
}
for_each_thread (delete_thread_info);
- windows_process.siginfo_er.ExceptionCode = 0;
}
/* Implementation of target_ops::kill. */
@@ -1271,14 +1270,18 @@ win32_process_target::qxfer_siginfo (const char *annex,
unsigned const char *writebuf,
CORE_ADDR offset, int len)
{
- if (windows_process.siginfo_er.ExceptionCode == 0)
+ windows_thread_info *th = windows_process.find_thread (current_thread->id);
+
+ if (th->last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
return -1;
if (readbuf == nullptr)
return -1;
- char *buf = (char *) &windows_process.siginfo_er;
- size_t bufsize = sizeof (windows_process.siginfo_er);
+ EXCEPTION_RECORD &er = th->last_event.u.Exception.ExceptionRecord;
+
+ char *buf = (char *) &er;
+ size_t bufsize = sizeof (er);
#ifdef __x86_64__
EXCEPTION_RECORD32 er32;
@@ -1287,17 +1290,15 @@ win32_process_target::qxfer_siginfo (const char *annex,
buf = (char *) &er32;
bufsize = sizeof (er32);
- er32.ExceptionCode = windows_process.siginfo_er.ExceptionCode;
- er32.ExceptionFlags = windows_process.siginfo_er.ExceptionFlags;
+ er32.ExceptionCode = er.ExceptionCode;
+ er32.ExceptionFlags = er.ExceptionFlags;
er32.ExceptionRecord
- = (uintptr_t) windows_process.siginfo_er.ExceptionRecord;
+ = (uintptr_t) er.ExceptionRecord;
er32.ExceptionAddress
- = (uintptr_t) windows_process.siginfo_er.ExceptionAddress;
- er32.NumberParameters = windows_process.siginfo_er.NumberParameters;
- int i;
- for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
- er32.ExceptionInformation[i]
- = windows_process.siginfo_er.ExceptionInformation[i];
+ = (uintptr_t) er.ExceptionAddress;
+ er32.NumberParameters = er.NumberParameters;
+ for (int i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
+ er32.ExceptionInformation[i] = er.ExceptionInformation[i];
}
#endif
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 22/28] Add backpointer from windows_thread_info to windows_process_info
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (20 preceding siblings ...)
2026-04-24 20:45 ` [pushed 21/28] Windows gdb+gdbserver: Make siginfo_er " Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 23/28] Windows gdb+gdbserver: Share $_siginfo reading code Pedro Alves
` (5 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
The next patch will move some duplicated code in gdb and gdbserver to
gdb/nat/windows-nat.c, where it would be convenient to get at the
Windows process info of a given Windows thread info, from within a
windows_thread_info method.
I first thought of passing down the windows_process_info pointer as
argument to the windows_thread_info method, but that looked a bit odd.
I think it looks better to just add a back pointer, so that's what
this patch does. The following patch will then add a use of it.
I suspect this will help moving more duplicated code to
gdb/nat/windows-nat.c in the future, too.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I47fc0d3323be5b6f6fcfe912b768051a41910666
---
gdb/nat/windows-nat.h | 10 ++++++++--
gdb/windows-nat.c | 2 +-
gdbserver/win32-low.cc | 2 +-
3 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 152410580f0..59662c3570c 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -48,13 +48,16 @@ struct pending_stop
target_waitstatus status;
};
+struct windows_process_info;
/* Thread information structure used to track extra information about
each thread. */
struct windows_thread_info
{
- windows_thread_info (DWORD tid_, HANDLE h_, CORE_ADDR tlb)
- : tid (tid_),
+ windows_thread_info (windows_process_info *proc_,
+ DWORD tid_, HANDLE h_, CORE_ADDR tlb)
+ : proc (proc_),
+ tid (tid_),
h (h_),
thread_local_base (tlb)
{
@@ -73,6 +76,9 @@ struct windows_thread_info
the next call. */
const char *thread_name ();
+ /* The process this thread belongs to. */
+ windows_process_info *const proc;
+
/* The Win32 thread identifier. */
DWORD tid;
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 1593524f900..143a5e487fd 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -322,7 +322,7 @@ windows_nat_target::add_thread (ptid_t ptid, HANDLE h, void *tlb,
if (windows_process->wow64_process)
base += 0x2000;
#endif
- th = new windows_thread_info (ptid.lwp (), h, base);
+ th = new windows_thread_info (windows_process, ptid.lwp (), h, base);
windows_process->thread_list.emplace_back (th);
/* Add this new thread to the list of threads.
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index cc4a63319ef..bf3490542e2 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -144,7 +144,7 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
if (windows_process.wow64_process)
base += 2 * 4096; /* page size = 4096 */
#endif
- th = new windows_thread_info (tid, h, base);
+ th = new windows_thread_info (&windows_process, tid, h, base);
find_process_pid (pid)->add_thread (ptid, th);
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 23/28] Windows gdb+gdbserver: Share $_siginfo reading code
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (21 preceding siblings ...)
2026-04-24 20:45 ` [pushed 22/28] Add backpointer from windows_thread_info to windows_process_info Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 24/28] Windows gdb+gdbserver: Eliminate struct pending_stop Pedro Alves
` (4 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
Both GDB and GDBserver have similar code to read the $_siginfo data.
This patch moves the bulk of it to gdb/nat/windows-nat.c so it can be
shared.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I58f9074caf6362274453c78ed1fc9e31249f6854
---
gdb/nat/windows-nat.c | 49 ++++++++++++++++++++++++++++++++++++++++++
gdb/nat/windows-nat.h | 9 ++++++++
gdb/windows-nat.c | 40 +++-------------------------------
gdbserver/win32-low.cc | 42 ++++--------------------------------
4 files changed, 65 insertions(+), 75 deletions(-)
diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c
index 1f8967dcd8d..c6434348607 100644
--- a/gdb/nat/windows-nat.c
+++ b/gdb/nat/windows-nat.c
@@ -153,6 +153,55 @@ windows_thread_info::thread_name ()
return name.get ();
}
+/* Read Windows signal info. See nat/windows-nat.h. */
+
+bool
+windows_thread_info::xfer_siginfo (gdb_byte *readbuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len)
+{
+ if (last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
+ return false;
+
+ if (readbuf == nullptr)
+ return false;
+
+ EXCEPTION_RECORD &er = last_event.u.Exception.ExceptionRecord;
+
+ char *buf = (char *) &er;
+ size_t bufsize = sizeof (er);
+
+#ifdef __x86_64__
+ EXCEPTION_RECORD32 er32;
+ if (proc->wow64_process)
+ {
+ buf = (char *) &er32;
+ bufsize = sizeof (er32);
+
+ er32.ExceptionCode = er.ExceptionCode;
+ er32.ExceptionFlags = er.ExceptionFlags;
+ er32.ExceptionRecord
+ = (uintptr_t) er.ExceptionRecord;
+ er32.ExceptionAddress
+ = (uintptr_t) er.ExceptionAddress;
+ er32.NumberParameters = er.NumberParameters;
+ for (int i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
+ er32.ExceptionInformation[i] = er.ExceptionInformation[i];
+ }
+#endif
+
+ if (offset > bufsize)
+ return false;
+
+ if (offset + len > bufsize)
+ len = bufsize - offset;
+
+ memcpy (readbuf, buf + offset, len);
+ *xfered_len = len;
+
+ return true;
+}
+
/* Try to determine the executable filename.
EXE_NAME_RET is a pointer to a buffer whose size is EXE_NAME_MAX_LEN.
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 59662c3570c..cb7456c624e 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -76,6 +76,15 @@ struct windows_thread_info
the next call. */
const char *thread_name ();
+ /* Read LEN bytes of the thread's $_siginfo into READBUF, starting
+ at OFFSET. Store the number of actually-read bytes in
+ XFERED_LEN. Returns true on success, false on failure. Passing
+ READBUF as NULL indicates that the caller is trying to write to
+ $_siginfo, which is a failure case. */
+ bool xfer_siginfo (gdb_byte *readbuf,
+ ULONGEST offset, ULONGEST len,
+ ULONGEST *xfered_len);
+
/* The process this thread belongs to. */
windows_process_info *const proc;
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 143a5e487fd..062ecf1e063 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -2454,44 +2454,10 @@ windows_xfer_siginfo (gdb_byte *readbuf, ULONGEST offset, ULONGEST len,
{
windows_thread_info *th = windows_process->find_thread (inferior_ptid);
- if (th->last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
- return TARGET_XFER_E_IO;
-
- EXCEPTION_RECORD &er = th->last_event.u.Exception.ExceptionRecord;
-
- char *buf = (char *) &er;
- size_t bufsize = sizeof (er);
-
-#ifdef __x86_64__
- EXCEPTION_RECORD32 er32;
- if (windows_process->wow64_process)
- {
- buf = (char *) &er32;
- bufsize = sizeof (er32);
-
- er32.ExceptionCode = er.ExceptionCode;
- er32.ExceptionFlags = er.ExceptionFlags;
- er32.ExceptionRecord = (uintptr_t) er.ExceptionRecord;
- er32.ExceptionAddress = (uintptr_t) er.ExceptionAddress;
- er32.NumberParameters = er.NumberParameters;
- for (int i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
- er32.ExceptionInformation[i] = er.ExceptionInformation[i];
- }
-#endif
-
- if (readbuf == nullptr)
- return TARGET_XFER_E_IO;
-
- if (offset > bufsize)
+ if (th->xfer_siginfo (readbuf, offset, len, xfered_len))
+ return TARGET_XFER_OK;
+ else
return TARGET_XFER_E_IO;
-
- if (offset + len > bufsize)
- len = bufsize - offset;
-
- memcpy (readbuf, buf + offset, len);
- *xfered_len = len;
-
- return TARGET_XFER_OK;
}
enum target_xfer_status
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index bf3490542e2..0741dd24ed5 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -1272,45 +1272,11 @@ win32_process_target::qxfer_siginfo (const char *annex,
{
windows_thread_info *th = windows_process.find_thread (current_thread->id);
- if (th->last_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
- return -1;
-
- if (readbuf == nullptr)
- return -1;
-
- EXCEPTION_RECORD &er = th->last_event.u.Exception.ExceptionRecord;
-
- char *buf = (char *) &er;
- size_t bufsize = sizeof (er);
-
-#ifdef __x86_64__
- EXCEPTION_RECORD32 er32;
- if (windows_process.wow64_process)
- {
- buf = (char *) &er32;
- bufsize = sizeof (er32);
-
- er32.ExceptionCode = er.ExceptionCode;
- er32.ExceptionFlags = er.ExceptionFlags;
- er32.ExceptionRecord
- = (uintptr_t) er.ExceptionRecord;
- er32.ExceptionAddress
- = (uintptr_t) er.ExceptionAddress;
- er32.NumberParameters = er.NumberParameters;
- for (int i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++)
- er32.ExceptionInformation[i] = er.ExceptionInformation[i];
- }
-#endif
-
- if (offset > bufsize)
+ ULONGEST xfered_len;
+ if (th->xfer_siginfo (readbuf, offset, len, &xfered_len))
+ return xfered_len;
+ else
return -1;
-
- if (offset + len > bufsize)
- len = bufsize - offset;
-
- memcpy (readbuf, buf + offset, len);
-
- return len;
}
bool
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 24/28] Windows gdb+gdbserver: Eliminate struct pending_stop
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (22 preceding siblings ...)
2026-04-24 20:45 ` [pushed 23/28] Windows gdb+gdbserver: Share $_siginfo reading code Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 25/28] Windows gdb: Change serial_event management Pedro Alves
` (3 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
After the previous patches, struct pending_stop only contains one
field. So move that field into the windows_thread_info structure
directly, and eliminate struct pending_stop.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I7955884b3f378d8b39b908f6252d215f6568b367
commit-id:fb68c808
---
gdb/nat/windows-nat.h | 21 ++++++++-------------
gdb/windows-nat.c | 14 +++++++-------
gdbserver/win32-low.cc | 12 ++++++------
3 files changed, 21 insertions(+), 26 deletions(-)
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index cb7456c624e..5258ce52a0f 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -38,16 +38,6 @@
namespace windows_nat
{
-/* Info about a potential pending stop. Each thread holds one of
- these. See "windows_thread_info::pending_stop" for more
- information. */
-struct pending_stop
-{
- /* The target waitstatus we computed. TARGET_WAITKIND_IGNORE if the
- thread does not have a pending stop. */
- target_waitstatus status;
-};
-
struct windows_process_info;
/* Thread information structure used to track extra information about
@@ -111,9 +101,14 @@ struct windows_thread_info
queues the stop events. However, this can result in the strange
effect of trying to single step thread A -- leaving all other
threads suspended -- and then seeing a stop in thread B. To handle
- this scenario, we queue all such "pending" stops here, and then
- process them once the step has completed. See PR gdb/22992. */
- struct pending_stop pending_stop {};
+ this scenario, we queue the "pending" stop here, and then
+ process it once the step has completed. See PR gdb/22992.
+ If we do have a pending event, its Windows event info is in
+ LAST_EVENT.
+
+ TARGET_WAITKIND_IGNORE if the thread does not have a pending
+ stop. */
+ target_waitstatus pending_status;
/* The last Windows event returned by WaitForDebugEvent for this
thread. */
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 062ecf1e063..e0589c4de97 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -727,7 +727,7 @@ windows_nat_target::windows_continue (DWORD continue_status, int id,
{
if ((id == -1 || id == (int) th->tid)
&& !th->suspended
- && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
{
DEBUG_EVENTS ("got matching pending stop event "
"for 0x%x, not resuming",
@@ -923,18 +923,18 @@ windows_nat_target::get_windows_debug_event
DWORD thread_id = 0;
/* If there is a relevant pending stop, report it now. See the
- comment by the definition of "windows_thread_info::pending_stop"
+ comment by the definition of "windows_thread_info::pending_status"
for details on why this is needed. */
for (auto &th : windows_process->thread_list)
{
if (!th->suspended
- && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
{
DEBUG_EVENTS ("reporting pending event for 0x%x", th->tid);
thread_id = th->tid;
- *ourstatus = th->pending_stop.status;
- th->pending_stop.status.set_ignore ();
+ *ourstatus = th->pending_status;
+ th->pending_status.set_ignore ();
*current_event = th->last_event;
ptid_t ptid (windows_process->process_id, thread_id);
@@ -1159,7 +1159,7 @@ windows_nat_target::get_windows_debug_event
if (th->suspended)
{
/* Pending stop. See the comment by the definition of
- "pending_stops" for details on why this is needed. */
+ "pending_status" for details on why this is needed. */
DEBUG_EVENTS ("get_windows_debug_event - "
"unexpected stop in suspended thread 0x%x",
thread_id);
@@ -1172,7 +1172,7 @@ windows_nat_target::get_windows_debug_event
th->pc_adjusted = false;
}
- th->pending_stop.status = *ourstatus;
+ th->pending_status = *ourstatus;
ourstatus->set_ignore ();
continue_last_debug_event_main_thread
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 0741dd24ed5..e712033898c 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -814,7 +814,7 @@ win32_process_target::resume (thread_resume *resume_info, size_t n)
}
if (!th->suspended
- && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
any_pending = true;
});
@@ -957,10 +957,10 @@ get_child_debug_event (DWORD *continue_status,
auto *th = static_cast<windows_thread_info *> (thread.target_data ());
if (!th->suspended
- && th->pending_stop.status.kind () != TARGET_WAITKIND_IGNORE)
+ && th->pending_status.kind () != TARGET_WAITKIND_IGNORE)
{
- *ourstatus = th->pending_stop.status;
- th->pending_stop.status.set_ignore ();
+ *ourstatus = th->pending_status;
+ th->pending_status.set_ignore ();
*current_event = th->last_event;
ptid = debug_event_ptid (current_event);
switch_to_thread (find_thread_ptid (ptid));
@@ -1120,13 +1120,13 @@ get_child_debug_event (DWORD *continue_status,
if (th != nullptr && th->suspended)
{
/* Pending stop. See the comment by the definition of
- "windows_thread_info::pending_stop" for details on why this
+ "windows_thread_info::pending_status" for details on why this
is needed. */
OUTMSG2 (("get_windows_debug_event - "
"unexpected stop in suspended thread 0x%x\n",
th->tid));
maybe_adjust_pc (*current_event);
- th->pending_stop.status = *ourstatus;
+ th->pending_status = *ourstatus;
ourstatus->set_spurious ();
}
else
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 25/28] Windows gdb: Change serial_event management
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (23 preceding siblings ...)
2026-04-24 20:45 ` [pushed 24/28] Windows gdb+gdbserver: Eliminate struct pending_stop Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 26/28] Windows gdb: cygwin_set_dr => windows_set_dr, etc Pedro Alves
` (2 subsequent siblings)
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
windows_nat_target::windows_continue, when it finds a resumed thread
that has a pending event, does:
/* There's no need to really continue, because there's already
another event pending. However, we do need to inform the
event loop of this. */
serial_event_set (m_wait_event);
return TRUE;
If we have more than one pending event ready to be consumed, and,
windows_nat_target::wait returns without calling
windows_nat_target::windows_continue, which it will with the non-stop
support in a later patch, then we will miss waking up the event loop.
This patch makes windows-nat.c manage the serial_event similarly to
how linux-nat.c does it. Clear it on entry to
windows_nat_target::wait, and set it if there may be more events to
process. With this, there's no need to set it from
windows_nat_target::wait_for_debug_event_main_thread, so the patch
also makes us not do it.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I44e1682721aa4866f1dbb052b3cfb4870fb13579
commit-id:669a42f6
---
gdb/windows-nat.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index e0589c4de97..b2142469474 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -251,7 +251,6 @@ windows_nat_target::wait_for_debug_event_main_thread (DEBUG_EVENT *event)
{
*event = m_last_debug_event;
m_debug_event_pending = false;
- serial_event_clear (m_wait_event);
}
else
wait_for_debug_event (event, INFINITE);
@@ -1190,6 +1189,11 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
{
int pid = -1;
+ /* serial_event is a manual-reset event. Clear it first. We'll set
+ it again if we may need to wake up the event loop to get here
+ again. */
+ serial_event_clear (m_wait_event);
+
/* We loop when we get a non-standard exception rather than return
with a SPURIOUS because resume can try and step or modify things,
which needs a current_thread->h. But some of these exceptions mark
@@ -1238,6 +1242,10 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
thr->suspend ();
}
+ /* If something came out, assume there may be more. This is
+ needed because there may be pending events ready to
+ consume. */
+ serial_event_set (m_wait_event);
return result;
}
else
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 26/28] Windows gdb: cygwin_set_dr => windows_set_dr, etc.
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (24 preceding siblings ...)
2026-04-24 20:45 ` [pushed 25/28] Windows gdb: Change serial_event management Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 27/28] Windows gdb: Move debug macros to windows-nat.h Pedro Alves
2026-04-24 20:45 ` [pushed 28/28] Windows gdb: Avoid writing debug registers if watchpoint hit pending Pedro Alves
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
The Windows backend functions that manipulate the x86 debug registers
are called "cygwin_foo", which is outdated, because native MinGW gdb
also uses those functions, they are not Cygwin-specific. Rename them
to "windows_foo" to avoid confusion.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I46df3b44f5272adadf960da398342a3cbdb98533
commit-id:896523e0
---
gdb/x86-windows-nat.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index baa6b969e99..5c09efce32f 100644
--- a/gdb/x86-windows-nat.c
+++ b/gdb/x86-windows-nat.c
@@ -299,10 +299,10 @@ x86_windows_nat_target::is_sw_breakpoint (const EXCEPTION_RECORD *er) const
Here we just store the address in dr array, the registers will be
actually set up when windows_continue is called. */
static void
-cygwin_set_dr (int i, CORE_ADDR addr)
+windows_set_dr (int i, CORE_ADDR addr)
{
if (i < 0 || i > 3)
- internal_error (_("Invalid register %d in cygwin_set_dr.\n"), i);
+ internal_error (_("Invalid register %d in windows_set_dr.\n"), i);
for (auto &th : x86_windows_process.thread_list)
th->debug_registers_changed = true;
@@ -312,7 +312,7 @@ cygwin_set_dr (int i, CORE_ADDR addr)
register. Here we just store the address in D_REGS, the watchpoint
will be actually set up in windows_wait. */
static void
-cygwin_set_dr7 (unsigned long val)
+windows_set_dr7 (unsigned long val)
{
for (auto &th : x86_windows_process.thread_list)
th->debug_registers_changed = true;
@@ -321,7 +321,7 @@ cygwin_set_dr7 (unsigned long val)
/* Get the value of debug register I from the inferior. */
static CORE_ADDR
-cygwin_get_dr (int i)
+windows_get_dr (int i)
{
windows_thread_info *th = windows_process->find_thread (inferior_ptid);
@@ -352,18 +352,18 @@ cygwin_get_dr (int i)
inferior. */
static unsigned long
-cygwin_get_dr6 (void)
+windows_get_dr6 (void)
{
- return cygwin_get_dr (6);
+ return windows_get_dr (6);
}
/* Get the value of the DR7 debug status register from the
inferior. */
static unsigned long
-cygwin_get_dr7 (void)
+windows_get_dr7 (void)
{
- return cygwin_get_dr (7);
+ return windows_get_dr (7);
}
static int
@@ -489,11 +489,11 @@ display_selectors (const char * args, int from_tty)
INIT_GDB_FILE (x86_windows_nat)
{
- x86_dr_low.set_control = cygwin_set_dr7;
- x86_dr_low.set_addr = cygwin_set_dr;
- x86_dr_low.get_addr = cygwin_get_dr;
- x86_dr_low.get_status = cygwin_get_dr6;
- x86_dr_low.get_control = cygwin_get_dr7;
+ x86_dr_low.set_control = windows_set_dr7;
+ x86_dr_low.set_addr = windows_set_dr;
+ x86_dr_low.get_addr = windows_get_dr;
+ x86_dr_low.get_status = windows_get_dr6;
+ x86_dr_low.get_control = windows_get_dr7;
/* x86_dr_low.debug_register_length field is set by
calling x86_set_debug_register_length function
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 27/28] Windows gdb: Move debug macros to windows-nat.h
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (25 preceding siblings ...)
2026-04-24 20:45 ` [pushed 26/28] Windows gdb: cygwin_set_dr => windows_set_dr, etc Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
2026-04-24 20:45 ` [pushed 28/28] Windows gdb: Avoid writing debug registers if watchpoint hit pending Pedro Alves
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches
Move the Windows native debug macros to gdb/windows-nat.h, so that
they can by used in x86-windows-nat.c.
Change-Id: I002b3e3e319bcb005b2c6424affd750a0fcd1a83
commit-id:5ec9181a
---
gdb/windows-nat.c | 20 +++++---------------
gdb/windows-nat.h | 17 +++++++++++++++++
2 files changed, 22 insertions(+), 15 deletions(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index b2142469474..fba1ae6c40b 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -87,17 +87,11 @@ windows_per_inferior *windows_process;
#define _CYGWIN_SIGNAL_STRING "cYgSiGw00f"
#endif
-#define DEBUG_EXEC(fmt, ...) \
- debug_prefixed_printf_cond (debug_exec, "windows exec", fmt, ## __VA_ARGS__)
-#define DEBUG_EVENTS(fmt, ...) \
- debug_prefixed_printf_cond (debug_events, "windows events", fmt, \
- ## __VA_ARGS__)
-#define DEBUG_MEM(fmt, ...) \
- debug_prefixed_printf_cond (debug_memory, "windows mem", fmt, \
- ## __VA_ARGS__)
-#define DEBUG_EXCEPT(fmt, ...) \
- debug_prefixed_printf_cond (debug_exceptions, "windows except", fmt, \
- ## __VA_ARGS__)
+/* Debug options. */
+bool debug_exec = false; /* show execution */
+bool debug_events = false; /* show events from kernel */
+bool debug_memory = false; /* show target memory accesses */
+bool debug_exceptions = false; /* show target exceptions */
/* User options. */
static bool new_console = false;
@@ -105,10 +99,6 @@ static bool new_console = false;
static bool cygwin_exceptions = false;
#endif
static bool new_group = true;
-static bool debug_exec = false; /* show execution */
-static bool debug_events = false; /* show events from kernel */
-static bool debug_memory = false; /* show target memory accesses */
-static bool debug_exceptions = false; /* show target exceptions */
static bool useshell = false; /* use shell for subprocesses */
/* See windows_nat_target::resume to understand why this is commented
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index 15a177214f2..e5084631e5d 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -26,6 +26,23 @@
#include "nat/windows-nat.h"
#include "ser-event.h"
+extern bool debug_exec; /* show execution */
+extern bool debug_events; /* show events from kernel */
+extern bool debug_memory; /* show target memory accesses */
+extern bool debug_exceptions; /* show target exceptions */
+
+#define DEBUG_EXEC(fmt, ...) \
+ debug_prefixed_printf_cond (debug_exec, "windows exec", fmt, ## __VA_ARGS__)
+#define DEBUG_EVENTS(fmt, ...) \
+ debug_prefixed_printf_cond (debug_events, "windows events", fmt, \
+ ## __VA_ARGS__)
+#define DEBUG_MEM(fmt, ...) \
+ debug_prefixed_printf_cond (debug_memory, "windows mem", fmt, \
+ ## __VA_ARGS__)
+#define DEBUG_EXCEPT(fmt, ...) \
+ debug_prefixed_printf_cond (debug_exceptions, "windows except", fmt, \
+ ## __VA_ARGS__)
+
using windows_nat::windows_thread_info;
/* A pointer to a function that should return non-zero iff REGNUM
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed 28/28] Windows gdb: Avoid writing debug registers if watchpoint hit pending
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
` (26 preceding siblings ...)
2026-04-24 20:45 ` [pushed 27/28] Windows gdb: Move debug macros to windows-nat.h Pedro Alves
@ 2026-04-24 20:45 ` Pedro Alves
27 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-24 20:45 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
Several watchpoint-related testcases, such as
gdb.threads/watchthreads.exp for example, when tested with the backend
in non-stop mode, exposed an interesting detail of the Windows debug
API that wasn't considered before. The symptom observed is spurious
SIGTRAPs, like:
Thread 1 "watchthreads" received signal SIGTRAP, Trace/breakpoint trap.
0x00000001004010b1 in main () at .../src/gdb/testsuite/gdb.threads/watchthreads.c:48
48 args[i] = 1; usleep (1); /* Init value. */
After a good amount of staring at logs and headscratching, I realized
the problem:
#0 - It all starts with the fact that multiple threads can hit an
event at the same time. Say, a watchpoint for thread A, and a
breakpoint for thread B.
#1 - Say, WaitForDebugEvent reports the breakpoint hit for thread B
first, then GDB for some reason decides to update debug
registers, and continue. Updating debug registers means writing
the debug registers to _all_ threads, with SetThreadContext.
#2 - WaitForDebugEvent reports the watchpoint hit for thread A.
Watchpoint hits are reported as EXCEPTION_SINGLE_STEP.
#3 - windows-nat checks the Dr6 debug register to check if the step
was a watchpoint or hardware breakpoint stop, and finds that Dr6
is completely cleared. So windows-nat reports a plain SIGTRAP
(given EXCEPTION_SINGLE_STEP) to the core.
#4 - Thread A was not supposed to be stepping, so infrun reports the
SIGTRAP to the user as a random signal.
The strange part is #3 above. Why was Dr6 cleared?
Turns out that (at least in Windows 10 & 11), writing to _any_ debug
register has the side effect of clearing Dr6, even if you write the
same values the registers already had, back to the registers.
I confirmed it clearly by adding this hack to GDB:
if (th->context.ContextFlags == 0)
{
th->context.ContextFlags = CONTEXT_DEBUGGER_DR;
/* Get current values of debug registers. */
CHECK (GetThreadContext (th->h, &th->context));
DEBUG_EVENTS ("For 0x%x (once), Dr6=0x%llx", th->tid, th->context.Dr6);
/* Write debug registers back to thread, same values,
and re-read them. */
CHECK (SetThreadContext (th->h, &th->context));
CHECK (GetThreadContext (th->h, &th->context));
DEBUG_EVENTS ("For 0x%x (twice), Dr6=0x%llx", th->tid, th->context.Dr6);
}
Which showed Dr6=0 after the write + re-read:
[windows events] fill_thread_context: For 0x6a0 (once), Dr6=0xffff0ff1
[windows events] fill_thread_context: For 0x6a0 (twice), Dr6=0x0
This commit fixes the issue by detecting that a thread has a pending
watchpoint hit to report (Dr6 has interesting bits set), and if so,
avoid modifying any debug register. Instead, let the pending
watchpoint hit be reported by WaitForDebugEvent. If infrun did want
to modify watchpoints, it will still be done when the thread is
eventually re-resumed after the pending watchpoint hit is reported.
(infrun knows how to gracefully handle the case of a watchpoint hit
for a watchpoint that has since been deleted.)
Move the fill_thread_context method from windows_nat_target to
windows_per_inferior so it can be used by gdbserver too.
Approved-By: Tom Tromey <tom@tromey.com>
Change-Id: I21a3daa9e34eecfa054f0fea706e5ab40aabe70a
commit-id:a28f8d4e
---
gdb/aarch64-windows-nat.c | 7 ++---
gdb/nat/windows-nat.h | 6 ++++
gdb/windows-nat.c | 2 +-
gdb/windows-nat.h | 3 --
gdb/x86-windows-nat.c | 64 ++++++++++++++++++++++++++++++++-------
gdbserver/win32-low.cc | 8 +++++
gdbserver/win32-low.h | 2 ++
7 files changed, 73 insertions(+), 19 deletions(-)
diff --git a/gdb/aarch64-windows-nat.c b/gdb/aarch64-windows-nat.c
index 871531bb93a..2b082805708 100644
--- a/gdb/aarch64-windows-nat.c
+++ b/gdb/aarch64-windows-nat.c
@@ -30,6 +30,7 @@ struct aarch64_windows_per_inferior : public windows_per_inferior
{
aarch64_debug_reg_state dr_state;
+ void fill_thread_context (windows_thread_info *th) override;
void invalidate_thread_context (windows_thread_info *th) override;
};
@@ -42,8 +43,6 @@ struct aarch64_windows_nat_target final
void initialize_windows_arch (bool attaching) override;
void cleanup_windows_arch () override;
- void fill_thread_context (windows_thread_info *th) override;
-
void thread_context_continue (windows_thread_info *th, int killed) override;
void thread_context_step (windows_thread_info *th) override;
@@ -176,10 +175,10 @@ aarch64_windows_nat_target::cleanup_windows_arch ()
aarch64_remove_debug_reg_state (inferior_ptid.pid ());
}
-/* See windows-nat.h. */
+/* See nat/windows-nat.h. */
void
-aarch64_windows_nat_target::fill_thread_context (windows_thread_info *th)
+aarch64_windows_per_inferior::fill_thread_context (windows_thread_info *th)
{
CONTEXT *context = &th->context;
diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h
index 5258ce52a0f..45d186f771a 100644
--- a/gdb/nat/windows-nat.h
+++ b/gdb/nat/windows-nat.h
@@ -174,6 +174,12 @@ struct windows_process_info
This function must be supplied by the embedding application. */
virtual windows_thread_info *find_thread (ptid_t ptid) = 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 OUTPUT_DEBUG_STRING_EVENT from child process. Updates
OURSTATUS and returns the thread id if this represents a thread
change (this is specific to Cygwin), otherwise 0.
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index fba1ae6c40b..17166fdcfad 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -386,7 +386,7 @@ windows_nat_target::fetch_registers (struct regcache *regcache, int r)
if (th == NULL)
return;
- fill_thread_context (th);
+ windows_process->fill_thread_context (th);
if (r < 0)
for (r = 0; r < gdbarch_num_regs (regcache->arch()); r++)
diff --git a/gdb/windows-nat.h b/gdb/windows-nat.h
index e5084631e5d..235844a7c95 100644
--- a/gdb/windows-nat.h
+++ b/gdb/windows-nat.h
@@ -226,9 +226,6 @@ struct windows_nat_target : public inf_child_target
/* Cleanup arch-specific data after inferior exit. */
virtual void cleanup_windows_arch () = 0;
- /* Reload the thread context. */
- virtual void fill_thread_context (windows_thread_info *th) = 0;
-
/* Prepare the thread context for continuing. */
virtual void thread_context_continue (windows_thread_info *th,
int killed) = 0;
diff --git a/gdb/x86-windows-nat.c b/gdb/x86-windows-nat.c
index 5c09efce32f..5a4876b7fbe 100644
--- a/gdb/x86-windows-nat.c
+++ b/gdb/x86-windows-nat.c
@@ -48,6 +48,7 @@ struct x86_windows_per_inferior : public windows_per_inferior
a segment register or not. */
segment_register_p_ftype *segment_register_p = nullptr;
+ void fill_thread_context (windows_thread_info *th) override;
void invalidate_thread_context (windows_thread_info *th) override;
};
@@ -56,8 +57,6 @@ struct x86_windows_nat_target final : public x86_nat_target<windows_nat_target>
void initialize_windows_arch (bool attaching) override;
void cleanup_windows_arch () override;
- void fill_thread_context (windows_thread_info *th) override;
-
void thread_context_continue (windows_thread_info *th, int killed) override;
void thread_context_step (windows_thread_info *th) override;
@@ -102,10 +101,10 @@ x86_windows_nat_target::cleanup_windows_arch ()
x86_cleanup_dregs ();
}
-/* See windows-nat.h. */
+/* See nat/windows-nat.h. */
void
-x86_windows_nat_target::fill_thread_context (windows_thread_info *th)
+x86_windows_per_inferior::fill_thread_context (windows_thread_info *th)
{
x86_windows_process.with_context (th, [&] (auto *context)
{
@@ -141,13 +140,56 @@ x86_windows_nat_target::thread_context_continue (windows_thread_info *th,
if (th->debug_registers_changed)
{
- context->ContextFlags |= WindowsContext<decltype(context)>::debug;
- context->Dr0 = state->dr_mirror[0];
- context->Dr1 = state->dr_mirror[1];
- context->Dr2 = state->dr_mirror[2];
- context->Dr3 = state->dr_mirror[3];
- context->Dr6 = DR6_CLEAR_VALUE;
- context->Dr7 = state->dr_control_mirror;
+ windows_process->fill_thread_context (th);
+
+ gdb_assert ((context->ContextFlags & 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 ((context->Dr6 & ~DR6_CLEAR_VALUE) != 0)
+ {
+ 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->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
+ }
+ }
+ else
+ DEBUG_EVENTS ("0x%x has no dr6 set", th->tid);
+
+ if ((context->ContextFlags & CONTEXT_DEBUG_REGISTERS) != 0)
+ {
+ DEBUG_EVENTS ("0x%x changing dregs", th->tid);
+ context->Dr0 = state->dr_mirror[0];
+ context->Dr1 = state->dr_mirror[1];
+ context->Dr2 = state->dr_mirror[2];
+ context->Dr3 = state->dr_mirror[3];
+ context->Dr6 = DR6_CLEAR_VALUE;
+ context->Dr7 = state->dr_control_mirror;
+ }
+
th->debug_registers_changed = false;
}
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index e712033898c..826d21867ae 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -118,6 +118,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 01a904043eb..ff680492756 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -185,6 +185,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
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [pushed 07/28] Windows gdb: Simplify windows_nat_target::wait
2026-04-24 20:44 ` [pushed 07/28] Windows gdb: Simplify windows_nat_target::wait Pedro Alves
@ 2026-04-26 12:52 ` Hannes Domani
2026-04-27 12:09 ` [pushed/ob] Restore windows_nat_target::wait is_sw_breakpoint change Pedro Alves
0 siblings, 1 reply; 32+ messages in thread
From: Hannes Domani @ 2026-04-26 12:52 UTC (permalink / raw)
To: gdb-patches, Pedro Alves; +Cc: Tom Tromey
I initially missed these commits, because I was once again unsubscribed
from gdb-patches (because of bounces) when these emails where sent.
I only noticed in the GNU weekly news mention of many Windows
improvements which I didn't remember, so I checked again.
On 4/24/26 22:44, Pedro Alves wrote:
> The logic in windows_nat_target::wait, where we decide what to do
> depending on the result from get_windows_debug_event is harder to
> grasp than it looks.
>
> It is not easy to tell what should happen when in async mode
> get_windows_debug_event returns that there's no event to process.
>
> And then, if get_windows_debug_event returns null_ptid /
> TARGET_WAITKIND_SPURIOUS, then we need to issue a ContinueDebugEvent.
>
> There's also this comment in windows_nat_target::wait, which we're not
> really implementing today:
>
> ~~~~
> /* We loop when we get a non-standard exception rather than return
> with a SPURIOUS because resume can try and step or modify things,
> which needs a current_thread->h. But some of these exceptions mark
> the birth or death of threads, which mean that the current thread
> isn't necessarily what you think it is. */
> ~~~~
>
> This patch changes things a bit so that the code is more obvious:
>
> - look at the status kind, instead of ptid_t.
>
> - add an explicit early return case for no-event.
>
> - add an explicit case for TARGET_WAITKIND_SPURIOUS.
>
> - with those, we no longer need to handle the case of find_thread not
> finding a thread, so we can drop one indentation level.
>
> Approved-By: Tom Tromey <tom@tromey.com>
> Change-Id: I76c41762e1f893a7ff23465856ccf6a44af1f0e7
> commit-id:aff7fc4a
> ---
> gdb/windows-nat.c | 39 +++++++++++++++++++++++++--------------
> 1 file changed, 25 insertions(+), 14 deletions(-)
>
> diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
> index 2dd372ae7b2..30b07221b62 100644
> --- a/gdb/windows-nat.c
> +++ b/gdb/windows-nat.c
> @@ -1139,7 +1139,10 @@ windows_nat_target::get_windows_debug_event
> }
>
> if (thread_id == 0)
> - return null_ptid;
> + {
> + ourstatus->set_ignore ();
> + return null_ptid;
> + }
> return ptid_t (windows_process->current_event.dwProcessId, thread_id, 0);
> }
>
> @@ -1160,25 +1163,33 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
> {
> ptid_t result = get_windows_debug_event (pid, ourstatus, options);
>
> - if (result != null_ptid)
> + if ((options & TARGET_WNOHANG) != 0
> + && ourstatus->kind () == TARGET_WAITKIND_IGNORE)
> + return result;
> +
> + if (ourstatus->kind () == TARGET_WAITKIND_SPURIOUS)
> + {
> + CHECK (windows_continue (DBG_CONTINUE,
> + windows_process->desired_stop_thread_id, 0));
> + }
> + else if (ourstatus->kind () != TARGET_WAITKIND_IGNORE)
> {
> if (ourstatus->kind () != TARGET_WAITKIND_EXITED
> - && ourstatus->kind () != TARGET_WAITKIND_SIGNALLED)
> + && ourstatus->kind () != TARGET_WAITKIND_SIGNALLED)
> {
> windows_thread_info *th = windows_process->find_thread (result);
>
> - if (th != nullptr)
> + th->stopped_at_software_breakpoint = false;
> + if (windows_process->current_event.dwDebugEventCode
> + == EXCEPTION_DEBUG_EVENT
> + && ((windows_process->current_event.u.Exception.ExceptionRecord.ExceptionCode
> + == EXCEPTION_BREAKPOINT)
> + || (windows_process->current_event.u.Exception.ExceptionRecord.ExceptionCode
> + == STATUS_WX86_BREAKPOINT))
> + && windows_process->windows_initialization_done)
> {
> - th->stopped_at_software_breakpoint = false;
> - if (windows_process->current_event.dwDebugEventCode
> - == EXCEPTION_DEBUG_EVENT
> - && is_sw_breakpoint (&windows_process->current_event
> - .u.Exception.ExceptionRecord)
> - && windows_process->windows_initialization_done)
> - {
> - th->stopped_at_software_breakpoint = true;
> - th->pc_adjusted = false;
> - }
> + th->stopped_at_software_breakpoint = true;
> + th->pc_adjusted = false;
> }
> }
>
This reverted the is_sw_breakpoint change done for aarch64 support.
Hannes
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed/ob] Restore windows_nat_target::wait is_sw_breakpoint change
2026-04-26 12:52 ` Hannes Domani
@ 2026-04-27 12:09 ` Pedro Alves
0 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-27 12:09 UTC (permalink / raw)
To: Hannes Domani, gdb-patches; +Cc: Tom Tromey
Hi!
On 2026-04-26 13:52, Hannes Domani wrote:
> This reverted the is_sw_breakpoint change done for aarch64 support.
Whoops, sorry about that. Lost in rebase conflict resolution.
I've pushed the obvious fix below.
From 20699002b9630ed90b2518735c7d945e89445a83 Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Mon, 27 Apr 2026 11:46:21 +0100
Subject: [PATCH] Restore windows_nat_target::wait is_sw_breakpoint change
Hannes noticed that commit db040a86c8 ("Windows gdb: Simplify
windows_nat_target::wait") inadvertently reverted the earlier
is_sw_breakpoint change in windows_nat_target::wait from 6fc89bae17
("Move software breakpoint recognition code into x86-windows-nat.c")
for Aarch64 support. This commit restores it.
Change-Id: Ice2181bf4a6c8dcefd127a85ebfc660a3f430517
commit-id:546f39de
---
gdb/windows-nat.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c
index 17166fdcfad..1dfe2df389d 100644
--- a/gdb/windows-nat.c
+++ b/gdb/windows-nat.c
@@ -1216,10 +1216,8 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus,
th->stopped_at_software_breakpoint = false;
if (current_event.dwDebugEventCode
== EXCEPTION_DEBUG_EVENT
- && ((current_event.u.Exception.ExceptionRecord.ExceptionCode
- == EXCEPTION_BREAKPOINT)
- || (current_event.u.Exception.ExceptionRecord.ExceptionCode
- == STATUS_WX86_BREAKPOINT))
+ && is_sw_breakpoint (¤t_event
+ .u.Exception.ExceptionRecord)
&& windows_process->windows_initialization_done)
{
th->stopped_at_software_breakpoint = true;
base-commit: 831f056f767fe775c8862a09dc3bbe2e69c5e17c
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
* [pushed] Mention Windows scheduler-locking in NEWS (Re: [pushed 17/28] Windows gdb: Enable "set scheduler-locking on")
2026-04-24 20:44 ` [pushed 17/28] Windows gdb: Enable "set scheduler-locking on" Pedro Alves
@ 2026-04-27 12:57 ` Pedro Alves
0 siblings, 0 replies; 32+ messages in thread
From: Pedro Alves @ 2026-04-27 12:57 UTC (permalink / raw)
To: gdb-patches; +Cc: Tom Tromey
On 2026-04-24 21:44, Pedro Alves wrote:
> Surprisingly (to me), enabling scheduler locking on Windows currently
> fails:
>
> (gdb)
> set scheduler-locking on
> Target 'native' cannot support this command.
>
> The backend itself does support scheduler-locking. This patch
> implements windows_nat_target::get_thread_control_capabilities so that
> the core knows schedlocking works for this target.
>
I had the news about scheduler-locking in the same patch as the non-stop support
news, back in the v2 non-stop series.
Since scheduler-locking is already merged, I've now split the NEWS bit about it
to its own patch and merged it, as below.
Pedro Alves
From 8e78d43dbcd122e852d59abe29c8d6aeb6c04925 Mon Sep 17 00:00:00 2001
From: Pedro Alves <pedro@palves.net>
Date: Mon, 22 May 2023 19:16:30 +0100
Subject: [PATCH] Mention Windows scheduler-locking in NEWS
Add a note to gdb/NEWS mentioning Windows native target
scheduler-locking support.
Approved-By: Eli Zaretskii <eliz@gnu.org>
Change-Id: Id0e28525c06e57120c47b07f978581d1627d70f3
commit-id:ba1d5868
---
gdb/NEWS | 3 +++
1 file changed, 3 insertions(+)
diff --git a/gdb/NEWS b/gdb/NEWS
index 18f1b6308da..e233906153a 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -72,6 +72,9 @@
automatically set to UTF-8. (Users can use the Windows 'chcp'
command to change the output codepage of the console.)
+* The Windows native target now supports scheduler-locking. E.g.,
+ "set scheduler-locking on" now works. Previously it gave an error.
+
* New targets
GNU/Linux/MicroBlaze (gdbserver) microblazeel-*linux*
base-commit: 20699002b9630ed90b2518735c7d945e89445a83
--
2.53.0
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2026-04-27 12:57 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-24 20:44 [pushed 00/28] Windows non-stop prep Pedro Alves
2026-04-24 20:44 ` [pushed 01/28] Windows gdb+gdbserver: New find_thread, replaces thread_rec(DONT_INVALIDATE_CONTEXT) Pedro Alves
2026-04-24 20:44 ` [pushed 02/28] Windows gdb: handle_output_debug_string return type Pedro Alves
2026-04-24 20:44 ` [pushed 03/28] Windows gdb: Eliminate reload_context Pedro Alves
2026-04-24 20:44 ` [pushed 04/28] Windows gdb+gdbserver: Eliminate thread_rec(INVALIDATE_CONTEXT) calls Pedro Alves
2026-04-24 20:44 ` [pushed 05/28] Windows gdb+gdbserver: Eliminate DONT_SUSPEND Pedro Alves
2026-04-24 20:44 ` [pushed 06/28] Windows gdb+gdbserver: Eliminate windows_process_info::thread_rec Pedro Alves
2026-04-24 20:44 ` [pushed 07/28] Windows gdb: Simplify windows_nat_target::wait Pedro Alves
2026-04-26 12:52 ` Hannes Domani
2026-04-27 12:09 ` [pushed/ob] Restore windows_nat_target::wait is_sw_breakpoint change Pedro Alves
2026-04-24 20:44 ` [pushed 08/28] Windows gdb+gdbserver: Move suspending thread to when returning event Pedro Alves
2026-04-24 20:44 ` [pushed 09/28] Windows gdb: Introduce continue_last_debug_event_main_thread Pedro Alves
2026-04-24 20:44 ` [pushed 10/28] Windows gdb: Introduce windows_continue_flags Pedro Alves
2026-04-24 20:44 ` [pushed 11/28] Windows gdb: Factor code out of windows_nat_target::windows_continue Pedro Alves
2026-04-24 20:44 ` [pushed 12/28] Windows gdb: Pending stop and current_event Pedro Alves
2026-04-24 20:44 ` [pushed 13/28] Windows gdb+gdbserver: Elim desired_stop_thread_id / rework pending_stops Pedro Alves
2026-04-24 20:44 ` [pushed 14/28] Windows gdb+gdbserver: Introduce get_last_debug_event_ptid Pedro Alves
2026-04-24 20:44 ` [pushed 15/28] Windows gdb: Can't pass signal to thread other than last stopped thread Pedro Alves
2026-04-24 20:44 ` [pushed 16/28] Windows gdbserver: Fix scheduler-locking Pedro Alves
2026-04-24 20:44 ` [pushed 17/28] Windows gdb: Enable "set scheduler-locking on" Pedro Alves
2026-04-27 12:57 ` [pushed] Mention Windows scheduler-locking in NEWS (Re: [pushed 17/28] Windows gdb: Enable "set scheduler-locking on") Pedro Alves
2026-04-24 20:44 ` [pushed 18/28] Windows gdbserver: Eliminate soft-interrupt mechanism Pedro Alves
2026-04-24 20:44 ` [pushed 19/28] Windows gdb+gdbserver: Make current_event per-thread state Pedro Alves
2026-04-24 20:45 ` [pushed 20/28] Windows gdb+gdbserver: Make last_sig " Pedro Alves
2026-04-24 20:45 ` [pushed 21/28] Windows gdb+gdbserver: Make siginfo_er " Pedro Alves
2026-04-24 20:45 ` [pushed 22/28] Add backpointer from windows_thread_info to windows_process_info Pedro Alves
2026-04-24 20:45 ` [pushed 23/28] Windows gdb+gdbserver: Share $_siginfo reading code Pedro Alves
2026-04-24 20:45 ` [pushed 24/28] Windows gdb+gdbserver: Eliminate struct pending_stop Pedro Alves
2026-04-24 20:45 ` [pushed 25/28] Windows gdb: Change serial_event management Pedro Alves
2026-04-24 20:45 ` [pushed 26/28] Windows gdb: cygwin_set_dr => windows_set_dr, etc Pedro Alves
2026-04-24 20:45 ` [pushed 27/28] Windows gdb: Move debug macros to windows-nat.h Pedro Alves
2026-04-24 20:45 ` [pushed 28/28] Windows gdb: Avoid writing debug registers if watchpoint hit pending Pedro Alves
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox