* [PATCH v4 00/44] A new target to debug Intel GPUs
@ 2026-08-12 13:27 Markus Metzger
2026-08-12 13:27 ` [PATCH v4 01/44] bfd: add intelgt target to BFD Markus Metzger
` (43 more replies)
0 siblings, 44 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Intel would like to submit patches to enable fundamental debug support for
Intel GPU devices. In the future, we plan to add more patches that
improve the performance and the user experience. Those patches are
already available in the downstream "Intel Distribution for GDB" debugger
at
https://github.com/intel/gdb
Previous revisions of the submission:
v1: https://sourceware.org/pipermail/gdb-patches/2024-July/210264.html
v2: https://sourceware.org/pipermail/gdb-patches/2024-December/214029.html
v3: https://sourceware.org/pipermail/gdb-patches/2025-August/219593.html
This revision (v4) makes the following changes:
- The comments received from Simon have been addressed.
- The UNAVAILABLE thread state has been removed.
- The vAck packet has been removed.
- Add basic support for allowing processes without threads.
- Add %Library notifications to RSP.
- Patches are rebased on the master branch, addressing pre-commit
findings.
GPU threads operate in a SIMD/SIMT (single instruction multiple data,
single instruction multiple thread) manner: they are vectorized, where
each lane (also known as "execution channel") executes the same
instruction but using different data values. Lanes of the same thread
execute in a lock-step movement. Displaying the value of a source
program variable therefore requires not only a thread context but also
a lane context. GDB currently does not have this knowledge built-in.
Furthermore, some DWARF extensions are necessary to express data
locations in a lane-relative way, which are currently under discussion
of or to be submitted to the DWARF committee. Hence, with this
submission, variables may appear with an error like "<error reading
variable: Unhandled dwarf expression opcode 0xed>". Similar
restrictions apply also to the AMD ROCm (AMDGPU) target in the
upstream GDB for the same reasons. The downstream "Intel Distribution
for GDB" debugger implements lane support as well as DWARF extensions
and hence is able to print lane-relative values properly. Lane
support is a future GDB topic; see the presentation hosted by Intel
and AMD in GNU Tools Cauldron 2025 for more details if interested.
We provide a gdbserver low target definition. The target uses the
Level-Zero debug API:
https://oneapi-src.github.io/level-zero-spec/level-zero/latest/tools/PROG.html#program-debug
https://oneapi-src.github.io/level-zero-spec/level-zero/latest/tools/api.html#debug
The user-space implementation of the Level-Zero Debug API comes from
"Intel(R) Graphics Compute Runtime for oneAPI Level Zero and OpenCL(TM)
Driver":
https://github.com/intel/compute-runtime
The kernel-space implementation of the Level-Zero Debug API, i.e. the
'eudebug' feature of the "Xe Intel graphics driver", is under submission.
See the discussions at
https://lists.freedesktop.org/archives/intel-xe/
E.g. Submission v9:
https://lists.freedesktop.org/archives/intel-xe/2026-June/128834.html
For Level-Zero based devices, we show threads that are active when GDB
obtains the thread list, e.g. on an event from the target. This is
consistent with remote debugging on other architectures. GPU threads are
typically short-lived, so as long as threads get dispatched and exit
without creating an event, they will not be visible to GDB.
This patch series implements support for a non-stop extended remote target
with explicit attach. Start gdbserver with --multi, then attach to the
host process PID.
The Intel GPU target can be used in combination with a native target,
relying on GDB's multi-target feature, to debug the GPU and the host
application in the same debug session. For this, bring the native app
(e.g. a SYCL [https://www.khronos.org/sycl/] program) to a state
where the Level-Zero backend for the GPU has been initialized (e.g.
after the first queue has been created in SYCL), then create a
gdbserver instance and connect to it from a second inferior.
At GNU Tools Cauldron 2024, we gave a talk presenting the approach.
We invite interested parties to watch the recording.
https://www.youtube.com/watch?v=sYep57kjvHM
Below is a sample session that shows how to set up inferiors and
targets manually. In the downstream debugger, a Python script is used
to take these steps in an automated manner for better user experience.
$ gdb demo
...
(gdb) maintenance set target-non-stop on
(gdb) tbreak 60
Temporary breakpoint 1 at 0x4049c8: file demo.cpp, line 60.
(gdb) run
...
[SYCL] Using device: [Intel(R) Graphics ...] from [Intel(R) oneAPI Unified Runtime over Level-Zero]
Thread 1 "demo" hit Temporary breakpoint 1, main (argc=1, argv=0x7fffffffd9b8) at demo.cpp:60
60 range data_range{length};
(gdb)
Connect the Intel GT gdbserver by specifying the host inferior PID.
(gdb) add-inferior -no-connection
[New inferior 2]
Added inferior 2
(gdb) inferior 2
[Switching to inferior 2 [<null>] (<noexec>)]
(gdb) info inferiors
Num Description Connection Executable
1 process 16458 1 (native) /temp/demo
* 2 <null>
(gdb) target extended-remote | gdbserver-intelgt --multi -
Remote debugging using | gdbserver-intelgt --multi -
Remote debugging using stdio
(gdb) attach 16458
Attaching to process 16458
Attached; given pid = 16458, updated to 1
(gdb)
We also include patches for the testsuite, where we introduce the
infrastructure and a number of test cases using SYCL.
For convenience, the patches in this series are available at
https://github.com/intel/gdb/tree/upstream/intelgt-mvp
To those who may want to try the debugger, we also provide
https://github.com/intel/gdb/tree/upstream/intelgt-mvp-plus
with a number of additional patches (not yet submitted) that bring (1)
lane support, (2) ability to make GPU threads do inferior calls for
expression evaluation, (3) a minimal Python script that starts and
connects gdbserver-intelgt automatically for more convenience. Submission
of these additional features (and more) is planned for future after the
fundamental debug support is accepted and after aligning with the upstream
community on GPU support (e.g. lanes) in GDB.
Albertano Caruso (2):
gdb, intelgt: add disassemble feature for the Intel GT architecture.
testsuite, arch, intelgt: add a disassembly test
Markus Metzger (24):
config.sub: recognize level-zero as "ze"
gdb, arch, intelgt: add intelgt arch definitions
gdbserver: move dlls_changed initialization on attach into targets
gdbserver: check process when we need a process
gdb: use process in xfer_partial if inferior_ptid is null_ptid
gdb: allow inferiors without threads in all_matching_threads_iterator
gdbserver: improve threads debug output for process general thread
gdb, gdbserver: allow setting null_ptid as general thread
gdbserver: allow inferiors without threads
gdb: allow creating and attaching to inferiors without threads
gdb, remote: don't create an inferior on attach
gdb: allow switching to an inferior without threads
gdb: allow resuming an inferior with no threads
gdb: allow continuing an inferior without threads
gdb: partially fix C-c not working
gdb, linux-nat: use current_inferior()->pid in mourn_inferior()
gdb: inferior events
gdb, remote: allow deleting the last thread in inferior in
update_thread_list()
gdb: keep target registered in inferior_event_handler()
gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
gdb, gdbserver, ze: in-memory libraries
gdb, gdbserver: library notifications
gdbserver, ze, intelgt: introduce ze-low and intelgt-ze-low targets
testsuite, intelgt: add a test for interrupting an exited thread
Natalia Saiapova (1):
bfd: add intelgt target to BFD
Nils-Christian Kempke (1):
gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
Tankut Baris Aktemur (16):
opcodes: add intelgt as a configuration
gdbserver: allow configuring for a heterogeneous target
gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script
gdb: add a new extract_integer variant that takes two array_views
gdb, intelgt: add the target-dependent definitions for the Intel GT
architecture
gdb: revise the pid_to_exec_file target op
gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior'
earlier
gdbserver: adjust pid after the target attaches
gdbserver: add a pointer to the owner thread in regcache
testsuite, sycl: add SYCL support
testsuite, sycl: add test for backtracing inside a kernel
testsuite, sycl: add test for 'info locals' and 'info args'
testsuite, sycl: add tests for stepping
testsuite, sycl: add test for 1-D and 2-D parallel_for kernels
testsuite, sycl: add test for scheduler-locking
testsuite, arch, intelgt: add intelgt-program-bp.exp
bfd/Makefile.am | 2 +
bfd/Makefile.in | 4 +
bfd/archures.c | 4 +
bfd/bfd-in2.h | 2 +
bfd/config.bfd | 5 +
bfd/configure | 1 +
bfd/configure.ac | 1 +
bfd/cpu-intelgt.c | 41 +
bfd/elf64-intelgt.c | 48 +
bfd/targets.c | 2 +
binutils/dwarf.c | 6 +
binutils/readelf.c | 5 +
config.sub | 3 +-
gdb/Makefile.in | 8 +-
gdb/NEWS | 19 +
gdb/README | 8 +
gdb/arch/intelgt.c | 191 ++
gdb/arch/intelgt.h | 185 ++
gdb/config.in | 3 +
gdb/configure | 565 +++-
gdb/configure.ac | 47 +
gdb/configure.tgt | 5 +
gdb/disasm-selftests.c | 12 +
gdb/doc/gdb.texinfo | 69 +-
gdb/dwarf2/expr.c | 37 +
gdb/dwarf2/expr.h | 5 +
gdb/dwarf2/loc.c | 2 +
gdb/exec.c | 6 +
gdb/extract-store-integer.c | 53 +-
gdb/extract-store-integer.h | 8 +
gdb/features/gdb-target.dtd | 11 +-
gdb/features/library-list.dtd | 6 +-
gdb/inf-loop.c | 11 +-
gdb/infcmd.c | 70 +-
gdb/inferior.c | 39 +-
gdb/inferior.h | 15 +
gdb/infrun.c | 426 ++-
gdb/intelgt-tdep.c | 1010 ++++++
gdb/linux-nat.c | 8 +-
gdb/remote-notif.c | 1 +
gdb/remote-notif.h | 2 +
gdb/remote.c | 228 +-
gdb/selftest-arch.c | 3 +-
gdb/solib-target.c | 66 +-
gdb/solib.c | 80 +-
gdb/solib.h | 23 +-
gdb/target-descriptions.c | 19 +
gdb/target.c | 15 +-
gdb/target.h | 3 +
gdb/testsuite/README | 9 +
gdb/testsuite/boards/intel-offload.exp | 36 +
.../gdb.arch/intelgt-disassemble.exp | 82 +
.../intelgt-interrupt-exited-thread.exp | 85 +
gdb/testsuite/gdb.arch/intelgt-program-bp.exp | 83 +
gdb/testsuite/gdb.arch/sycl-hello.cpp | 33 +
.../gdb.base/annota-input-while-running.exp | 2 +-
gdb/testsuite/gdb.base/annota1.exp | 4 +-
.../gdb.base/breakpoint-in-ro-region.exp | 4 +-
gdb/testsuite/gdb.base/multi-forks.exp | 12 +-
.../premature-dummy-frame-removal.exp | 16 +-
.../gdb.base/sss-bp-on-user-bp-2.exp | 4 +-
gdb/testsuite/gdb.base/ui-redirect.exp | 2 +-
gdb/testsuite/gdb.cp/annota2.exp | 2 +-
gdb/testsuite/gdb.replay/missing-thread.exp | 28 +-
gdb/testsuite/gdb.sycl/break.cpp | 127 +
gdb/testsuite/gdb.sycl/break.exp | 246 ++
gdb/testsuite/gdb.sycl/call-stack.cpp | 92 +
gdb/testsuite/gdb.sycl/call-stack.exp | 176 +
.../gdb.sycl/info-locals-and-args.exp | 78 +
gdb/testsuite/gdb.sycl/parallel-for-1D.cpp | 72 +
gdb/testsuite/gdb.sycl/parallel-for-1D.exp | 57 +
gdb/testsuite/gdb.sycl/parallel-for-2D.cpp | 73 +
gdb/testsuite/gdb.sycl/parallel-for-2D.exp | 57 +
gdb/testsuite/gdb.sycl/scheduler-locking.exp | 63 +
gdb/testsuite/gdb.sycl/step.exp | 48 +
gdb/testsuite/gdb.threads/ia64-sigill.exp | 8 +-
.../process-dies-while-detaching.exp | 5 +-
...al-while-stepping-over-bp-other-thread.exp | 2 +-
.../gdb.threads/stepi-random-signal.exp | 4 +-
.../gdb.threads/watchthreads-reorder.exp | 4 +-
gdb/testsuite/lib/gdb.exp | 17 +-
gdb/testsuite/lib/intelgt-utils.exp | 43 +
gdb/testsuite/lib/sycl-devices.cpp | 106 +
gdb/testsuite/lib/sycl-hello.cpp | 33 +
gdb/testsuite/lib/sycl-util.cpp | 135 +
gdb/testsuite/lib/sycl.exp | 399 +++
gdb/thread-iter.c | 2 +-
gdb/top.c | 10 +
gdb/xml-tdesc.c | 36 +
gdbserver/Makefile.in | 4 +-
gdbserver/acinclude.m4 | 5 +
gdbserver/config.in | 3 +
gdbserver/configure | 500 +++
gdbserver/configure.ac | 18 +
gdbserver/configure.srv | 15 +-
gdbserver/dll.cc | 147 +-
gdbserver/dll.h | 51 +-
gdbserver/gdbthread.h | 2 +-
gdbserver/intelgt-ze-low.cc | 1002 ++++++
gdbserver/linux-low.cc | 11 +-
gdbserver/linux-low.h | 2 +-
gdbserver/netbsd-low.cc | 2 +-
gdbserver/netbsd-low.h | 2 +-
gdbserver/notif.cc | 4 +
gdbserver/notif.h | 4 +
gdbserver/regcache.cc | 1 +
gdbserver/regcache.h | 3 +
gdbserver/remote-utils.cc | 3 +-
gdbserver/server.cc | 150 +-
gdbserver/server.h | 3 +
gdbserver/target.cc | 38 +-
gdbserver/target.h | 45 +-
gdbserver/tdesc.cc | 21 +-
gdbserver/tdesc.h | 3 +
gdbserver/win32-low.cc | 11 +-
gdbserver/win32-low.h | 2 +-
gdbserver/ze-low.cc | 2846 +++++++++++++++++
gdbserver/ze-low.h | 465 +++
gdbsupport/tdesc.cc | 24 +
gdbsupport/tdesc.h | 54 +
include/dwarf2.def | 4 +
include/elf/intelgt.h | 45 +
opcodes/configure | 1 +
opcodes/configure.ac | 1 +
124 files changed, 10699 insertions(+), 471 deletions(-)
create mode 100644 bfd/cpu-intelgt.c
create mode 100644 bfd/elf64-intelgt.c
create mode 100644 gdb/arch/intelgt.c
create mode 100644 gdb/arch/intelgt.h
create mode 100644 gdb/intelgt-tdep.c
create mode 100644 gdb/testsuite/boards/intel-offload.exp
create mode 100644 gdb/testsuite/gdb.arch/intelgt-disassemble.exp
create mode 100644 gdb/testsuite/gdb.arch/intelgt-interrupt-exited-thread.exp
create mode 100644 gdb/testsuite/gdb.arch/intelgt-program-bp.exp
create mode 100644 gdb/testsuite/gdb.arch/sycl-hello.cpp
create mode 100644 gdb/testsuite/gdb.sycl/break.cpp
create mode 100644 gdb/testsuite/gdb.sycl/break.exp
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.cpp
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.exp
create mode 100644 gdb/testsuite/gdb.sycl/info-locals-and-args.exp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.exp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.exp
create mode 100644 gdb/testsuite/gdb.sycl/scheduler-locking.exp
create mode 100644 gdb/testsuite/gdb.sycl/step.exp
create mode 100644 gdb/testsuite/lib/intelgt-utils.exp
create mode 100644 gdb/testsuite/lib/sycl-devices.cpp
create mode 100644 gdb/testsuite/lib/sycl-hello.cpp
create mode 100644 gdb/testsuite/lib/sycl-util.cpp
create mode 100644 gdb/testsuite/lib/sycl.exp
create mode 100644 gdbserver/intelgt-ze-low.cc
create mode 100644 gdbserver/ze-low.cc
create mode 100644 gdbserver/ze-low.h
create mode 100644 include/elf/intelgt.h
--
2.43.0
--- b4-submit-tracking ---
{
"series": {
"revision": 4,
"change-id": "20241213-upstream-intelgt-mvp-684d5f2f6730",
"prefixes": []
}
}
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 01/44] bfd: add intelgt target to BFD
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 02/44] opcodes: add intelgt as a configuration Markus Metzger
` (42 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Natalia Saiapova
From: Natalia Saiapova <natalia.saiapova@intel.com>
Add description of IntelGT target to BFD. Describe its relocation
types.
To: <binutils@sourceware.org>
---
bfd/Makefile.am | 2 ++
bfd/Makefile.in | 4 ++++
bfd/archures.c | 4 ++++
bfd/bfd-in2.h | 2 ++
bfd/config.bfd | 5 +++++
bfd/configure | 1 +
bfd/configure.ac | 1 +
bfd/cpu-intelgt.c | 41 ++++++++++++++++++++++++++++++++++++
bfd/elf64-intelgt.c | 48 +++++++++++++++++++++++++++++++++++++++++++
bfd/targets.c | 2 ++
binutils/readelf.c | 5 +++++
include/elf/intelgt.h | 45 ++++++++++++++++++++++++++++++++++++++++
12 files changed, 160 insertions(+)
create mode 100644 bfd/cpu-intelgt.c
create mode 100644 bfd/elf64-intelgt.c
create mode 100644 include/elf/intelgt.h
diff --git a/bfd/Makefile.am b/bfd/Makefile.am
index e4df05441eb..1976b76ca50 100644
--- a/bfd/Makefile.am
+++ b/bfd/Makefile.am
@@ -119,6 +119,7 @@ ALL_MACHINES = \
cpu-i386.lo \
cpu-ia64.lo \
cpu-iamcu.lo \
+ cpu-intelgt.lo \
cpu-ip2k.lo \
cpu-iq2000.lo \
cpu-kvx.lo \
@@ -202,6 +203,7 @@ ALL_MACHINES_CFILES = \
cpu-i386.c \
cpu-ia64.c \
cpu-iamcu.c \
+ cpu-intelgt.c \
cpu-ip2k.c \
cpu-iq2000.c \
cpu-kvx.c \
diff --git a/bfd/Makefile.in b/bfd/Makefile.in
index 14512cf61fb..5656faf4cc6 100644
--- a/bfd/Makefile.in
+++ b/bfd/Makefile.in
@@ -587,6 +587,7 @@ ALL_MACHINES = \
cpu-i386.lo \
cpu-ia64.lo \
cpu-iamcu.lo \
+ cpu-intelgt.lo \
cpu-ip2k.lo \
cpu-iq2000.lo \
cpu-kvx.lo \
@@ -670,6 +671,7 @@ ALL_MACHINES_CFILES = \
cpu-i386.c \
cpu-ia64.c \
cpu-iamcu.c \
+ cpu-intelgt.c \
cpu-ip2k.c \
cpu-iq2000.c \
cpu-kvx.c \
@@ -1028,6 +1030,7 @@ BFD64_BACKENDS = \
elf64-hppa.lo \
elf64-ia64-vms.lo \
elf64-ia64.lo \
+ elf64-intelgt.lo \
elf64-kvx.lo \
elf64-loongarch.lo \
elf64-mips.lo \
@@ -1499,6 +1502,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-i386.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-ia64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-iamcu.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-intelgt.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-ip2k.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-iq2000.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpu-kvx.Plo@am__quote@
diff --git a/bfd/archures.c b/bfd/archures.c
index 18472b820bc..3bf2a93ab35 100644
--- a/bfd/archures.c
+++ b/bfd/archures.c
@@ -399,6 +399,8 @@ DESCRIPTION
. bfd_arch_ia64, {* HP/Intel ia64. *}
.#define bfd_mach_ia64_elf64 64
.#define bfd_mach_ia64_elf32 32
+. bfd_arch_intelgt, {* Intel(R) Graphics Technology. *}
+.#define bfd_mach_intelgt 9
. bfd_arch_ip2k, {* Ubicom IP2K microcontrollers. *}
.#define bfd_mach_ip2022 1
.#define bfd_mach_ip2022ext 2
@@ -667,6 +669,7 @@ extern const bfd_arch_info_type bfd_hppa_arch;
extern const bfd_arch_info_type bfd_i386_arch;
extern const bfd_arch_info_type bfd_iamcu_arch;
extern const bfd_arch_info_type bfd_ia64_arch;
+extern const bfd_arch_info_type bfd_intelgt_arch;
extern const bfd_arch_info_type bfd_ip2k_arch;
extern const bfd_arch_info_type bfd_iq2000_arch;
extern const bfd_arch_info_type bfd_kvx_arch;
@@ -755,6 +758,7 @@ static const bfd_arch_info_type * const bfd_archures_list[] =
&bfd_i386_arch,
&bfd_iamcu_arch,
&bfd_ia64_arch,
+ &bfd_intelgt_arch,
&bfd_ip2k_arch,
&bfd_iq2000_arch,
&bfd_kvx_arch,
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 8d12ddf6723..611403f6925 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1656,6 +1656,8 @@ enum bfd_architecture
bfd_arch_ia64, /* HP/Intel ia64. */
#define bfd_mach_ia64_elf64 64
#define bfd_mach_ia64_elf32 32
+ bfd_arch_intelgt, /* Intel(R) Graphics Technology. */
+#define bfd_mach_intelgt 9
bfd_arch_ip2k, /* Ubicom IP2K microcontrollers. */
#define bfd_mach_ip2022 1
#define bfd_mach_ip2022ext 2
diff --git a/bfd/config.bfd b/bfd/config.bfd
index 8eb2131128d..82c2dfef0e8 100644
--- a/bfd/config.bfd
+++ b/bfd/config.bfd
@@ -204,6 +204,7 @@ fido*) targ_archs=bfd_m68k_arch ;;
hppa*) targ_archs=bfd_hppa_arch ;;
i[3-7]86) targ_archs=bfd_i386_arch ;;
ia16) targ_archs=bfd_i386_arch ;;
+intelgt) targ_archs=bfd_intelgt_arch ;;
loongarch*) targ_archs=bfd_loongarch_arch ;;
m6811*|m68hc11*) targ_archs="bfd_m68hc11_arch bfd_m68hc12_arch bfd_m9s12x_arch bfd_m9s12xg_arch" ;;
m6812*|m68hc12*) targ_archs="bfd_m68hc12_arch bfd_m68hc11_arch bfd_m9s12x_arch bfd_m9s12xg_arch" ;;
@@ -657,6 +658,10 @@ case "${targ}" in
targ_selvecs=alpha_vms_lib_txt_vec
want64=true
;;
+ intelgt-*-elf)
+ targ_defvec=intelgt_elf64_vec
+ want64=true
+ ;;
#endif /* BFD64 */
ip2k-*-elf)
diff --git a/bfd/configure b/bfd/configure
index 12d43a5aeae..71192288275 100755
--- a/bfd/configure
+++ b/bfd/configure
@@ -15947,6 +15947,7 @@ do
ia64_elf64_hpux_be_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;;
ia64_elf64_vms_vec) tb="$tb elf64-ia64-vms.lo elf64-ia64.lo elfxx-ia64.lo elf64.lo vms-lib.lo vms-misc.lo $elf"; target_size=64 ;;
ia64_pei_vec) tb="$tb pei-ia64.lo pepigen.lo $coff"; target_size=64 ;;
+ intelgt_elf64_vec) tb="$tb elf64-intelgt.lo elf64.lo $elf"; target_size=64 ;;
ip2k_elf32_vec) tb="$tb elf32-ip2k.lo elf32.lo $elf" ;;
iq2000_elf32_vec) tb="$tb elf32-iq2000.lo elf32.lo $elf" ;;
kvx_elf32_vec) tb="$tb elf32-kvx.lo elfxx-kvx.lo elf32.lo $elf $ipa" ;;
diff --git a/bfd/configure.ac b/bfd/configure.ac
index ee335f75dd9..dd8c9d87776 100644
--- a/bfd/configure.ac
+++ b/bfd/configure.ac
@@ -488,6 +488,7 @@ do
ia64_elf64_hpux_be_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;;
ia64_elf64_vms_vec) tb="$tb elf64-ia64-vms.lo elf64-ia64.lo elfxx-ia64.lo elf64.lo vms-lib.lo vms-misc.lo $elf"; target_size=64 ;;
ia64_pei_vec) tb="$tb pei-ia64.lo pepigen.lo $coff"; target_size=64 ;;
+ intelgt_elf64_vec) tb="$tb elf64-intelgt.lo elf64.lo $elf"; target_size=64 ;;
ip2k_elf32_vec) tb="$tb elf32-ip2k.lo elf32.lo $elf" ;;
iq2000_elf32_vec) tb="$tb elf32-iq2000.lo elf32.lo $elf" ;;
kvx_elf32_vec) tb="$tb elf32-kvx.lo elfxx-kvx.lo elf32.lo $elf $ipa" ;;
diff --git a/bfd/cpu-intelgt.c b/bfd/cpu-intelgt.c
new file mode 100644
index 00000000000..87341eda015
--- /dev/null
+++ b/bfd/cpu-intelgt.c
@@ -0,0 +1,41 @@
+/* BFD support for the Intel(R) Graphics Technology architecture.
+ Copyright (C) 2019-2026 Free Software Foundation, Inc.
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+
+const bfd_arch_info_type bfd_intelgt_arch =
+ {
+ 32, /* 32 bits in a word. */
+ 64, /* 64 bits in an address. */
+ 8, /* 8 bits in a byte. */
+ bfd_arch_intelgt, /* Architecture. */
+ bfd_mach_intelgt, /* Machine number. */
+ "intelgt", /* Architecture name. */
+ "intelgt", /* Printable name. */
+ 3, /* Section alignment power. */
+ true, /* Default machine for this architecture. */
+ bfd_default_compatible, /* Check for compatibility. */
+ bfd_default_scan, /* Check for an arch and mach hit. */
+ bfd_arch_default_fill, /* Allocate and fill bfd. */
+ NULL, /* Pointer to next. */
+ 0 /* Maximum offset of a reloc from the start of an insn. */
+ };
diff --git a/bfd/elf64-intelgt.c b/bfd/elf64-intelgt.c
new file mode 100644
index 00000000000..11369242f15
--- /dev/null
+++ b/bfd/elf64-intelgt.c
@@ -0,0 +1,48 @@
+/* Intel(R) Graphics Technology-specific support for ELF
+ Copyright (C) 2022-2026 Free Software Foundation, Inc.
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "sysdep.h"
+#include "bfd.h"
+#include "libbfd.h"
+#include "elf-bfd.h"
+
+#include "elf/common.h"
+#include "elf/intelgt.h"
+
+
+static bool
+elf64_intelgt_elf_object_p (bfd *abfd)
+{
+ return bfd_default_set_arch_mach (abfd, bfd_arch_intelgt, bfd_mach_intelgt);
+}
+
+#define TARGET_LITTLE_SYM intelgt_elf64_vec
+#define TARGET_LITTLE_NAME "elf64-intelgt"
+#define ELF_ARCH bfd_arch_intelgt
+#define ELF_MACHINE_CODE EM_INTELGT
+#define ELF_OSABI 0
+#define ELF_MAXPAGESIZE 0x40000000
+
+#define elf_backend_object_p elf64_intelgt_elf_object_p
+
+#define bfd_elf64_bfd_reloc_type_lookup bfd_default_reloc_type_lookup
+#define bfd_elf64_bfd_reloc_name_lookup _bfd_norelocs_bfd_reloc_name_lookup
+
+#include "elf64-target.h"
diff --git a/bfd/targets.c b/bfd/targets.c
index fcf650410dc..5a665acd3fe 100644
--- a/bfd/targets.c
+++ b/bfd/targets.c
@@ -766,6 +766,7 @@ extern const bfd_target ia64_elf64_le_vec;
extern const bfd_target ia64_elf64_hpux_be_vec;
extern const bfd_target ia64_elf64_vms_vec;
extern const bfd_target ia64_pei_vec;
+extern const bfd_target intelgt_elf64_vec;
extern const bfd_target ip2k_elf32_vec;
extern const bfd_target iq2000_elf32_vec;
extern const bfd_target kvx_elf32_vec;
@@ -1121,6 +1122,7 @@ static const bfd_target * const _bfd_target_vector[] =
&ia64_elf64_hpux_be_vec,
&ia64_elf64_vms_vec,
&ia64_pei_vec,
+ &intelgt_elf64_vec,
#endif
&ip2k_elf32_vec,
diff --git a/binutils/readelf.c b/binutils/readelf.c
index b5865d9717e..a6a6f390995 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -125,6 +125,7 @@
#include "elf/kvx.h"
#include "elf/lm32.h"
#include "elf/iq2000.h"
+#include "elf/intelgt.h"
#include "elf/m32c.h"
#include "elf/m32r.h"
#include "elf/m68k.h"
@@ -2687,6 +2688,10 @@ dump_relocations (Filedata * filedata,
case EM_AMDGPU:
rtype = elf_amdgpu_reloc_type (type);
break;
+
+ case EM_INTELGT:
+ rtype = elf_intelgt_reloc_type (type);
+ break;
}
char *symbol_name = NULL;
diff --git a/include/elf/intelgt.h b/include/elf/intelgt.h
new file mode 100644
index 00000000000..bcc62ee8f35
--- /dev/null
+++ b/include/elf/intelgt.h
@@ -0,0 +1,45 @@
+/* Copyright (C) 2022-2026 Free Software Foundation, Inc.
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+/* This file holds definitions specific to the IntelGT ABI. */
+
+#ifndef _ELF_INTELGT_H_
+#define _ELF_INTELGT_H_
+
+#include "elf/reloc-macros.h"
+
+START_RELOC_NUMBERS (elf_intelgt_reloc_type)
+ RELOC_NUMBER (R_ZE_NONE, 0)
+ /* 64-bit address. */
+ RELOC_NUMBER (R_ZE_SYM_ADDR, 1)
+ /* 32-bit address or lower 32-bit of a 64-bit address. */
+ RELOC_NUMBER (R_ZE_SYM_ADDR_32, 2)
+ /* Higher 32bits of a 64-bit address. */
+ RELOC_NUMBER (R_ZE_SYM_ADDR32_HI, 3)
+ /* 32-bit field of payload offset of per-thread data. */
+ RELOC_NUMBER (R_ZE_PER_THREAD_PAYLOAD_OFFSET_32, 4)
+ /* 32-bit global immediate. */
+ RELOC_NUMBER (R_ZE_GLOBAL_IMM_32, 5)
+ /* SEND instruction offset, used for BTI patching. */
+ RELOC_NUMBER (R_ZE_SEND, 6)
+ /* 16-bit address or immediate. */
+ RELOC_NUMBER (R_ZE_SYM_ADDR_16, 7)
+END_RELOC_NUMBERS (R_ZE_max)
+
+#endif /* _ELF_INTELGT_H_ */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 02/44] opcodes: add intelgt as a configuration
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
2026-08-12 13:27 ` [PATCH v4 01/44] bfd: add intelgt target to BFD Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 03/44] gdbserver: allow configuring for a heterogeneous target Markus Metzger
` (41 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Intel GT does not use the opcodes lib for disassembling instructions.
Therefore, add the simplest configuration to satisfy building without
errors.
To: <binutils@sourceware.org>
---
opcodes/configure | 1 +
opcodes/configure.ac | 1 +
2 files changed, 2 insertions(+)
diff --git a/opcodes/configure b/opcodes/configure
index 3e0962b9408..433f0c8fc6d 100755
--- a/opcodes/configure
+++ b/opcodes/configure
@@ -14346,6 +14346,7 @@ if test x${all_targets} = xfalse ; then
bfd_i386_arch|bfd_iamcu_arch)
ta="$ta i386-dis.lo" ;;
bfd_ia64_arch) ta="$ta ia64-dis.lo ia64-opc.lo" ;;
+ bfd_intelgt_arch) ;;
bfd_ip2k_arch) ta="$ta ip2k-asm.lo ip2k-desc.lo ip2k-dis.lo ip2k-ibld.lo ip2k-opc.lo" using_cgen=yes ;;
bfd_epiphany_arch) ta="$ta epiphany-asm.lo epiphany-desc.lo epiphany-dis.lo epiphany-ibld.lo epiphany-opc.lo" using_cgen=yes ;;
bfd_iq2000_arch) ta="$ta iq2000-asm.lo iq2000-desc.lo iq2000-dis.lo iq2000-ibld.lo iq2000-opc.lo" using_cgen=yes ;;
diff --git a/opcodes/configure.ac b/opcodes/configure.ac
index 2c2556598c9..f90fa1d18f2 100644
--- a/opcodes/configure.ac
+++ b/opcodes/configure.ac
@@ -285,6 +285,7 @@ if test x${all_targets} = xfalse ; then
bfd_i386_arch|bfd_iamcu_arch)
ta="$ta i386-dis.lo" ;;
bfd_ia64_arch) ta="$ta ia64-dis.lo ia64-opc.lo" ;;
+ bfd_intelgt_arch) ;;
bfd_ip2k_arch) ta="$ta ip2k-asm.lo ip2k-desc.lo ip2k-dis.lo ip2k-ibld.lo ip2k-opc.lo" using_cgen=yes ;;
bfd_epiphany_arch) ta="$ta epiphany-asm.lo epiphany-desc.lo epiphany-dis.lo epiphany-ibld.lo epiphany-opc.lo" using_cgen=yes ;;
bfd_iq2000_arch) ta="$ta iq2000-asm.lo iq2000-desc.lo iq2000-dis.lo iq2000-ibld.lo iq2000-opc.lo" using_cgen=yes ;;
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 03/44] gdbserver: allow configuring for a heterogeneous target
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
2026-08-12 13:27 ` [PATCH v4 01/44] bfd: add intelgt target to BFD Markus Metzger
2026-08-12 13:27 ` [PATCH v4 02/44] opcodes: add intelgt as a configuration Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 04/44] config.sub: recognize level-zero as "ze" Markus Metzger
` (40 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
gdbserver's configure.srv requires host and target systems to be the
same. This prevents building gdbserver for when a target other than
the native CPU is to be debugged, e.g. a heterogeneous system with an
Intel GPU where we want to debug the GPU.
Allow for such a combination by special-casing the Intel GPU target to
preserve the existing behavior for other cases.
---
gdbserver/configure.srv | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 7bdd92e3f82..69611db4698 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -30,11 +30,18 @@ srv_linux_obj="linux-low.o nat/linux-osdata.o nat/linux-procfs.o nat/linux-ptrac
# Input is taken from the "${host}" and "${target}" variables.
-# GDBserver can only debug native programs.
+# GDBserver can only debug native programs. An exception to this is a
+# heterogeneous system, e.g. an Intel GPU.
if test "${target}" = "${host}"; then
gdbserver_host=${host}
else
- gdbserver_host=
+ case "${target}" in
+ intelgt*) gdbserver_host=${target}
+ ;;
+
+ *) gdbserver_host=
+ ;;
+ esac
fi
case "${gdbserver_host}" in
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 04/44] config.sub: recognize level-zero as "ze"
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (2 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 03/44] gdbserver: allow configuring for a heterogeneous target Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 05/44] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Markus Metzger
` (39 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: config-patches
Note: This patch will come from the config project. It is included in
this series to enable building.
Recognize Level-Zero (https://oneapi-src.github.io/level-zero-spec/).
CC: config-patches@gnu.org
---
config.sub | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/config.sub b/config.sub
index 3d35cde174d..e127b767fe2 100755
--- a/config.sub
+++ b/config.sub
@@ -2154,6 +2154,7 @@ case $os in
| winnt* \
| xenix* \
| xray* \
+ | ze* \
| zephyr* \
| zvmoe* )
;;
@@ -2216,7 +2217,7 @@ esac
case $kernel-$os-$obj in
linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
| linux-mlibc*- | linux-musl*- | linux-newlib*- \
- | linux-relibc*- | linux-uclibc*- | linux-ohos*- )
+ | linux-relibc*- | linux-uclibc*- | linux-ohos*- | linux-ze*- )
;;
uclinux-uclibc*- | uclinux-gnu*- )
;;
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 05/44] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (3 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 04/44] config.sub: recognize level-zero as "ze" Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 06/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Markus Metzger
` (38 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
AC_LIB_HAVE_LINKFLAGS macro is available in gdb/configure.ac but
was not in gdbserver/configure.ac. Include the same m4 files that
gdb/configure.ac includes for AC_LIB_HAVE_LINKFLAGS.
---
gdbserver/acinclude.m4 | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/gdbserver/acinclude.m4 b/gdbserver/acinclude.m4
index 9b1cf85a699..7690521def3 100644
--- a/gdbserver/acinclude.m4
+++ b/gdbserver/acinclude.m4
@@ -13,6 +13,11 @@ m4_include(../gdbsupport/compiler-type.m4)
dnl This gets AM_GDB_WARNINGS.
m4_include(../gdbsupport/warning.m4)
+dnl For AC_LIB_HAVE_LINKFLAGS.
+sinclude(../../config/lib-ld.m4)
+sinclude(../../config/lib-prefix.m4)
+sinclude(../../config/lib-link.m4)
+
dnl codeset.m4 is needed for common.m4, but not for
dnl anything else in gdbserver.
m4_include(../config/codeset.m4)
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 06/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (4 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 05/44] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 07/44] gdb, arch, intelgt: add intelgt arch definitions Markus Metzger
` (37 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Nils-Christian Kempke, Tankut Baris Aktemur, Eli Zaretskii
From: Nils-Christian Kempke <nils-christian.kempke@intel.com>
Add <device> tag to the XML target description. The tag is a list of
device attributes. The extension enables passing device information
within the target description XML sent from gdbserver to gdb.
Co-authored-by: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 8 ++++++
gdb/doc/gdb.texinfo | 17 ++++++++++++
gdb/features/gdb-target.dtd | 11 +++++++-
gdb/target-descriptions.c | 19 +++++++++++++
gdb/xml-tdesc.c | 36 +++++++++++++++++++++++++
gdbserver/tdesc.cc | 16 +++++++++++
gdbserver/tdesc.h | 3 +++
gdbsupport/tdesc.cc | 24 +++++++++++++++++
gdbsupport/tdesc.h | 54 +++++++++++++++++++++++++++++++++++++
9 files changed, 187 insertions(+), 1 deletion(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 10c182067f9..0b949ae7825 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,14 @@
*** Changes since GDB 18
+* Changed remote packets
+
+qXfer:features:read:target.xml
+
+ The XML that is sent as a response can now include a "device" element
+ that can be used for passing information when the remote inferior
+ represents a device, e.g. a GPU.
+
*** Changes in GDB 18
* Support for the Common Trace Format (CTF) has been removed. GDB now
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 0030698dcee..ce4bbc48385 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -49423,6 +49423,7 @@ are explained further below.
@r{[}@var{architecture}@r{]}
@r{[}@var{osabi}@r{]}
@r{[}@var{compatible}@r{]}
+ @r{[}@var{device}@r{]}
@r{[}@var{feature}@dots{}@r{]}
</target>
@end smallexample
@@ -49520,6 +49521,22 @@ capability with @samp{<compatible>} is as follows:
<compatible>spu</compatible>
@end smallexample
+@subsection Device Information
+@cindex <device>
+
+A @samp{<device>} element can be used for passing device information
+for when, e.g.@: the remote target represents a device, such as a GPU.
+@value{GDBN} can then use this information to display it to the user
+for informative purposes or to execute device-specific functions
+appropriately. The element contains a number of attributes, best
+shown with the example below. All attributes are optional.
+
+@smallexample
+ <device vendor-id="0x8086"
+ target-id="0x56a1"
+ name="Intel(R) Arc(TM) A750 Graphics" />
+@end smallexample
+
@subsection Features
@cindex <feature>
diff --git a/gdb/features/gdb-target.dtd b/gdb/features/gdb-target.dtd
index 2c405af8add..e22a07f9b48 100644
--- a/gdb/features/gdb-target.dtd
+++ b/gdb/features/gdb-target.dtd
@@ -9,7 +9,9 @@
<!-- The osabi and compatible elements were added post GDB 6.8. The version
wasn't bumped, since older GDBs silently ignore unknown elements. -->
-<!ELEMENT target (architecture?, osabi?, compatible*, feature*)>
+<!ELEMENT target
+ (architecture?, osabi?, compatible*, device?, feature*)>
+
<!ATTLIST target
version CDATA #FIXED "1.0">
@@ -19,6 +21,13 @@
<!ELEMENT compatible (#PCDATA)>
+<!ELEMENT device EMPTY>
+<!ATTLIST device
+ vendor-id CDATA #IMPLIED
+ target-id CDATA #IMPLIED
+ name CDATA #IMPLIED
+ >
+
<!ELEMENT feature
((vector | flags | struct | union )*, reg*)>
<!ATTLIST feature
diff --git a/gdb/target-descriptions.c b/gdb/target-descriptions.c
index e1821fe7114..6e1bc2fa5e7 100644
--- a/gdb/target-descriptions.c
+++ b/gdb/target-descriptions.c
@@ -355,6 +355,9 @@ struct target_desc : tdesc_element
/* The list of compatible architectures reported by the target. */
std::vector<tdesc_compatible_info_up> compatible;
+ /* The device reported by the target, if any. */
+ tdesc_device_up device;
+
/* Any architecture-specific properties specified by the target. */
std::vector<property> properties;
@@ -633,6 +636,22 @@ tdesc_osabi_name (const struct target_desc *target_desc)
return nullptr;
}
+/* See gdbsupport/tdesc.h. */
+
+void
+set_tdesc_device_info (target_desc *target_desc, tdesc_device *device)
+{
+ target_desc->device.reset (device);
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const tdesc_device *
+tdesc_device_info (const target_desc *target_desc)
+{
+ return target_desc->device.get ();
+}
+
/* Return 1 if this target description includes any registers. */
int
diff --git a/gdb/xml-tdesc.c b/gdb/xml-tdesc.c
index c0b1b635315..0481e16fab5 100644
--- a/gdb/xml-tdesc.c
+++ b/gdb/xml-tdesc.c
@@ -137,6 +137,33 @@ tdesc_end_compatible (struct gdb_xml_parser *parser,
tdesc_add_compatible (data->tdesc, arch);
}
+/* Handle the start of a <device> element and its value. */
+
+static void
+tdesc_start_device (struct gdb_xml_parser *parser,
+ const gdb_xml_element *element,
+ void *user_data, std::vector<gdb_xml_value> &attributes)
+{
+ tdesc_parsing_data *data = (struct tdesc_parsing_data *) user_data;
+ tdesc_device *device = new tdesc_device ();
+
+ gdb_xml_value *attr;
+
+ attr = xml_find_attribute (attributes, "vendor-id");
+ if (attr != nullptr)
+ device->vendor_id = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "target-id");
+ if (attr != nullptr)
+ device->target_id = * (ULONGEST *) attr->value.get ();
+
+ attr = xml_find_attribute (attributes, "name");
+ if (attr != nullptr)
+ device->name = (char *) attr->value.get ();
+
+ set_tdesc_device_info (data->tdesc, device);
+}
+
/* Handle the start of a <target> element. */
static void
@@ -587,6 +614,13 @@ static const struct gdb_xml_element feature_children[] = {
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
+static const struct gdb_xml_attribute device_attributes[] = {
+ { "vendor-id", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "target-id", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+ { "name", GDB_XML_AF_OPTIONAL, NULL, NULL },
+ { NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
static const struct gdb_xml_attribute target_attributes[] = {
{ "version", GDB_XML_AF_NONE, NULL, NULL },
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
@@ -599,6 +633,8 @@ static const struct gdb_xml_element target_children[] = {
NULL, tdesc_end_osabi },
{ "compatible", NULL, NULL, GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
NULL, tdesc_end_compatible },
+ { "device", device_attributes, NULL, GDB_XML_EF_OPTIONAL,
+ tdesc_start_device, NULL },
{ "feature", feature_attributes, feature_children,
GDB_XML_EF_OPTIONAL | GDB_XML_EF_REPEATABLE,
tdesc_start_feature, NULL },
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index 1d5f52b8948..cb8aad84f54 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -190,6 +190,22 @@ set_tdesc_osabi (struct target_desc *target_desc, enum gdb_osabi osabi)
/* See gdbsupport/tdesc.h. */
+void
+set_tdesc_device_info (target_desc *target_desc, tdesc_device *device)
+{
+ target_desc->device.reset (device);
+}
+
+/* See gdbsupport/tdesc.h. */
+
+const tdesc_device *
+tdesc_device_info (const target_desc *target_desc)
+{
+ return target_desc->device.get ();
+}
+
+/* See gdbsupport/tdesc.h. */
+
const char *
tdesc_get_features_xml (const target_desc *tdesc)
{
diff --git a/gdbserver/tdesc.h b/gdbserver/tdesc.h
index 076451f4815..ffff346cf09 100644
--- a/gdbserver/tdesc.h
+++ b/gdbserver/tdesc.h
@@ -60,6 +60,9 @@ struct target_desc final : tdesc_element
/* The value of <osabi> element in the XML, replying GDB. */
gdb::unique_xmalloc_ptr<char> osabi;
+ /* The value of the <device> element in the XML, replying GDB. */
+ tdesc_device_up device;
+
public:
target_desc ()
: registers_size (0)
diff --git a/gdbsupport/tdesc.cc b/gdbsupport/tdesc.cc
index 4b6aa815c81..c6ce443dac4 100644
--- a/gdbsupport/tdesc.cc
+++ b/gdbsupport/tdesc.cc
@@ -417,6 +417,8 @@ void print_xml_feature::visit_pre (const target_desc *e)
for (const auto &c : compatible_list)
add_line ("<compatible>%s</compatible>",
tdesc_compatible_info_arch_name (c));
+
+ this->visit (tdesc_device_info (e));
#endif
}
@@ -426,6 +428,28 @@ void print_xml_feature::visit_post (const target_desc *e)
add_line ("</target>");
}
+void
+print_xml_feature::visit (const tdesc_device *device)
+{
+ if (device == nullptr)
+ return;
+
+ std::string tmp = "<device";
+ if (device->vendor_id.has_value ())
+ string_appendf (tmp, " vendor-id=\"0x%04" PRIx32 "\"",
+ *device->vendor_id);
+
+ if (device->target_id.has_value ())
+ string_appendf (tmp, " target-id=\"0x%04" PRIx32 "\"",
+ *device->target_id);
+
+ if (!device->name.empty ())
+ string_appendf (tmp, " name=\"%s\"", device->name.c_str ());
+
+ string_appendf (tmp, "/>");
+ add_line (tmp);
+}
+
/* See gdbsupport/tdesc.h. */
void
diff --git a/gdbsupport/tdesc.h b/gdbsupport/tdesc.h
index a2ef21987bf..1051a1a98fb 100644
--- a/gdbsupport/tdesc.h
+++ b/gdbsupport/tdesc.h
@@ -26,6 +26,7 @@ struct tdesc_type_builtin;
struct tdesc_type_vector;
struct tdesc_type_with_fields;
struct tdesc_reg;
+struct tdesc_device;
struct target_desc;
/* The interface to visit different elements of target description. */
@@ -56,6 +57,9 @@ class tdesc_element_visitor
virtual void visit (const tdesc_reg *e)
{}
+
+ virtual void visit (const tdesc_device *e)
+ {}
};
class tdesc_element
@@ -315,6 +319,48 @@ struct tdesc_feature : tdesc_element
using tdesc_feature_up = std::unique_ptr<tdesc_feature>;
+/* The device information in a target description. */
+
+struct tdesc_device : tdesc_element
+{
+ tdesc_device ()
+ {
+ name = "";
+ }
+
+ virtual ~tdesc_device () = default;
+
+ DISABLE_COPY_AND_ASSIGN (tdesc_device);
+
+ /* The id of the device vendor. */
+ std::optional<uint32_t> vendor_id;
+
+ /* The id of the device, given by its vendor. */
+ std::optional<uint32_t> target_id;
+
+ /* The name of the device. */
+ std::string name;
+
+ void accept (tdesc_element_visitor &v) const override
+ {
+ v.visit (this);
+ }
+
+ bool operator== (const tdesc_device &other) const
+ {
+ return (vendor_id == other.vendor_id
+ && target_id == other.target_id
+ && name == other.name);
+ }
+
+ bool operator!= (const tdesc_device &other) const
+ {
+ return !(*this == other);
+ }
+};
+
+typedef std::unique_ptr<tdesc_device> tdesc_device_up;
+
/* A deleter adapter for a target_desc. There are different
implementations of this deleter class in gdb and gdbserver because even
though the target_desc name is shared between the two projects, the
@@ -349,6 +395,13 @@ void set_tdesc_osabi (target_desc *target_desc, enum gdb_osabi osabi);
or NULL if no osabi was specified. */
const char *tdesc_osabi_name (const struct target_desc *target_desc);
+/* Set TARGET_DESC's device information. */
+void set_tdesc_device_info (target_desc *target_desc, tdesc_device *device);
+
+/* Return the device information associated with this target
+ description or NULL if device info does not exist. */
+const tdesc_device *tdesc_device_info (const target_desc *target_desc);
+
/* Return the type associated with ID in the context of FEATURE, or
NULL if none. */
struct tdesc_type *tdesc_named_type (const struct tdesc_feature *feature,
@@ -441,6 +494,7 @@ class print_xml_feature : public tdesc_element_visitor
void visit (const tdesc_type_vector *type) override;
void visit (const tdesc_type_with_fields *type) override;
void visit (const tdesc_reg *reg) override;
+ void visit (const tdesc_device *device) override;
private:
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 07/44] gdb, arch, intelgt: add intelgt arch definitions
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (5 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 06/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 08/44] gdb: add a new extract_integer variant that takes two array_views Markus Metzger
` (36 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Cc: Tankut Baris Aktemur, Mihails Strasuns, Natalia Saiapova,
Thiago Jung Bauermann, Simon Marchi
Provide Intel GT architecture-specific definitions that can be used by
both the low target at the server side and tdep at the GDB side.
Other than, for example, IA, Intel GT does not have a dedicated
breakpoint instruction. Instead, it has a breakpoint bit in each
instruction. We define arch methods for dealing with instruction
breakpoint bits.
Co-authored-by: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Co-authored-by: Mihails Strasuns <mihails.strasuns@intel.com>
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
Reviewed-By: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
Approved-By: Simon Marchi <simon.marchi@efficios.com>
---
gdb/Makefile.in | 1 +
gdb/arch/intelgt.c | 191 +++++++++++++++++++++++++++++++++++++++++++++
gdb/arch/intelgt.h | 185 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 377 insertions(+)
create mode 100644 gdb/arch/intelgt.c
create mode 100644 gdb/arch/intelgt.h
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 57f384170ab..4794e01246e 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -749,6 +749,7 @@ ALL_64_TARGET_OBS = \
arch/aarch64-scalable-linux.o \
arch/amd64-linux-tdesc.o \
arch/amd64.o \
+ arch/intelgt.o \
arch/riscv.o \
bpf-tdep.o \
ia64-linux-tdep.o \
diff --git a/gdb/arch/intelgt.c b/gdb/arch/intelgt.c
new file mode 100644
index 00000000000..7453de2e465
--- /dev/null
+++ b/gdb/arch/intelgt.c
@@ -0,0 +1,191 @@
+/* Copyright (C) 2019-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "intelgt.h"
+#include <stdlib.h>
+
+namespace intelgt {
+
+/* Get the bit at POS in INST. */
+
+bool
+get_inst_bit (gdb::array_view<const gdb_byte> inst, int pos)
+{
+ if (pos < 0 || (inst.size () * 8) <= pos)
+ internal_error (_("bad bit offset: %d"), pos);
+
+ const int idx = pos >> 3;
+ const int off = pos & 7;
+ const int mask = 1 << off;
+ const gdb_byte byte = inst[idx];
+
+ return (byte & mask) != 0;
+}
+
+/* Set the bit at POS in INST. */
+
+bool
+set_inst_bit (gdb::array_view<gdb_byte> inst, int pos)
+{
+ if (pos < 0 || (inst.size () * 8) <= pos)
+ internal_error (_("bad bit offset: %d"), pos);
+
+ const int idx = pos >> 3;
+ const int off = pos & 7;
+ const int mask = 1 << off;
+ const gdb_byte byte = inst[idx];
+
+ const bool old = (byte & mask) != 0;
+ inst[idx] |= mask;
+
+ return old;
+}
+
+/* Clear the bit at POS in INST. */
+
+bool
+clear_inst_bit (gdb::array_view<gdb_byte> inst, int pos)
+{
+ if (pos < 0 || (inst.size () * 8) <= pos)
+ internal_error (_("bad bit offset: %d"), pos);
+
+ const int idx = pos >> 3;
+ const int off = pos & 7;
+ const int mask = 1 << off;
+ const gdb_byte byte = inst[idx];
+
+ const bool old = (byte & mask) != 0;
+ inst[idx] &= ~mask;
+
+ return old;
+}
+
+/* See arch/intelgt.h. */
+
+xe_version
+get_xe_version (uint32_t device_id)
+{
+ switch (device_id)
+ {
+ case 0x4F80:
+ case 0x4F81:
+ case 0x4F82:
+ case 0x4F83:
+ case 0x4F84:
+ case 0x4F85:
+ case 0x4F86:
+ case 0x4F87:
+ case 0x4F88:
+ case 0x5690:
+ case 0x5691:
+ case 0x5692:
+ case 0x5693:
+ case 0x5694:
+ case 0x5695:
+ case 0x5696:
+ case 0x5697:
+ case 0x5698:
+ case 0x56A0:
+ case 0x56A1:
+ case 0x56A2:
+ case 0x56A3:
+ case 0x56A4:
+ case 0x56A5:
+ case 0x56A6:
+ case 0x56A7:
+ case 0x56A8:
+ case 0x56A9:
+ case 0x56B0:
+ case 0x56B1:
+ case 0x56B2:
+ case 0x56B3:
+ case 0x56BA:
+ case 0x56BB:
+ case 0x56BC:
+ case 0x56BD:
+ case 0x56C0:
+ case 0x56C1:
+ case 0x56C2:
+ case 0x56CF:
+ case 0x7D40:
+ case 0x7D45:
+ case 0x7D67:
+ case 0x7D41:
+ case 0x7D55:
+ case 0x7DD5:
+ case 0x7D51:
+ case 0x7DD1:
+ return xe_version::XE_HPG;
+
+ case 0x0201:
+ case 0x0202:
+ case 0x0203:
+ case 0x0204:
+ case 0x0205:
+ case 0x0206:
+ case 0x0207:
+ case 0x0208:
+ case 0x0209:
+ case 0x020A:
+ case 0x020B:
+ case 0x020C:
+ case 0x020D:
+ case 0x020E:
+ case 0x020F:
+ case 0x0210:
+ return xe_version::XE_HP;
+
+ case 0x0BD0:
+ case 0x0BD4:
+ case 0x0BD5:
+ case 0x0BD6:
+ case 0x0BD7:
+ case 0x0BD8:
+ case 0x0BD9:
+ case 0x0BDA:
+ case 0x0BDB:
+ case 0x0B69:
+ case 0x0B6E:
+ return xe_version::XE_HPC;
+
+ case 0x6420:
+ case 0x64A0:
+ case 0x64B0:
+
+ case 0xE202:
+ case 0xE20B:
+ case 0xE20C:
+ case 0xE20D:
+ case 0xE212:
+ return xe_version::XE2;
+
+ case 0xB080:
+ case 0xB081:
+ case 0xB082:
+ case 0xB083:
+ case 0xB08F:
+ case 0xB090:
+ case 0xB0A0:
+ case 0xB0B0:
+ return xe_version::XE3;
+
+ default:
+ return xe_version::INVALID;
+ }
+}
+
+} /* namespace intelgt */
diff --git a/gdb/arch/intelgt.h b/gdb/arch/intelgt.h
new file mode 100644
index 00000000000..1803810add9
--- /dev/null
+++ b/gdb/arch/intelgt.h
@@ -0,0 +1,185 @@
+/* Copyright (C) 2019-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_ARCH_INTELGT_H
+#define GDB_ARCH_INTELGT_H
+
+namespace intelgt {
+
+/* Various arch constants. */
+
+enum breakpoint_kind : int
+{
+ BP_INSTRUCTION = 1,
+};
+
+/* The length of a full and compact IntelGT instruction in bytes. */
+
+constexpr int MAX_INST_LENGTH = 16;
+constexpr int COMPACT_INST_LENGTH = 8;
+
+/* Feature names.
+
+ They correspond to register sets defined in zet_intel_gpu_debug.h. We
+ declare feature names in the order used in that header.
+
+ The SBA register set consists of a set of base registers in the order
+ defined in that header file.
+
+ Not all registers have DWARF numbers. See DWARF_REGSETS below for a
+ list of features that do. */
+constexpr const char *FEATURE_GRF = "org.gnu.gdb.intelgt.grf";
+constexpr const char *FEATURE_ADDR = "org.gnu.gdb.intelgt.addr";
+constexpr const char *FEATURE_FLAG = "org.gnu.gdb.intelgt.flag";
+constexpr const char *FEATURE_CE = "org.gnu.gdb.intelgt.ce";
+constexpr const char *FEATURE_SR = "org.gnu.gdb.intelgt.sr";
+constexpr const char *FEATURE_CR = "org.gnu.gdb.intelgt.cr";
+constexpr const char *FEATURE_TDR = "org.gnu.gdb.intelgt.tdr";
+constexpr const char *FEATURE_ACC = "org.gnu.gdb.intelgt.acc";
+constexpr const char *FEATURE_MME = "org.gnu.gdb.intelgt.mme";
+constexpr const char *FEATURE_SP = "org.gnu.gdb.intelgt.sp";
+constexpr const char *FEATURE_SBA = "org.gnu.gdb.intelgt.sba";
+constexpr const char *FEATURE_DBG = "org.gnu.gdb.intelgt.dbg";
+constexpr const char *FEATURE_FC = "org.gnu.gdb.intelgt.fc";
+constexpr const char *FEATURE_DEBUGGER = "org.gnu.gdb.intelgt.debugger";
+
+/* Register sets/groups needed for DWARF mapping. Used for
+ declaring static arrays for various mapping tables. */
+
+enum dwarf_regsets : int
+{
+ REGSET_SBA = 0,
+ REGSET_GRF,
+ REGSET_ADDR,
+ REGSET_FLAG,
+ REGSET_ACC,
+ REGSET_MME,
+ REGSET_COUNT
+};
+
+/* Map of dwarf_regset values to the target description
+ feature names. */
+
+constexpr const char *DWARF_REGSET_FEATURES[REGSET_COUNT] = {
+ FEATURE_SBA,
+ FEATURE_GRF,
+ FEATURE_ADDR,
+ FEATURE_FLAG,
+ FEATURE_ACC,
+ FEATURE_MME
+};
+
+/* The encoding for XE version enumerators follows this pattern, which is
+ aligned with the Intel Graphics (dis)Assembler (IGA) encoding. */
+
+constexpr int XE_VERSION (int major, int minor)
+{
+ return ((major << 24) | minor);
+}
+
+/* Supported GDB XE platforms. */
+
+enum class xe_version
+{
+ INVALID = 0,
+ XE_HP = XE_VERSION (1, 1),
+ XE_HPG = XE_VERSION (1, 2),
+ XE_HPC = XE_VERSION (1, 4),
+ XE2 = XE_VERSION (2, 0),
+ XE3 = XE_VERSION (3, 0),
+};
+
+/* Helper function to translate the device id to a device version. */
+
+xe_version get_xe_version (uint32_t device_id);
+
+/* Get the bit at POS in INST. */
+
+bool get_inst_bit (gdb::array_view<const gdb_byte> inst, int pos);
+
+/* Set the bit at POS in INST. */
+
+bool set_inst_bit (gdb::array_view<gdb_byte> inst, int pos);
+
+/* Clear the bit at POS in INST. */
+
+bool clear_inst_bit (gdb::array_view<gdb_byte> inst, int pos);
+
+static inline int
+breakpoint_bit_offset (gdb::array_view<const gdb_byte> inst,
+ uint32_t device_id)
+{
+ xe_version device_version = get_xe_version (device_id);
+ switch (device_version)
+ {
+ case intelgt::xe_version::XE_HP:
+ case intelgt::xe_version::XE_HPG:
+ case intelgt::xe_version::XE_HPC:
+ case intelgt::xe_version::XE2:
+ case intelgt::xe_version::XE3:
+ /* Check the CmptCtrl flag (bit 29). */
+ return (((inst[3] & 0x20) != 0) ? 7 : 30);
+
+ case intelgt::xe_version::INVALID:
+ break;
+ }
+ error (_("Unsupported device id 0x%" PRIx32), device_id);
+}
+
+static inline bool
+set_breakpoint (gdb::array_view<gdb_byte> inst, uint32_t device_id)
+{
+ return set_inst_bit (inst, breakpoint_bit_offset (inst, device_id));
+}
+
+static inline bool
+clear_breakpoint (gdb::array_view<gdb_byte> inst, uint32_t device_id)
+{
+ return clear_inst_bit (inst, breakpoint_bit_offset (inst, device_id));
+}
+
+static inline bool
+has_breakpoint (gdb::array_view<const gdb_byte> inst, uint32_t device_id)
+{
+ return get_inst_bit (inst, breakpoint_bit_offset (inst, device_id));
+}
+
+static inline unsigned int
+inst_length (gdb::array_view<const gdb_byte> inst, uint32_t device_id)
+{
+ xe_version device_version = get_xe_version (device_id);
+ switch (device_version)
+ {
+ case intelgt::xe_version::XE_HP:
+ case intelgt::xe_version::XE_HPG:
+ case intelgt::xe_version::XE_HPC:
+ case intelgt::xe_version::XE2:
+ case intelgt::xe_version::XE3:
+ /* Check the CmptCtrl flag (bit 29). */
+ return (((inst[3] & 0x20) != 0)
+ ? COMPACT_INST_LENGTH
+ : MAX_INST_LENGTH);
+
+ case intelgt::xe_version::INVALID:
+ break;
+ }
+ error (_("Unsupported device id 0x%" PRIx32), device_id);
+}
+
+} /* namespace intelgt */
+
+#endif /* GDB_ARCH_INTELGT_H */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 08/44] gdb: add a new extract_integer variant that takes two array_views
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (6 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 07/44] gdb, arch, intelgt: add intelgt arch definitions Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 09/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Markus Metzger
` (35 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Add a new variant of extract integer that takes both the source and
the destination values as array_view parameter. The existing
templated extract_integer then becomes a wrapper around the new
variant. The new variant gives flexibility and ease of use in cases
where the destination buffer and length are runtime values.
---
| 53 ++++++++++++++++++++++++-------------
| 8 ++++++
2 files changed, 42 insertions(+), 19 deletions(-)
--git a/gdb/extract-store-integer.c b/gdb/extract-store-integer.c
index 67e4534a27d..41116d6cd3c 100644
--- a/gdb/extract-store-integer.c
+++ b/gdb/extract-store-integer.c
@@ -20,26 +20,27 @@
#include "gdbarch.h"
#include "gdbsupport/selftest.h"
-template<typename T, typename>
-T
-extract_integer (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order)
-{
- typename std::make_unsigned<T>::type retval = 0;
+/* See extract-store-integer.h. */
- /* It is ok if BUF is wider than T, but only if the value is
+void
+extract_integer (gdb::array_view<gdb_byte> dst,
+ gdb::array_view<const gdb_byte> buf,
+ enum bfd_endian byte_order,
+ bool is_signed)
+{
+ /* It is ok if BUF is wider than DST, but only if the value is
representable. */
bool bad_repr = false;
- if (buf.size () > (int) sizeof (T))
+ if (buf.size () > dst.size ())
{
- const size_t end = buf.size () - sizeof (T);
+ const size_t end = buf.size () - dst.size ();
if (byte_order == BFD_ENDIAN_BIG)
{
for (size_t i = 0; i < end; ++i)
{
/* High bytes == 0 are always ok, and high bytes == 0xff
are ok when the type is signed. */
- if ((buf[i] == 0
- || (std::is_signed<T>::value && buf[i] == 0xff))
+ if ((buf[i] == 0 || (is_signed && buf[i] == 0xff))
/* All the high bytes must be the same, no
alternating 0 and 0xff. */
&& (i == 0 || buf[i - 1] == buf[i]))
@@ -61,8 +62,7 @@ extract_integer (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order
{
/* High bytes == 0 are always ok, and high bytes == 0xff
are ok when the type is signed. */
- if ((buf[i] == 0
- || (std::is_signed<T>::value && buf[i] == 0xff))
+ if ((buf[i] == 0 || (is_signed && buf[i] == 0xff))
/* All the high bytes must be the same, no
alternating 0 and 0xff. */
&& (i == bufsz || buf[i] == buf[i + 1]))
@@ -81,7 +81,7 @@ extract_integer (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order
if (bad_repr)
error (_("Value cannot be represented as integer of %d bytes."),
- (int) sizeof (T));
+ (int) dst.size ());
/* Start at the most significant end of the integer, and work towards
the least significant. */
@@ -89,28 +89,43 @@ extract_integer (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order
{
size_t i = 0;
- if (std::is_signed<T>::value)
+ if (is_signed)
{
/* Do the sign extension once at the start. */
- retval = ((LONGEST) buf[i] ^ 0x80) - 0x80;
+ dst[buf.size () - 1] = ((LONGEST) buf[i] ^ 0x80) - 0x80;
++i;
}
for (; i < buf.size (); ++i)
- retval = (retval << 8) | buf[i];
+ dst[buf.size () - 1 - i] = buf[i];
}
else
{
ssize_t i = buf.size () - 1;
- if (std::is_signed<T>::value)
+ if (is_signed)
{
/* Do the sign extension once at the start. */
- retval = ((LONGEST) buf[i] ^ 0x80) - 0x80;
+ dst[i] = ((LONGEST) buf[i] ^ 0x80) - 0x80;
--i;
}
for (; i >= 0; --i)
- retval = (retval << 8) | buf[i];
+ dst[i] = buf[i];
}
+
+ /* Extend the upper bytes when DST is wider than BUF. */
+ if (is_signed && (dst[buf.size () - 1] & 0x80) != 0)
+ for (size_t i = buf.size (); i < dst.size (); ++i)
+ dst[i] = 0xff;
+}
+
+template<typename T, typename>
+T
+extract_integer (gdb::array_view<const gdb_byte> buf, enum bfd_endian byte_order)
+{
+ typename std::make_unsigned<T>::type retval = 0;
+ gdb::array_view<gdb_byte> dst ((gdb_byte *) &retval, sizeof (T));
+ extract_integer (dst, buf, byte_order, std::is_signed<T>::value);
+
return retval;
}
--git a/gdb/extract-store-integer.h b/gdb/extract-store-integer.h
index b6f734d23a7..5d9d95e7036 100644
--- a/gdb/extract-store-integer.h
+++ b/gdb/extract-store-integer.h
@@ -20,6 +20,14 @@
#include <type_traits>
+/* Convert the target-format buffer SRC into the host-format buffer
+ DST, according to BYTE_ORDER and taking IS_SIGNED into account. */
+
+void extract_integer (gdb::array_view<gdb_byte> dst,
+ gdb::array_view<const gdb_byte> src,
+ enum bfd_endian byte_order,
+ bool is_signed);
+
template<typename T, typename = std::is_integral<T>>
T extract_integer (gdb::array_view<const gdb_byte>, enum bfd_endian byte_order);
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 09/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (7 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 08/44] gdb: add a new extract_integer variant that takes two array_views Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 10/44] gdb, intelgt: add disassemble feature " Markus Metzger
` (34 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Cc: Tankut Baris Aktemur, Natalia Saiapova, Mihails Strasuns,
Mohamed Bouhaouel, Simon Marchi
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Introduce gdb/intelgt-tdep.c for the Intel GT target. The target is
defined in a future patch as a gdbserver low target implementation.
Unlike, for example, X86-64, IntelGT does not have a dedicated
breakpoint instruction. Instead, it has a breakpoint bit in each
instruction. Hence, any instruction can be used as a breakpoint
instruction.
It further supports ignoring breakpoints for stepping over or resuming
from a breakpoint. This only works, of course, if we use breakpoint
bits inside the original instruction rather than replacing it with a
fixed breakpoint instruction.
Add gdbarch methods for inserting and removing memory breakpoints by
setting and clearing those breakpoint bits.
We define one pseudo-register, $framedesc, that provides a type alias
for the frame descriptor register in GRF, representing it as a struct.
The value of the $pc register is the $ip register, which is provided
in $cr0.2, offset by $isabase.
A translation function to map the device_id to a gen_version is
provided. This will be useful in various places, in particular to
generate the correct disassembly.
Co-authored-by: Markus Metzger <markus.t.metzger@intel.com>
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
Co-authored-by: Mihails Strasuns <mihails.strasuns@intel.com>
Co-authored-by: Mohamed Bouhaouel <mohamed.bouhaouel@intel.com>
Reviewed-By: Simon Marchi <simon.marchi@efficios.com>
---
gdb/Makefile.in | 1 +
gdb/configure.tgt | 5 +
gdb/intelgt-tdep.c | 889 ++++++++++++++++++++++++++++++++++++++++++++
gdb/selftest-arch.c | 3 +-
4 files changed, 897 insertions(+), 1 deletion(-)
create mode 100644 gdb/intelgt-tdep.c
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4794e01246e..2993e6b55c8 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -755,6 +755,7 @@ ALL_64_TARGET_OBS = \
ia64-linux-tdep.o \
ia64-tdep.o \
ia64-vms-tdep.o \
+ intelgt-tdep.o \
loongarch-linux-tdep.o \
loongarch-tdep.o \
mips-fbsd-tdep.o \
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 7313b31e42a..5fb492b7acd 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -362,6 +362,11 @@ ia64-*-*vms*)
gdb_target_obs="ia64-vms-tdep.o"
;;
+intelgt-*-elf)
+ # Target: Intel(R) Graphics Technology graphics processor
+ gdb_target_obs="intelgt-tdep.o arch/intelgt.o"
+ ;;
+
iq2000-*-*)
gdb_target_obs="iq2000-tdep.o"
;;
diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
new file mode 100644
index 00000000000..b4c4f3fa115
--- /dev/null
+++ b/gdb/intelgt-tdep.c
@@ -0,0 +1,889 @@
+/* Target-dependent code for the Intel(R) Graphics Technology architecture.
+
+ Copyright (C) 2019-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "arch-utils.h"
+#include "arch/intelgt.h"
+#include "cli/cli-cmds.h"
+#include "dwarf2/frame.h"
+#include "extract-store-integer.h"
+#include "frame-unwind.h"
+#include "gdbtypes.h"
+#include "target.h"
+#include "target-descriptions.h"
+#include "value.h"
+#include "gdbthread.h"
+#include "inferior.h"
+#include "user-regs.h"
+#include <algorithm>
+
+/* Global debug flag. */
+static bool intelgt_debug = false;
+
+/* Print an "intelgt" debug statement. */
+
+#define intelgt_debug_printf(fmt, ...) \
+ debug_prefixed_printf_cond (intelgt_debug, "intelgt", fmt, ##__VA_ARGS__)
+
+/* Regnum pair describing the assigned regnum range for a single
+ regset. START is inclusive, END is exclusive. '-1' means
+ not-yet-set. */
+
+struct regnum_range
+{
+ int start = -1;
+ int end = -1;
+};
+
+/* Data specific for this architecture. */
+
+struct intelgt_gdbarch_tdep : gdbarch_tdep_base
+{
+ intelgt_gdbarch_tdep () = default;
+
+ /* Return regnum where frame descriptors are stored. */
+ int framedesc_base_regnum ()
+ {
+ /* For EM_INTELGT frame descriptors are stored at MAX_GRF - 1. */
+ gdb_assert (regset_ranges[intelgt::REGSET_GRF].end > 1);
+ return regset_ranges[intelgt::REGSET_GRF].end - 1;
+ }
+
+ /* $r0 GRF register number. */
+ int r0_regnum = -1;
+
+ /* $ce register number in the regcache. */
+ int ce_regnum = -1;
+
+ /* Register number for the GRF containing function return value. */
+ int retval_regnum = -1;
+
+ /* Register number for the control register. */
+ int cr0_regnum = -1;
+
+ /* Register number for the state register. */
+ int sr0_regnum = -1;
+
+ /* Register number for the instruction base virtual register. */
+ int isabase_regnum = -1;
+
+ /* Register number for the general state base SBA register. */
+ int genstbase_regnum = -1;
+
+ /* Register number for the DBG0 register. */
+ int dbg0_regnum = -1;
+
+ /* Assigned regnum ranges for DWARF regsets. */
+ regnum_range regset_ranges[intelgt::REGSET_COUNT];
+
+ /* Enabled pseudo-registers for the current target description. */
+ std::vector<std::string> enabled_pseudo_regs;
+
+ /* The framedesc user register's type. */
+ type *framedesc_type = nullptr;
+};
+
+/* Per-inferior cached data for the Intelgt target. */
+
+struct intelgt_inferior_data
+{
+ /* Device target id. */
+ uint32_t device_id = 0u;
+};
+
+static const registry<inferior>::key<intelgt_inferior_data>
+ intelgt_inferior_data_handle;
+
+/* Fetch the per-inferior data. */
+
+static intelgt_inferior_data *
+get_intelgt_inferior_data (inferior *inf)
+{
+ return &intelgt_inferior_data_handle.try_emplace (inf);
+}
+
+/* Helper function to return the device id using GDBARCH. */
+
+static uint32_t
+get_device_id (gdbarch *gdbarch)
+{
+ const target_desc *tdesc = gdbarch_target_desc (gdbarch);
+ const tdesc_device *device_info = tdesc_device_info (tdesc);
+ if (!device_info->target_id.has_value ())
+ error (_("A target id for the device is required."));
+
+ return *device_info->target_id;
+}
+
+/* Helper function to return the device id using the inferior. */
+
+static uint32_t
+get_device_id (inferior *inferior)
+{
+ intelgt_inferior_data *inf_data = get_intelgt_inferior_data (inferior);
+ if (inf_data->device_id == 0u)
+ inf_data->device_id = get_device_id (inferior->arch ());
+
+ return inf_data->device_id;
+}
+
+/* Read part of REGNUM at OFFSET into BUFFER. The length of data to
+ read is SIZE. Consider using this helper function when reading
+ subregisters of CR0, SR0, and R0. */
+
+static void
+intelgt_read_register_part (readable_regcache *regcache, int regnum,
+ size_t offset,
+ gdb::array_view<gdb_byte> buffer,
+ const char *error_message)
+{
+ if (regnum == -1)
+ error (_("%s Unexpected reg num '-1'."), error_message);
+
+ gdbarch *arch = regcache->arch ();
+ const char *regname = gdbarch_register_name (arch, regnum);
+ int regsize = register_size (arch, regnum);
+
+ if (offset + buffer.size () > regsize)
+ error (_("%s %s[%zu:%zu] is outside the range of %s[%d:0]."),
+ error_message, regname, (offset + buffer.size () - 1), offset,
+ regname, (regsize - 1));
+
+ gdb_byte *bytes = (gdb_byte *) alloca (buffer.size ());
+ gdb::array_view<gdb_byte> view (bytes, buffer.size ());
+
+ register_status reg_status
+ = regcache->cooked_read_part (regnum, offset, view);
+
+ if (reg_status == REG_UNAVAILABLE)
+ throw_error (NOT_AVAILABLE_ERROR,
+ _("%s Register %s (%d) is not available."),
+ error_message, regname, regnum);
+
+ if (reg_status == REG_UNKNOWN)
+ error (_("%s Register %s (%d) is unknown."), error_message,
+ regname, regnum);
+
+ extract_integer (buffer, view, gdbarch_byte_order (arch), false);
+}
+
+/* Write BUFFER into part of REGNUM at OFFSET. The length of data to
+ read is SIZE. */
+
+static void
+intelgt_write_register_part (regcache *regcache, int regnum,
+ size_t offset,
+ gdb::array_view<const gdb_byte> buffer,
+ const char *error_message)
+{
+ if (regnum == -1)
+ error (_("%s Unexpected reg num '-1'."), error_message);
+
+ gdbarch *arch = regcache->arch ();
+ const char *regname = gdbarch_register_name (arch, regnum);
+ int regsize = register_size (arch, regnum);
+
+ if (offset + buffer.size () > regsize)
+ error (_("%s[%ld:%ld] is outside the range of %s[%d:0]."),
+ regname, (offset + buffer.size () - 1), offset, regname,
+ (regsize - 1));
+
+ gdb_byte *bytes = (gdb_byte *) alloca (buffer.size ());
+ gdb::array_view<gdb_byte> view (bytes, buffer.size ());
+
+ extract_integer (view, buffer, gdbarch_byte_order (arch), false);
+ regcache->cooked_write_part (regnum, offset, view);
+}
+
+/* Utility function to look up the pseudo-register number by name. Exact
+ amount of pseudo-registers may differ and thus fixed constants can't be
+ used for this. */
+
+static int
+intelgt_pseudo_register_num (gdbarch *arch, const char *name)
+{
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+ auto iter = std::find (data->enabled_pseudo_regs.begin (),
+ data->enabled_pseudo_regs.end (), name);
+ gdb_assert (iter != data->enabled_pseudo_regs.end ());
+ return gdbarch_num_regs (arch) + (iter - data->enabled_pseudo_regs.begin ());
+}
+
+/* Convert a DWARF register number to a GDB register number. This
+ function requires the register listing in the target description to
+ be in the same order in each regset as the intended DWARF numbering
+ order. Currently this always holds true when gdbserver generates
+ the target description. */
+
+static int
+intelgt_dwarf_reg_to_regnum (gdbarch *gdbarch, int num)
+{
+ constexpr int ip = 0;
+ constexpr int ce = 1;
+
+ /* Register sets follow this format: [START, END), where START is
+ inclusive and END is exclusive. */
+ constexpr regnum_range dwarf_nums[intelgt::REGSET_COUNT] = {
+ [intelgt::REGSET_SBA] = { 5, 12 },
+ [intelgt::REGSET_GRF] = { 16, 272 },
+ [intelgt::REGSET_ADDR] = { 272, 288 },
+ [intelgt::REGSET_FLAG] = { 288, 304 },
+ [intelgt::REGSET_ACC] = { 304, 320 },
+ [intelgt::REGSET_MME] = { 320, 336 },
+ };
+
+ /* Number of SBA registers. */
+ constexpr size_t sba_dwarf_len = dwarf_nums[intelgt::REGSET_SBA].end
+ - dwarf_nums[intelgt::REGSET_SBA].start;
+
+ /* Map the DWARF register numbers of SBA registers to their names.
+ Base number is dwarf_nums[intelgt::REGSET_SBA].start. */
+ constexpr const char *sba_dwarf_reg_order[sba_dwarf_len] {
+ "btbase",
+ "scrbase0",
+ "genstbase",
+ "sustbase",
+ "blsustbase",
+ "blsastbase",
+ "scrbase1"
+ };
+
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+
+ if (num == ip)
+ return intelgt_pseudo_register_num (gdbarch, "ip");
+ if (num == ce)
+ return data->ce_regnum;
+
+ for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset)
+ if (num >= dwarf_nums[regset].start && num < dwarf_nums[regset].end)
+ {
+ if (regset == intelgt::REGSET_SBA)
+ {
+ /* For SBA registers we first find out the name of the
+ register out of DWARF register number and then find the
+ register number corresponding to the name. */
+ int sba_num = num - dwarf_nums[intelgt::REGSET_SBA].start;
+ std::string name (sba_dwarf_reg_order[sba_num]);
+
+ return user_reg_map_name_to_regnum (gdbarch, name);
+ }
+ else
+ {
+ int candidate = (data->regset_ranges[regset].start + num
+ - dwarf_nums[regset].start);
+
+ if (candidate < data->regset_ranges[regset].end)
+ return candidate;
+ }
+ }
+
+ return -1;
+}
+
+/* Return the PC of the first real instruction. */
+
+static CORE_ADDR
+intelgt_skip_prologue (gdbarch *gdbarch, CORE_ADDR start_pc)
+{
+ intelgt_debug_printf ("start_pc: %s", paddress (gdbarch, start_pc));
+ CORE_ADDR func_addr;
+
+ if (find_pc_partial_function (start_pc, nullptr, &func_addr, nullptr))
+ {
+ CORE_ADDR post_prologue_pc
+ = skip_prologue_using_sal (gdbarch, func_addr);
+
+ intelgt_debug_printf ("post prologue pc: %s",
+ paddress (gdbarch, post_prologue_pc));
+
+ if (post_prologue_pc != 0)
+ return post_prologue_pc;
+ }
+
+ /* Could not find the end of prologue using SAL. */
+ return start_pc;
+}
+
+/* Implementation of gdbarch's return_value method. */
+
+static enum return_value_convention
+intelgt_return_value_as_value (gdbarch *gdbarch, value *function,
+ type *valtype, regcache *regcache,
+ value **read_value, const gdb_byte *writebuf)
+{
+ gdb_assert_not_reached ("intelgt_return_value_as_value is to be "
+ "implemented later.");
+}
+
+/* Architecture-specific register state initialization. */
+
+static void
+intelgt_init_reg (gdbarch *gdbarch, int regnum, dwarf2_frame_state_reg *reg,
+ const frame_info_ptr &this_frame)
+{
+ int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip");
+
+ if (regnum == ip_regnum)
+ reg->how = DWARF2_FRAME_REG_RA;
+ else if (regnum == gdbarch_sp_regnum (gdbarch))
+ reg->how = DWARF2_FRAME_REG_CFA;
+}
+
+/* A helper function that returns the value of the ISABASE register. */
+
+static CORE_ADDR
+intelgt_get_isabase (readable_regcache *regcache)
+{
+ gdbarch *gdbarch = regcache->arch ();
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+ gdb_assert (data->isabase_regnum != -1);
+
+ uint64_t isabase = 0;
+ if (regcache->cooked_read (data->isabase_regnum, &isabase) != REG_VALID)
+ throw_error (NOT_AVAILABLE_ERROR,
+ _("Register %d (isabase) is not available"),
+ data->isabase_regnum);
+ return isabase;
+}
+
+/* The 'unwind_pc' gdbarch method. */
+
+static CORE_ADDR
+intelgt_unwind_pc (gdbarch *gdbarch, const frame_info_ptr &next_frame)
+{
+ /* Use ip register here, as IGC uses 32bit values (pc is 64bit). */
+ int ip_regnum = intelgt_pseudo_register_num (gdbarch, "ip");
+ CORE_ADDR prev_ip = frame_unwind_register_unsigned (next_frame,
+ ip_regnum);
+ intelgt_debug_printf ("prev_ip: %s", paddress (gdbarch, prev_ip));
+
+ /* Program counter is $ip + $isabase. */
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+ gdb_assert (data->isabase_regnum != -1);
+ CORE_ADDR isabase
+ = frame_unwind_register_unsigned (next_frame,
+ data->isabase_regnum);
+
+ return isabase + prev_ip;
+}
+
+/* Frame unwinding. */
+
+static void
+intelgt_frame_this_id (const frame_info_ptr &this_frame,
+ void **this_prologue_cache, frame_id *this_id)
+{
+ /* FIXME: Assembly-level unwinding for intelgt is not available at
+ the moment. Stop at the first frame. */
+ *this_id = outer_frame_id;
+}
+
+/* This is a "bare minimum" unwinder that avoids crashing GDB
+ if debugging a program without debug info. */
+
+static const frame_unwind_legacy intelgt_frame_unwind (
+ "intelgt prologue",
+ NORMAL_FRAME, /* type */
+ FRAME_UNWIND_ARCH, /* class */
+ default_frame_unwind_stop_reason, /* stop_reason */
+ intelgt_frame_this_id, /* this_id */
+ nullptr, /* prev_register */
+ nullptr, /* unwind_data */
+ default_frame_sniffer /* sniffer */
+);
+
+/* The memory_insert_breakpoint gdbarch method. */
+
+static int
+intelgt_memory_insert_breakpoint (gdbarch *gdbarch, bp_target_info *bp)
+{
+ intelgt_debug_printf ("req ip: %s", paddress (gdbarch,
+ bp->reqstd_address));
+
+ /* Ensure that we have enough space in the breakpoint. */
+ static_assert (intelgt::MAX_INST_LENGTH <= BREAKPOINT_MAX);
+
+ gdb_byte inst[intelgt::MAX_INST_LENGTH];
+ int err = target_read_memory (bp->reqstd_address, inst,
+ intelgt::MAX_INST_LENGTH);
+ if (err != 0)
+ {
+ /* We could fall back to reading a full and then a compacted
+ instruction but we should rather allow short reads than
+ having the caller try smaller and smaller sizes. */
+ intelgt_debug_printf ("Failed to read memory at %s (%s).",
+ paddress (gdbarch, bp->reqstd_address),
+ strerror (err));
+ return err;
+ }
+
+ bp->placed_address = bp->reqstd_address;
+ uint32_t device_id = get_device_id (current_inferior ());
+ bp->shadow_len = intelgt::inst_length (inst, device_id);
+
+ /* Make a copy before we set the breakpoint so we can restore the
+ original instruction when removing the breakpoint again.
+
+ This isn't strictly necessary but it saves one target access. */
+ memcpy (bp->shadow_contents, inst, bp->shadow_len);
+
+ const bool already = intelgt::set_breakpoint (inst, device_id);
+ if (already)
+ {
+ /* Warn if the breakpoint bit is already set.
+
+ There is still a breakpoint, probably hard-coded, and it should
+ still trigger and we're still able to step over it. It's just
+ not our breakpoint. */
+ warning (_("Using permanent breakpoint at %s."),
+ paddress (gdbarch, bp->placed_address));
+
+ /* There's no need to write the unmodified instruction back. */
+ return 0;
+ }
+
+ err = target_write_raw_memory (bp->placed_address, inst, bp->shadow_len);
+ if (err != 0)
+ intelgt_debug_printf ("Failed to insert breakpoint at %s (%s).",
+ paddress (gdbarch, bp->placed_address),
+ strerror (err));
+
+ return err;
+}
+
+/* The memory_remove_breakpoint gdbarch method. */
+
+static int
+intelgt_memory_remove_breakpoint (gdbarch *gdbarch, struct bp_target_info *bp)
+{
+ intelgt_debug_printf ("req ip: %s, placed ip: %s",
+ paddress (gdbarch, bp->reqstd_address),
+ paddress (gdbarch, bp->placed_address));
+
+ uint32_t device_id = get_device_id (current_inferior ());
+ if (intelgt::has_breakpoint (bp->shadow_contents, device_id))
+ warning (_("Re-inserting permanent breakpoint at %s."),
+ paddress (gdbarch, bp->placed_address));
+
+ int err = target_write_raw_memory (bp->placed_address, bp->shadow_contents,
+ bp->shadow_len);
+ if (err != 0)
+ intelgt_debug_printf ("Failed to remove breakpoint at %s (%s).",
+ paddress (gdbarch, bp->placed_address),
+ strerror (err));
+
+ return err;
+}
+
+/* The program_breakpoint_here_p gdbarch method. */
+
+static bool
+intelgt_program_breakpoint_here_p (gdbarch *gdbarch, CORE_ADDR pc)
+{
+ intelgt_debug_printf ("pc: %s", paddress (gdbarch, pc));
+
+ gdb_byte inst[intelgt::MAX_INST_LENGTH];
+ int err = target_read_memory (pc, inst, intelgt::MAX_INST_LENGTH);
+ if (err != 0)
+ {
+ /* We could fall back to reading a full and then a compacted
+ instruction but we should rather allow short reads than
+ having the caller try smaller and smaller sizes. */
+ intelgt_debug_printf ("Failed to read memory at %s (%s).",
+ paddress (gdbarch, pc), strerror (err));
+ return err;
+ }
+
+ uint32_t device_id = get_device_id (current_inferior ());
+ const bool is_bkpt = intelgt::has_breakpoint (inst, device_id);
+
+ intelgt_debug_printf ("%sbreakpoint found.", is_bkpt ? "" : "no ");
+
+ return is_bkpt;
+}
+
+/* The 'breakpoint_kind_from_pc' gdbarch method.
+ This is a required gdbarch function. */
+
+static int
+intelgt_breakpoint_kind_from_pc (gdbarch *gdbarch, CORE_ADDR *pcptr)
+{
+ intelgt_debug_printf ("*pcptr: %s", paddress (gdbarch, *pcptr));
+
+ return intelgt::BP_INSTRUCTION;
+}
+
+/* The 'sw_breakpoint_from_kind' gdbarch method. */
+
+static const gdb_byte *
+intelgt_sw_breakpoint_from_kind (gdbarch *gdbarch, int kind, int *size)
+{
+ intelgt_debug_printf ("kind: %d", kind);
+
+ /* We do not support breakpoint instructions.
+
+ We use breakpoint bits in instructions, instead. See
+ intelgt_memory_insert_breakpoint. */
+ *size = 0;
+ return nullptr;
+}
+
+/* Print one instruction from MEMADDR on INFO->STREAM. */
+
+static int
+intelgt_print_insn (bfd_vma memaddr, struct disassemble_info *info)
+{
+ /* Disassembler is to be added in a later patch. */
+ return -1;
+}
+
+/* The "read_pc" gdbarch method. */
+
+static CORE_ADDR
+intelgt_read_pc (readable_regcache *regcache)
+{
+ gdbarch *arch = regcache->arch ();
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ /* Instruction pointer is stored in CR0.2. */
+ uint32_t ip;
+ intelgt_read_register_part (regcache, data->cr0_regnum,
+ sizeof (uint32_t) * 2,
+ gdb::make_array_view ((gdb_byte *) &ip,
+ sizeof (uint32_t)),
+ _("Cannot compute PC."));
+
+ /* Program counter is $ip + $isabase. */
+ CORE_ADDR isabase = intelgt_get_isabase (regcache);
+ return isabase + ip;
+}
+
+/* The "write_pc" gdbarch method. */
+
+static void
+intelgt_write_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+ gdbarch *arch = regcache->arch ();
+ /* Program counter is $ip + $isabase, can only modify $ip. Need
+ to ensure that the new value fits within $ip modification range
+ and propagate the write accordingly. */
+ CORE_ADDR isabase = intelgt_get_isabase (regcache);
+ if (pc < isabase || pc > isabase + UINT32_MAX)
+ error (_("Can't update $pc to value %s, out of range"),
+ paddress (arch, pc));
+
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ /* Instruction pointer is stored in CR0.2. */
+ uint32_t ip = pc - isabase;
+ intelgt_write_register_part (regcache, data->cr0_regnum,
+ sizeof (uint32_t) * 2,
+ gdb::make_array_view ((gdb_byte *) &ip,
+ sizeof (uint32_t)),
+ _("Cannot write IP into CR0."));
+}
+
+/* Return the name of pseudo-register REGNUM. */
+
+static const char *
+intelgt_pseudo_register_name (gdbarch *arch, int regnum)
+{
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+ int base_num = gdbarch_num_regs (arch);
+ if (regnum < base_num
+ || regnum >= base_num + data->enabled_pseudo_regs.size ())
+ error (_("Invalid pseudo-register regnum %d"), regnum);
+ return data->enabled_pseudo_regs[regnum - base_num].c_str ();
+}
+
+/* Return the GDB type object for the "standard" data type of data in
+ pseudo-register REGNUM. */
+
+static type *
+intelgt_pseudo_register_type (gdbarch *arch, int regnum)
+{
+ const char *name = intelgt_pseudo_register_name (arch, regnum);
+ const struct builtin_type *bt = builtin_type (arch);
+
+ if (strcmp (name, "ip") == 0)
+ return bt->builtin_uint32;
+
+ return nullptr;
+}
+
+/* Read the value of a pseudo-register REGNUM. */
+
+static struct value *
+intelgt_pseudo_register_read_value (gdbarch *arch,
+ const frame_info_ptr &next_frame,
+ int pseudo_regnum)
+{
+ const char *name = intelgt_pseudo_register_name (arch, pseudo_regnum);
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ if (strcmp (name, "ip") == 0)
+ {
+ int regsize = register_size (arch, pseudo_regnum);
+ /* Instruction pointer is stored in CR0.2. */
+ gdb_assert (data->cr0_regnum != -1);
+ /* CR0 elements are 4 byte wide. */
+ gdb_assert (regsize + 8 <= register_size (arch, data->cr0_regnum));
+
+ return pseudo_from_raw_part (next_frame, pseudo_regnum,
+ data->cr0_regnum, 8);
+ }
+
+ return nullptr;
+}
+
+/* Write the value of a pseudo-register REGNUM. */
+
+static void
+intelgt_pseudo_register_write (gdbarch *arch,
+ const frame_info_ptr &next_frame,
+ int pseudo_regnum,
+ gdb::array_view<const gdb_byte> buf)
+{
+ const char *name = intelgt_pseudo_register_name (arch, pseudo_regnum);
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ if (strcmp (name, "ip") == 0)
+ {
+ /* Instruction pointer is stored in CR0.2. */
+ gdb_assert (data->cr0_regnum != -1);
+ int cr0_size = register_size (arch, data->cr0_regnum);
+
+ /* CR0 elements are 4 byte wide. */
+ int reg_size = register_size (arch, pseudo_regnum);
+ gdb_assert (reg_size + 8 <= cr0_size);
+ pseudo_to_raw_part (next_frame, buf, data->cr0_regnum, 8);
+ }
+ else
+ error ("Pseudo-register %s is read-only", name);
+}
+
+/* Called by tdesc_use_registers each time a new regnum
+ is assigned. Used to track down assigned numbers for
+ any important regnums. */
+
+static int
+intelgt_unknown_register_cb (gdbarch *arch, tdesc_feature *feature,
+ const char *reg_name, int possible_regnum)
+{
+ intelgt_gdbarch_tdep *data = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+
+ /* First, check if this a beginning of a not yet tracked regset
+ assignment. */
+
+ for (int regset = 0; regset < intelgt::REGSET_COUNT; ++regset)
+ if (data->regset_ranges[regset].start == -1
+ && feature->name == intelgt::DWARF_REGSET_FEATURES[regset])
+ {
+ data->regset_ranges[regset].start = possible_regnum;
+ data->regset_ranges[regset].end
+ = feature->registers.size () + possible_regnum;
+ break;
+ }
+
+ /* Second, check if it is any specific individual register that
+ needs to be tracked. */
+
+ if (strcmp ("r0", reg_name) == 0)
+ data->r0_regnum = possible_regnum;
+ else if (strcmp ("r26", reg_name) == 0)
+ data->retval_regnum = possible_regnum;
+ else if (strcmp ("cr0", reg_name) == 0)
+ data->cr0_regnum = possible_regnum;
+ else if (strcmp ("sr0", reg_name) == 0)
+ data->sr0_regnum = possible_regnum;
+ else if (strcmp ("isabase", reg_name) == 0)
+ data->isabase_regnum = possible_regnum;
+ else if (strcmp ("ce", reg_name) == 0)
+ data->ce_regnum = possible_regnum;
+ else if (strcmp ("genstbase", reg_name) == 0)
+ data->genstbase_regnum = possible_regnum;
+ else if (strcmp ("dbg0", reg_name) == 0)
+ data->dbg0_regnum = possible_regnum;
+
+ return possible_regnum;
+}
+
+/* Read the 'framedesc' user register, a structured alias
+ of the actual GRF. */
+
+static value *
+intelgt_value_of_framedesc_user_reg (const frame_info_ptr &frame,
+ const void *baton)
+{
+ gdbarch *arch = get_frame_arch (frame);
+ intelgt_gdbarch_tdep *tdep = gdbarch_tdep<intelgt_gdbarch_tdep> (arch);
+ type *framedesc_type = tdep->framedesc_type;
+ int framedesc_size = framedesc_type->length ();
+ int grf_num = tdep->framedesc_base_regnum ();
+ int grf_size = register_size (arch, grf_num);
+ gdb_assert (framedesc_size <= grf_size);
+
+ value *grf
+ = value_of_register (grf_num, get_next_frame_sentinel_okay (frame));
+ value *result = value::allocate (framedesc_type);
+ grf->contents_copy (result, 0, 0, framedesc_size);
+ return result;
+}
+
+/* Architecture initialization. */
+
+static gdbarch *
+intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
+{
+ /* If there is already a candidate, use it. */
+ arches = gdbarch_list_lookup_by_info (arches, &info);
+ if (arches != nullptr)
+ return arches->gdbarch;
+
+ const target_desc *tdesc = info.target_desc;
+ gdbarch_up gdbarch_u
+ (gdbarch_alloc (&info,
+ gdbarch_tdep_up (new intelgt_gdbarch_tdep)));
+ gdbarch *gdbarch = gdbarch_u.get ();
+ intelgt_gdbarch_tdep *data
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+
+ set_gdbarch_ptr_bit (gdbarch, 64);
+ set_gdbarch_addr_bit (gdbarch, 64);
+ set_gdbarch_long_bit (gdbarch, 64);
+
+ /* Initialize register info. */
+ set_gdbarch_num_regs (gdbarch, 0);
+ set_gdbarch_register_name (gdbarch, tdesc_register_name);
+
+ if (tdesc_has_registers (tdesc))
+ {
+ /* First assign register numbers to all registers. The
+ callback function will record any relevant metadata
+ about it in the intelgt_gdbarch_tdep instance to be
+ inspected after. */
+
+ tdesc_use_registers (gdbarch, tdesc, tdesc_data_alloc (),
+ intelgt_unknown_register_cb);
+
+ /* Now check the collected metadata to ensure that all
+ mandatory pieces are in place. */
+
+ if (data->ce_regnum == -1)
+ error (_("Debugging requires $ce to be provided by the target"));
+ if (data->retval_regnum == -1)
+ error (_("Debugging requires return value register to be provided "
+ "by the target"));
+ if (data->cr0_regnum == -1)
+ error (_("Debugging requires control register to be provided by "
+ "the target"));
+ if (data->sr0_regnum == -1)
+ error (_("Debugging requires state register to be provided by "
+ "the target"));
+
+ /* Add the 'framedesc' register as a structured user register. */
+ user_reg_add (gdbarch, "framedesc",
+ intelgt_value_of_framedesc_user_reg, nullptr);
+
+ /* Create the framedesc user register's type. */
+ const struct builtin_type *bt = builtin_type (gdbarch);
+ type *framedesc = arch_composite_type (gdbarch, "framedesc",
+ TYPE_CODE_STRUCT);
+ append_composite_type_field (framedesc, "return_ip", bt->builtin_int32);
+ append_composite_type_field (framedesc, "return_callmask",
+ bt->builtin_int32);
+ append_composite_type_field (framedesc, "be_sp", bt->builtin_int32);
+ append_composite_type_field (framedesc, "be_fp", bt->builtin_int32);
+ append_composite_type_field (framedesc, "fe_fp", bt->builtin_int64);
+ append_composite_type_field (framedesc, "fe_sp", bt->builtin_int64);
+ data->framedesc_type = framedesc;
+
+ /* Unconditionally enabled pseudo-registers: */
+ data->enabled_pseudo_regs.push_back ("ip");
+
+ set_gdbarch_num_pseudo_regs (gdbarch, data->enabled_pseudo_regs.size ());
+ set_gdbarch_pseudo_register_read_value
+ (gdbarch, intelgt_pseudo_register_read_value);
+ set_gdbarch_pseudo_register_write (gdbarch,
+ intelgt_pseudo_register_write);
+ set_tdesc_pseudo_register_type (gdbarch, intelgt_pseudo_register_type);
+ set_tdesc_pseudo_register_name (gdbarch, intelgt_pseudo_register_name);
+ set_gdbarch_read_pc (gdbarch, intelgt_read_pc);
+ set_gdbarch_write_pc (gdbarch, intelgt_write_pc);
+ }
+
+ /* Populate gdbarch fields. */
+ set_gdbarch_register_type (gdbarch, tdesc_register_type);
+ set_gdbarch_dwarf2_reg_to_regnum (gdbarch, intelgt_dwarf_reg_to_regnum);
+
+ set_gdbarch_skip_prologue (gdbarch, intelgt_skip_prologue);
+ set_gdbarch_inner_than (gdbarch, core_addr_greaterthan);
+ set_gdbarch_unwind_pc (gdbarch, intelgt_unwind_pc);
+ dwarf2_append_unwinders (gdbarch);
+ frame_unwind_append_unwinder (gdbarch, &intelgt_frame_unwind);
+
+ set_gdbarch_return_value_as_value (gdbarch, intelgt_return_value_as_value);
+
+ /* We don't really have global breakpoints, but we have inferiors
+ without threads, so GDB thinks that nothing is executing and would
+ remove breakpoints, while we may get new threads at any time. */
+ set_gdbarch_has_global_breakpoints (gdbarch, 1);
+ set_gdbarch_memory_insert_breakpoint (gdbarch,
+ intelgt_memory_insert_breakpoint);
+ set_gdbarch_memory_remove_breakpoint (gdbarch,
+ intelgt_memory_remove_breakpoint);
+ set_gdbarch_program_breakpoint_here_p (gdbarch,
+ intelgt_program_breakpoint_here_p);
+ set_gdbarch_breakpoint_kind_from_pc (gdbarch,
+ intelgt_breakpoint_kind_from_pc);
+ set_gdbarch_sw_breakpoint_from_kind (gdbarch,
+ intelgt_sw_breakpoint_from_kind);
+ dwarf2_frame_set_init_reg (gdbarch, intelgt_init_reg);
+
+ /* Disassembly. */
+ set_gdbarch_print_insn (gdbarch, intelgt_print_insn);
+
+ return gdbarch_u.release ();
+}
+
+static void
+show_intelgt_debug (ui_file *file, int from_tty,
+ cmd_list_element *c, const char *value)
+{
+ gdb_printf (file, _("Intel(R) Graphics Technology debugging is "
+ "%s.\n"), value);
+}
+
+INIT_GDB_FILE (intelgt_tdep)
+{
+ gdbarch_register (bfd_arch_intelgt, intelgt_gdbarch_init);
+
+ /* Debugging flag. */
+ add_setshow_boolean_cmd ("intelgt", class_maintenance, &intelgt_debug,
+ _("Set Intel(R) Graphics Technology debugging."),
+ _("Show Intel(R) Graphics Technology debugging."),
+ _("When on, Intel(R) Graphics Technology "
+ "debugging is enabled."),
+ nullptr,
+ show_intelgt_debug,
+ &setdebuglist, &showdebuglist);
+}
diff --git a/gdb/selftest-arch.c b/gdb/selftest-arch.c
index f8e28f89eca..6c21bab967b 100644
--- a/gdb/selftest-arch.c
+++ b/gdb/selftest-arch.c
@@ -124,7 +124,8 @@ selftest_skip_warning_arch (struct gdbarch *gdbarch)
that seems more trouble than it's worth. */
return (streq (name, "m68hc11")
|| streq (name, "m68hc12")
- || streq (name, "m68hc12:HCS12"));
+ || streq (name, "m68hc12:HCS12")
+ || streq (name, "intelgt"));
}
} /* namespace selftests */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 10/44] gdb, intelgt: add disassemble feature for the Intel GT architecture.
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (8 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 09/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 11/44] gdb: revise the pid_to_exec_file target op Markus Metzger
` (33 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Albertano Caruso
From: Albertano Caruso <albertano.caruso@intel.com>
Disassembly of Intel GT programs is done via the Intel Gen Assembler
library. Add this library as an optional dependency. If the library
is not found, disassembly is disabled.
---
gdb/Makefile.in | 6 +-
gdb/README | 8 +
gdb/config.in | 3 +
gdb/configure | 565 ++++++++++++++++++++++++++++++++++++++++-
gdb/configure.ac | 47 ++++
gdb/disasm-selftests.c | 12 +
gdb/doc/gdb.texinfo | 5 +
gdb/intelgt-tdep.c | 123 ++++++++-
gdb/top.c | 10 +
9 files changed, 770 insertions(+), 9 deletions(-)
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 2993e6b55c8..d6fac9fd6f1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -246,6 +246,10 @@ CODESIGN_CERT = @CODESIGN_CERT@
# Flags to pass to gdb when invoked with "make run".
GDBFLAGS =
+# Where is libiga (i.e. the disassembler lib for the intelgt target)?
+# This will be empty if libiga was not available.
+LIBIGA = @LIBIGA64@
+
# Helper code from gnulib.
GNULIB_PARENT_DIR = ..
include $(GNULIB_PARENT_DIR)/gnulib/Makefile.gnulib.inc
@@ -677,7 +681,7 @@ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) $(ZSTD_LIBS) \
$(LIBEXPAT) $(LIBLZMA) $(LIBIPT) \
$(WIN32LIBS) $(LIBGNU) $(LIBGNU_EXTRA_LIBS) $(LIBICONV) \
$(GMPLIBS) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \
- $(DEBUGINFOD_LIBS)
+ $(DEBUGINFOD_LIBS) $(LIBIGA)
CDEPS = $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) $(CTF_DEPS) \
$(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) \
$(LIBSUPPORT)
diff --git a/gdb/README b/gdb/README
index 492b0891668..20da08085b8 100644
--- a/gdb/README
+++ b/gdb/README
@@ -524,6 +524,14 @@ more obscure GDB `configure' options are not listed here.
the overall build. See the GDB manual instructions on how to do
this.
+`--with-libiga64`
+ Build GDB with Intel Gen Assembler (IGA) library. If requested and
+ the IntelGT target is enabled but the library cannot be found,
+ configure gives an error. If not requested explicitly and cannot
+ be found, disassembly feature for the IntelGT target will not be
+ available in GDB. IGA is available as part of the Intel Graphics
+ Compiler: `https://github.com/intel/intel-graphics-compiler`
+
`--with-lzma'
Build GDB with LZMA, a compression library. (Done by default if
liblzma is installed and found at configure time.) LZMA is used
diff --git a/gdb/config.in b/gdb/config.in
index 1ef5dc5c2f4..7bc23da7ab0 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -289,6 +289,9 @@
/* Define to 1 if you have the `libiconvlist' function. */
#undef HAVE_LIBICONVLIST
+/* Define if you have the iga64 library. */
+#undef HAVE_LIBIGA64
+
/* Define if you have the ipt library. */
#undef HAVE_LIBIPT
diff --git a/gdb/configure b/gdb/configure
index 303d6ea011c..37c98157923 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -714,6 +714,9 @@ SYSTEM_GDBINIT
TARGET_SYSTEM_ROOT
CONFIG_LDFLAGS
RDYNAMIC
+LTLIBIGA64
+LIBIGA64
+HAVE_LIBIGA64
SRCHIGH_CFLAGS
SRCHIGH_LIBS
HAVE_GUILE_FALSE
@@ -959,6 +962,9 @@ enable_py_limited_api
with_guile
enable_gdb_compile
enable_source_highlight
+with_libiga64
+with_libiga64_prefix
+with_libiga64_type
with_sysroot
with_system_gdbinit
with_system_gdbinit_dir
@@ -1738,6 +1744,10 @@ Optional Packages:
search for python's libraries in DIR
--with-guile[=GUILE] include guile support
(auto/yes/no/<guile-version>/<pkg-config-program>)
+ --with-libiga64 include IntelGT disassembly support (auto/yes/no)
+ --with-libiga64-prefix[=DIR] search for libiga64 in DIR/include and DIR/lib
+ --without-libiga64-prefix don't search for libiga64 in includedir and libdir
+ --with-libiga64-type=TYPE type of library to search for (auto/static/shared)
--with-sysroot[=DIR] search for usr/lib et al within DIR
--with-system-gdbinit=PATH
automatically load a system-wide gdbinit file
@@ -4970,7 +4980,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -5016,7 +5026,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -5040,7 +5050,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -5085,7 +5095,7 @@ else
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -5109,7 +5119,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
We can't simply define LARGE_OFF_T to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
+#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31))
int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
&& LARGE_OFF_T % 2147483647 == 1)
? 1 : -1];
@@ -11878,7 +11888,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 11881 "configure"
+#line 11891 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -11984,7 +11994,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
-#line 11987 "configure"
+#line 11997 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -20022,6 +20032,8 @@ main ()
if (*(data + i) != *(data3 + i))
return 14;
close (fd);
+ free (data);
+ free (data3);
return 0;
}
_ACEOF
@@ -29506,6 +29518,545 @@ fi
+# Check for Intel(R) Graphics Technology assembler library
+intelgt_target=false
+
+for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'`
+do
+ if test "$targ_alias" = "all"; then
+ intelgt_target=true
+ else
+ case "$targ_alias" in
+ intelgt-*)
+ intelgt_target=true
+ ;;
+ esac
+ fi
+done
+
+
+# Check whether --with-libiga64 was given.
+if test "${with_libiga64+set}" = set; then :
+ withval=$with_libiga64;
+else
+ with_libiga64=auto
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use libiga64" >&5
+$as_echo_n "checking whether to use libiga64... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_libiga64" >&5
+$as_echo "$with_libiga64" >&6; }
+
+if test "${intelgt_target}" != true; then
+ HAVE_LIBIGA64=no
+else
+ if test "${with_libiga64}" = no; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: IntelGT disassembly support disabled; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: IntelGT disassembly support disabled; some features may be unavailable." >&2;}
+ HAVE_LIBIGA64=no
+ else
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+
+
+
+
+
+
+
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+# Check whether --with-libiga64-prefix was given.
+if test "${with_libiga64_prefix+set}" = set; then :
+ withval=$with_libiga64_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/lib"
+ fi
+ fi
+
+fi
+
+
+# Check whether --with-libiga64-type was given.
+if test "${with_libiga64_type+set}" = set; then :
+ withval=$with_libiga64_type; with_libiga64_type=$withval
+else
+ with_libiga64_type=auto
+fi
+
+ lib_type=`eval echo \$with_libiga64_type`
+
+ LIBIGA64=
+ LTLIBIGA64=
+ INCIGA64=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='iga64 '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+ found_dir="$additional_libdir"
+ found_so="$additional_libdir/lib$name.$shlibext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ elif test x$lib_type != xshared; then
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+ found_dir="$dir"
+ found_so="$dir/lib$name.$shlibext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ elif test x$lib_type != xshared; then
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_so"
+ else
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$found_a"
+ else
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */lib | */lib/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCIGA64="${INCIGA64}${INCIGA64:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/lib"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/lib"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBIGA64; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$dep"
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-l$name"
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-l$name"
+ else
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }-l:lib$name.$libext"
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-l:lib$name.$libext"
+ fi
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBIGA64="${LIBIGA64}${LIBIGA64:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBIGA64="${LTLIBIGA64}${LTLIBIGA64:+ }-R$found_dir"
+ done
+ fi
+
+
+ ac_save_CPPFLAGS="$CPPFLAGS"
+
+ for element in $INCIGA64; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libiga64" >&5
+$as_echo_n "checking for libiga64... " >&6; }
+if ${ac_cv_libiga64+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBIGA64"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include "assert.h"
+ #include "iga/iga.h"
+int
+main ()
+{
+iga_version_string();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_link "$LINENO"; then :
+ ac_cv_libiga64=yes
+else
+ ac_cv_libiga64=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$ac_save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libiga64" >&5
+$as_echo "$ac_cv_libiga64" >&6; }
+ if test "$ac_cv_libiga64" = yes; then
+ HAVE_LIBIGA64=yes
+
+$as_echo "#define HAVE_LIBIGA64 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libiga64" >&5
+$as_echo_n "checking how to link with libiga64... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBIGA64" >&5
+$as_echo "$LIBIGA64" >&6; }
+ else
+ HAVE_LIBIGA64=no
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBIGA64=
+ LTLIBIGA64=
+ fi
+
+
+
+
+
+
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+ if test "$HAVE_LIBIGA64" != yes; then
+ if test "$with_libiga64" = yes; then
+ as_fn_error $? "libiga64 is missing or unusable" "$LINENO" 5
+ else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libiga64 is missing or unusable; some features may be unavailable." >&5
+$as_echo "$as_me: WARNING: libiga64 is missing or unusable; some features may be unavailable." >&2;}
+ fi
+ fi
+ fi
+fi
+
# ------------------------- #
# Checks for header files. #
# ------------------------- #
diff --git a/gdb/configure.ac b/gdb/configure.ac
index e55a733fba7..c0cfd0fd7f2 100644
--- a/gdb/configure.ac
+++ b/gdb/configure.ac
@@ -1430,6 +1430,53 @@ fi
AC_SUBST(SRCHIGH_LIBS)
AC_SUBST(SRCHIGH_CFLAGS)
+# Check for Intel(R) Graphics Technology assembler library
+intelgt_target=false
+
+for targ_alias in `echo $target_alias $enable_targets | sed 's/,/ /g'`
+do
+ if test "$targ_alias" = "all"; then
+ intelgt_target=true
+ else
+ case "$targ_alias" in
+ intelgt-*)
+ intelgt_target=true
+ ;;
+ esac
+ fi
+done
+
+AC_ARG_WITH(libiga64,
+ AS_HELP_STRING([--with-libiga64],
+ [include IntelGT disassembly support (auto/yes/no)]),
+ [], [with_libiga64=auto])
+AC_MSG_CHECKING([whether to use libiga64])
+AC_MSG_RESULT([$with_libiga64])
+
+if test "${intelgt_target}" != true; then
+ HAVE_LIBIGA64=no
+else
+ if test "${with_libiga64}" = no; then
+ AC_MSG_WARN([IntelGT disassembly support disabled; some features may be unavailable.])
+ HAVE_LIBIGA64=no
+ else
+ AC_LANG_PUSH([C++])
+ AC_LIB_HAVE_LINKFLAGS([iga64], [],
+ [#include "assert.h"
+ #include "iga/iga.h"],
+ [iga_version_string();])
+ AC_LANG_POP([C++])
+
+ if test "$HAVE_LIBIGA64" != yes; then
+ if test "$with_libiga64" = yes; then
+ AC_MSG_ERROR([libiga64 is missing or unusable])
+ else
+ AC_MSG_WARN([libiga64 is missing or unusable; some features may be unavailable.])
+ fi
+ fi
+ fi
+fi
+
# ------------------------- #
# Checks for header files. #
# ------------------------- #
diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c
index 485c1245118..7650c50d864 100644
--- a/gdb/disasm-selftests.c
+++ b/gdb/disasm-selftests.c
@@ -116,6 +116,10 @@ get_test_insn (struct gdbarch *gdbarch, size_t *len)
*len = bplen;
}
break;
+ case bfd_arch_intelgt:
+ /* The intelgt architecture needs to initialize the gdbarch with
+ an IGA context to be able to use the disassembler. */
+ return insn;
case bfd_arch_i386:
{
const struct bfd_arch_info *info = gdbarch_bfd_arch_info (gdbarch);
@@ -313,6 +317,14 @@ memory_error_test (struct gdbarch *gdbarch)
return;
}
+#if !defined (HAVE_LIBIGA64)
+ if (gdbarch_bfd_arch_info (gdbarch)->arch == bfd_arch_intelgt)
+ {
+ /* Disassembler for intelgt requires libiga64. */
+ return;
+ }
+#endif
+
gdb_disassembler_test di (gdbarch);
bool saw_memory_error = false;
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index ce4bbc48385..be46ff9f6e2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -41436,6 +41436,11 @@ to download GNU Libiconv, unpack it inside the top-level directory of
the @value{GDBN} source tree, and then rename the directory holding
the Libiconv source code to @samp{libiconv}.
+@item libiga64 (IGA)
+@value{GDBN} can disassemble IntelGT machine code if this library is
+included. Use the @option{--with-libiga64-prefix} option to specify
+its non-standard location.
+
@cindex compressed debug sections
@item lzma
@value{GDBN} can support debugging sections that are compressed with
diff --git a/gdb/intelgt-tdep.c b/gdb/intelgt-tdep.c
index b4c4f3fa115..85ca2139abf 100644
--- a/gdb/intelgt-tdep.c
+++ b/gdb/intelgt-tdep.c
@@ -31,6 +31,10 @@
#include "inferior.h"
#include "user-regs.h"
#include <algorithm>
+#include "disasm.h"
+#if defined (HAVE_LIBIGA64)
+#include "iga/iga.h"
+#endif /* defined (HAVE_LIBIGA64) */
/* Global debug flag. */
static bool intelgt_debug = false;
@@ -96,6 +100,11 @@ struct intelgt_gdbarch_tdep : gdbarch_tdep_base
/* The framedesc user register's type. */
type *framedesc_type = nullptr;
+
+#if defined (HAVE_LIBIGA64)
+ /* libiga context for disassembly. */
+ iga_context_t iga_ctx = nullptr;
+#endif
};
/* Per-inferior cached data for the Intelgt target. */
@@ -547,13 +556,83 @@ intelgt_sw_breakpoint_from_kind (gdbarch *gdbarch, int kind, int *size)
return nullptr;
}
+#if defined (HAVE_LIBIGA64)
+/* Map CORE_ADDR to symbol names for jump labels in an IGA disassembly. */
+
+static const char *
+intelgt_disasm_sym_cb (int addr, void *ctx)
+{
+ disassemble_info *info = (disassemble_info *) ctx;
+ symbol *sym = find_symbol_for_pc (addr + (uintptr_t) info->private_data);
+ return sym ? sym->linkage_name () : nullptr;
+}
+#endif /* defined (HAVE_LIBIGA64) */
+
/* Print one instruction from MEMADDR on INFO->STREAM. */
static int
intelgt_print_insn (bfd_vma memaddr, struct disassemble_info *info)
{
- /* Disassembler is to be added in a later patch. */
+#if !defined (HAVE_LIBIGA64)
+ gdb_printf (_("\nDisassemble feature not available: libiga64 "
+ "is missing.\n"));
return -1;
+#else
+ gdb_disassemble_info *di
+ = static_cast<gdb_disassemble_info *> (info->application_data);
+ struct gdbarch *gdbarch = di->arch ();
+
+ iga_context_t iga_ctx
+ = gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch)->iga_ctx;
+ if (iga_ctx == nullptr)
+ {
+ gdb_printf (_("\nDisassemble feature not available: libiga64 "
+ "context is invalid.\n"));
+ return -1;
+ }
+
+ bfd_byte insn[intelgt::MAX_INST_LENGTH];
+
+ int status = (*info->read_memory_func) (memaddr, insn,
+ intelgt::COMPACT_INST_LENGTH,
+ info);
+ if (status != 0)
+ {
+ /* Aborts disassembling with a memory_error exception. */
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+
+ uint32_t device_id = get_device_id (current_inferior ());
+ gdb::array_view<bfd_byte> insn_view
+ = gdb::make_array_view (insn, intelgt::COMPACT_INST_LENGTH);
+ unsigned int length = intelgt::inst_length (insn_view, device_id);
+
+ if (length == intelgt::MAX_INST_LENGTH)
+ {
+ status = (*info->read_memory_func) (memaddr, insn,
+ intelgt::MAX_INST_LENGTH, info);
+ if (status != 0)
+ {
+ /* Aborts disassembling with a memory_error exception. */
+ (*info->memory_error_func) (status, memaddr, info);
+ return -1;
+ }
+ }
+
+ char *dbuf;
+ iga_disassemble_options_t dopts = IGA_DISASSEMBLE_OPTIONS_INIT ();
+ iga_status_t iga_status
+ = iga_context_disassemble_instruction (iga_ctx, &dopts, insn,
+ intelgt_disasm_sym_cb,
+ info, &dbuf);
+ if (iga_status != IGA_SUCCESS)
+ return -1;
+
+ (*info->fprintf_func) (info->stream, "%s", dbuf);
+
+ return length;
+#endif /* defined (HAVE_LIBIGA64) */
}
/* The "read_pc" gdbarch method. */
@@ -767,6 +846,48 @@ intelgt_gdbarch_init (gdbarch_info info, gdbarch_list *arches)
intelgt_gdbarch_tdep *data
= gdbarch_tdep<intelgt_gdbarch_tdep> (gdbarch);
+#if defined (HAVE_LIBIGA64)
+ iga_gen_t iga_version = IGA_GEN_INVALID;
+
+ if (tdesc != nullptr)
+ {
+ const tdesc_device *device_info = tdesc_device_info (tdesc);
+ if (!(device_info->vendor_id.has_value ()
+ && device_info->target_id.has_value ()))
+ {
+ warning (_("\
+Device vendor id and target id not found in intelgt target description."));
+ return nullptr;
+ }
+
+ uint32_t vendor_id = *device_info->vendor_id;
+ uint32_t device_id = *device_info->target_id;
+ if (vendor_id != 0x8086)
+ {
+ warning (_("Device not recognized: vendor id=0x%04x,"
+ " device id=0x%04x"), vendor_id, device_id);
+ return nullptr;
+ }
+
+ iga_version = (iga_gen_t) intelgt::get_xe_version (device_id);
+ if (iga_version == IGA_GEN_INVALID)
+ warning (_("Intel GT device id is unrecognized: ID 0x%04x"),
+ device_id);
+ }
+
+ /* Take the best guess in case IGA_VERSION is still invalid. */
+ if (iga_version == IGA_GEN_INVALID)
+ iga_version = IGA_XE_HPC;
+
+ const iga_context_options_t options = IGA_CONTEXT_OPTIONS_INIT (iga_version);
+ if (iga_context_create (&options, &data->iga_ctx) != IGA_SUCCESS)
+ {
+ warning (_("Intel GT disassembler context could not be created"));
+ data->iga_ctx = nullptr;
+ }
+
+#endif
+
set_gdbarch_ptr_bit (gdbarch, 64);
set_gdbarch_addr_bit (gdbarch, 64);
set_gdbarch_long_bit (gdbarch, 64);
diff --git a/gdb/top.c b/gdb/top.c
index 477c385da61..5f615429e79 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1736,6 +1736,16 @@ This GDB was configured as follows:\n\
"));
#endif
+#ifdef HAVE_LIBIGA64
+ gdb_printf (stream, _("\
+ --with-libiga64\n\
+"));
+#else
+ gdb_printf (stream, _("\
+ --without-libiga64\n\
+"));
+#endif
+
#if HAVE_SOURCE_HIGHLIGHT
gdb_printf (stream, _("\
--enable-source-highlight\n\
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 11/44] gdb: revise the pid_to_exec_file target op
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (9 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 10/44] gdb, intelgt: add disassemble feature " Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 12/44] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Markus Metzger
` (32 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Some targets, such as Intel GT, do not have the notion of an exec
file. These targets may leave the pid_to_exec_file target op
unimplemented, but from GDB's point of view, this means the exec file
could not be determined, and GDB issues a warning.
Extend the meaning of the pid_to_exec_file target op, so that if the
exec file path is empty, it means that the target does not have exec
files at all. This way, GDB can omit checking the exec file while
also not issuing the warning.
---
gdb/exec.c | 6 ++++++
gdb/target.h | 3 +++
2 files changed, 9 insertions(+)
diff --git a/gdb/exec.c b/gdb/exec.c
index f4c82519449..2a0626d6161 100644
--- a/gdb/exec.c
+++ b/gdb/exec.c
@@ -337,6 +337,12 @@ exec_file_locate_attach (int pid, int defer_bp_reset, int from_tty)
return;
}
+ if (*exec_file_target == '\0')
+ {
+ /* The target does not have the notion of an exec file. */
+ return;
+ }
+
scoped_normalized_path path_storage (&exec_file_target);
gdb::unique_xmalloc_ptr<char> exec_file_host
diff --git a/gdb/target.h b/gdb/target.h
index 6d1c21f29f6..fd25afb12dc 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -2025,6 +2025,9 @@ extern gdb::array_view<const gdb_byte> target_thread_info_to_thread_handle
If the executable file cannot be determined, NULL is returned.
+ If the target does not have the notion of executable files,
+ an empty string is returned.
+
Else, a pointer to a character string containing the pathname
is returned. This string should be copied into a buffer by
the client if the string will not be immediately used, or if
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 12/44] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (10 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 11/44] gdb: revise the pid_to_exec_file target op Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 13/44] gdbserver: move dlls_changed initialization on attach into targets Markus Metzger
` (31 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
In remote_target::remote_notice_new_inferior, we check if the inferior
for the reported ptid exists, and if not, we add that inferior via
'remote_add_inferior'. However, before adding the new inferior, we
may have already added a new thread, because the thread is not found
via 'in_thread_list'. This may bring the odd situation that a new
thread is being added before its inferior exists. Fix this problem by
moving the check and creation of a new inferior to an earlier spot in
the execution flow.
Note that the code has a check for inferior_ptid.is_pid, but this is
not useful to avoid the problem described above because the core
infrun clears inferior_ptid (i.e. sets to null_ptid) before waiting on
targets for stop events.
---
gdb/remote.c | 35 +++++++++++++++++------------------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 194c4cbd9bb..05cda9d0b0c 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3223,6 +3223,23 @@ remote_target::remote_notice_new_inferior (ptid_t currthread,
struct inferior *inf = NULL;
int pid = currthread.pid ();
+ /* When connecting to a target remote, or to a target
+ extended-remote which already was debugging an inferior, we
+ may not know about it yet. Add it before adding its child
+ thread, so notifications are emitted in a sensible order. */
+ if (find_inferior_pid (this, pid) == nullptr)
+ {
+ bool fake_pid_p = !m_features.remote_multi_process_p ();
+
+ inf = remote_add_inferior (fake_pid_p, pid, -1, 1);
+
+ /* Fetch the target description for this inferior. Make sure to
+ leave the currently selected inferior unchanged. */
+ scoped_restore_current_thread restore_thread;
+ switch_to_inferior_no_thread (inf);
+ target_find_description ();
+ }
+
if (inferior_ptid.is_pid ()
&& pid == inferior_ptid.pid ())
{
@@ -3253,24 +3270,6 @@ remote_target::remote_notice_new_inferior (ptid_t currthread,
return;
}
- /* When connecting to a target remote, or to a target
- extended-remote which already was debugging an inferior, we
- may not know about it yet. Add it before adding its child
- thread, so notifications are emitted in a sensible order. */
- if (find_inferior_pid (this, currthread.pid ()) == NULL)
- {
- bool fake_pid_p = !m_features.remote_multi_process_p ();
-
- inf = remote_add_inferior (fake_pid_p,
- currthread.pid (), -1, 1);
-
- /* Fetch the target description for this inferior. Make sure to
- leave the currently selected inferior unchanged. */
- scoped_restore_current_thread restore_thread;
- switch_to_inferior_no_thread (inf);
- target_find_description ();
- }
-
/* This is really a new thread. Add it. */
thread_info *new_thr
= remote_add_thread (currthread, state, internal_state, false);
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 13/44] gdbserver: move dlls_changed initialization on attach into targets
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (11 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 12/44] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 14/44] gdbserver: adjust pid after the target attaches Markus Metzger
` (30 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
This prepares for a new target that may not select a thread or process
during attach and the initial wait.
---
gdbserver/linux-low.cc | 5 +++++
gdbserver/server.cc | 6 ------
gdbserver/win32-low.cc | 7 +++++++
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index f1be62225e2..f14e5b1dc32 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -1213,6 +1213,11 @@ linux_process_target::attach (unsigned long pid)
error (_("Cannot attach to process %ld: %s"), pid, reason.c_str ());
}
+ /* Don't report shared library events after attaching, even if some
+ libraries are preloaded. GDB will always poll the library list.
+ Avoids the "stopped by shared library event" notice on the GDB
+ side. */
+ proc->dlls_changed = false;
open_proc_mem_file (proc);
/* Don't ignore the initial SIGSTOP if we just attached to this
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 4a727c465f0..471d6782091 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -3365,12 +3365,6 @@ handle_v_attach (char *own_buf)
{
if (attach_inferior (pid) == 0)
{
- /* Don't report shared library events after attaching, even if
- some libraries are preloaded. GDB will always poll the
- library list. Avoids the "stopped by shared library event"
- notice on the GDB side. */
- current_process ()->dlls_changed = false;
-
if (non_stop)
{
/* In non-stop, we don't send a resume reply. Stop events
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 7629beca213..935ac6657f7 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -373,6 +373,13 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
= inferior_started_by_cygwin (pid, attached);
#endif
+ /* Don't report shared library events after attaching, even if some
+ libraries are preloaded. GDB will always poll the library list.
+ Avoids the "stopped by shared library event" notice on the GDB
+ side. */
+ if (attached)
+ proc->dlls_changed = false;
+
windows_process.child_initialization_done = 1;
}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 14/44] gdbserver: adjust pid after the target attaches
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (12 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 13/44] gdbserver: move dlls_changed initialization on attach into targets Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 15/44] gdbserver: check process when we need a process Markus Metzger
` (29 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
The PID argument of the attach operation may be interpreted in a
target specific way, and may not necessarily mean that the process we
attached to has PID as its process id. This is for example the case
for Intel GT targets, where the PID refers to the process id of the
host application that is using the devices. The target in that case
creates a new process for each device where the PID of the process is
the device id/ordinal. Therefore, once the target completes the
attach, we want adjust the PID value. For this, revise the meaning
of the return value of the 'attach' target op in case of success:
return the PID of the process that was attached to.
The PID argument to the 'attach' target op is of type 'int' in caller
sites. Also, the type of the PID field in ptid_t is of type 'int'.
Hence, update the parameter of 'attach' to 'int', too.
---
gdbserver/linux-low.cc | 6 +++---
gdbserver/linux-low.h | 2 +-
gdbserver/netbsd-low.cc | 2 +-
gdbserver/netbsd-low.h | 2 +-
gdbserver/server.cc | 18 ++++++++++++++----
gdbserver/target.h | 11 ++++++++---
gdbserver/win32-low.cc | 4 ++--
gdbserver/win32-low.h | 2 +-
8 files changed, 31 insertions(+), 16 deletions(-)
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index f14e5b1dc32..8f8bcfb72ea 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -1191,7 +1191,7 @@ static void async_file_mark (void);
of its threads. */
int
-linux_process_target::attach (unsigned long pid)
+linux_process_target::attach (int pid)
{
struct process_info *proc;
thread_info *initial_thread;
@@ -1210,7 +1210,7 @@ linux_process_target::attach (unsigned long pid)
this->remove_linux_process (proc);
std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
- error (_("Cannot attach to process %ld: %s"), pid, reason.c_str ());
+ error (_("Cannot attach to process %d: %s"), pid, reason.c_str ());
}
/* Don't report shared library events after attaching, even if some
@@ -1279,7 +1279,7 @@ linux_process_target::attach (unsigned long pid)
gdb_assert (proc->tdesc != NULL);
}
- return 0;
+ return pid;
}
static int
diff --git a/gdbserver/linux-low.h b/gdbserver/linux-low.h
index 60154240ddd..f8cddef7236 100644
--- a/gdbserver/linux-low.h
+++ b/gdbserver/linux-low.h
@@ -150,7 +150,7 @@ class linux_process_target : public process_stratum_target
void post_create_inferior () override;
- int attach (unsigned long pid) override;
+ int attach (int pid) override;
int kill (process_info *proc) override;
diff --git a/gdbserver/netbsd-low.cc b/gdbserver/netbsd-low.cc
index 326b4d55860..c74c1da6fed 100644
--- a/gdbserver/netbsd-low.cc
+++ b/gdbserver/netbsd-low.cc
@@ -105,7 +105,7 @@ netbsd_process_target::post_create_inferior ()
/* Implement the attach target_ops method. */
int
-netbsd_process_target::attach (unsigned long pid)
+netbsd_process_target::attach (int pid)
{
/* Unimplemented. */
return -1;
diff --git a/gdbserver/netbsd-low.h b/gdbserver/netbsd-low.h
index 265009739a9..65866308902 100644
--- a/gdbserver/netbsd-low.h
+++ b/gdbserver/netbsd-low.h
@@ -46,7 +46,7 @@ class netbsd_process_target : public process_stratum_target
void post_create_inferior () override;
- int attach (unsigned long pid) override;
+ int attach (int pid) override;
int kill (process_info *proc) override;
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 471d6782091..13a806421a0 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -301,16 +301,26 @@ static int
attach_inferior (int pid)
{
client_state &cs = get_client_state ();
- /* myattach should return -1 if attaching is unsupported,
- 0 if it succeeded, and call error() otherwise. */
if (find_process_pid (pid) != nullptr)
error (_("Already attached to process %d\n"), pid);
- if (myattach (pid) != 0)
+ /* If attaching is unsupported, myattach returns -1. If successful,
+ it returns the PID of the process that was attached to. In other
+ cases, it calls error(). */
+ int new_pid = myattach (pid);
+ if (new_pid == -1)
return -1;
- fprintf (stderr, "Attached; pid = %d\n", pid);
+ if (new_pid == pid)
+ fprintf (stderr, "Attached; pid = %d\n", pid);
+ else
+ {
+ fprintf (stderr, "Attached; given pid = %d, updated to %d\n",
+ pid, new_pid);
+ pid = new_pid;
+ }
+
fflush (stderr);
/* FIXME - It may be that we should get the SIGNAL_PID from the
diff --git a/gdbserver/target.h b/gdbserver/target.h
index 82eab9d1243..e38570dabb2 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -95,9 +95,14 @@ class process_stratum_target
PID is the process ID to attach to, specified by the user
or a higher layer.
- Returns -1 if attaching is unsupported, 0 on success, and calls
- error() otherwise. */
- virtual int attach (unsigned long pid) = 0;
+ If attaching is unsupported, returns -1.
+
+ If successful, returns the ID of the process that was attached to.
+ This return value may be different from the argument PID, depending
+ on how the target interpreted the argument.
+
+ Calls error() in other cases. */
+ virtual int attach (int pid) = 0;
/* Kill process PROC. Return -1 on failure, and 0 on success. */
virtual int kill (process_info *proc) = 0;
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
index 935ac6657f7..ecfdd030d8b 100644
--- a/gdbserver/win32-low.cc
+++ b/gdbserver/win32-low.cc
@@ -604,7 +604,7 @@ win32_process_target::create_inferior (const char *program,
PID is the process ID to attach to, specified by the user
or a higher layer. */
int
-win32_process_target::attach (unsigned long pid)
+win32_process_target::attach (int pid)
{
HANDLE h;
DWORD err;
@@ -619,7 +619,7 @@ win32_process_target::attach (unsigned long pid)
/* win32_wait needs to know we're attaching. */
windows_process.attaching = 1;
do_initial_child_stuff (h, pid, 1);
- return 0;
+ return pid;
}
CloseHandle (h);
diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h
index 439adb84bc2..03bd625e23d 100644
--- a/gdbserver/win32-low.h
+++ b/gdbserver/win32-low.h
@@ -100,7 +100,7 @@ class win32_process_target : public process_stratum_target
int create_inferior (const char *program,
const std::string &program_args) override;
- int attach (unsigned long pid) override;
+ int attach (int pid) override;
int kill (process_info *proc) override;
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 15/44] gdbserver: check process when we need a process
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (13 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 14/44] gdbserver: adjust pid after the target attaches Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 16/44] gdb: use process in xfer_partial if inferior_ptid is null_ptid Markus Metzger
` (28 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Many qXfer features deal with process properties but check that there is a
current thread. Check that we have a current process, instead. This is
clearer and allows later patches to remove the need for having a thread.
---
gdbserver/server.cc | 16 +++++++++-------
gdbserver/tdesc.cc | 5 +++--
2 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 13a806421a0..a7e976c0f75 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1810,11 +1810,11 @@ handle_qxfer_auxv (const char *annex,
if (!the_target->supports_read_auxv () || writebuf != NULL)
return -2;
- if (annex[0] != '\0' || current_thread == NULL)
+ process_info *process = current_process ();
+ if (annex[0] != '\0' || process == nullptr)
return -1;
- return the_target->read_auxv (current_thread->id.pid (), offset, readbuf,
- len);
+ return the_target->read_auxv (process->pid, offset, readbuf, len);
}
/* Handle qXfer:exec-file:read. */
@@ -1832,10 +1832,11 @@ handle_qxfer_exec_file (const char *annex,
if (annex[0] == '\0')
{
- if (current_thread == NULL)
+ process_info *process = current_process ();
+ if (process == nullptr)
return -1;
- pid = current_thread->id.pid ();
+ pid = process->pid;
}
else
{
@@ -1876,7 +1877,7 @@ handle_qxfer_features (const char *annex,
if (writebuf != NULL)
return -2;
- if (!target_running ())
+ if (!current_process ())
return -1;
/* Grab the correct annex. */
@@ -1906,7 +1907,8 @@ handle_qxfer_libraries (const char *annex,
if (writebuf != NULL)
return -2;
- if (annex[0] != '\0' || current_thread == NULL)
+ process_info *process = current_process ();
+ if (annex[0] != '\0' || process == nullptr)
return -1;
std::string document = "<library-list version=\"1.0\">\n";
diff --git a/gdbserver/tdesc.cc b/gdbserver/tdesc.cc
index cb8aad84f54..bf49f3a8b53 100644
--- a/gdbserver/tdesc.cc
+++ b/gdbserver/tdesc.cc
@@ -127,10 +127,11 @@ copy_target_description (struct target_desc *dest,
const struct target_desc *
current_target_desc (void)
{
- if (current_thread == NULL)
+ process_info *process = current_process ();
+ if (process == nullptr)
return &default_description;
- return current_process ()->tdesc;
+ return process->tdesc;
}
/* An empty structure. */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 16/44] gdb: use process in xfer_partial if inferior_ptid is null_ptid
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (14 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 15/44] gdbserver: check process when we need a process Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 17/44] gdb: allow inferiors without threads in all_matching_threads_iterator Markus Metzger
` (27 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Some objects we want to xfer are per-process and do not necessarily
require a thread, e.g. the target description or libraries.
If we have not selected a thread, i.e. inferior_ptid is null_ptid, fall
back to the process for setting the general thread. This allows fetching
those per-process objects.
This is in preparation for allowing inferiors without threads.
---
gdb/remote.c | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 05cda9d0b0c..50e90ad8440 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -12237,8 +12237,17 @@ remote_target::xfer_partial (enum target_object object,
int unit_size
= gdbarch_addressable_memory_unit_size (current_inferior ()->arch ());
+ /* Some objects require a thread, whereas others only require a process.
+ If we have no current thread, use the current process, instead. */
+ ptid_t ptid = inferior_ptid;
+ if (ptid == null_ptid)
+ {
+ inferior *inf = current_inferior ();
+ if (inf != nullptr)
+ ptid = ptid_t (inf->pid);
+ }
set_remote_traceframe ();
- set_general_thread (inferior_ptid);
+ set_general_thread (ptid);
rs = get_remote_state ();
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 17/44] gdb: allow inferiors without threads in all_matching_threads_iterator
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (15 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 16/44] gdb: use process in xfer_partial if inferior_ptid is null_ptid Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 18/44] gdbserver: improve threads debug output for process general thread Markus Metzger
` (26 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
When iterating over all threads, the iterator already skips inferiors
without threads. When iterating over a specific inferior, however, the
iterator crashes when encountering an inferior without threads.
Fix it.
---
gdb/thread-iter.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gdb/thread-iter.c b/gdb/thread-iter.c
index d7ccb176d61..2a424088b6e 100644
--- a/gdb/thread-iter.c
+++ b/gdb/thread-iter.c
@@ -113,7 +113,7 @@ all_matching_threads_iterator::all_matching_threads_iterator
m_mode = mode::ALL_THREADS_OF_INFERIOR;
m_inf = find_inferior_pid (filter_target, filter_ptid.pid ());
- if (m_inf != nullptr)
+ if (m_inf != nullptr && !m_inf->thread_list.empty ())
m_thr = &m_inf->thread_list.front ();
}
else
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 18/44] gdbserver: improve threads debug output for process general thread
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (16 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 17/44] gdb: allow inferiors without threads in all_matching_threads_iterator Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 19/44] gdb, gdbserver: allow setting null_ptid as " Markus Metzger
` (25 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Setting the general thread to a process produces
[threads] set_desired_thread: did not find thread for general_thread <pid>.0.0, but found process
Handle that case separately in set_desired_thread() and produce
[threads] set_desired_thread: did not find process for general_thread <pid>.0.0
if no such process can be found.
---
gdbserver/target.cc | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 43ef421df74..d97d2334344 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -34,8 +34,20 @@ bool
set_desired_thread ()
{
client_state &cs = get_client_state ();
- thread_info *found = find_thread_ptid (cs.general_thread);
+ if (cs.general_thread.is_pid ())
+ {
+ process_info *proc = find_process_pid (cs.general_thread.pid ());
+ if (proc == nullptr)
+ {
+ threads_debug_printf
+ ("did not find process for general_thread %s",
+ cs.general_thread.to_string ().c_str ());
+ }
+ switch_to_process (proc);
+ return false;
+ }
+ thread_info *found = find_thread_ptid (cs.general_thread);
if (found == nullptr)
{
process_info *proc = find_process_pid (cs.general_thread.pid ());
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 19/44] gdb, gdbserver: allow setting null_ptid as general thread
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (17 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 18/44] gdbserver: improve threads debug output for process general thread Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 20/44] gdbserver: allow inferiors without threads Markus Metzger
` (24 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
When debugging a GPU inferior, we may not have any thread selected.
As a common scenario, when adding a new inferior and attaching to GPU
devices when the host process is just starting, we will have an inferior
representing a GPU device but, since no work has been submitted yet, there
will be no thread, so inferior_ptid == null_ptid.
---
gdb/remote.c | 15 ++++++++-------
gdbserver/server.cc | 17 ++++++-----------
gdbserver/target.cc | 6 ++++++
3 files changed, 20 insertions(+), 18 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 50e90ad8440..753b59a0819 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -3490,10 +3490,11 @@ remote_target::program_signals (gdb::array_view<const unsigned char> signals)
}
}
-/* If PTID is MAGIC_NULL_PTID, don't set any thread. If PTID is
- MINUS_ONE_PTID, set the thread to -1, so the stub returns the
- thread. If GEN is set, set the general thread, if not, then set
- the step/continue thread. */
+/* If PTID is MAGIC_NULL_PTID, NULL_PTID, or ANY_THREAD_PTID don't set any
+ thread and let the remote stub select the thread. If PTID is
+ MINUS_ONE_PTID, set the thread to -1, so the stub returns the thread.
+ If GEN is set, set the general thread, if not, then set the
+ step/continue thread. */
void
remote_target::set_thread (ptid_t ptid, int gen)
{
@@ -3507,9 +3508,9 @@ remote_target::set_thread (ptid_t ptid, int gen)
*buf++ = 'H';
*buf++ = gen ? 'g' : 'c';
- if (ptid == magic_null_ptid)
- xsnprintf (buf, endbuf - buf, "0");
- else if (ptid == any_thread_ptid)
+ if (ptid == magic_null_ptid
+ || ptid == null_ptid
+ || ptid == any_thread_ptid)
xsnprintf (buf, endbuf - buf, "0");
else if (ptid == minus_one_ptid)
xsnprintf (buf, endbuf - buf, "-1");
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index a7e976c0f75..03a40c7a19e 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -4858,14 +4858,8 @@ process_serial_event (void)
{
/* The ptid represents a pid. */
thread_info *thread = find_any_thread_of_pid (thread_id.pid ());
-
- if (thread == NULL)
- {
- write_enn (cs.own_buf);
- break;
- }
-
- thread_id = thread->id;
+ if (thread != nullptr)
+ thread_id = thread->id;
}
else
{
@@ -4885,14 +4879,15 @@ process_serial_event (void)
the currently selected thread is still valid. If
it is not, select the first available. */
thread_info *thread = find_thread_ptid (cs.general_thread);
- if (thread == NULL)
+ if (thread == nullptr)
thread = get_first_thread ();
- thread_id = thread->id;
+
+ if (thread != nullptr)
+ thread_id = thread->id;
}
cs.general_thread = thread_id;
set_desired_thread ();
- gdb_assert (current_thread != NULL);
}
else if (cs.own_buf[1] == 'c')
cs.cont_thread = thread_id;
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index d97d2334344..5f0d9000254 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -34,6 +34,12 @@ bool
set_desired_thread ()
{
client_state &cs = get_client_state ();
+ if (cs.general_thread == null_ptid)
+ {
+ switch_to_thread (nullptr);
+ return false;
+ }
+
if (cs.general_thread.is_pid ())
{
process_info *proc = find_process_pid (cs.general_thread.pid ());
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 20/44] gdbserver: allow inferiors without threads
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (18 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 19/44] gdb, gdbserver: allow setting null_ptid as " Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 21/44] gdb: allow creating and attaching to " Markus Metzger
` (23 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
An Intel GPU device is modeled as an inferior and GPU software threads are
modeled as threads. Software threads represent the work that has been
dispatched to a GPU device. When a thread completes its work, it exits.
When new work is dispatched to a GPU device, new software threads are
created.
In such a model, when no work is currently dispatched to a GPU device, we
are left with the GPU inferior representing the GPU device but no threads.
---
gdbserver/server.cc | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 03a40c7a19e..7ad93e5e303 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -278,7 +278,7 @@ struct notif_server notif_stop =
static int
target_running (void)
{
- return get_first_thread () != NULL;
+ return (get_first_process () != nullptr);
}
/* See gdbsupport/common-inferior.h. */
@@ -339,8 +339,12 @@ attach_inferior (int pid)
&& cs.last_status.sig () == GDB_SIGNAL_STOP)
cs.last_status.set_stopped (GDB_SIGNAL_TRAP);
- current_thread->last_resume_kind = resume_stop;
- current_thread->last_status = cs.last_status;
+ /* We may not have any threads, yet, after attaching. */
+ if (current_thread != nullptr)
+ {
+ current_thread->last_resume_kind = resume_stop;
+ current_thread->last_status = cs.last_status;
+ }
}
return 0;
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 21/44] gdb: allow creating and attaching to inferiors without threads
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (19 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 20/44] gdbserver: allow inferiors without threads Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 22/44] gdb, remote: don't create an inferior on attach Markus Metzger
` (22 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
An Intel GPU device is modeled as an inferior and GPU software threads are
modeled as threads. Software threads represent the work that has been
dispatched to a GPU device. When a thread completes its work, it exits.
When new work is dispatched to a GPU device, new software threads are
created.
In such a model, when no work is currently dispatched to a GPU device, we
are left with the GPU inferior representing the GPU device but no threads.
---
gdb/infcmd.c | 46 +++++++++++++++++++++++++++++++++++-----------
1 file changed, 35 insertions(+), 11 deletions(-)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 3e943123519..c2a860cb288 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -58,6 +58,7 @@
#include "cli/cli-style.h"
#include "gdbsupport/selftest.h"
#include "finish-thread-state.h"
+#include "event-top.h"
/* Local functions: */
@@ -383,18 +384,21 @@ post_create_inferior (int from_tty, bool set_pspace_solib_ops)
/* Now that we know the register layout, retrieve current PC. But
if the PC is unavailable (e.g., we're opening a core file with
missing registers info), ignore it. */
- thread_info *thr = inferior_thread ();
-
- thr->clear_stop_pc ();
- try
- {
- regcache *rc = get_thread_regcache (thr);
- thr->set_stop_pc (regcache_read_pc (rc));
- }
- catch (const gdb_exception_error &ex)
+ if (inferior_ptid != null_ptid)
{
- if (ex.error != NOT_AVAILABLE_ERROR)
- throw;
+ thread_info *thr = inferior_thread ();
+
+ thr->clear_stop_pc ();
+ try
+ {
+ regcache *rc = get_thread_regcache (thr);
+ thr->set_stop_pc (regcache_read_pc (rc));
+ }
+ catch (const gdb_exception_error &ex)
+ {
+ if (ex.error != NOT_AVAILABLE_ERROR)
+ throw;
+ }
}
if (set_pspace_solib_ops)
@@ -2905,6 +2909,26 @@ attach_command (const char *args, int from_tty)
finished. */
target_terminal::inferior ();
+ /* If the attach does not create a thread, there's nothing to stop.
+
+ Also leave setting up any inferior it created to the target. */
+ if (inferior_ptid == null_ptid)
+ {
+ target_terminal::ours_for_output ();
+
+ /* We disabled stdin in prepare_execution_command (). There won't
+ be any normal_stop () to enable them again. Do so now. */
+ if (async_exec == 0)
+ {
+ SWITCH_THRU_ALL_UIS ()
+ {
+ async_enable_stdin ();
+ }
+ }
+
+ return;
+ }
+
inferior->needs_setup = true;
if (target_is_non_stop_p ())
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 22/44] gdb, remote: don't create an inferior on attach
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (20 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 21/44] gdb: allow creating and attaching to " Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 23/44] gdb: allow switching to an inferior without threads Markus Metzger
` (21 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
When attaching to a process in an extended remote target, we pass the PID
to attach to in the vAttach packet. Some targets may interpret that PID
differently and provide inferiors with other IDs later on. Rely on thread
events to learn about inferiors.
Old targets that do not support multi-process or that do not support the
qAttached packet will not be able to tell us about process IDs. For those
targets, we still create the default inferior using the PID passed to
attach.
Not creating the inferior early in extended_remote_target::attach() may
cause the inferior to which we attached to be noticed two times and each
time we will add an attach_post_wait() continuation. The first time in
update_thread_list(), where we resume the inferior afterwards. The second
time in attach_command() where we want to stop the inferior.
The first time had been avoided by adding the inferior without going
through remote_notice_new_inferior().
The two continuations are executed in reverse order, thus first stopping
the inferior and then resuming it again. Fix that by adding continuations
to the end of the continuation list.
This has been exposed by gdb.base/dlmopen.exp.
When resuming in the first continuation with schedule-multiple on, we
resume not only the inferior we attached to, but also other inferiors that
might already be stopped. In non-stop mode, we take care to stop only
that inferior, but in all-stop mode, we do not. Temporarily disable
schedule-multiple to only resume that inferior.
This has been exposed by gdb.threads/detach-step-over.exp.
This leaves gdb.threads/detach-step-over.exp failing because we now issue
a stop notification for all threads in non-stop mode, whereas the original
behavior was to suppress the stop notification for the one thread that was
already stopped by the target. This is a side-effect of not having the
additional attach_post_wait() continuation that initially resumes all
threads, including the one that had been stopped.
This is achieved by returning early from remote_notice_new_inferior() in
the special case that inferior_ptid has been set to ptid_t (pid):
if (inferior_ptid.is_pid ()
&& pid == inferior_ptid.pid ())
{
/* inferior_ptid has no thread member yet. This can happen
with the vAttach -> remote_wait,"TAAthread:" path if the
stub doesn't support qC. This is the first stop reported
after an attach, so this is the main thread. Update the
ptid in the thread list. */
if (in_thread_list (this, ptid_t (pid)))
thread_change_ptid (this, inferior_ptid, currthread);
else
{
thread_info *thr
= remote_add_thread (currthread, running, executing, false);
switch_to_thread (thr);
}
return;
}
While resuming threads when noticing a new inferior only to stop them a
bit later as part of attach doesn't seem a big deal, given that we already
stopped and resumed them in update_thread_list(), it differs from the
behavior of the native target, and we want the same test to cover both.
This patch removes setting inferior_ptid and leaves it at null_ptid.
Adjust this special case to also consider null_ptid.
Since we're now avoiding a second attach_post_wait(), we wouldn't need to
change how continuations are handled. I find it more intuitive that they
are added to the back, however, so I leave this hunk in the patch.
---
gdb/infcmd.c | 3 +++
gdb/inferior.c | 2 +-
gdb/remote.c | 55 +++++++++++++++++++++++++++++++++++++++-----------
3 files changed, 47 insertions(+), 13 deletions(-)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index c2a860cb288..3da5a1366aa 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -2787,6 +2787,9 @@ attach_post_wait (int from_tty, enum attach_post_wait_mode mode)
{
if (inferior_thread ()->stop_signal () == GDB_SIGNAL_0)
{
+ scoped_restore save_multi
+ = make_scoped_restore (&sched_multi, 0);
+
clear_proceed_status (0);
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
}
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 229abdd1ef8..cc7d1cc4f94 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -180,7 +180,7 @@ inferior::set_arch (gdbarch *arch)
void
inferior::add_continuation (std::function<void ()> &&cont)
{
- m_continuations.emplace_front (std::move (cont));
+ m_continuations.emplace_back (std::move (cont));
}
void
diff --git a/gdb/remote.c b/gdb/remote.c
index 753b59a0819..5721e791612 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -1420,6 +1420,7 @@ class remote_target : public process_stratum_target
void remote_detach_pid (int pid);
void remote_vcont_probe ();
+ void remote_qattached_probe ();
void remote_resume_with_hc (ptid_t ptid, int step,
gdb_signal siggnal);
@@ -3220,6 +3221,7 @@ remote_target::remote_notice_new_inferior (ptid_t currthread,
if (!in_thread_list (this, currthread))
{
+ struct remote_state *rs = get_remote_state ();
struct inferior *inf = NULL;
int pid = currthread.pid ();
@@ -3240,8 +3242,8 @@ remote_target::remote_notice_new_inferior (ptid_t currthread,
target_find_description ();
}
- if (inferior_ptid.is_pid ()
- && pid == inferior_ptid.pid ())
+ if (inferior_ptid == null_ptid
+ || (inferior_ptid.is_pid () && pid == inferior_ptid.pid ()))
{
/* inferior_ptid has no thread member yet. This can happen
with the vAttach -> remote_wait,"TAAthread:" path if the
@@ -3254,7 +3256,9 @@ remote_target::remote_notice_new_inferior (ptid_t currthread,
{
thread_info *thr
= remote_add_thread (currthread, state, internal_state, false);
- switch_to_thread (thr);
+
+ if (!rs->starting_up)
+ switch_to_thread (thr);
}
return;
}
@@ -3278,13 +3282,8 @@ remote_target::remote_notice_new_inferior (ptid_t currthread,
it needs to with it (e.g., read shared libraries, insert
breakpoints), unless we're just setting up an all-stop
connection. */
- if (inf != NULL)
- {
- struct remote_state *rs = get_remote_state ();
-
- if (!rs->starting_up)
- notice_new_inferior (new_thr, internal_state, 0);
- }
+ if (inf != nullptr && !rs->starting_up)
+ notice_new_inferior (new_thr, internal_state, 0);
}
}
@@ -5457,6 +5456,9 @@ remote_target::start_remote_1 (int from_tty, int extended_p)
attribute. */
remote_vcont_probe ();
+ /* Similarly, probe qAttached. */
+ remote_qattached_probe ();
+
/* If the stub wants to get a QAllow, compose one and send it. */
if (m_features.packet_support (PACKET_QAllow) != PACKET_DISABLE)
set_permissions ();
@@ -7041,15 +7043,27 @@ extended_remote_target::attach (const char *args, int from_tty)
target_pid_to_str (ptid_t (pid)).c_str (), result.err_msg ());
}
- switch_to_inferior_no_thread (remote_add_inferior (false, pid, 1, 0));
+ /* Do not create a process assuming PID as the process ID. We will
+ learn about new processes from the remote target. This allows
+ targets to provide other process IDs than the one we passed to
+ vAttach.
- inferior_ptid = ptid_t (pid);
+ For targets that are not able to provide the information we need,
+ create a default inferior now. */
+ if (!m_features.remote_multi_process_p ()
+ || !(m_features.packet_support (PACKET_qAttached) == PACKET_ENABLE))
+ switch_to_inferior_no_thread (remote_add_inferior (false, pid, 1, 0));
if (target_is_non_stop_p ())
{
/* Get list of threads. */
update_thread_list ();
+ /* If we have got a new inferior for PID, switch to it. */
+ inferior *inferior = find_inferior_pid (this, pid);
+ if (inferior != nullptr)
+ switch_to_inferior_no_thread (inferior);
+
thread_info *thread = first_thread_of_inferior (current_inferior ());
if (thread != nullptr)
switch_to_thread (thread);
@@ -7063,6 +7077,10 @@ extended_remote_target::attach (const char *args, int from_tty)
ptid. */
ptid_t curr_ptid = remote_current_thread (ptid_t (pid));
+ /* We may not have created the inferior yet. */
+ if (find_inferior_pid (this, curr_ptid.pid ()) == nullptr)
+ remote_add_inferior (false, curr_ptid.pid (), 1, 0);
+
/* Add the main thread to the thread list. We add the thread
silently in this case (the final true parameter). */
thread_info *thr = remote_add_thread (curr_ptid, THREAD_RUNNING,
@@ -7164,6 +7182,19 @@ remote_target::remote_vcont_probe ()
m_features.packet_ok (rs->buf, PACKET_vCont);
}
+/* Probe whether the remote target supports qAttached. */
+
+void
+remote_target::remote_qattached_probe ()
+{
+ remote_state *rs = get_remote_state ();
+
+ strcpy (rs->buf.data (), "qAttached");
+ putpkt (rs->buf);
+ getpkt (&rs->buf);
+ m_features.packet_ok (rs->buf, PACKET_qAttached);
+}
+
/* Helper function for building "vCont" resumptions. Write a
resumption to P. ENDP points to one-passed-the-end of the buffer
we're allowed to write to. Returns BUF+CHARACTERS_WRITTEN. The
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 23/44] gdb: allow switching to an inferior without threads
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (21 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 22/44] gdb, remote: don't create an inferior on attach Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 24/44] gdb: allow resuming an inferior with no threads Markus Metzger
` (20 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
GPU inferiors may temporarily have no threads when no work is dispatched
to that GPU. We still want to allow switching to such inferiors and
resuming them in all-stop mode (subsequent patch).
Also refactor the code a bit to split switching and notifying based on the
(potentially new) state.
---
gdb/inferior.c | 36 ++++++++++++++++--------------------
1 file changed, 16 insertions(+), 20 deletions(-)
diff --git a/gdb/inferior.c b/gdb/inferior.c
index cc7d1cc4f94..b0112d4885c 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -767,29 +767,25 @@ inferior_command (const char *args, int from_tty)
if (inf == NULL)
error (_("Inferior ID %d not known."), num);
- if (inf->pid != 0)
+ if (inf->pid == 0)
+ switch_to_inferior_no_thread (inf);
+ else if (inf != current_inferior ())
{
- if (inf != current_inferior ())
- {
- thread_info *tp = any_thread_of_inferior (inf);
- if (tp == NULL)
- error (_("Inferior has no threads."));
-
- switch_to_thread (tp);
- }
-
- notify_user_selected_context_changed
- (USER_SELECTED_INFERIOR
- | USER_SELECTED_THREAD
- | USER_SELECTED_FRAME);
+ thread_info *tp = any_thread_of_inferior (inf);
+ if (tp == nullptr)
+ switch_to_inferior_no_thread (inf);
+ else
+ switch_to_thread (tp);
}
- else
- {
- switch_to_inferior_no_thread (inf);
- notify_user_selected_context_changed
- (USER_SELECTED_INFERIOR);
- }
+ if (inferior_ptid == null_ptid)
+ notify_user_selected_context_changed
+ (USER_SELECTED_INFERIOR);
+ else
+ notify_user_selected_context_changed
+ (USER_SELECTED_INFERIOR
+ | USER_SELECTED_THREAD
+ | USER_SELECTED_FRAME);
/* Switching current inferior may have made one of the inferiors
prunable, so prune it. */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 24/44] gdb: allow resuming an inferior with no threads
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (22 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 23/44] gdb: allow switching to an inferior without threads Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 25/44] gdb: allow continuing an inferior without threads Markus Metzger
` (19 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
When resuming an inferior without threads and schedule-multiple, we may
end up resuming threads of other inferiors in all-stop mode. Clear their
proceed status so we do not report the same event again.
Also, simplify clear_proceed_status () by collapsing the all-stop and
non-stop cases. In non-stop, user_visible_resume_ptid () returns
inferior_ptid, so iterating over all non-exited threads matching that ptid
in all-stop mode matches calling clear_proceed_status_thread () for
inferior_thread () in non-stop mode.
---
gdb/infrun.c | 39 ++++++++++++++++++---------------------
1 file changed, 18 insertions(+), 21 deletions(-)
diff --git a/gdb/infrun.c b/gdb/infrun.c
index c0767e7f764..ce7c376af14 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3173,32 +3173,29 @@ clear_proceed_status (int step, bool about_to_proceed)
&& !target_record_will_replay (inferior_ptid, execution_direction))
target_record_stop_replaying ();
- if (!non_stop && inferior_ptid != null_ptid)
- {
- ptid_t resume_ptid = user_visible_resume_ptid (step);
- process_stratum_target *resume_target
- = user_visible_resume_target (resume_ptid);
+ ptid_t resume_ptid = user_visible_resume_ptid (step);
+ process_stratum_target *resume_target
+ = user_visible_resume_target (resume_ptid);
- /* In all-stop mode, delete the per-thread status of all threads
- we're about to resume, implicitly and explicitly. */
- for (thread_info &tp : all_non_exited_threads (resume_target, resume_ptid))
- if (tp.internal_state () != THREAD_INT_RUNNING)
- clear_proceed_status_thread (&tp);
- }
+ if (resume_ptid == null_ptid)
+ return;
- if (inferior_ptid != null_ptid)
+ /* Delete the per-thread status of all threads we're about to resume,
+ implicitly and explicitly. */
+ for (thread_info &tp : all_non_exited_threads (resume_target, resume_ptid))
+ if (tp.internal_state () != THREAD_INT_RUNNING)
+ clear_proceed_status_thread (&tp);
+
+ for (inferior *inf : all_non_exited_inferiors (resume_target))
{
- struct inferior *inferior;
+ if (!inf->has_execution ())
+ continue;
- if (non_stop)
- {
- /* If in non-stop mode, only delete the per-thread status of
- the current thread. */
- clear_proceed_status_thread (inferior_thread ());
- }
+ ptid_t ptid { inf->pid };
+ if (!ptid.matches (resume_ptid))
+ continue;
- inferior = current_inferior ();
- inferior->control.stop_soon = NO_STOP_QUIETLY;
+ inf->control.stop_soon = NO_STOP_QUIETLY;
}
if (about_to_proceed)
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 25/44] gdb: allow continuing an inferior without threads
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (23 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 24/44] gdb: allow resuming an inferior with no threads Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 26/44] gdb: partially fix C-c not working Markus Metzger
` (18 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
When debugging an Intel GPU device, we may end up with a GPU inferior but
no threads, e.g. if we interrupted the device inferior and there was no
work dispatched onto that device.
We want to be able to continue the inferior in all-stop mode and go back
to waiting for events.
With schedule-multiple, we would not only continue the current inferior,
but other inferiors that we also stopped, as well.
When debugging the host process, one might argue that the user should
switch to a host thread before continuing. It's a matter of convenience
to not require this additional switch.
When debugging only GPU devices, however, there is no host thread to
switch to. To support that use-case, we have to allow continuing the
device inferior even though no thread is selected. There may not be any
threads at all at the moment.
---
gdb/infcmd.c | 21 +++++--
gdb/infrun.c | 159 +++++++++++++++++++++++++++++----------------------
2 files changed, 107 insertions(+), 73 deletions(-)
diff --git a/gdb/infcmd.c b/gdb/infcmd.c
index 3da5a1366aa..43126c5ff35 100644
--- a/gdb/infcmd.c
+++ b/gdb/infcmd.c
@@ -722,6 +722,9 @@ error_is_running (void)
static void
ensure_not_running (void)
{
+ if (inferior_ptid == null_ptid)
+ return;
+
if (inferior_thread ()->state () == THREAD_RUNNING)
error_is_running ();
}
@@ -787,7 +790,9 @@ continue_1 (bool all_threads_p)
}
else
{
- ensure_valid_thread ();
+ /* Allow continuing inferiors without threads. */
+ if (inferior_ptid != null_ptid)
+ ensure_valid_thread ();
ensure_not_running ();
clear_proceed_status (0);
proceed ((CORE_ADDR) -1, GDB_SIGNAL_DEFAULT);
@@ -836,7 +841,7 @@ continue_command (const char *args, int from_tty)
struct thread_info *tp;
if (non_stop)
- tp = inferior_thread ();
+ tp = (inferior_ptid == null_ptid ? nullptr : inferior_thread ());
else
{
process_stratum_target *last_target;
@@ -870,13 +875,21 @@ continue_command (const char *args, int from_tty)
ensure_not_tfind_mode ();
+ /* Switch away from an exited thread if that is the last thread in the
+ inferior to allow resuming that inferior. */
+ inferior* inf = current_inferior ();
+ if (any_live_thread_of_inferior (inf) == nullptr)
+ switch_to_inferior_no_thread (inf);
+
if (!non_stop || !all_threads_p)
{
- ensure_valid_thread ();
+ /* Allow continuing inferiors without threads. */
+ if (inferior_ptid != null_ptid)
+ ensure_valid_thread ();
ensure_not_running ();
}
- prepare_execution_command (current_inferior ()->top_target (), async_exec);
+ prepare_execution_command (inf->top_target (), async_exec);
if (from_tty)
gdb_printf (_("Continuing.\n"));
diff --git a/gdb/infrun.c b/gdb/infrun.c
index ce7c376af14..d181d20ba83 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3249,6 +3249,9 @@ thread_still_needs_step_over (struct thread_info *tp)
static bool
schedlock_applies (struct thread_info *tp)
{
+ if (tp == nullptr)
+ return (scheduler_mode == schedlock_on);
+
return (scheduler_mode == schedlock_on
|| (scheduler_mode == schedlock_step
&& tp->control.stepping_command)
@@ -3645,15 +3648,12 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
{
INFRUN_SCOPED_DEBUG_ENTER_EXIT;
- struct gdbarch *gdbarch;
- CORE_ADDR pc;
-
/* If we're stopped at a fork/vfork, switch to either the parent or child
thread as defined by the "set follow-fork-mode" command, or, if both
the parent and child are controlled by GDB, and schedule-multiple is
on, follow the child. If none of the above apply then we just proceed
resuming the current thread. */
- if (!follow_fork ())
+ if ((inferior_ptid != null_ptid) && !follow_fork ())
{
/* The target for some reason decided not to resume. */
normal_stop ();
@@ -3665,56 +3665,69 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
/* We'll update this if & when we switch to a new thread. */
update_previous_thread ();
- thread_info *cur_thr = inferior_thread ();
- infrun_debug_printf ("cur_thr = %s", cur_thr->ptid.to_string ().c_str ());
+ thread_info *cur_thr = nullptr;
+ regcache *regcache = nullptr;
+ gdbarch *gdbarch = nullptr;
+ CORE_ADDR pc = 0;
+ int step = 0;
- regcache *regcache = get_thread_regcache (cur_thr);
- gdbarch = regcache->arch ();
- pc = regcache_read_pc_protected (regcache);
+ if (inferior_ptid != null_ptid)
+ {
+ cur_thr = inferior_thread ();
+ infrun_debug_printf ("cur_thr = %s",
+ cur_thr->ptid.to_string ().c_str ());
- /* Fill in with reasonable starting values. */
- init_thread_stepping_state (cur_thr);
+ regcache = get_thread_regcache (cur_thr);
+ gdbarch = regcache->arch ();
+ pc = regcache_read_pc_protected (regcache);
- gdb_assert (!thread_is_in_step_over_chain (cur_thr));
+ /* Fill in with reasonable starting values. */
+ init_thread_stepping_state (cur_thr);
+ step = cur_thr->control.stepping_command;
+
+ gdb_assert (!thread_is_in_step_over_chain (cur_thr));
+ }
- ptid_t resume_ptid
- = user_visible_resume_ptid (cur_thr->control.stepping_command);
+ ptid_t resume_ptid = user_visible_resume_ptid (step);
process_stratum_target *resume_target
= user_visible_resume_target (resume_ptid);
check_multi_target_resumption (resume_target);
- if (addr == (CORE_ADDR) -1)
- {
- const address_space *aspace = cur_thr->inf->aspace.get ();
-
- if (cur_thr->stop_pc_p ()
- && pc == cur_thr->stop_pc ()
- && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here
- && execution_direction != EXEC_REVERSE)
- /* There is a breakpoint at the address we will resume at,
- step one instruction before inserting breakpoints so that
- we do not stop right away (and report a second hit at this
- breakpoint).
-
- Note, we don't do this in reverse, because we won't
- actually be executing the breakpoint insn anyway.
- We'll be (un-)executing the previous instruction. */
- cur_thr->stepping_over_breakpoint = 1;
- else if (gdbarch_single_step_through_delay_p (gdbarch)
- && gdbarch_single_step_through_delay (gdbarch,
- get_current_frame ()))
- /* We stepped onto an instruction that needs to be stepped
- again before re-inserting the breakpoint, do so. */
- cur_thr->stepping_over_breakpoint = 1;
- }
- else
+ if (inferior_ptid != null_ptid)
{
- regcache_write_pc (regcache, addr);
- }
+ if (addr == (CORE_ADDR) -1)
+ {
+ const address_space *aspace = cur_thr->inf->aspace.get ();
+
+ if (cur_thr->stop_pc_p ()
+ && pc == cur_thr->stop_pc ()
+ && breakpoint_here_p (aspace, pc) == ordinary_breakpoint_here
+ && execution_direction != EXEC_REVERSE)
+ /* There is a breakpoint at the address we will resume at,
+ step one instruction before inserting breakpoints so that
+ we do not stop right away (and report a second hit at this
+ breakpoint).
+
+ Note, we don't do this in reverse, because we won't
+ actually be executing the breakpoint insn anyway.
+ We'll be (un-)executing the previous instruction. */
+ cur_thr->stepping_over_breakpoint = 1;
+ else if (gdbarch_single_step_through_delay_p (gdbarch)
+ && gdbarch_single_step_through_delay (gdbarch,
+ get_current_frame ()))
+ /* We stepped onto an instruction that needs to be stepped
+ again before re-inserting the breakpoint, do so. */
+ cur_thr->stepping_over_breakpoint = 1;
+ }
+ else
+ {
+ regcache_write_pc (regcache, addr);
+ }
- if (siggnal != GDB_SIGNAL_DEFAULT)
- cur_thr->set_stop_signal (siggnal);
+ if (siggnal != GDB_SIGNAL_DEFAULT)
+ cur_thr->set_stop_signal (siggnal);
+ }
/* If an exception is thrown from this point on, make sure to
propagate GDB's knowledge of the executing state to the
@@ -3724,11 +3737,15 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
/* Even if RESUME_PTID is a wildcard, and we end up resuming fewer
threads (e.g., we might need to set threads stepping over
breakpoints first), from the user/frontend's point of view, all
- threads in RESUME_PTID are now running. */
- set_state (resume_target, resume_ptid, THREAD_RUNNING);
+ threads in RESUME_PTID are now running.
+
+ Note that RESUME_PTID may be NULL_PTID when resuming an inferior that
+ (currently) has no threads. */
+ if (resume_ptid != null_ptid)
+ set_state (resume_target, resume_ptid, THREAD_RUNNING);
infrun_debug_printf ("addr=%s, signal=%s, resume_ptid=%s",
- paddress (gdbarch, addr),
+ hex_string (addr),
gdb_signal_to_symbol_string (siggnal),
resume_ptid.to_string ().c_str ());
@@ -3744,6 +3761,9 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
Ctrl-C from within target_pass_ctrlc). */
target_terminal::inferior ();
+ /* We don't want the user to see a different selected thread. */
+ scoped_restore_current_thread restore_thread;
+
/* In a multi-threaded task we may select another thread and
then continue or step.
@@ -3757,8 +3777,10 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
/* If scheduler locking applies, we can avoid iterating over all
threads. */
- if (!non_stop && !schedlock_applies (cur_thr))
+ if (resume_ptid != null_ptid && !non_stop && !schedlock_applies (cur_thr))
{
+ scoped_restore_current_thread restore;
+
for (thread_info &tp : all_non_exited_threads (resume_target,
resume_ptid))
{
@@ -3779,21 +3801,22 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
global_thread_step_over_chain_enqueue (&tp);
}
-
- switch_to_thread (cur_thr);
}
- /* Enqueue the current thread last, so that we move all other
- threads over their breakpoints first. */
- if (cur_thr->stepping_over_breakpoint)
- global_thread_step_over_chain_enqueue (cur_thr);
+ if (cur_thr != nullptr)
+ {
+ /* Enqueue the current thread last, so that we move all other
+ threads over their breakpoints first. */
+ if (cur_thr->stepping_over_breakpoint)
+ global_thread_step_over_chain_enqueue (cur_thr);
- /* If the thread isn't started, we'll still need to set its prev_pc,
- so that switch_back_to_stepped_thread knows the thread hasn't
- advanced. Must do this before resuming any thread, as in
- all-stop/remote, once we resume we can't send any other packet
- until the target stops again. */
- cur_thr->prev_pc = regcache_read_pc_protected (regcache);
+ /* If the thread isn't started, we'll still need to set its prev_pc,
+ so that switch_back_to_stepped_thread knows the thread hasn't
+ advanced. Must do this before resuming any thread, as in
+ all-stop/remote, once we resume we can't send any other packet
+ until the target stops again. */
+ cur_thr->prev_pc = regcache_read_pc_protected (regcache);
+ }
{
scoped_disable_commit_resumed disable_commit_resumed ("proceeding");
@@ -3817,14 +3840,17 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
/* In all-stop, but the target is always in non-stop mode.
Start all other threads that are implicitly resumed too. */
- for (thread_info &tp : all_non_exited_threads (resume_target,
- resume_ptid))
+ if (resume_ptid != null_ptid)
{
- switch_to_thread_no_regs (&tp);
- proceed_resume_thread_checked (&tp);
+ for (thread_info &tp : all_non_exited_threads (resume_target,
+ resume_ptid))
+ {
+ switch_to_thread_no_regs (&tp);
+ proceed_resume_thread_checked (&tp);
+ }
}
}
- else
+ else if (cur_thr != nullptr)
proceed_resume_thread_checked (cur_thr);
disable_commit_resumed.reset_and_commit ();
@@ -3832,11 +3858,6 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
finish_state.release ();
- /* If we've switched threads above, switch back to the previously
- current thread. We don't want the user to see a different
- selected thread. */
- switch_to_thread (cur_thr);
-
/* Tell the event loop to wait for it to stop. If the target
supports asynchronous execution, it'll do this from within
target_resume. */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 26/44] gdb: partially fix C-c not working
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (24 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 25/44] gdb: allow continuing an inferior without threads Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 27/44] gdb, linux-nat: use current_inferior()->pid in mourn_inferior() Markus Metzger
` (17 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
When resuming an inferior without threads, e.g. an Intel GPU device
inferior, and then interrupting with C-c, the stop request is not
delivered. This appears as a hang.
Pedro Alves has a patch series to actually fix this issue. This is a
partial workaround to address one instance of this that I encountered.
---
gdb/target.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/gdb/target.c b/gdb/target.c
index 5d937f3ae85..a41735ef888 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -3830,6 +3830,18 @@ target_pass_ctrlc (void)
}
}
}
+
+ /* Inferior may not (currently) have threads. If we didn't find a
+ target with running threads, pass it to the current inferior's
+ target. */
+ inferior *inf = current_inferior ();
+ if (inf != nullptr)
+ {
+ inf->top_target ()->pass_ctrlc ();
+ return;
+ }
+
+ warning (_("Dropping interrupt request."));
}
/* See target.h. */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 27/44] gdb, linux-nat: use current_inferior()->pid in mourn_inferior()
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (25 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 26/44] gdb: partially fix C-c not working Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 28/44] gdb: inferior events Markus Metzger
` (16 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
In infrun.c:handle_process_exited(), we switch to either the eventing
thread or the eventing process depending on the ptid we get for the event:
static void
handle_process_exited (execution_control_state *ecs)
{
/* Depending on the system, ecs->ptid may point to a thread or to a
process. On some targets, target_mourn_inferior may need to have
access to the just-exited thread. That is the case of
GNU/Linux's "checkpoint" support, for example. Switch context
appropriately. */
thread_info *thr = ecs->target->find_thread (ecs->ptid);
if (thr != nullptr)
switch_to_thread (thr);
else
{
inferior *inf = find_inferior_ptid (ecs->target, ecs->ptid);
switch_to_inferior_no_thread (inf);
}
While target_mourn_inferior() takes a ptid argument that could be set to
the event ptid, the actual mourn_inferior() target method does not take
any argument. Almost all targets use current_inferior() to determine
which inferior to mourn.
The linux-nat target, however, uses inferior_ptid.pid(), which is zero
when switching to the event inferior and no thread, so it ended up not
finding the exited inferior's LWPs when the event is delivered with a
process ptid instead of a thread ptid.
Change linux-nat to use current_inferior() like everybody else.
I considered passing the ptid argument of target_mourn_inferior() down to
the target methods to not rely on global state. To do this right, we'd
need to not rely on current_inferior() inside the targets and instead find
the inferior based on the ptid argument. Or pass it down since the caller
most likely already has an inferior pointer.
This has been exposed by gdb.multi/multi-exit.exp on a subsequent patch
that introduces inferior events to allow deleting the last thread of an
inferior.
---
gdb/linux-nat.c | 5 +++--
gdb/target.c | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 8840dbe3f75..9e015b57379 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -3732,13 +3732,14 @@ linux_nat_target::mourn_inferior ()
{
LINUX_NAT_SCOPED_DEBUG_ENTER_EXIT;
- int pid = inferior_ptid.pid ();
+ inferior *inf = current_inferior ();
+ int pid = inf->pid;
purge_lwp_list (pid);
close_proc_mem_file (pid);
- if (! forks_exist_p (current_inferior ()))
+ if (! forks_exist_p (inf))
/* Normal case, no other forks available. */
inf_ptrace_target::mourn_inferior ();
else
diff --git a/gdb/target.c b/gdb/target.c
index a41735ef888..35d39161495 100644
--- a/gdb/target.c
+++ b/gdb/target.c
@@ -2767,7 +2767,8 @@ default_mourn_inferior (struct target_ops *self)
void
target_mourn_inferior (ptid_t ptid)
{
- gdb_assert (ptid.pid () == inferior_ptid.pid ());
+ gdb_assert (ptid == null_ptid || ptid.pid () == current_inferior ()->pid);
+ gdb_assert (ptid == null_ptid || ptid.pid () == inferior_ptid.pid ());
current_inferior ()->top_target ()->mourn_inferior ();
}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 28/44] gdb: inferior events
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (26 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 27/44] gdb, linux-nat: use current_inferior()->pid in mourn_inferior() Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 29/44] gdb, remote: allow deleting the last thread in inferior in update_thread_list() Markus Metzger
` (15 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
We currently keep the last thread of an inferior alive artificially even
if the remote target does not report any more threads in that inferior.
In stop_all_threads(), we use that last thread to attach a process exit
event to it that will be reported when resuming that (already exited)
thread later on.
We also set that thread's internal state to stopped, thus preventing the
event from getting reported by the event loop. When resuming that thread
later on, we set the internal state (back) to event pending instead of
resuming the thread, so it can now get reported by the event loop.
To prepare for a subsequent patch that stops keeping the last thread alive
artificially, store any process event directly in the inferior and hide it
until that inferior is resumed.
---
gdb/inferior.c | 1 +
gdb/inferior.h | 15 ++++
gdb/infrun.c | 230 +++++++++++++++++++++++++++++++------------------
3 files changed, 163 insertions(+), 83 deletions(-)
diff --git a/gdb/inferior.c b/gdb/inferior.c
index b0112d4885c..ce90ee82394 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -375,6 +375,7 @@ inferior_appeared (struct inferior *inf, int pid)
inf->pid = pid;
inf->has_exit_code = false;
inf->exit_code = 0;
+ inf->waitstatus.set_ignore ();
notify_inferior_appeared (inf);
}
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 217741adc07..05c4692dcbf 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -666,6 +666,21 @@ class inferior : public refcounted_object,
/* Private data used by the process_stratum target. */
std::unique_ptr<private_inferior> priv;
+ /* The wait status for a process event.
+
+ This is TARGET_WAITKIND_IGNORE until we get a process event and will
+ be reset to this once the event has been handled. */
+ target_waitstatus waitstatus;
+
+ /* Hide the above process event when received during stop_all_threads ()
+ and unhide it again when resuming that inferior.
+
+ This looks hacky, and I'd say it is hacky, but it matches exactly
+ what we had done previously by attaching a process event to the last,
+ artificially-kept-alive thread, and then setting that thread's
+ internal state to stopped instead of event pending. */
+ bool waitstatus_hidden = false;
+
/* HAS_EXIT_CODE is true if the inferior exited with an exit code.
In this case, the EXIT_CODE field is also valid. */
bool has_exit_code = false;
diff --git a/gdb/infrun.c b/gdb/infrun.c
index d181d20ba83..f93a489f2dd 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -3552,6 +3552,15 @@ proceed_resume_thread_checked (thread_info *tp)
return;
}
+ if (!tp->inf->waitstatus_hidden
+ && (tp->inf->waitstatus.kind () != TARGET_WAITKIND_IGNORE))
+ {
+ infrun_debug_printf ("[%s] inferior has pending wait status %s",
+ tp->ptid.to_string ().c_str (),
+ tp->inf->waitstatus.to_string ().c_str ());
+ return;
+ }
+
if (tp->internal_state () != THREAD_INT_STOPPED)
{
infrun_debug_printf ("[%s] resumed",
@@ -3838,6 +3847,16 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
INFRUN_SCOPED_DEBUG_START_END
("resuming threads, all-stop-on-top-of-non-stop");
+ /* Unhide any process event that had been received during
+ stop_all_threads () so we can now report it when resuming the
+ inferior. */
+ for (inferior *inf : all_non_exited_inferiors (resume_target))
+ {
+ if ((resume_ptid == minus_one_ptid)
+ || (resume_ptid.pid () == inf->pid))
+ inf->waitstatus_hidden = false;
+ }
+
/* In all-stop, but the target is always in non-stop mode.
Start all other threads that are implicitly resumed too. */
if (resume_ptid != null_ptid)
@@ -3851,18 +3870,25 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
}
}
else if (cur_thr != nullptr)
- proceed_resume_thread_checked (cur_thr);
+ {
+ /* Unhide any process event that had been received during
+ stop_all_threads () so we can now report it when resuming the
+ inferior. */
+ cur_thr->inf->waitstatus_hidden = false;
+
+ proceed_resume_thread_checked (cur_thr);
+ }
disable_commit_resumed.reset_and_commit ();
}
finish_state.release ();
- /* Tell the event loop to wait for it to stop. If the target
- supports asynchronous execution, it'll do this from within
- target_resume. */
- if (!target_can_async_p ())
- mark_async_event_handler (infrun_async_inferior_event_token);
+ /* Tell the event loop to wait for it to stop.
+
+ We can not rely on target_resume in case of process events on targets
+ without any threads. */
+ mark_async_event_handler (infrun_async_inferior_event_token);
}
\f
@@ -4112,10 +4138,18 @@ do_target_wait_1 (inferior *inf, ptid_t ptid,
the wait code relies on it - doing so is always a mistake. */
switch_to_inferior_no_thread (inf);
- /* First check if there is a resumed thread with a wait status
- pending. */
+ /* First check if there is a process event or a resumed thread with a
+ wait status pending. */
if (ptid == minus_one_ptid || ptid.is_pid ())
{
+ if (!inf->waitstatus_hidden
+ && (inf->waitstatus.kind () != TARGET_WAITKIND_IGNORE))
+ {
+ *status = inf->waitstatus;
+ inf->waitstatus.set_ignore ();
+ return ptid_t (inf->pid);
+ }
+
tp = random_pending_event_thread (inf, ptid);
}
else
@@ -4818,11 +4852,10 @@ fetch_inferior_event ()
stop_all_threads_if_all_stop_mode call above must
have seen the process-exit event, as it will see
one stop for each and every (running) thread of the
- process. Look at the pending statuses of all
- threads, and see if we have a process-exit status.
- If so, prefer handling it now and report the
- inferior exit to the user instead of reporting the
- original thread exit.
+ process. Handling that stop would have propagated the
+ process event to the thread's inferior. If so, prefer
+ handling it now and report the inferior exit to the
+ user instead of reporting the original thread exit.
Do not do this if are handling any other kind of
event, like e.g., a breakpoint hit, which the user
@@ -4832,32 +4865,23 @@ fetch_inferior_event ()
skip this "prefer process-exit" if such a
catchpoint is installed. */
if (ecs.ws.kind () == TARGET_WAITKIND_THREAD_EXITED
- && !non_stop && exists_non_stop_target ())
+ && !non_stop && exists_non_stop_target ()
+ && ((inf->waitstatus.kind () == TARGET_WAITKIND_EXITED)
+ || (inf->waitstatus.kind ()
+ == TARGET_WAITKIND_SIGNALLED)))
{
- for (thread_info &thread : inf->non_exited_threads ())
- {
- if (thread.has_pending_waitstatus ()
- && ((thread.pending_waitstatus ().kind ()
- == TARGET_WAITKIND_EXITED)
- || (thread.pending_waitstatus ().kind ()
- == TARGET_WAITKIND_SIGNALLED)))
- {
- /* Found a pending process-exit event.
- Prefer handling and reporting it now
- over the thread-exit event. */
- infrun_debug_printf
- ("found pending process-exit event, preferring it");
- ecs.ws = thread.pending_waitstatus ();
- thread.clear_pending_waitstatus ();
- ecs.event_thread = nullptr;
- ecs.ptid = thread.ptid;
- /* Re-record the last target status. */
- set_last_target_status (ecs.target, ecs.ptid,
- ecs.ws);
- handle_process_exited (&ecs);
- break;
- }
- }
+ /* Found a pending process-exit event. Prefer
+ handling and reporting it now over the thread-exit
+ event. */
+ infrun_debug_printf
+ ("found pending process-exit event, preferring it");
+ ecs.ws = inf->waitstatus;
+ inf->waitstatus.set_ignore ();
+ ecs.event_thread = nullptr;
+ ecs.ptid = ptid_t (inf->pid);
+ /* Re-record the last target status. */
+ set_last_target_status (ecs.target, ecs.ptid, ecs.ws);
+ handle_process_exited (&ecs);
}
}
@@ -5383,6 +5407,37 @@ poll_one_curr_target (struct target_waitstatus *ws)
return event_ptid;
}
+/* Poll one event out of any target. */
+
+static wait_one_event
+poll_one ()
+{
+ for (inferior *inf : all_inferiors ())
+ {
+ process_stratum_target *target = inf->process_target ();
+ if (target == nullptr
+ || !target->is_async_p ())
+ continue;
+
+ switch_to_inferior_no_thread (inf);
+
+ wait_one_event event;
+ event.target = target;
+ event.ptid = poll_one_curr_target (&event.ws);
+
+ if (event.ws.kind () == TARGET_WAITKIND_NO_RESUMED)
+ {
+ /* If nothing is resumed, remove the target from the
+ event loop. */
+ target_async (false);
+ }
+ else if (event.ws.kind () != TARGET_WAITKIND_IGNORE)
+ return event;
+ }
+
+ return wait_one_event {};
+}
+
/* Wait for one event out of any target. */
static wait_one_event
@@ -5560,47 +5615,26 @@ handle_one (const wait_one_event &event)
/* All resumed threads exited. */
return true;
}
- else if (event.ws.kind () == TARGET_WAITKIND_THREAD_EXITED
- || event.ws.kind () == TARGET_WAITKIND_EXITED
+ else if (event.ws.kind () == TARGET_WAITKIND_EXITED
|| event.ws.kind () == TARGET_WAITKIND_SIGNALLED)
{
- /* One thread/process exited/signalled. */
-
- thread_info *t = nullptr;
+ /* One process exited/signaled. */
+ inferior *inf = find_inferior_pid (event.target, event.ptid.pid ());
+ gdb_assert (inf->waitstatus.kind () == TARGET_WAITKIND_IGNORE);
+ inf->waitstatus = event.ws;
- /* The target may have reported just a pid. If so, try
- the first non-exited thread. */
- if (event.ptid.is_pid ())
- {
- int pid = event.ptid.pid ();
- inferior *inf = find_inferior_pid (event.target, pid);
- for (thread_info &tp : inf->non_exited_threads ())
- {
- t = &tp;
- break;
- }
-
- /* If there is no available thread, the event would
- have to be appended to a per-inferior event list,
- which does not exist (and if it did, we'd have
- to adjust run control command to be able to
- resume such an inferior). We assert here instead
- of going into an infinite loop. */
- gdb_assert (t != nullptr);
-
- infrun_debug_printf
- ("using %s", t->ptid.to_string ().c_str ());
- }
- else
- {
- t = event.target->find_thread (event.ptid);
- /* Check if this is the first time we see this thread.
- Don't bother adding if it individually exited. */
- if (t == nullptr
- && event.ws.kind () != TARGET_WAITKIND_THREAD_EXITED)
- t = add_thread (event.target, event.ptid);
- }
+ /* Set the threads as internally stopped to avoid another stop
+ attempt on them. */
+ ptid_t ptid (inf->pid);
+ switch_to_inferior_no_thread (inf);
+ mark_internally_stopped_threads (event.target, ptid, event.ws);
+ }
+ else if (event.ws.kind () == TARGET_WAITKIND_THREAD_EXITED)
+ {
+ /* One thread exited. Don't bother creating the thread if we do not
+ know about it already; simply ignore the event. */
+ thread_info *t = event.target->find_thread (event.ptid);
if (t != nullptr)
{
/* Set the threads as internally stopped to avoid another
@@ -5611,14 +5645,11 @@ handle_one (const wait_one_event &event)
save_waitstatus (t, event.ws);
t->stop_requested = false;
- if (event.ws.kind () == TARGET_WAITKIND_THREAD_EXITED)
+ if (displaced_step_finish (t, event.ws)
+ != DISPLACED_STEP_FINISH_STATUS_OK)
{
- if (displaced_step_finish (t, event.ws)
- != DISPLACED_STEP_FINISH_STATUS_OK)
- {
- gdb_assert_not_reached ("displaced_step_finish on "
- "exited thread failed");
- }
+ gdb_assert_not_reached ("displaced_step_finish on "
+ "exited thread failed");
}
}
}
@@ -5789,6 +5820,12 @@ stop_all_threads (const char *reason, inferior *inf)
infrun_debug_show_threads ("non-exited threads",
all_non_exited_threads ());
+ /* Hide process events, both old events that had been received on a
+ previous stop_all_threads () and new events that will be received
+ below. */
+ for (inferior *inferior : all_non_exited_inferiors ())
+ inferior->waitstatus_hidden = true;
+
scoped_restore_current_thread restore_thread;
/* Enable thread events on relevant targets. */
@@ -5892,7 +5929,34 @@ stop_all_threads (const char *reason, inferior *inf)
}
if (waits_needed == 0)
- break;
+ {
+ /* Poll process (exited/signalled) events.
+
+ GDB used to keep the last thread of an inferior alive
+ even though it received a thread exited event, so it
+ could attach the process exited/signalled event to that
+ last thread.
+
+ If we encountered this while stopping threads, since the
+ stopped threads are set internally stopped, this hid
+ those events from the event queue until we resumed the
+ thread again.
+
+ We now remove those last threads. To preserve the
+ existing behavior, we poll any pending events (which
+ should only be process events since there are no threads
+ to stop). */
+ for (;;)
+ {
+ wait_one_event event = poll_one ();
+ if (event.target == nullptr)
+ break;
+ if (handle_one (event))
+ break;
+ }
+
+ break;
+ }
/* If we find new threads on the second iteration, restart
over. We want to see two iterations in a row with all
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 29/44] gdb, remote: allow deleting the last thread in inferior in update_thread_list()
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (27 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 28/44] gdb: inferior events Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 30/44] gdb: keep target registered in inferior_event_handler() Markus Metzger
` (14 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
We do want to support inferiors without threads, e.g. for GPU inferiors
that may not have any thread when no work is dispatched to that GPU but
that may get new threads again when new work is dispatched.
The test (added in a subsequent patch)
gdb.arch/intelgt-interrupt-exited-thread.exp
resumes a single GPU thread with scheduler-locking on and then interrupts
the target with C-c. The GPU thread meanwhile finished its dispatch, so
when the target interrupts the device, it does not respond.
In response to vCtrlC, gdbserver-intelgt sends
%Stop:N
to indicate that nothing is running on the device anymore. GDB receives
the notification. In handle_no_resumed(), GDB updates the thread list.
In this scenario, the thread that GDB had resumed was the last thread on
the device, so the thread list is now empty. If we're not deleting that
thread, handle_no_resumed() will ignore the event since it found a resumed
thread, and we're stuck.
To the user, it would appear as if GDB were hanging. Interrupting the
target with C-c does not have any effect and there is no way to get the
prompt back.
This patch causes regressions in
gdb.replay/missing.thread.exp
When the replay log is updated to remove all threads like this
w $qXfer:threads:read::0,1000#92
r $l<threads>\n</threads>\n#68<Timeout: 0 seconds>
GDB removes the thread and no longer interacts with it, so replay fails at
the subsequent
w $QThreadOptions;0#00
r $OK#9a<Timeout: 0 seconds>
This patch removes that part of the test.
---
gdb/remote.c | 20 ---------------
gdb/testsuite/gdb.replay/missing-thread.exp | 28 +++------------------
2 files changed, 3 insertions(+), 45 deletions(-)
diff --git a/gdb/remote.c b/gdb/remote.c
index 5721e791612..10bceb2c798 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -4479,18 +4479,6 @@ remote_target::remote_get_threads_with_qthreadinfo (threads_listing_context *con
return 0;
}
-/* Return true if INF only has one non-exited thread. */
-
-static bool
-has_single_non_exited_thread (inferior *inf)
-{
- int count = 0;
- for (thread_info &tp ATTRIBUTE_UNUSED : inf->non_exited_threads ())
- if (++count > 1)
- break;
- return count == 1;
-}
-
/* Implement the to_update_thread_list function for the remote
targets. */
@@ -4526,14 +4514,6 @@ remote_target::update_thread_list ()
for (thread_info &tp : all_threads_safe (this))
if (!context.contains_thread (tp.ptid))
{
- /* Do not remove the thread if it is the last thread in
- the inferior. This situation happens when we have a
- pending exit process status to process. Otherwise we
- may end up with a seemingly live inferior (i.e. pid
- != 0) that has no threads. */
- if (has_single_non_exited_thread (tp.inf))
- continue;
-
/* Do not remove the thread if we've requested to be
notified of its exit. For example, the thread may be
displaced stepping, infrun will need to handle the
diff --git a/gdb/testsuite/gdb.replay/missing-thread.exp b/gdb/testsuite/gdb.replay/missing-thread.exp
index 23bbddac556..a27a0181c7f 100644
--- a/gdb/testsuite/gdb.replay/missing-thread.exp
+++ b/gdb/testsuite/gdb.replay/missing-thread.exp
@@ -83,10 +83,7 @@ proc_with_prefix record_initial_logfile { log_filename } {
# The line to be modified is the last <threads>...</threads> line, this is
# the reply from the remote that indicates the thread list. It is expected
# that the thread list will contain two threads.
-#
-# When DROP_BOTH is true then both threads will be removed from the modified
-# line. Otherwise, only the second thread is removed.
-proc update_replay_log { in_filename out_filename drop_both } {
+proc update_replay_log { in_filename out_filename } {
# Read IN_FILENAME into a list.
set fd [open $in_filename]
set data [read $fd]
@@ -108,11 +105,7 @@ proc update_replay_log { in_filename out_filename drop_both } {
set fixed_log false
if {[regexp "^(r .*<threads>\\\\n)(<thread id.*/>\\\\n)(<thread id.*/>\\\\n)(</threads>.*)$" $line \
match part1 part2 part3 part4]} {
- if { $drop_both } {
- set line $part1$part4
- } else {
- set line $part1$part2$part4
- }
+ set line $part1$part2$part4
set lines [lreplace $lines $idx $idx $line]
set fixed_log true
}
@@ -192,18 +185,13 @@ proc run_test { non_stop } {
# The replay log is placed in 'replay.log'.
set remote_log [standard_output_file replay${suffix}.log]
set missing_1_log [standard_output_file replay-missing-1${suffix}.log]
- set missing_2_log [standard_output_file replay-missing-2${suffix}.log]
record_initial_logfile $remote_log
- if { ![update_replay_log $remote_log $missing_1_log false] } {
+ if { ![update_replay_log $remote_log $missing_1_log] } {
fail "couldn't update remote replay log (drop 1 case)"
}
- if { ![update_replay_log $remote_log $missing_2_log true] } {
- fail "couldn't update remote replay log (drop 2 case)"
- }
-
with_test_prefix "with unmodified log" {
# Replay with the unmodified log. This confirms that we can replay this
# scenario correctly.
@@ -216,16 +204,6 @@ proc run_test { non_stop } {
# error when the inferior stops.
replay_with_log $missing_1_log true $non_stop
}
-
- with_test_prefix "missing 2 threads log" {
- # When we drop both threads from the <threads> reply, GDB doesn't
- # actually remove both threads from the inferior; an inferior must
- # always have at least one thread. So in this case, as the primary
- # thread is first, GDB drops this, then retains the second thread, which
- # is the one we're stopping in, and so, we don't expect to see the error
- # in this case.
- replay_with_log $missing_2_log false $non_stop
- }
}
# Run the test twice, with non-stop on and off.
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 30/44] gdb: keep target registered in inferior_event_handler()
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (28 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 29/44] gdb, remote: allow deleting the last thread in inferior in update_thread_list() Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 31/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Markus Metzger
` (13 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Even if no threads are currently running, maybe because there are
currently no threads, the target may still generate events, e.g. new
threads getting dispatched onto a GPU, new modules getting loaded, or new
stop replies in response to interrupt requests.
The test (added in a subsequent patch)
gdb.arch/intelgt-interrupt-exited-thread.exp
resumes a GPU inferior with no threads and schedule-multiple off, then
interrupts it. We disable schedule-multiple to mimic a scenario where
we're only debugging the GPU part.
In response to vCtrlC, gdbserver-intelgt sends
%Stop:N
to indicate that there are still no threads on the GPU. GDB, however, is
not able to receive the stop notification, causing the C-c to not have any
effect. Subsequent C-c enqueue more stop replies but are not able to
interrupt the inferior. GDB appears to hang.
Similarly, GDB would not be able to receive %Library notifications,
causing the host inferior to hang when GDB is not acknowledging the new
library.
While we could re-enable target_async() in pass_ctrlc(), this wouldn't
solve the inferior hang on library acknowledgement.
Several tests that enable debug output need changes because there is debug
output after the prompt. Changes are included in this patch.
This patch causes regressions in
gdb.base/multi-forks.exp
because inferiors are pruned from fetch_inferior_event() right after
detaching or killing them, whereas the test expects
<null>
inferiors to remain in the inferior list. Changes to make the test
tolerate the new behavior are included in this patch.
And in
gdb.threads/killed-outside.exp
because the SIGKILL is fetched early, and when the target is resumed, the
command is aborted when regcache_read_pc() throws in status_callback()
from linux_nat_wait_1() because the signalled leader, the only remaining
thread, is still stopped at a breakpoint. This patch contains a fix.
And in
gdb.base/annota-input-while-running.exp
gdb.base/annota1.exp
gdb.cp/annota2.exp
because there may be additional
\x1a\x1aframes-invalid
annotations after the
\x1a\x1apre-prompt
(gdb)
\x1a\x1aprompt
annotation sequence. Changes to make the tests tolerate the new behavior
are included in this patch.
And in
gdb.threads/process-dies-while-detaching.exp
because exited threads are pruned right after killing them, leaving GDB
without a live thread for the 'continue' command that is expected to
notice thread exits. Changes to make the test tolerate the new behavior
are included in this patch.
And in
gdb.threads/signal-while-stepping-over-bp-other-thread.exp
because of an additional fetch_inferior_event cycle after the prompt.
Changes to make the test tolerate the new behavior are included in this
patch.
---
gdb/inf-loop.c | 11 +++--------
gdb/linux-nat.c | 3 +++
.../gdb.base/annota-input-while-running.exp | 2 +-
gdb/testsuite/gdb.base/annota1.exp | 4 ++--
.../gdb.base/breakpoint-in-ro-region.exp | 4 ++--
gdb/testsuite/gdb.base/multi-forks.exp | 12 +++++++++++-
.../gdb.base/premature-dummy-frame-removal.exp | 16 +++++++++++-----
gdb/testsuite/gdb.base/sss-bp-on-user-bp-2.exp | 4 ++--
gdb/testsuite/gdb.base/ui-redirect.exp | 2 +-
gdb/testsuite/gdb.cp/annota2.exp | 2 +-
gdb/testsuite/gdb.threads/ia64-sigill.exp | 8 ++++----
.../gdb.threads/process-dies-while-detaching.exp | 5 +++--
...ignal-while-stepping-over-bp-other-thread.exp | 2 +-
.../gdb.threads/stepi-random-signal.exp | 4 ++--
.../gdb.threads/watchthreads-reorder.exp | 4 ++--
15 files changed, 49 insertions(+), 34 deletions(-)
diff --git a/gdb/inf-loop.c b/gdb/inf-loop.c
index 95fd4f8a2c5..a68eb740d93 100644
--- a/gdb/inf-loop.c
+++ b/gdb/inf-loop.c
@@ -43,14 +43,9 @@ inferior_event_handler (enum inferior_event_type event_type)
break;
case INF_EXEC_COMPLETE:
- if (!non_stop)
- {
- /* Unregister the inferior from the event loop. This is done
- so that when the inferior is not running we don't get
- distracted by spurious inferior output. */
- if (target_has_execution () && target_can_async_p ())
- target_async (false);
- }
+ /* We need to keep the inferior registered with the event loop.
+ Even if there are currently no threads in the inferior, so
+ nothing is running, we may get notifications from the target. */
/* Do all continuations associated with the whole inferior (not
a particular thread). */
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 9e015b57379..f28e9367c2c 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -2627,6 +2627,9 @@ status_callback (struct lwp_info *lp)
if (!lwp_status_pending_p (lp))
return false;
+ if (is_lwp_marked_dead (lp))
+ return true;
+
if (lp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT
|| lp->stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT)
{
diff --git a/gdb/testsuite/gdb.base/annota-input-while-running.exp b/gdb/testsuite/gdb.base/annota-input-while-running.exp
index 747175f0ab5..f825bf7a7a3 100644
--- a/gdb/testsuite/gdb.base/annota-input-while-running.exp
+++ b/gdb/testsuite/gdb.base/annota-input-while-running.exp
@@ -36,7 +36,7 @@ if {![runto_main]} {
# gdb_test_multiple.
set old_gdb_prompt $gdb_prompt
-set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
+set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n(\r\n\032\032frames-invalid\r\n)?"
# Like gdb_test, but cope with the annotation prompt.
proc gdb_annota_test {command pattern message} {
diff --git a/gdb/testsuite/gdb.base/annota1.exp b/gdb/testsuite/gdb.base/annota1.exp
index 35eaf26103e..2839c2de12f 100644
--- a/gdb/testsuite/gdb.base/annota1.exp
+++ b/gdb/testsuite/gdb.base/annota1.exp
@@ -57,7 +57,7 @@ gdb_test "break ${srcfile}:${main_line}" \
# true with this annotated prompt.
set old_gdb_prompt $gdb_prompt
-set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
+set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n(\r\n\032\032frames-invalid\r\n)?"
#
# Escape all the characters in the path that need it. For instance
@@ -546,7 +546,7 @@ proc thread_test {} {
gdb_breakpoint "$linenum"
set gdb_prompt \
- "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
+ "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n(\r\n\032\032frames-invalid\r\n)?"
gdb_test_multiple "set annotate 2" "" {
-re "set annotate 2\r\n$gdb_prompt$" {
diff --git a/gdb/testsuite/gdb.base/breakpoint-in-ro-region.exp b/gdb/testsuite/gdb.base/breakpoint-in-ro-region.exp
index b7ae91155d4..24bc69a3f90 100644
--- a/gdb/testsuite/gdb.base/breakpoint-in-ro-region.exp
+++ b/gdb/testsuite/gdb.base/breakpoint-in-ro-region.exp
@@ -44,11 +44,11 @@ proc probe_target_hardware_step {} {
gdb_test_no_output "set debug target 1"
set test "probe target hardware step"
gdb_test_multiple "pipe si | grep resume" $test {
- -re "resume \\(\[^\r\n\]+, step, .*$gdb_prompt $" {
+ -re "resume \\(\[^\r\n\]+, step, .*$gdb_prompt" {
set hw_step 1
pass $test
}
- -re "$gdb_prompt $" {
+ -re "$gdb_prompt" {
pass $test
}
}
diff --git a/gdb/testsuite/gdb.base/multi-forks.exp b/gdb/testsuite/gdb.base/multi-forks.exp
index 63e83ca023a..a9d6e3ccc98 100644
--- a/gdb/testsuite/gdb.base/multi-forks.exp
+++ b/gdb/testsuite/gdb.base/multi-forks.exp
@@ -186,5 +186,15 @@ gdb_test "detach inferior 5" "Detaching .*" "detach 5"
for {set i 6} { $i <= 16} {incr i} {
gdb_test_no_output "kill inferior $i" "kill $i"
- gdb_test "info inferior $i" "<null>.*" "did kill $i"
+ gdb_test_multiple "info inferior $i" "did kill $i" {
+ -re -wrap "<null>.*" {
+ pass $gdb_test_name
+ }
+ -re -wrap "No inferiors." {
+ pass $gdb_test_name
+ }
+ -re -wrap ".*" {
+ fail $gdb_test_name
+ }
+ }
}
diff --git a/gdb/testsuite/gdb.base/premature-dummy-frame-removal.exp b/gdb/testsuite/gdb.base/premature-dummy-frame-removal.exp
index 39c59736420..1aaab6369b0 100644
--- a/gdb/testsuite/gdb.base/premature-dummy-frame-removal.exp
+++ b/gdb/testsuite/gdb.base/premature-dummy-frame-removal.exp
@@ -80,10 +80,16 @@ gdb_test_multiple "p some_func ()" "" {
# debug, to format of which isn't fixed. All we care about is that
# GDB is still running afterwards.
#
-gdb_test_no_output "set debug frame on"
-gdb_test_lines "p some_func ()" \
- "repeat p some_func () with frame debug on" \
- ".*"
-gdb_test_no_output "set debug frame off"
+gdb_test "set debug frame on"
+gdb_test_multiple "p some_func ()" \
+ "repeat p some_func () with frame debug on" {
+ -re "$gdb_prompt" {
+ pass $gdb_test_name
+ }
+ -re "\[^\r\n\]*\r\n" {
+ exp_continue
+ }
+ }
+gdb_test "set debug frame off"
gdb_test "p 1 + 2 + 3" " = 6"
diff --git a/gdb/testsuite/gdb.base/sss-bp-on-user-bp-2.exp b/gdb/testsuite/gdb.base/sss-bp-on-user-bp-2.exp
index be215045048..b7f279b528a 100644
--- a/gdb/testsuite/gdb.base/sss-bp-on-user-bp-2.exp
+++ b/gdb/testsuite/gdb.base/sss-bp-on-user-bp-2.exp
@@ -65,11 +65,11 @@ gdb_test_no_output "set debug target 1"
set hardware_step 0
set test "probe target hardware step"
gdb_test_multiple "pipe si | grep resume" $test {
- -re "resume \\(\[^\r\n\]+, step, .*$gdb_prompt $" {
+ -re "resume \\(\[^\r\n\]+, step, .*$gdb_prompt" {
set hardware_step 1
pass $test
}
- -re "$gdb_prompt $" {
+ -re "$gdb_prompt" {
pass $test
}
}
diff --git a/gdb/testsuite/gdb.base/ui-redirect.exp b/gdb/testsuite/gdb.base/ui-redirect.exp
index bed11bd2e87..36c60e38782 100644
--- a/gdb/testsuite/gdb.base/ui-redirect.exp
+++ b/gdb/testsuite/gdb.base/ui-redirect.exp
@@ -119,7 +119,7 @@ with_test_prefix "debugging" {
"Copying output to /dev/null.*Copying debug output to /dev/null\\."
gdb_test \
- -prompt "$gdb_prompt \\\[infrun\\\] fetch_inferior_event: exit\r\n$" \
+ -prompt "$gdb_prompt \\\[infrun\\\] fetch_inferior_event: exit\r\n" \
"continue" \
"Continuing.*\\\[infrun\\\] .*\\\[infrun\\\] .*Breakpoint ${::decimal}, foo.*"
diff --git a/gdb/testsuite/gdb.cp/annota2.exp b/gdb/testsuite/gdb.cp/annota2.exp
index 466cab05b8a..7243e74435d 100644
--- a/gdb/testsuite/gdb.cp/annota2.exp
+++ b/gdb/testsuite/gdb.cp/annota2.exp
@@ -62,7 +62,7 @@ gdb_test "break 25" \
#
set old_gdb_prompt $gdb_prompt
-set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n"
+set gdb_prompt "\r\n\032\032pre-prompt\r\n$gdb_prompt \r\n\032\032prompt\r\n(\r\n\032\032frames-invalid\r\n)?"
send_gdb "set annotate 2\n"
gdb_expect {
diff --git a/gdb/testsuite/gdb.threads/ia64-sigill.exp b/gdb/testsuite/gdb.threads/ia64-sigill.exp
index b19c62e8ded..782d0dbb3e7 100644
--- a/gdb/testsuite/gdb.threads/ia64-sigill.exp
+++ b/gdb/testsuite/gdb.threads/ia64-sigill.exp
@@ -64,7 +64,7 @@ gdb_test_multiple "continue" "continue" -prompt $prompt {
set ok 1
exp_continue
}
- -re -wrap "" {
+ -re "$gdb_prompt" {
gdb_assert { $ok } $gdb_test_name
}
-re "\r\n\[^\r\n\]*(?=\r\n\[^\r\n\]*\r\n)" {
@@ -72,7 +72,7 @@ gdb_test_multiple "continue" "continue" -prompt $prompt {
}
}
-gdb_test_no_output {delete $sigill_bpnum}
+gdb_test {delete $sigill_bpnum}
set ok 0
gdb_test_multiple "continue" "continue for the pending signal" -prompt $prompt {
@@ -80,10 +80,10 @@ gdb_test_multiple "continue" "continue for the pending signal" -prompt $prompt {
# Breakpoint has been skipped in the other thread.
set ok 1
}
- -re " received signal .*\r\n$gdb_prompt $" {
+ -re " received signal .*\r\n$gdb_prompt" {
fail $gdb_test_name
}
- -re -wrap "" {
+ -re "$gdb_prompt" {
gdb_assert { $ok } $gdb_test_name
}
-re "\r\n\[^\r\n\]*(?=\r\n\[^\r\n\]*\r\n)" {
diff --git a/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp b/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp
index edc15f25b06..fe5e1d42c46 100644
--- a/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp
+++ b/gdb/testsuite/gdb.threads/process-dies-while-detaching.exp
@@ -166,6 +166,7 @@ proc do_detach {multi_process cmd child_exit} {
error "unhandled \$child_exit: $child_exit"
}
+ set no_live_thread_re "Cannot execute this command without a live selected thread."
set is_remote [expr {[target_info exists gdb_protocol]
&& [target_info gdb_protocol] == "remote"}]
@@ -184,7 +185,7 @@ proc do_detach {multi_process cmd child_exit} {
} elseif {$cmd == "continue"} {
# Make sure that continuing works and that the parent process
# exits cleanly.
- gdb_test "continue" $continue_re
+ gdb_test "continue" ($continue_re|$no_live_thread_re)
} else {
perror "unhandled command: $cmd"
}
@@ -197,7 +198,7 @@ proc do_detach {multi_process cmd child_exit} {
if {$cmd == "detach"} {
gdb_test "detach" "Detaching from .*, process ${decimal}\r\n\\\[Inferior $decimal \\(.*\\) detached\\\]$extra"
} elseif {$cmd == "continue"} {
- gdb_test "continue" $continue_re
+ gdb_test "continue" ($continue_re|$no_live_thread_re)
} else {
perror "unhandled command: $cmd"
}
diff --git a/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp b/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp
index f7bc6cb36b9..d268299ff4a 100644
--- a/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp
+++ b/gdb/testsuite/gdb.threads/signal-while-stepping-over-bp-other-thread.exp
@@ -97,7 +97,7 @@ gdb_test "set scheduler-locking off"
gdb_test "set debug infrun 1"
set test "step"
-gdb_test_sequence $test $test -prompt "$gdb_prompt \\\[infrun\\\] fetch_inferior_event: exit\r\n$" {
+gdb_test_sequence $test $test -prompt "$gdb_prompt .*\\\[infrun\\\] fetch_inferior_event: exit\r\n$" {
"need to step-over"
"resume_1: step=1,"
"signal arrived while stepping over breakpoint"
diff --git a/gdb/testsuite/gdb.threads/stepi-random-signal.exp b/gdb/testsuite/gdb.threads/stepi-random-signal.exp
index 64b07ec00bb..dc9aca7d759 100644
--- a/gdb/testsuite/gdb.threads/stepi-random-signal.exp
+++ b/gdb/testsuite/gdb.threads/stepi-random-signal.exp
@@ -83,13 +83,13 @@ if {$prev_addr == ""} {
set seen 0
set test "stepi"
-set prompt "$gdb_prompt \\\[infrun\\\] fetch_inferior_event: exit\r\n$"
+set prompt "$gdb_prompt \\\[infrun\\\] fetch_inferior_event: exit\r\n"
if {[gdb_test_multiple "stepi" "$test" -prompt $prompt {
-re {\[infrun\] handle_signal_stop: random signal} {
set seen 1
exp_continue
}
- -re "$prompt$" {
+ -re "$prompt" {
}
}] != 0} {
return
diff --git a/gdb/testsuite/gdb.threads/watchthreads-reorder.exp b/gdb/testsuite/gdb.threads/watchthreads-reorder.exp
index 192996b51ca..42e611b33ca 100644
--- a/gdb/testsuite/gdb.threads/watchthreads-reorder.exp
+++ b/gdb/testsuite/gdb.threads/watchthreads-reorder.exp
@@ -89,9 +89,9 @@ foreach reorder {0 1} { with_test_prefix "reorder$reorder" {
# found in the DEBUG_INFRUN code path.
gdb_test "set debug infrun 1"
- set prompt "$gdb_prompt \\\[infrun\\\] fetch_inferior_event: exit\r\n$"
+ set prompt "$gdb_prompt \\\[infrun\\\] fetch_inferior_event: exit\r\n"
gdb_test_multiple "continue" "continue to breakpoint: break-at-exit" -prompt $prompt {
- -re ".*break-at-exit.*$prompt$" {
+ -re ".*break-at-exit.*$prompt" {
pass $gdb_test_name
}
}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 31/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (29 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 30/44] gdb: keep target registered in inferior_event_handler() Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 32/44] gdbserver: add a pointer to the owner thread in regcache Markus Metzger
` (12 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: binutils
Add support for a new DWARF expression proposed in
https://dwarfstd.org/ShowIssue.php?issue=201007.1.
Cc: <binutils@sourceware.org>
---
binutils/dwarf.c | 6 ++++++
gdb/dwarf2/expr.c | 37 +++++++++++++++++++++++++++++++++++++
gdb/dwarf2/expr.h | 5 +++++
gdb/dwarf2/loc.c | 2 ++
include/dwarf2.def | 4 ++++
5 files changed, 54 insertions(+)
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 829bcb761f7..bdeaf393572 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -1882,6 +1882,12 @@ decode_location_expression (unsigned char * data,
printf ("DW_OP_PGI_omp_thread_num");
break;
+ /* Intel wide registers extension. */
+ case DW_OP_INTEL_regval_bits:
+ SAFE_BYTE_GET_AND_INC (uvalue, data, 1, end);
+ printf ("DW_OP_INTEL_regval_bits: %lu", (unsigned long) uvalue);
+ break;
+
default:
if (op >= DW_OP_lo_user
&& op <= DW_OP_hi_user)
diff --git a/gdb/dwarf2/expr.c b/gdb/dwarf2/expr.c
index 3a6b8f58199..03dae5bb064 100644
--- a/gdb/dwarf2/expr.c
+++ b/gdb/dwarf2/expr.c
@@ -946,6 +946,29 @@ dwarf_expr_context::deref (CORE_ADDR addr, int size, struct type *type)
/* See expr.h. */
+void
+dwarf_expr_context::read_reg (gdb_byte *buf, size_t bitoffset,
+ size_t bitsize, int dwregnum)
+{
+ struct gdbarch * const gdbarch = get_frame_arch (this->m_frame);
+ const int regnum = dwarf_reg_to_regnum_or_error (gdbarch, dwregnum);
+
+ const int regsize = register_size (gdbarch, regnum);
+ if ((regsize * 8) < (bitsize + bitoffset))
+ error (_("DWARF expr: error accessing %s[%" PRIu64 ":%" PRIu64 "]"),
+ gdbarch_register_name (gdbarch, regnum),
+ bitsize + bitoffset - 1, bitoffset);
+
+ gdb_byte * const regbuf = (gdb_byte *) alloca (regsize);
+ get_frame_register (this->m_frame, regnum,
+ gdb::make_array_view (regbuf, regsize));
+
+ const enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ copy_bitwise (buf, 0, regbuf, bitoffset, bitsize, byte_order);
+}
+
+/* See expr.h. */
+
void
dwarf_expr_context::push_dwarf_reg_entry_value (call_site_parameter_kind kind,
call_site_parameter_u kind_u,
@@ -2459,6 +2482,20 @@ dwarf_expr_context::execute_stack_op (gdb::array_view<const gdb_byte> expr)
}
break;
+ case DW_OP_INTEL_regval_bits:
+ {
+ uint8_t size = *op_ptr++;
+ const ULONGEST off = value_as_long (fetch (0));
+ pop ();
+ const LONGEST dwregnum = value_as_long (fetch (0));
+ pop ();
+
+ result = 0;
+ read_reg ((gdb_byte *) &result, off, size, dwregnum);
+ result_val = value_from_ulongest (address_type, result);
+ }
+ break;
+
case DW_OP_convert:
case DW_OP_GNU_convert:
case DW_OP_reinterpret:
diff --git a/gdb/dwarf2/expr.h b/gdb/dwarf2/expr.h
index 02b0e41f6fd..bc07ae4b95f 100644
--- a/gdb/dwarf2/expr.h
+++ b/gdb/dwarf2/expr.h
@@ -256,6 +256,11 @@ struct dwarf_expr_context
memory reads to come from the passed-in buffer. */
void read_mem (gdb_byte *buf, CORE_ADDR addr, size_t length);
+ /* Read BITSIZE bits from the register indicated by the DWARF register
+ number DWREGNUM starting at bit BITOFFSET into BUF. */
+ void read_reg (gdb_byte *buf, size_t bitoffset, size_t bitsize,
+ int dwregnum);
+
/* Deref ADDR with size SIZE and return a value of type TYPE.
If TYPE == nullptr, defaults to this->address_type (). */
value *deref (CORE_ADDR addr, int size, struct type *type = nullptr);
diff --git a/gdb/dwarf2/loc.c b/gdb/dwarf2/loc.c
index 8e2b401ba34..d19695d96bd 100644
--- a/gdb/dwarf2/loc.c
+++ b/gdb/dwarf2/loc.c
@@ -2100,6 +2100,7 @@ dwarf2_get_symbol_read_needs (gdb::array_view<const gdb_byte> expr,
case DW_OP_GNU_parameter_ref:
case DW_OP_regval_type:
case DW_OP_GNU_regval_type:
+ case DW_OP_INTEL_regval_bits:
symbol_needs = SYMBOL_NEEDS_FRAME;
break;
@@ -3348,6 +3349,7 @@ disassemble_dwarf_expression (struct ui_file *stream,
break;
case DW_OP_const1u:
+ case DW_OP_INTEL_regval_bits:
ul = extract_unsigned_integer (data, 1, gdbarch_byte_order (arch));
data += 1;
gdb_printf (stream, " %s", pulongest (ul));
diff --git a/include/dwarf2.def b/include/dwarf2.def
index 22b4b7d5b5a..056bb51a280 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -699,6 +699,10 @@ DW_OP (DW_OP_GNU_const_index, 0xfc)
/* The GNU variable value extension.
See http://dwarfstd.org/ShowIssue.php?issue=161109.2 . */
DW_OP (DW_OP_GNU_variable_value, 0xfd)
+/* https://dwarfstd.org/ShowIssue.php?issue=201007.1
+
+ The DW_OP_regval_bits operation extracts a value from a register. */
+DW_OP (DW_OP_INTEL_regval_bits, 0xfe)
/* HP extensions. */
DW_OP_DUP (DW_OP_HP_unknown, 0xe0) /* Ouch, the same as GNU_push_tls_address. */
DW_OP (DW_OP_HP_is_value, 0xe1)
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 32/44] gdbserver: add a pointer to the owner thread in regcache
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (30 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 31/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 33/44] gdb, gdbserver, ze: in-memory libraries Markus Metzger
` (11 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Add a back-link in regcache to the thread that owns the regcache.
This will help us in future patches to refer to the right thread
object without having to rely on the global current_thread pointer.
---
gdbserver/regcache.cc | 1 +
gdbserver/regcache.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/gdbserver/regcache.cc b/gdbserver/regcache.cc
index 7e1fd9a82e0..1084e61d62a 100644
--- a/gdbserver/regcache.cc
+++ b/gdbserver/regcache.cc
@@ -44,6 +44,7 @@ get_thread_regcache (thread_info *thread, bool fetch)
thread->set_regcache (std::make_unique<struct regcache> (proc->tdesc));
regcache = thread->regcache ();
+ regcache->thread = thread;
}
if (fetch && !regcache->registers_fetched)
diff --git a/gdbserver/regcache.h b/gdbserver/regcache.h
index 2f0fac6e7d0..abe19f9ec41 100644
--- a/gdbserver/regcache.h
+++ b/gdbserver/regcache.h
@@ -34,6 +34,9 @@ struct regcache : public reg_buffer_common
/* The regcache's target description. */
const struct target_desc *tdesc = nullptr;
+ /* Back-link to the thread to which this regcache belongs. */
+ thread_info *thread = nullptr;
+
/* Whether the REGISTERS buffer's contents are fetched. If false,
we haven't fetched the registers from the target yet. Note that
this register cache is _not_ pass-through, unlike GDB's. Also,
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 33/44] gdb, gdbserver, ze: in-memory libraries
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (31 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 32/44] gdbserver: add a pointer to the owner thread in regcache Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 34/44] gdb, gdbserver: library notifications Markus Metzger
` (10 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Eli Zaretskii
For Intel GPU devices, device libraries live in the host memory and are
loaded onto the device from there.
Add support for reporting such in-memory shared libraries via
qXfer:libraries:read
and have GDB read them from target memory.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/NEWS | 7 +++
gdb/doc/gdb.texinfo | 39 +++++++++----
gdb/features/library-list.dtd | 6 +-
gdb/solib-target.c | 66 +++++++++++++++++++++-
gdb/solib.c | 80 ++++++++++++++++++++-------
gdb/solib.h | 23 +++++++-
gdbserver/dll.cc | 101 ++++++++++++++++++++++++++++------
gdbserver/dll.h | 30 +++++++++-
gdbserver/server.cc | 27 ++++++++-
9 files changed, 320 insertions(+), 59 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 0b949ae7825..879f8e8865d 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -11,6 +11,13 @@ qXfer:features:read:target.xml
that can be used for passing information when the remote inferior
represents a device, e.g. a GPU.
+* New remote packets
+
+qXfer:libraries:read's response
+
+ The qXfer:libraries:read query supports reporting in-memory libraries.
+ Older GDB will silently ignore them.
+
*** Changes in GDB 18
* Support for the Common Trace Format (CTF) has been removed. GDB now
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index be46ff9f6e2..4cc755a0999 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -48899,9 +48899,10 @@ queries the target's operating system and reports which libraries
are loaded.
The @samp{qXfer:libraries:read} packet returns an XML document which
-lists loaded libraries and their offsets. Each library has an
-associated name and one or more segment or section base addresses,
-which report where the library was loaded in memory.
+lists loaded libraries and their offsets. Each library has either an
+associated name or begin and end addresses and one or more segment or
+section base addresses, which report where the library was loaded in
+memory.
For the common case of libraries that are fully linked binaries, the
library should have a list of segments. If the target supports
@@ -48913,6 +48914,9 @@ depend on the library's link-time base addresses.
@value{GDBN} must be linked with the Expat library to support XML
library lists. @xref{Expat}.
+Versions of @value{GDBN} that do not support in-memory library
+elements will silently ignore them.
+
A simple memory map, with one loaded library relocated by a single
offset, looks like this:
@@ -48924,6 +48928,16 @@ offset, looks like this:
</library-list>
@end smallexample
+A corresponding memory map for an in-memory library looks like this:
+
+@smallexample
+<library-list>
+ <in-memory-library begin="0xa000000" end="0xa001000">
+ <segment address="0x10000000"/>
+ </in-memory-library>
+</library-list>
+@end smallexample
+
Another simple memory map, with one loaded library with three
allocated sections (.text, .data, .bss), looks like this:
@@ -48941,14 +48955,17 @@ The format of a library list is described by this DTD:
@smallexample
<!-- library-list: Root element with versioning -->
-<!ELEMENT library-list (library)*>
-<!ATTLIST library-list version CDATA #FIXED "1.0">
-<!ELEMENT library (segment*, section*)>
-<!ATTLIST library name CDATA #REQUIRED>
-<!ELEMENT segment EMPTY>
-<!ATTLIST segment address CDATA #REQUIRED>
-<!ELEMENT section EMPTY>
-<!ATTLIST section address CDATA #REQUIRED>
+<!ELEMENT library-list (library | in-memory-library)*>
+<!ATTLIST library-list version CDATA #FIXED "1.0">
+<!ELEMENT library (segment*, section*)>
+<!ATTLIST library name CDATA #REQUIRED>
+<!ELEMENT in-memory-library (segment*, section*)>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED>
+<!ELEMENT segment EMPTY>
+<!ATTLIST segment address CDATA #REQUIRED>
+<!ELEMENT section EMPTY>
+<!ATTLIST section address CDATA #REQUIRED>
@end smallexample
In addition, segments and section descriptors cannot be mixed within a
diff --git a/gdb/features/library-list.dtd b/gdb/features/library-list.dtd
index c612d409ad4..890aac4d80c 100644
--- a/gdb/features/library-list.dtd
+++ b/gdb/features/library-list.dtd
@@ -5,12 +5,16 @@
notice and this notice are preserved. -->
<!-- library-list: Root element with versioning -->
-<!ELEMENT library-list (library)*>
+<!ELEMENT library-list (library | in-memory-library)*>
<!ATTLIST library-list version CDATA #FIXED "1.0">
<!ELEMENT library (segment*, section*)>
<!ATTLIST library name CDATA #REQUIRED>
+<!ELEMENT in-memory-library (segment*, section*)>
+<!ATTLIST in-memory-library begin CDATA #REQUIRED
+ end CDATA #REQUIRED>
+
<!ELEMENT segment EMPTY>
<!ATTLIST segment address CDATA #REQUIRED>
diff --git a/gdb/solib-target.c b/gdb/solib-target.c
index ac965df20e9..3508bc792a0 100644
--- a/gdb/solib-target.c
+++ b/gdb/solib-target.c
@@ -26,9 +26,25 @@
#include <vector>
#include "inferior.h"
+/* The location of a loaded library. */
+
+enum lm_location_t
+{
+ lm_on_disk,
+ lm_in_memory
+};
+
/* Private data for each loaded library. */
struct lm_info_target final : public lm_info
{
+ /* The library's location. */
+ lm_location_t location = lm_on_disk;
+
+ /* The library's begin and end memory addresses.
+
+ This is only valid if location == lm_in_memory. */
+ CORE_ADDR begin = 0ull, end = 0ull;
+
/* The target can either specify segment bases or section bases, not
both. */
@@ -133,6 +149,26 @@ library_list_start_library (struct gdb_xml_parser *parser,
std::make_unique<lm_info_target> () });
}
+/* Handle the start of a <in-memory-library> element. */
+
+static void
+in_memory_library_list_start_library (struct gdb_xml_parser *parser,
+ const struct gdb_xml_element *element,
+ void *user_data,
+ std::vector<gdb_xml_value> &attributes)
+{
+ const auto list = static_cast<std::vector<target_library> *> (user_data);
+ list->emplace_back (target_library { "",
+ std::make_unique<lm_info_target> () });
+
+ lm_info_target &info = *list->back ().info;
+ info.location = lm_in_memory;
+ info.begin = (CORE_ADDR) *(ULONGEST *)
+ xml_find_attribute (attributes, "begin")->value.get ();
+ info.end = (CORE_ADDR) *(ULONGEST *)
+ xml_find_attribute (attributes, "end")->value.get ();
+}
+
static void
library_list_end_library (struct gdb_xml_parser *parser,
const struct gdb_xml_element *element,
@@ -196,10 +232,19 @@ static const struct gdb_xml_attribute library_attributes[] = {
{ NULL, GDB_XML_AF_NONE, NULL, NULL }
};
+static const struct gdb_xml_attribute in_memory_library_attributes[] = {
+ { "begin", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { "end", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+ { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
static const struct gdb_xml_element library_list_children[] = {
{ "library", library_attributes, library_children,
GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
library_list_start_library, library_list_end_library },
+ { "in-memory-library", in_memory_library_attributes, library_children,
+ GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
+ in_memory_library_list_start_library, library_list_end_library },
{ NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
};
@@ -249,8 +294,25 @@ target_solib_ops::current_sos () const
/* Build a struct solib for each entry on the list. */
for (auto &library : library_list)
- sos.emplace_back (std::move (library.info), library.name, library.name,
- *this);
+ {
+ switch (library.info->location)
+ {
+ case lm_on_disk:
+ sos.emplace_back (std::move (library.info), library.name,
+ library.name, *this);
+ break;
+
+ case lm_in_memory:
+ if (library.info->end <= library.info->begin)
+ warning (_("bad in-memory-library location: begin=%s, end=%s"),
+ core_addr_to_string_nz (library.info->begin),
+ core_addr_to_string_nz (library.info->end));
+ else
+ sos.emplace_back (std::move (library.info), library.info->begin,
+ library.info->end, *this);
+ break;
+ }
+ }
return sos;
}
diff --git a/gdb/solib.c b/gdb/solib.c
index 869b769d043..11cd4432d33 100644
--- a/gdb/solib.c
+++ b/gdb/solib.c
@@ -395,13 +395,36 @@ solib_bfd_fopen (const char *pathname, int fd)
return abfd;
}
+/* Initialize ABFD. */
+
+static void
+solib_bfd_init (bfd *abfd)
+{
+ const struct bfd_arch_info *b;
+
+ /* Check bfd format. */
+ if (!gdb_bfd_check_format (abfd, bfd_object))
+ error (_("`%ps': not in executable format: %s"),
+ styled_string (file_name_style.style (),
+ bfd_get_filename (abfd)),
+ bfd_errmsg (bfd_get_error ()));
+
+ /* Check bfd arch. */
+ b = gdbarch_bfd_arch_info (current_inferior ()->arch ());
+ if (!b->compatible (b, bfd_get_arch_info (abfd)))
+ error (_("`%ps': Shared library architecture %s is not compatible "
+ "with target architecture %s."),
+ styled_string (file_name_style.style (),
+ bfd_get_filename (abfd)),
+ bfd_get_arch_info (abfd)->printable_name, b->printable_name);
+}
+
/* Find shared library PATHNAME and open a BFD for it. */
gdb_bfd_ref_ptr
solib_bfd_open (const char *pathname)
{
int found_file;
- const struct bfd_arch_info *b;
/* Search for shared library file. */
gdb::unique_xmalloc_ptr<char> found_pathname
@@ -418,22 +441,7 @@ solib_bfd_open (const char *pathname)
/* Open bfd for shared library. */
gdb_bfd_ref_ptr abfd (solib_bfd_fopen (found_pathname.get (), found_file));
-
- /* Check bfd format. */
- if (!gdb_bfd_check_format (abfd.get (), bfd_object))
- error (_("`%ps': not in executable format: %s"),
- styled_string (file_name_style.style (),
- bfd_get_filename (abfd.get ())),
- bfd_errmsg (bfd_get_error ()));
-
- /* Check bfd arch. */
- b = gdbarch_bfd_arch_info (current_inferior ()->arch ());
- if (!b->compatible (b, bfd_get_arch_info (abfd.get ())))
- error (_("`%ps': Shared library architecture %s is not compatible "
- "with target architecture %s."),
- styled_string (file_name_style.style (),
- bfd_get_filename (abfd.get ())),
- bfd_get_arch_info (abfd.get ())->printable_name, b->printable_name);
+ solib_bfd_init (abfd.get ());
return abfd;
}
@@ -456,6 +464,23 @@ solib_ops::iterate_over_objfiles_in_search_order
return;
}
+gdb_bfd_ref_ptr
+solib_ops::bfd_open_from_target_memory (CORE_ADDR addr,
+ CORE_ADDR size,
+ const char *target) const
+{
+ /* Open bfd for shared library. */
+ gdb_bfd_ref_ptr abfd
+ = gdb_bfd_open_from_target_memory (addr, size, target);
+ if (abfd == nullptr)
+ error (_("Could not open file from target '%s' "
+ "at address %s with size %s."), target,
+ core_addr_to_string_nz (addr), core_addr_to_string_nz (size));
+ solib_bfd_init (abfd.get ());
+
+ return abfd;
+}
+
/* Given a pointer to one of the shared objects in our list of mapped
objects, use the recorded name to open a bfd descriptor for the
object, build a section table, relocate all the section addresses
@@ -471,9 +496,22 @@ solib_ops::iterate_over_objfiles_in_search_order
static int
solib_map_sections (solib &so)
{
- gdb::unique_xmalloc_ptr<char> filename
- = gdb_rl_tilde_expand (so.name.c_str ());
- gdb_bfd_ref_ptr abfd (so.ops ().bfd_open (filename.get ()));
+ gdb_bfd_ref_ptr abfd;
+ if (!so.name.empty ())
+ {
+ gdb::unique_xmalloc_ptr<char> filename
+ = gdb_rl_tilde_expand (so.name.c_str ());
+ abfd = so.ops ().bfd_open (filename.get ());
+ }
+ else if (so.begin != 0 && so.end != 0)
+ {
+ gdb_assert (so.begin < so.end);
+ abfd = so.ops ().bfd_open_from_target_memory (so.begin,
+ so.end - so.begin,
+ gnutarget);
+ }
+ else
+ internal_error (_("solib has neither a name nor a memory range"));
/* If we have a core target then the core target might have some helpful
information (i.e. build-ids) about the shared libraries we are trying
@@ -520,7 +558,7 @@ solib_map_sections (solib &so)
{
warning (_("Build-id of %ps does not match core file."),
styled_string (file_name_style.style (),
- filename.get ()));
+ so.name.c_str ()));
abfd = nullptr;
}
}
diff --git a/gdb/solib.h b/gdb/solib.h
index 662d467b0cc..6914fc79a96 100644
--- a/gdb/solib.h
+++ b/gdb/solib.h
@@ -58,12 +58,21 @@ struct solib_ops;
struct solib : intrusive_list_node<solib>
{
- /* Constructor
+ /* On-disk solib constructor.
OPS is the solib_ops implementation providing this solib. */
explicit solib (lm_info_up lm_info, std::string original_name,
std::string name, const solib_ops &ops);
+ /* In-memory solib constructor. */
+ explicit solib (lm_info_up lm_info, CORE_ADDR begin, CORE_ADDR end,
+ const solib_ops &ops)
+ : lm_info (std::move (lm_info)),
+ begin (begin),
+ end (end),
+ m_ops (&ops)
+ {}
+
/* Return the solib_ops implementation providing this solib. */
const solib_ops &ops () const
{ return *m_ops; }
@@ -95,9 +104,14 @@ struct solib : intrusive_list_node<solib>
map we've already loaded. */
std::string original_name;
- /* Shared object file name, expanded to something GDB can open. */
+ /* Shared object file name, expanded to something GDB can open.
+ This is an empty string for in-memory shared objects. */
std::string name;
+ /* The address range of an in-memory shared object. Both BEGIN and END
+ are zero for on-disk shared objects. */
+ CORE_ADDR begin = 0, end = 0;
+
/* The following fields of the structure are built from
information gathered from the shared object file itself, and
are set when we actually add it to our symbol tables.
@@ -283,6 +297,11 @@ struct solib_ops
(iterate_over_objfiles_in_search_order_cb_ftype cb,
objfile *current_objfile) const;
+ /* Open an in-memory shared library at ADDR of at most SIZE bytes.
+ The TARGET string is used to identify the target. */
+ virtual gdb_bfd_ref_ptr bfd_open_from_target_memory
+ (CORE_ADDR addr, CORE_ADDR size, const char *target) const;
+
protected:
/* The program space for which this solib_ops was created. */
program_space *m_pspace;
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index 9ed2f5c80fc..32d6dfb21a7 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -40,34 +40,52 @@ loaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}
-/* Record that the DLL with NAME and BASE_ADDR has been unloaded
- from the current process. */
+/* Record a newly loaded in-memory DLL at BASE_ADDR for PROC. */
void
-unloaded_dll (const char *name, CORE_ADDR base_addr)
+loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
{
- unloaded_dll (current_process (), name, base_addr);
+ gdb_assert (proc != nullptr);
+
+ /* We do not support overlapping in-memory libraries. */
+ std::list<dll_info> &dlls = proc->all_dlls;
+ std::list<dll_info>::iterator it
+ = std::find_if (dlls.begin (), dlls.end (),
+ [begin, end] (const dll_info &dll)
+ {
+ /* DLL precedes the new library; note that end is exclusive. */
+ if (dll.end <= begin)
+ return false;
+ /* DLL succeeds the new library; note that end is exclusive. */
+ if (end <= dll.begin)
+ return false;
+ /* DLL overlaps with the new library. */
+ return true;
+ });
+
+ if (it != dlls.end ())
+ error (_("In-memory library [%s;%s) overlaps with [%s;%s)."),
+ paddress (begin), paddress (end), paddress (it->begin),
+ paddress (it->end));
+
+ proc->all_dlls.emplace_back (begin, end, base_addr);
+ proc->dlls_changed = true;
}
/* Record that the DLL with NAME and BASE_ADDR has been unloaded
- from PROC. */
+ from the current process. */
void
-unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+unloaded_dll (const char *name, CORE_ADDR base_addr)
{
- gdb_assert (proc != nullptr);
- auto pred = [&] (const dll_info &dll)
- {
- if (base_addr != UNSPECIFIED_CORE_ADDR
- && base_addr == dll.base_addr)
- return true;
-
- if (name != NULL && dll.name == name)
- return true;
-
- return false;
- };
+ unloaded_dll (current_process (), name, base_addr);
+}
+static void
+unload_dll_if (process_info *proc,
+ std::function<bool (const dll_info &)> pred)
+{
auto iter = std::find_if (proc->all_dlls.begin (), proc->all_dlls.end (),
pred);
@@ -89,3 +107,50 @@ unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
proc->dlls_changed = true;
}
}
+
+/* Record that the DLL with NAME and BASE_ADDR has been unloaded
+ from PROC. */
+
+void
+unloaded_dll (process_info *proc, const char *name, CORE_ADDR base_addr)
+{
+ unload_dll_if (proc, [&] (const dll_info &dll)
+ {
+ if (dll.location != dll_info::on_disk)
+ return false;
+
+ if (base_addr != UNSPECIFIED_CORE_ADDR
+ && base_addr == dll.base_addr)
+ return true;
+
+ if (name != NULL && dll.name == name)
+ return true;
+
+ return false;
+ });
+}
+
+/* Record that the in-memory DLL from BEGIN to END loaded at BASE_ADDR has been
+ unloaded. */
+
+void
+unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr)
+{
+ unload_dll_if (proc, [&] (const dll_info &dll)
+ {
+ if (dll.location != dll_info::in_memory)
+ return false;
+
+ if (base_addr != UNSPECIFIED_CORE_ADDR && base_addr == dll.base_addr)
+ return true;
+
+ /* We do not require the end address to be specified - we don't
+ support partially unloaded libraries, anyway. */
+ if ((begin == dll.begin)
+ && (end == UNSPECIFIED_CORE_ADDR || end == dll.end))
+ return true;
+
+ return false;
+ });
+}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index 624e142f557..ab8ffdc1d8c 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -24,19 +24,47 @@ struct process_info;
struct dll_info
{
+ enum location_t
+ {
+ on_disk,
+ in_memory
+ };
+
dll_info (const std::string &name_, CORE_ADDR base_addr_)
- : name (name_), base_addr (base_addr_)
+ : location (on_disk), name (name_), base_addr (base_addr_)
+ {}
+
+ dll_info (CORE_ADDR begin_, CORE_ADDR end_, CORE_ADDR base_addr_)
+ : location (in_memory), begin (begin_), end (end_), base_addr (base_addr_)
{}
+ /* Where the library bits are stored. */
+ location_t location;
+
+ /* The name of a file on disk containing the library.
+
+ This is only valid if LOCATION == ON_DISK. */
std::string name;
+
+ /* The address range in memory containing the library.
+
+ This is only valid if LOCATION == IN_MEMORY. */
+ CORE_ADDR begin;
+ CORE_ADDR end;
+
+ /* The base address at which the library is loaded. */
CORE_ADDR base_addr;
};
extern void loaded_dll (const char *name, CORE_ADDR base_addr);
extern void loaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
+extern void loaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr);
extern void unloaded_dll (const char *name, CORE_ADDR base_addr);
extern void unloaded_dll (process_info *proc, const char *name,
CORE_ADDR base_addr);
+extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
+ CORE_ADDR base_addr);
#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 7ad93e5e303..67ef448780f 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -1901,6 +1901,29 @@ handle_qxfer_features (const char *annex,
return len;
}
+/* Print a qXfer:libraries:read entry for DLL. */
+
+static std::string
+print_qxfer_libraries_entry (const dll_info &dll)
+{
+ switch (dll.location)
+ {
+ case dll_info::in_memory:
+ return string_printf
+ (" <in-memory-library begin=\"0x%s\" end=\"0x%s\">"
+ "<segment address=\"0x%s\"/></in-memory-library>\n",
+ paddress (dll.begin), paddress (dll.end),
+ paddress (dll.base_addr));
+
+ case dll_info::on_disk:
+ return string_printf
+ (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
+ dll.name.c_str (), paddress (dll.base_addr));
+ }
+
+ gdb_assert_not_reached ("unknown dll location: %x", dll.location);
+}
+
/* Handle qXfer:libraries:read. */
static int
@@ -1919,9 +1942,7 @@ handle_qxfer_libraries (const char *annex,
process_info *proc = current_process ();
for (const dll_info &dll : proc->all_dlls)
- document += string_printf
- (" <library name=\"%s\"><segment address=\"0x%s\"/></library>\n",
- dll.name.c_str (), paddress (dll.base_addr));
+ document += print_qxfer_libraries_entry (dll);
document += "</library-list>\n";
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 34/44] gdb, gdbserver: library notifications
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (32 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 33/44] gdb, gdbserver, ze: in-memory libraries Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 35/44] gdbserver, ze, intelgt: introduce ze-low and intelgt-ze-low targets Markus Metzger
` (9 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches
Support asynchronous library notifications for non-stop remote targets.
Libraries for GPU targets are loaded onto the device by the host process.
The IntelGT remote target models devices as separate inferiors. This
allows debugging only the device code of a process or only a particular
device.
To learn about changes in loaded device libraries, the target receives
events from the Level-Zero debug API. Such events do not bind to a stop
on the device (there may not even be any threads dispatched).
To ensure that GDB had the chance to insert breakpoint into newly loaded
libraries, the host thread that loaded a library will be blocked inside a
system call until the library load is acknowledged by GDB.
Add a new notification
%Library:<pid>
to asynchronously indicate changes in loaded libraries. This is similar
to the 'library' stop reason.
In the traditional flow, the thread that loaded or unloaded a library
would hit a magic breakpoint and gdbserver would report the stop to GDB
with the library stop reason. GDB would then query the list of libraries
of the current process, insert breakpoints that bind into newly loaded
libraries, and, when it is done, resume the thread.
In this new flow, gdbserver would receive a library event and notify GDB.
GDB would then query the list of libraries for the process indicated by
the notification, insert breakpoints that bind into newly loaded
libraries, and, when it is done, acknowledge the notification.
Since libraries are changed from the outside, new libraries may be added
and gdbserver may receive new library load events. To ensure that we
acknowledge a library only when it has been processed by GDB, we hide
newly added libraries until we send a %Library notification. When GDB
acknowledges the notification, we know which libraries GDB has seen.
A remote target needs to choose whether it wants to use library stop
reasons or library notifications.
---
gdb/NEWS | 4 ++
gdb/doc/gdb.texinfo | 8 ++++
gdb/remote-notif.c | 1 +
gdb/remote-notif.h | 2 +
gdb/remote.c | 92 +++++++++++++++++++++++++++++++++++++++
gdbserver/dll.cc | 46 ++++++++++++++++++++
gdbserver/dll.h | 21 +++++++++
gdbserver/notif.cc | 4 ++
gdbserver/notif.h | 4 ++
gdbserver/remote-utils.cc | 3 +-
gdbserver/server.cc | 56 +++++++++++++++++++++++-
gdbserver/server.h | 3 ++
gdbserver/target.cc | 18 ++++++++
gdbserver/target.h | 34 +++++++++++++++
14 files changed, 294 insertions(+), 2 deletions(-)
diff --git a/gdb/NEWS b/gdb/NEWS
index 879f8e8865d..e6ed63128ec 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -18,6 +18,10 @@ qXfer:libraries:read's response
The qXfer:libraries:read query supports reporting in-memory libraries.
Older GDB will silently ignore them.
+%Library
+ This notification informs about asynchronous changes in the loaded
+ libraries of a process.
+
*** Changes in GDB 18
* Support for the Common Trace Format (CTF) has been removed. GDB now
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 4cc755a0999..b36b7d01b23 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -47592,6 +47592,14 @@ for information on how these notifications are acknowledged by
@value{GDBN}.
@tab Report an asynchronous stop event in non-stop mode.
+@item Library
+@tab vLibrary
+@tab @var{pid}. The process for which libraries have changed.
+@tab Report asynchronous changes in the loaded libraries.
+@value{GDBN} should use @samp{qXfer:libraries:read} to fetch a new
+list of loaded libraries and acknowledge the notification when the
+changes have been processed.
+
@end multitable
@node Remote Non-Stop
diff --git a/gdb/remote-notif.c b/gdb/remote-notif.c
index 4884761f3ad..7fd9ad80a3d 100644
--- a/gdb/remote-notif.c
+++ b/gdb/remote-notif.c
@@ -48,6 +48,7 @@ bool notif_debug = false;
static const notif_client *const notifs[] =
{
¬if_client_stop,
+ ¬if_client_library,
};
static_assert (ARRAY_SIZE (notifs) == REMOTE_NOTIF_LAST);
diff --git a/gdb/remote-notif.h b/gdb/remote-notif.h
index cec7db8025b..85b49eee245 100644
--- a/gdb/remote-notif.h
+++ b/gdb/remote-notif.h
@@ -41,6 +41,7 @@ using notif_event_up = std::unique_ptr<notif_event>;
enum REMOTE_NOTIF_ID
{
REMOTE_NOTIF_STOP = 0,
+ REMOTE_NOTIF_LIBRARY,
REMOTE_NOTIF_LAST,
};
@@ -128,6 +129,7 @@ void remote_notif_process (struct remote_notif_state *state,
remote_notif_state *remote_notif_state_allocate (remote_target *remote);
extern const notif_client notif_client_stop;
+extern const notif_client notif_client_library;
extern bool notif_debug;
diff --git a/gdb/remote.c b/gdb/remote.c
index 10bceb2c798..20447fb5480 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -108,7 +108,9 @@ struct protocol_feature;
struct packet_reg;
struct stop_reply;
+struct library_reply;
using stop_reply_up = std::unique_ptr<stop_reply>;
+using library_reply_up = std::unique_ptr<library_reply>;
/* Generic configuration support for packets the stub optionally
supports. Allows the user to specify the use of the packet as well
@@ -1641,6 +1643,17 @@ struct stop_reply : public notif_event
int core;
};
+struct library_reply : public notif_event
+{
+ /* The identifier of the process about this event. */
+ int pid;
+
+ /* The remote state this event is associated with. When the remote
+ connection, represented by a remote_state object, is closed,
+ all the associated library_reply events should be released. */
+ struct remote_state *rs;
+};
+
/* Return TARGET as a remote_target if it is one, else nullptr. */
static remote_target *
@@ -5312,6 +5325,16 @@ as_stop_reply_up (notif_event_up event)
return stop_reply_up (stop_reply);
}
+/* Transfer ownership of the library_reply owned by EVENT to a
+ library_reply_up object. */
+
+static library_reply_up
+as_library_reply_up (notif_event_up event)
+{
+ library_reply *reply = static_cast<library_reply *> (event.release ());
+ return library_reply_up (reply);
+}
+
/* Read, decode, and return a hex-encoded string from *PTR. Update *PTR to
point at the first character past the end of the string that was read
in. */
@@ -8161,6 +8184,75 @@ const notif_client notif_client_stop =
REMOTE_NOTIF_STOP,
};
+static void
+remote_notif_library_parse (remote_target *remote,
+ const notif_client *self, const char *buf,
+ notif_event *event)
+{
+ library_reply *reply = (library_reply *) event;
+
+ ULONGEST pid;
+ const char *endp = unpack_varlen_hex (buf, &pid);
+ if (endp == buf)
+ error (_("No process id in Library notification: %s."), buf);
+ if (endp[0] != 0)
+ error (_("Trailing junk in Library notification: %s."), buf);
+
+ reply->pid = (int) pid;
+ if (((ULONGEST) reply->pid) != pid)
+ error (_("Bad Library notification process id: %s."), buf);
+}
+
+static void
+remote_notif_library_ack (remote_target *remote,
+ const notif_client *self, const char *buf,
+ notif_event_up event)
+{
+ library_reply_up reply = as_library_reply_up (std::move (event));
+
+ scoped_restore_current_thread restore_thread;
+ inferior *process = find_inferior_pid (remote, reply->pid);
+ if (process == nullptr)
+ {
+ process = remote->remote_add_inferior (false, reply->pid, -1, 1);
+ process->needs_setup = 1;
+ }
+
+ switch_to_inferior_no_thread (process);
+ if (process->needs_setup)
+ setup_inferior (0);
+
+ handle_solib_event ();
+
+ putpkt (remote, self->ack_command);
+}
+
+static int
+remote_notif_library_can_get_pending_events (remote_target *remote,
+ const notif_client *self)
+{
+ return 1;
+}
+
+static notif_event_up
+remote_notif_library_alloc_reply ()
+{
+ return notif_event_up (new library_reply ());
+}
+
+/* A client of notification Library. */
+
+const notif_client notif_client_library =
+{
+ "Library",
+ "vLibrary",
+ remote_notif_library_parse,
+ remote_notif_library_ack,
+ remote_notif_library_can_get_pending_events,
+ remote_notif_library_alloc_reply,
+ REMOTE_NOTIF_LIBRARY,
+};
+
/* If CONTEXT contains any fork/vfork/clone child threads that have
not been reported yet, remove them from the CONTEXT list. If such
a thread exists it is because we are stopped at a fork/vfork/clone
diff --git a/gdbserver/dll.cc b/gdbserver/dll.cc
index 32d6dfb21a7..c2accb1ab7e 100644
--- a/gdbserver/dll.cc
+++ b/gdbserver/dll.cc
@@ -154,3 +154,49 @@ unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
return false;
});
}
+
+/* See dll.h. */
+
+void
+notify_dlls (process_info *proc)
+{
+ if (proc == nullptr)
+ return;
+
+ for (dll_info &dll : proc->all_dlls)
+ dll.hidden = false;
+}
+
+/* Acknowledge DLL in PROC. */
+
+static void
+ack_dll (process_info *proc, const dll_info &dll)
+{
+ switch (dll.location)
+ {
+ case dll_info::on_disk:
+ target_ack_library (proc, dll.name.c_str ());
+ return;
+
+ case dll_info::in_memory:
+ target_ack_in_memory_library (proc, dll.begin, dll.end);
+ return;
+ }
+
+ gdb_assert_not_reached ("unknown dll location: %x", dll.location);
+}
+
+/* See dll.h. */
+
+void
+ack_dlls (process_info *proc)
+{
+ if (proc == nullptr)
+ return;
+
+ for (dll_info &dll : proc->all_dlls)
+ {
+ if (!dll.hidden)
+ ack_dll (proc, dll);
+ }
+}
diff --git a/gdbserver/dll.h b/gdbserver/dll.h
index ab8ffdc1d8c..6a54ce3ece3 100644
--- a/gdbserver/dll.h
+++ b/gdbserver/dll.h
@@ -54,6 +54,21 @@ struct dll_info
/* The base address at which the library is loaded. */
CORE_ADDR base_addr;
+
+ /* Whether to tell GDB about this library.
+
+ We use this when library notifications are used to track which
+ libraries belong to the current library event, so when GDB
+ acknowledges the event, we know which libraries GDB has acknowledged.
+
+ New libraries that were added in the meantime need to wait for the
+ next library event.
+
+ For targets that do not use library notifications, this will be
+ ignored and GDB will always get the full list of libraries. This
+ means that library annotations in stop replies cannot be mixed with
+ library notifications. */
+ bool hidden = true;
};
extern void loaded_dll (const char *name, CORE_ADDR base_addr);
@@ -67,4 +82,10 @@ extern void unloaded_dll (process_info *proc, const char *name,
extern void unloaded_dll (process_info *proc, CORE_ADDR begin, CORE_ADDR end,
CORE_ADDR base_addr);
+/* Clear the hidden flag for all libraries in PROC. */
+extern void notify_dlls (process_info *proc);
+
+/* Acknowledge all non-hidden libraries. */
+extern void ack_dlls (process_info *proc);
+
#endif /* GDBSERVER_DLL_H */
diff --git a/gdbserver/notif.cc b/gdbserver/notif.cc
index 1745a96a051..c5649173eb6 100644
--- a/gdbserver/notif.cc
+++ b/gdbserver/notif.cc
@@ -52,6 +52,7 @@
static struct notif_server *notifs[] =
{
¬if_stop,
+ ¬if_library,
};
/* Write another event or an OK, if there are no more left, to
@@ -104,6 +105,9 @@ handle_notif_ack (char *own_buf, int packet_len)
remote_debug_printf ("%s: acking %d", np->ack_name,
(int) np->queue.size ());
+ if (np->ack != nullptr)
+ np->ack (head);
+
delete head;
}
diff --git a/gdbserver/notif.h b/gdbserver/notif.h
index 18b507dce5b..49805b0048f 100644
--- a/gdbserver/notif.h
+++ b/gdbserver/notif.h
@@ -54,10 +54,14 @@ struct notif_server
/* Write event EVENT to OWN_BUF. */
void (*write) (struct notif_event *event, char *own_buf);
+
+ /* EVENT has been acknowledged by GDB. */
+ void (*ack) (struct notif_event *event);
};
using notif_server_p = struct notif_server *;
extern struct notif_server notif_stop;
+extern struct notif_server notif_library;
int handle_notif_ack (char *own_buf, int packet_len);
void notif_write_event (struct notif_server *notif, char *own_buf);
diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index ffde4956776..ffd7d3b0fd5 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1239,7 +1239,8 @@ prepare_resume_reply (char *buf, ptid_t ptid, const target_waitstatus &status)
}
}
- if (current_process ()->dlls_changed)
+ if (current_process ()->dlls_changed
+ && !target_uses_library_notifications ())
{
strcpy (buf, "library:;");
buf += strlen (buf);
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 67ef448780f..880c077286d 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -272,9 +272,60 @@ in_queued_stop_replies (ptid_t ptid)
struct notif_server notif_stop =
{
- "vStopped", "Stop", {}, vstop_notif_reply,
+ "vStopped", "Stop", {}, vstop_notif_reply, nullptr,
};
+/* Library notifications. */
+
+struct vlibrary_notif : public notif_event
+{
+ vlibrary_notif (ptid_t::pid_type pid_) : pid (pid_)
+ {}
+
+ /* The process that got the event. */
+ int pid;
+};
+
+static void
+vlibrary_notif_reply (struct notif_event *event, char *own_buf)
+{
+ gdb_assert (target_uses_library_notifications ());
+
+ vlibrary_notif *notif = (vlibrary_notif *) event;
+ int pid = notif->pid;
+
+ process_info *process = find_process_pid (pid);
+ notify_dlls (process);
+
+ if (pid < 0)
+ sprintf (own_buf, "-%x", pid);
+ else
+ sprintf (own_buf, "%x", pid);
+}
+
+static void
+vlibrary_notif_ack (struct notif_event *event)
+{
+ vlibrary_notif *notif = (vlibrary_notif *) event;
+ process_info *process = find_process_pid (notif->pid);
+ ack_dlls (process);
+}
+
+struct notif_server notif_library =
+{
+ "vLibrary", "Library", {}, vlibrary_notif_reply, vlibrary_notif_ack,
+};
+
+void
+push_notif_library (process_info *process)
+{
+ if (!process->dlls_changed)
+ return;
+
+ notif_push (¬if_library, new vlibrary_notif (process->pid));
+ process->dlls_changed = false;
+}
+
static int
target_running (void)
{
@@ -1906,6 +1957,9 @@ handle_qxfer_features (const char *annex,
static std::string
print_qxfer_libraries_entry (const dll_info &dll)
{
+ if (dll.hidden && target_uses_library_notifications ())
+ return "";
+
switch (dll.location)
{
case dll_info::in_memory:
diff --git a/gdbserver/server.h b/gdbserver/server.h
index bbba3650de7..23ac219138e 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -92,6 +92,9 @@ extern void discard_queued_stop_replies (ptid_t ptid);
the vStopped notifications queue. */
extern int in_queued_stop_replies (ptid_t ptid);
+/* Push a notification to GDB for library changes of PROCESS. */
+void push_notif_library (process_info *process);
+
#include "remote-utils.h"
#include "utils.h"
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 5f0d9000254..3c569c8c66c 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -875,3 +875,21 @@ process_stratum_target::get_ipa_tdesc_idx ()
{
return 0;
}
+
+bool
+process_stratum_target::uses_library_notifications ()
+{
+ return false;
+}
+
+void
+process_stratum_target::ack_library (process_info *process, const char *name)
+{
+}
+
+void
+process_stratum_target::ack_in_memory_library (process_info *process,
+ CORE_ADDR begin,
+ CORE_ADDR end)
+{
+}
diff --git a/gdbserver/target.h b/gdbserver/target.h
index e38570dabb2..685bdb8d113 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -535,6 +535,21 @@ class process_stratum_target
Returns true if successful and false otherwise. */
virtual bool store_memtags (CORE_ADDR address, size_t len,
const gdb::byte_vector &tags, int type);
+
+ /* Whether the target uses library notifications instead of library
+ clauses in stop replies. */
+ virtual bool uses_library_notifications ();
+
+ /* Acknowledge a library reported by name.
+
+ This if only relevant if using library notifications. */
+ virtual void ack_library (process_info *process, const char *name);
+
+ /* Acknowledge an in-memory library reported by address.
+
+ This if only relevant if using library notifications. */
+ virtual void ack_in_memory_library (process_info *process, CORE_ADDR begin,
+ CORE_ADDR end);
};
extern process_stratum_target *the_target;
@@ -732,6 +747,25 @@ target_thread_pending_child (thread_info *thread, target_waitkind *kind)
return the_target->thread_pending_child (thread, kind);
}
+static inline bool
+target_uses_library_notifications ()
+{
+ return the_target->uses_library_notifications ();
+}
+
+static inline void
+target_ack_library (process_info *process, const char *name)
+{
+ the_target->ack_library (process, name);
+}
+
+static inline void
+target_ack_in_memory_library (process_info *process, CORE_ADDR begin,
+ CORE_ADDR end)
+{
+ the_target->ack_in_memory_library (process, begin, end);
+}
+
/* Read LEN bytes from MEMADDR in the buffer MYADDR. Return 0 if the read
is successful, otherwise, return a non-zero error code. */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 35/44] gdbserver, ze, intelgt: introduce ze-low and intelgt-ze-low targets
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (33 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 34/44] gdb, gdbserver: library notifications Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 36/44] testsuite, sycl: add SYCL support Markus Metzger
` (8 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur, Natalia Saiapova
Add the Level-Zero based intelgt target. It is configured as
intelgt-*-zebin
This adds fundamental debug support for Intel GT devices. In the future,
we plan to add more patches that improve the performance as well the user
experience. Those patches are already available in the downstream "Intel
Distribution for GDB" debugger at
https://github.com/intel/gdb
On attach to PID, attach to that PID on all supported devices and create
target descriptions and an inferior for each device. We do not allow
attaching to or detaching from and reattaching to individual devices.
The target can be used in combination with a native target, relying on
GDB's multi-target feature, to debug the GPU and the host application in
the same debug session. For this, bring the native app to a state where
the Level-Zero backend for the GPU has been initialized, then create a
gdbserver instance and connect to it from a second inferior.
Below is a sample session that shows how to do this manually. In the
downstream debugger, a Python script is used to take these steps in an
automated manner for better user experience.
$ gdb demo
...
(gdb) maintenance set target-non-stop on
(gdb) tbreak 60
Temporary breakpoint 1 at 0x4049c8: file demo.cpp, line 60.
(gdb) run
...
Thread 1 "demo" hit Temporary breakpoint 1, main (argc=1, argv=0x7fffffffd9b8) at demo.cpp:60
60 range data_range{length};
(gdb)
Connect the Intel GT gdbserver by specifying the host inferior PID.
(gdb) add-inferior -no-connection
[New inferior 2]
Added inferior 2
(gdb) inferior 2
[Switching to inferior 2 [<null>] (<noexec>)]
(gdb) info inferiors
Num Description Connection Executable
1 process 16458 1 (native) /tmp/demo
* 2 <null>
(gdb) target extended-remote | gdbserver-intelgt --multi -
Remote debugging using | gdbserver-intelgt --multi -
Remote debugging using stdio
(gdb) attach 16458
Attaching to process 16458
Attached; given pid = 16458, updated to 1
(gdb)
For "continue" to conveniently resume both inferiors, set the
'schedule-multi' mode. Then switch back to the host inferior, define a
breakpoint inside the kernel, and continue to that BP.
(gdb) set schedule-multiple on
(gdb) inferior 1
[Switching to inferior 1 [process 16458] (/tmp/demo)]
[Switching to thread 1.1 (Thread 0x7ffff7bd8cc0 (LWP 16458))]
(gdb) break demo.cpp:32
Breakpoint 2 at 0x40651f: file demo.cpp, line 32.
(gdb) continue
Continuing.
...
Thread 2.201 hit Breakpoint 2.2, compute (index=sycl::_V1::id<1> = {...},
element=116) at demo.cpp:32
32 size_t id0 = GetDim(index, 0);
(gdb)
On Intel GT, the 32b IP register holds the offset from the 64b Instruction
Base Address, which we model as virtual 'isabase' register.
We port async support from linux-low.cc. There is no need to mask SIGCHLD
during pipe initialization as we're not using ptrace. We rely on GDB
explicitly requesting all-stop/non-stop mode to enable async.
For Level-Zero targets, module load and unload events are sent in the
context of the host process. They block a host system call until an
attached debugger acknowledges the event to give it enough time to place
breakpoints before the module may be used.
Accessing the memory requires specifying a debug session handle, which is
available through a thread. For convenience, we introduce overloaded
versions of read_memory and write_memory that take a `thread_info *` to
denote the context in which memory should be accessed. The existing
read_memory and write_memory target ops use the overloaded versions simply
by passing current_thread.
Co-authored-by: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
---
gdbserver/Makefile.in | 4 +-
gdbserver/config.in | 3 +
gdbserver/configure | 500 ++++++
gdbserver/configure.ac | 18 +
gdbserver/configure.srv | 4 +
gdbserver/gdbthread.h | 2 +-
gdbserver/intelgt-ze-low.cc | 1002 ++++++++++++
gdbserver/ze-low.cc | 2846 +++++++++++++++++++++++++++++++++++
gdbserver/ze-low.h | 465 ++++++
9 files changed, 4842 insertions(+), 2 deletions(-)
create mode 100644 gdbserver/intelgt-ze-low.cc
create mode 100644 gdbserver/ze-low.cc
create mode 100644 gdbserver/ze-low.h
diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index e45c89dc321..0da993c6eb0 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -104,6 +104,8 @@ LIBIBERTY = $(LIBIBERTY_NORMAL)
GDBSUPPORT_BUILDDIR = ../gdbsupport
GDBSUPPORT = $(GDBSUPPORT_BUILDDIR)/libgdbsupport.a
+LIBZE_LOADER = @LIBZE_LOADER@
+
# gnulib
GNULIB_PARENT_DIR = ..
include $(GNULIB_PARENT_DIR)/gnulib/Makefile.gnulib.inc
@@ -369,7 +371,7 @@ gdbserver$(EXEEXT): $(sort $(OBS)) ${CDEPS} $(LIBGNU) $(LIBIBERTY) \
$(CXXFLAGS) \
-o gdbserver$(EXEEXT) $(OBS) $(GDBSUPPORT) $(LIBGNU) \
$(LIBGNU_EXTRA_LIBS) $(LIBIBERTY) $(INTL) \
- $(GDBSERVER_LIBS) $(XM_CLIBS) $(WIN32APILIBS) $(MAYBE_LIBICONV)
+ $(GDBSERVER_LIBS) $(XM_CLIBS) $(WIN32APILIBS) $(MAYBE_LIBICONV) $(LIBZE_LOADER)
gdbreplay$(EXEEXT): $(sort $(GDBREPLAY_OBS)) $(LIBGNU) $(LIBIBERTY) \
$(INTL_DEPS) $(GDBSUPPORT)
diff --git a/gdbserver/config.in b/gdbserver/config.in
index ead66ce7a57..53fde1fe53d 100644
--- a/gdbserver/config.in
+++ b/gdbserver/config.in
@@ -164,6 +164,9 @@
/* Define if you have the xxhash library. */
#undef HAVE_LIBXXHASH
+/* Define if you have the ze_loader library. */
+#undef HAVE_LIBZE_LOADER
+
/* Define if the target supports branch tracing. */
#undef HAVE_LINUX_BTRACE
diff --git a/gdbserver/configure b/gdbserver/configure
index 46faecc3a20..7b1a059f134 100755
--- a/gdbserver/configure
+++ b/gdbserver/configure
@@ -631,6 +631,9 @@ srv_xmlfiles
srv_xmlbuiltin
GDBSERVER_LIBS
GDBSERVER_DEPFILES
+LTLIBZE_LOADER
+LIBZE_LOADER
+HAVE_LIBZE_LOADER
RDYNAMIC
REPORT_BUGS_TEXI
REPORT_BUGS_TO
@@ -780,6 +783,8 @@ with_pkgversion
with_bugurl
with_libthread_db
enable_inprocess_agent
+with_libze_loader_prefix
+with_libze_loader_type
'
ac_precious_vars='build_alias
host_alias
@@ -1450,6 +1455,9 @@ Optional Packages:
--with-bugurl=URL Direct users to URL to report a bug
--with-libthread-db=PATH
use given libthread_db directly
+ --with-libze_loader-prefix[=DIR] search for libze_loader in DIR/include and DIR/lib
+ --without-libze_loader-prefix don't search for libze_loader in includedir and libdir
+ --with-libze_loader-type=TYPE type of library to search for (auto/static/shared)
Some influential environment variables:
CC C compiler command
@@ -14502,6 +14510,498 @@ fi
+
+ use_additional=yes
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+
+# Check whether --with-libze_loader-prefix was given.
+if test "${with_libze_loader_prefix+set}" = set; then :
+ withval=$with_libze_loader_prefix;
+ if test "X$withval" = "Xno"; then
+ use_additional=no
+ else
+ if test "X$withval" = "X"; then
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+
+ eval additional_includedir=\"$includedir\"
+ eval additional_libdir=\"$libdir\"
+
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ else
+ additional_includedir="$withval/include"
+ additional_libdir="$withval/lib"
+ fi
+ fi
+
+fi
+
+
+# Check whether --with-libze_loader-type was given.
+if test "${with_libze_loader_type+set}" = set; then :
+ withval=$with_libze_loader_type; with_libze_loader_type=$withval
+else
+ with_libze_loader_type=auto
+fi
+
+ lib_type=`eval echo \$with_libze_loader_type`
+
+ LIBZE_LOADER=
+ LTLIBZE_LOADER=
+ INCZE_LOADER=
+ rpathdirs=
+ ltrpathdirs=
+ names_already_handled=
+ names_next_round='ze_loader '
+ while test -n "$names_next_round"; do
+ names_this_round="$names_next_round"
+ names_next_round=
+ for name in $names_this_round; do
+ already_handled=
+ for n in $names_already_handled; do
+ if test "$n" = "$name"; then
+ already_handled=yes
+ break
+ fi
+ done
+ if test -z "$already_handled"; then
+ names_already_handled="$names_already_handled $name"
+ uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+ eval value=\"\$HAVE_LIB$uppername\"
+ if test -n "$value"; then
+ if test "$value" = yes; then
+ eval value=\"\$LIB$uppername\"
+ test -z "$value" || LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$value"
+ eval value=\"\$LTLIB$uppername\"
+ test -z "$value" || LTLIBZE_LOADER="${LTLIBZE_LOADER}${LTLIBZE_LOADER:+ }$value"
+ else
+ :
+ fi
+ else
+ found_dir=
+ found_la=
+ found_so=
+ found_a=
+ if test $use_additional = yes; then
+ if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+ found_dir="$additional_libdir"
+ found_so="$additional_libdir/lib$name.$shlibext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ elif test x$lib_type != xshared; then
+ if test -f "$additional_libdir/lib$name.$libext"; then
+ found_dir="$additional_libdir"
+ found_a="$additional_libdir/lib$name.$libext"
+ if test -f "$additional_libdir/lib$name.la"; then
+ found_la="$additional_libdir/lib$name.la"
+ fi
+ fi
+ fi
+ fi
+ if test "X$found_dir" = "X"; then
+ for x in $LDFLAGS $LTLIBZE_LOADER; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ case "$x" in
+ -L*)
+ dir=`echo "X$x" | sed -e 's/^X-L//'`
+ if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+ found_dir="$dir"
+ found_so="$dir/lib$name.$shlibext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ elif test x$lib_type != xshared; then
+ if test -f "$dir/lib$name.$libext"; then
+ found_dir="$dir"
+ found_a="$dir/lib$name.$libext"
+ if test -f "$dir/lib$name.la"; then
+ found_la="$dir/lib$name.la"
+ fi
+ fi
+ fi
+ ;;
+ esac
+ if test "X$found_dir" != "X"; then
+ break
+ fi
+ done
+ fi
+ if test "X$found_dir" != "X"; then
+ LTLIBZE_LOADER="${LTLIBZE_LOADER}${LTLIBZE_LOADER:+ }-L$found_dir -l$name"
+ if test "X$found_so" != "X"; then
+ if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$found_so"
+ else
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $found_dir"
+ fi
+ if test "$hardcode_direct" = yes; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$found_so"
+ else
+ if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$found_so"
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $found_dir"
+ fi
+ else
+ haveit=
+ for x in $LDFLAGS $LIBZE_LOADER; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$found_dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }-L$found_dir"
+ fi
+ if test "$hardcode_minus_L" != no; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$found_so"
+ else
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }-l$name"
+ fi
+ fi
+ fi
+ fi
+ else
+ if test "X$found_a" != "X"; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$found_a"
+ else
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }-L$found_dir -l$name"
+ fi
+ fi
+ additional_includedir=
+ case "$found_dir" in
+ */lib | */lib/)
+ basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+ additional_includedir="$basedir/include"
+ ;;
+ esac
+ if test "X$additional_includedir" != "X"; then
+ if test "X$additional_includedir" != "X/usr/include"; then
+ haveit=
+ if test "X$additional_includedir" = "X/usr/local/include"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ for x in $CPPFLAGS $INCZE_LOADER; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-I$additional_includedir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_includedir"; then
+ INCZE_LOADER="${INCZE_LOADER}${INCZE_LOADER:+ }-I$additional_includedir"
+ fi
+ fi
+ fi
+ fi
+ fi
+ if test -n "$found_la"; then
+ save_libdir="$libdir"
+ case "$found_la" in
+ */* | *\\*) . "$found_la" ;;
+ *) . "./$found_la" ;;
+ esac
+ libdir="$save_libdir"
+ for dep in $dependency_libs; do
+ case "$dep" in
+ -L*)
+ additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+ if test "X$additional_libdir" != "X/usr/lib"; then
+ haveit=
+ if test "X$additional_libdir" = "X/usr/local/lib"; then
+ if test -n "$GCC"; then
+ case $host_os in
+ linux*) haveit=yes;;
+ esac
+ fi
+ fi
+ if test -z "$haveit"; then
+ haveit=
+ for x in $LDFLAGS $LIBZE_LOADER; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }-L$additional_libdir"
+ fi
+ fi
+ haveit=
+ for x in $LDFLAGS $LTLIBZE_LOADER; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X-L$additional_libdir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ if test -d "$additional_libdir"; then
+ LTLIBZE_LOADER="${LTLIBZE_LOADER}${LTLIBZE_LOADER:+ }-L$additional_libdir"
+ fi
+ fi
+ fi
+ fi
+ ;;
+ -R*)
+ dir=`echo "X$dep" | sed -e 's/^X-R//'`
+ if test "$enable_rpath" != no; then
+ haveit=
+ for x in $rpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ rpathdirs="$rpathdirs $dir"
+ fi
+ haveit=
+ for x in $ltrpathdirs; do
+ if test "X$x" = "X$dir"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ ltrpathdirs="$ltrpathdirs $dir"
+ fi
+ fi
+ ;;
+ -l*)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+ ;;
+ *.la)
+ names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+ ;;
+ *)
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$dep"
+ LTLIBZE_LOADER="${LTLIBZE_LOADER}${LTLIBZE_LOADER:+ }$dep"
+ ;;
+ esac
+ done
+ fi
+ else
+ if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }-l$name"
+ LTLIBZE_LOADER="${LTLIBZE_LOADER}${LTLIBZE_LOADER:+ }-l$name"
+ else
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }-l:lib$name.$libext"
+ LTLIBZE_LOADER="${LTLIBZE_LOADER}${LTLIBZE_LOADER:+ }-l:lib$name.$libext"
+ fi
+ fi
+ fi
+ fi
+ done
+ done
+ if test "X$rpathdirs" != "X"; then
+ if test -n "$hardcode_libdir_separator"; then
+ alldirs=
+ for found_dir in $rpathdirs; do
+ alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+ done
+ acl_save_libdir="$libdir"
+ libdir="$alldirs"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$flag"
+ else
+ for found_dir in $rpathdirs; do
+ acl_save_libdir="$libdir"
+ libdir="$found_dir"
+ eval flag=\"$hardcode_libdir_flag_spec\"
+ libdir="$acl_save_libdir"
+ LIBZE_LOADER="${LIBZE_LOADER}${LIBZE_LOADER:+ }$flag"
+ done
+ fi
+ fi
+ if test "X$ltrpathdirs" != "X"; then
+ for found_dir in $ltrpathdirs; do
+ LTLIBZE_LOADER="${LTLIBZE_LOADER}${LTLIBZE_LOADER:+ }-R$found_dir"
+ done
+ fi
+
+
+ ac_save_CPPFLAGS="$CPPFLAGS"
+
+ for element in $INCZE_LOADER; do
+ haveit=
+ for x in $CPPFLAGS; do
+
+ acl_save_prefix="$prefix"
+ prefix="$acl_final_prefix"
+ acl_save_exec_prefix="$exec_prefix"
+ exec_prefix="$acl_final_exec_prefix"
+ eval x=\"$x\"
+ exec_prefix="$acl_save_exec_prefix"
+ prefix="$acl_save_prefix"
+
+ if test "X$x" = "X$element"; then
+ haveit=yes
+ break
+ fi
+ done
+ if test -z "$haveit"; then
+ CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+ fi
+ done
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libze_loader" >&5
+$as_echo_n "checking for libze_loader... " >&6; }
+if ${ac_cv_libze_loader+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ ac_save_LIBS="$LIBS"
+ LIBS="$LIBS $LIBZE_LOADER"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include "level_zero/zet_api.h"
+int
+main ()
+{
+zeInit (0);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ ac_cv_libze_loader=yes
+else
+ ac_cv_libze_loader=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ LIBS="$ac_save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libze_loader" >&5
+$as_echo "$ac_cv_libze_loader" >&6; }
+ if test "$ac_cv_libze_loader" = yes; then
+ HAVE_LIBZE_LOADER=yes
+
+$as_echo "#define HAVE_LIBZE_LOADER 1" >>confdefs.h
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libze_loader" >&5
+$as_echo_n "checking how to link with libze_loader... " >&6; }
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBZE_LOADER" >&5
+$as_echo "$LIBZE_LOADER" >&6; }
+ else
+ HAVE_LIBZE_LOADER=no
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBZE_LOADER=
+ LTLIBZE_LOADER=
+ fi
+
+
+
+
+
+
+
+case "${target}" in
+ intelgt-*-ze)
+ if test "$HAVE_LIBZE_LOADER" != yes; then
+ as_fn_error $? "libze_loader is missing or unusable" "$LINENO" 5
+ fi
+ ;;
+ *)
+ # Do not link libze_loader spuriously
+ HAVE_LIBZE_LOADER=no
+ LIBZE_LOADER=
+ LTLIBZE_LOADER=
+ ;;
+esac
+
+
+
+
+
+
+
+
GNULIB=../gnulib/import
GNULIB_STDINT_H=
diff --git a/gdbserver/configure.ac b/gdbserver/configure.ac
index 9979afe7ece..d3076a046c3 100644
--- a/gdbserver/configure.ac
+++ b/gdbserver/configure.ac
@@ -401,6 +401,24 @@ if $want_ipa ; then
fi
fi
+dnl check for the ze loader
+AC_LIB_HAVE_LINKFLAGS([ze_loader], [], [#include "level_zero/zet_api.h"],
+ [zeInit (0);],)
+
+case "${target}" in
+ intelgt-*-ze)
+ if test "$HAVE_LIBZE_LOADER" != yes; then
+ AC_MSG_ERROR([libze_loader is missing or unusable])
+ fi
+ ;;
+ *)
+ # Do not link libze_loader spuriously
+ HAVE_LIBZE_LOADER=no
+ LIBZE_LOADER=
+ LTLIBZE_LOADER=
+ ;;
+esac
+
AC_SUBST(GDBSERVER_DEPFILES)
AC_SUBST(GDBSERVER_LIBS)
AC_SUBST(srv_xmlbuiltin)
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 69611db4698..04ab4c5ae0b 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -153,6 +153,10 @@ case "${gdbserver_host}" in
srv_tgtobj="$srv_linux_obj linux-ia64-low.o"
srv_linux_usrregs=yes
;;
+ intelgt-*-ze) srv_regobj=""
+ srv_xmlfiles=""
+ srv_tgtobj="ze-low.o intelgt-ze-low.o arch/intelgt.o"
+ ;;
loongarch*-*-linux*) srv_tgtobj="arch/loongarch.o linux-loongarch-low.o"
srv_tgtobj="${srv_tgtobj} ${srv_linux_obj}"
srv_tgtobj="$srv_tgtobj nat/loongarch-hw-point.o"
diff --git a/gdbserver/gdbthread.h b/gdbserver/gdbthread.h
index 78bfbc7b772..f27c5b33027 100644
--- a/gdbserver/gdbthread.h
+++ b/gdbserver/gdbthread.h
@@ -42,7 +42,7 @@ struct thread_info : public intrusive_list_node<thread_info>
void set_regcache (std::unique_ptr<struct regcache> regcache)
{ m_regcache = std::move (regcache); }
- void *target_data ()
+ void *target_data () const
{ return m_target_data; }
/* The id of this thread. */
diff --git a/gdbserver/intelgt-ze-low.cc b/gdbserver/intelgt-ze-low.cc
new file mode 100644
index 00000000000..17409ae1466
--- /dev/null
+++ b/gdbserver/intelgt-ze-low.cc
@@ -0,0 +1,1002 @@
+/* Target interface for Intel GT based on Level-Zero for gdbserver.
+
+ Copyright (C) 2020-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "ze-low.h"
+#include "arch/intelgt.h"
+#include "gdbsupport/osabi.h"
+
+#include <level_zero/zet_intel_gpu_debug.h>
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+
+/* FIXME make into a target method? */
+int using_threads = 1;
+
+/* Convenience macros. */
+
+#define dprintf(fmt, ...) \
+ debug_prefixed_printf_cond (debug_threads, "intelgt-ze-low", fmt, \
+ ##__VA_ARGS__)
+
+
+/* Determine the most suitable type to be used for a register with bit size
+ BITSIZE and element size ELEMSIZE. */
+
+static const char *
+intelgt_uint_reg_type (tdesc_feature *feature, uint32_t bitsize,
+ uint32_t elemsize)
+{
+ if (0 != (bitsize % elemsize))
+ error (_("unsupported combination of bitsize %" PRIu32 " and elemsize %"
+ PRIu32), bitsize, elemsize);
+ if ((elemsize < 8) || (elemsize > 128) || ((elemsize & (elemsize - 1)) != 0))
+ error (_("unsupported elemsize %" PRIu32), elemsize);
+
+ std::string type_name = "uint" + std::to_string (elemsize);
+ tdesc_type *type = tdesc_named_type (feature, type_name.c_str ());
+
+ if (elemsize == bitsize)
+ return type->name.c_str ();
+
+ uint32_t elements = bitsize / elemsize;
+ std::string vector_name = "vector" + std::to_string (elements) + "x"
+ + std::to_string (elemsize);
+ tdesc_type *vector
+ = tdesc_create_vector (feature, vector_name.c_str (), type, elements);
+
+ return vector->name.c_str ();
+}
+
+/* Add a (uniform) register set to FEATURE. */
+
+static void
+intelgt_add_regset (tdesc_feature *feature, int ®num, const char *prefix,
+ uint32_t count, const char *group, uint32_t bitsize,
+ const char *type, expedite_t &expedite)
+{
+ for (uint32_t reg = 0; reg < count; ++reg)
+ {
+ std::string name = std::string (prefix) + std::to_string (reg);
+
+ tdesc_create_reg (feature, name.c_str (), regnum++, 1, group,
+ bitsize, type);
+ }
+}
+
+/* Control Register details. */
+
+enum
+{
+ /* The position of the Breakpoint Suppress bit in CR0.0. */
+ INTELGT_CR0_0_BREAKPOINT_SUPPRESS = 15,
+
+ /* The position of the Breakpoint Status and Control bit in CR0.1. */
+ INTELGT_CR0_1_BREAKPOINT_STATUS = 31,
+
+ /* The position of the External Halt Status and Control bit in CR0.1. */
+ INTELGT_CR0_1_EXTERNAL_HALT_STATUS = 30,
+
+ /* The position of the Software Exception Control bit in CR0.1. */
+ INTELGT_CR0_1_SOFTWARE_EXCEPTION_CONTROL = 29,
+
+ /* The position of the Illegal Opcode Exception Status bit in CR0.1. */
+ INTELGT_CR0_1_ILLEGAL_OPCODE_STATUS = 28,
+
+ /* The position of the Force Exception Status and Control bit in CR0.1. */
+ INTELGT_CR0_1_FORCE_EXCEPTION_STATUS = 26,
+
+ /* The position of the Page Fault Status bit in CR0.1.
+ This is a software convention using a reserved bit to indicate
+ page faults by the user mode driver. */
+ INTELGT_CR0_1_PAGEFAULT_STATUS = 16,
+};
+
+/* Find a regset by type. */
+static ze_regset_info
+ze_regset_by_type (const ze_device_info &device, uint32_t type)
+{
+ for (const ze_regset_info ®set : device.regsets)
+ {
+ if (regset.type == type)
+ return regset;
+ }
+
+ internal_error ("Cannot find regset of type %" PRIu32
+ " on device %lu (%s).", type, device.ordinal,
+ device.properties.name);
+}
+
+/* Return CR0 in REGCACHE. */
+
+static std::vector<uint32_t>
+intelgt_read_cr0 (regcache *regcache)
+{
+ int cr0regno = find_regno (regcache->tdesc, "cr0");
+ int cr0size = register_size (regcache->tdesc, cr0regno);
+
+ gdb_assert ((cr0size % sizeof (uint32_t)) == 0);
+ std::vector<uint32_t> cr0 (cr0size / sizeof (uint32_t));
+
+ collect_register (regcache, cr0regno, cr0.data ());
+
+ enum register_status cr0status = regcache->get_register_status (cr0regno);
+ switch (cr0status)
+ {
+ case REG_VALID:
+ return cr0;
+
+ case REG_UNKNOWN:
+ internal_error (_("unknown register 'cr0'."));
+
+ case REG_UNAVAILABLE:
+ error (_("cr0 is not available"));
+ }
+
+ internal_error (_("unknown register status: %d."), cr0status);
+}
+
+/* Write VALUE into CR0 in REGCACHE. */
+
+static void
+intelgt_write_cr0 (regcache *regcache, std::vector<uint32_t> value)
+{
+ int cr0regno = find_regno (regcache->tdesc, "cr0");
+ int cr0size = register_size (regcache->tdesc, cr0regno);
+ gdb_assert (cr0size == (value.size () * sizeof (uint32_t)));
+
+ enum register_status cr0status = regcache->get_register_status (cr0regno);
+ switch (cr0status)
+ {
+ case REG_VALID:
+ supply_register (regcache, cr0regno, value.data ());
+ return;
+
+ case REG_UNKNOWN:
+ internal_error (_("unknown register 'cr0'."));
+
+ case REG_UNAVAILABLE:
+ error (_("cr0 is not available"));
+ }
+
+ internal_error (_("unknown register status: %d."), cr0status);
+}
+
+/* Return CR0 for TP. */
+
+static std::vector<uint32_t>
+intelgt_read_cr0 (thread_info *tp)
+{
+ regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+ return intelgt_read_cr0 (regcache);
+}
+
+/* Return CR0 for TID on DEVICE. */
+
+static std::vector<uint32_t>
+intelgt_read_cr0 (const ze_device_info &device,
+ const ze_device_thread_t &tid)
+{
+ thread_info *tp = ze_find_thread (device, tid);
+ if (tp != nullptr)
+ return intelgt_read_cr0 (tp);
+
+ /* If we do not have a thread for TID, yet, read the register directly.
+
+ We use this in get_stop_reason () and, depending on what we read and
+ if we're able to read registers at all, we may create a thread. */
+ uint32_t type = ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU;
+ ze_regset_info regset = ze_regset_by_type (device, type);
+
+ gdb_assert ((regset.size % sizeof (uint32_t)) == 0);
+ std::vector<uint32_t> cr0 (regset.size / sizeof (uint32_t));
+
+ ze_result_t status
+ = zetDebugReadRegisters (device.session, tid, type, 0, 1, cr0.data ());
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ return cr0;
+
+ default:
+ error (_("Error %x reading CR0 for %s on device %lu (%s)."), status,
+ ze_thread_id_str (tid).c_str (), device.ordinal,
+ device.properties.name);
+ }
+}
+
+/* Write VALUE into CR0 for TP. */
+
+static void
+intelgt_write_cr0 (thread_info *tp, std::vector<uint32_t> value)
+{
+ struct regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+ intelgt_write_cr0 (regcache, value);
+}
+
+/* Write VALUE into CR0 for TID on DEVICE. */
+
+static void
+intelgt_write_cr0 (const ze_device_info &device,
+ const ze_device_thread_t &tid,
+ std::vector<uint32_t> value)
+{
+ thread_info *tp = ze_find_thread (device, tid);
+ if (tp != nullptr)
+ {
+ intelgt_write_cr0 (tp, value);
+ return;
+ }
+
+ /* If we do not have a thread for TID, yet, write the register directly.
+
+ We use this in get_stop_reason () and, depending on what we read, we
+ may create a thread later. Or, we may want to clear the exception
+ status and not create a thread, at all. */
+ uint32_t type = ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU;
+ ze_regset_info regset = ze_regset_by_type (device, type);
+ gdb_assert (regset.size == (value.size () * sizeof (uint32_t)));
+
+ ze_result_t status
+ = zetDebugWriteRegisters (device.session, tid, type, 0, 1,
+ value.data ());
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ return;
+
+ default:
+ error (_("Error %x writing CR0 for %s on device %lu (%s)."), status,
+ ze_thread_id_str (tid).c_str (), device.ordinal,
+ device.properties.name);
+ }
+}
+
+/* Target op definitions for Intel GT target based on Level-Zero. */
+
+class intelgt_ze_target : public ze_target
+{
+public:
+ const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
+
+ bool supports_stopped_by_sw_breakpoint () override { return true; }
+ bool stopped_by_sw_breakpoint () override;
+
+ CORE_ADDR read_pc (regcache *regcache) override;
+ void write_pc (regcache *regcache, CORE_ADDR pc) override;
+
+protected:
+ bool is_device_supported
+ (const ze_device_properties_t &,
+ const std::vector<zet_debug_regset_properties_t> &) override;
+
+ target_desc *create_tdesc
+ (ze_device_info *dinfo,
+ const std::vector<zet_debug_regset_properties_t> &,
+ const ze_pci_ext_properties_t &) override;
+
+ target_stop_reason get_stop_reason (const ze_device_info &device,
+ const ze_device_thread_t &tid,
+ gdb_signal &) override;
+
+ void prepare_thread_resume (thread_info *tp) override;
+
+ /* Read one instruction from memory at PC into BUFFER and return the
+ number of bytes read on success or a negative errno error code.
+
+ BUFFER must be intelgt::MAX_INST_LENGTH bytes long. */
+ int read_inst (const ze_device_info &device, CORE_ADDR pc,
+ gdb::array_view<gdb_byte> buffer);
+
+ bool is_at_breakpoint (thread_info *tp) override;
+
+ /* Return whether TP is at an end-of-thread instruction. */
+ bool is_at_eot (thread_info *tp);
+
+private:
+ /* Add a register set for REGPROP on DEVICE to REGSETS and increment REGNUM
+ accordingly.
+
+ May optionally add registers to EXPEDITE. */
+ void add_regset (target_desc *tdesc, const ze_device_info &dinfo,
+ const zet_debug_regset_properties_t ®prop,
+ int ®num, ze_regset_info_t ®sets,
+ expedite_t &expedite);
+};
+
+const gdb_byte *
+intelgt_ze_target::sw_breakpoint_from_kind (int kind, int *size)
+{
+ /* We do not support breakpoint instructions.
+
+ Use gdbarch methods that use read/write memory target operations for
+ setting s/w breakopints. */
+ *size = 0;
+ return nullptr;
+}
+
+bool
+intelgt_ze_target::stopped_by_sw_breakpoint ()
+{
+ const ze_thread_info *zetp = ze_thread (current_thread);
+ if (zetp == nullptr)
+ return false;
+
+ ptid_t ptid = current_thread->id;
+
+ if (zetp->exec_state != ZE_THREAD_STATE_STOPPED)
+ {
+ dprintf ("not-stopped thread %s", ptid.to_string ().c_str ());
+ return false;
+ }
+
+ return (zetp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT);
+}
+
+CORE_ADDR
+intelgt_ze_target::read_pc (regcache *regcache)
+{
+ std::vector<uint32_t> cr0 = intelgt_read_cr0 (regcache);
+ gdb_assert (cr0.size () >= 2);
+ uint32_t ip = cr0[2];
+
+ uint64_t isabase;
+ collect_register_by_name (regcache, "isabase", &isabase);
+
+ CORE_ADDR pc = (CORE_ADDR) isabase + (CORE_ADDR) ip;
+ if (pc < isabase)
+ warning (_("PC '%s' outside of ISA range."),
+ core_addr_to_string_nz (pc));
+
+ return pc;
+}
+
+void
+intelgt_ze_target::write_pc (regcache *regcache, CORE_ADDR pc)
+{
+ uint64_t isabase;
+ collect_register_by_name (regcache, "isabase", &isabase);
+
+ if (pc < isabase)
+ error (_("PC '%s' outside of ISA range."), core_addr_to_string_nz (pc));
+
+ pc -= isabase;
+ if (UINT32_MAX < pc)
+ error (_("PC '%s' outside of ISA range."), core_addr_to_string_nz (pc));
+
+ std::vector<uint32_t> cr0 = intelgt_read_cr0 (regcache);
+ gdb_assert (cr0.size () >= 2);
+ cr0[2] = pc;
+ intelgt_write_cr0 (regcache, cr0);
+}
+
+bool
+intelgt_ze_target::is_device_supported
+ (const ze_device_properties_t &properties,
+ const std::vector<zet_debug_regset_properties_t> ®set_properties)
+{
+ if (properties.type != ZE_DEVICE_TYPE_GPU)
+ {
+ dprintf ("non-gpu (%x) device (%" PRIx32 "): %s", properties.type,
+ properties.deviceId, properties.name);
+ return false;
+ }
+
+ if (properties.vendorId != 0x8086)
+ {
+ dprintf ("unknown vendor (%" PRIx32 ") of device (%" PRIx32 "): %s",
+ properties.vendorId, properties.deviceId, properties.name);
+ return false;
+ }
+
+ /* We need a few registers to support an Intel GT device.
+
+ Those are registers that GDB itself uses. Without those, we might run into
+ internal errors at some point. We need others, too, that may be referenced
+ in debug information. */
+ bool have_grf = false;
+ bool have_isabase = false;
+ bool have_cr = false;
+ bool have_sr = false;
+ bool have_ce = false;
+ for (const zet_debug_regset_properties_t ®prop : regset_properties)
+ {
+ if (regprop.count < 1)
+ {
+ warning (_("Ignoring empty regset %u on device %s."),
+ regprop.type, properties.name);
+ continue;
+ }
+
+ switch (regprop.type)
+ {
+ case ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU:
+ have_grf = true;
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_CE_INTEL_GPU:
+ have_ce = true;
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU:
+ if (regprop.byteSize >= (3 * sizeof (uint32_t)))
+ have_cr = true;
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SR_INTEL_GPU:
+ have_sr = true;
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU:
+ /* We need 'isabase', which is at position 5 in version 0. */
+ if ((regprop.version == 0) && (regprop.count >= 5)
+ && (regprop.byteSize <= sizeof (uint64_t)))
+ have_isabase = true;
+ else
+ warning (_("Ignoring unknown SBA regset version %u on device "
+ "%s."), regprop.version, properties.name);
+ break;
+ }
+ }
+
+ if (have_grf && have_isabase && have_cr && have_sr && have_ce)
+ return true;
+
+ dprintf ("unsupported device (%" PRIx32 "): %s", properties.deviceId,
+ properties.name);
+ return false;
+}
+
+target_desc *
+intelgt_ze_target::create_tdesc
+ (ze_device_info *dinfo,
+ const std::vector<zet_debug_regset_properties_t> ®set_properties,
+ const ze_pci_ext_properties_t &pci_properties)
+{
+ const ze_device_properties_t &properties = dinfo->properties;
+
+ if (properties.vendorId != 0x8086)
+ error (_("unknown vendor (%" PRIx32 ") of device (%" PRIx32 "): %s"),
+ properties.vendorId, properties.deviceId, properties.name);
+
+ target_desc_up tdesc = allocate_target_description ();
+ set_tdesc_architecture (tdesc.get (), "intelgt");
+ set_tdesc_osabi (tdesc.get (), GDB_OSABI_LINUX);
+
+ tdesc_device *device_info = new tdesc_device ();
+ device_info->vendor_id = properties.vendorId;
+ device_info->target_id = properties.deviceId;
+ device_info->name = properties.name;
+
+ set_tdesc_device_info (tdesc.get (), device_info);
+
+ int regnum = 0;
+ for (const zet_debug_regset_properties_t ®prop : regset_properties)
+ add_regset (tdesc.get (), *dinfo, regprop, regnum,
+ dinfo->regsets, dinfo->expedite);
+
+ /* Tdesc expects a nullptr-terminated array. */
+ dinfo->expedite.push_back (nullptr);
+
+ init_target_desc (tdesc.get (), dinfo->expedite.data (), GDB_OSABI_LINUX);
+ return tdesc.release ();
+}
+
+target_stop_reason
+intelgt_ze_target::get_stop_reason (const ze_device_info &device,
+ const ze_device_thread_t &tid,
+ gdb_signal &signal)
+{
+ std::vector<uint32_t> cr0 = intelgt_read_cr0 (device, tid);
+ gdb_assert (cr0.size () >= 2);
+
+ dprintf ("thread %s stopped, cr0.0=%" PRIx32 ", .1=%" PRIx32
+ " [ %s%s%s%s%s%s], .2=%" PRIx32 ".",
+ ze_thread_id_str (tid).c_str (), cr0[0], cr0[1],
+ (((cr0[1] & (1 << INTELGT_CR0_1_BREAKPOINT_STATUS)) != 0)
+ ? "bp " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_ILLEGAL_OPCODE_STATUS)) != 0)
+ ? "ill " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_FORCE_EXCEPTION_STATUS)) != 0)
+ ? "fe " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_SOFTWARE_EXCEPTION_CONTROL)) != 0)
+ ? "sw " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_EXTERNAL_HALT_STATUS)) != 0)
+ ? "eh " : ""),
+ (((cr0[1] & (1 << INTELGT_CR0_1_PAGEFAULT_STATUS)) != 0)
+ ? "pf " : ""),
+ cr0[2]);
+
+ /* We will overwrite this signal if we recognize an exception. */
+ signal = GDB_SIGNAL_UNKNOWN;
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_PAGEFAULT_STATUS)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_PAGEFAULT_STATUS);
+ intelgt_write_cr0 (device, tid, cr0);
+
+ signal = GDB_SIGNAL_SEGV;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_BREAKPOINT_STATUS)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_BREAKPOINT_STATUS);
+ intelgt_write_cr0 (device, tid, cr0);
+
+ signal = GDB_SIGNAL_TRAP;
+
+ /* We cannot distinguish a single step exception from a breakpoint
+ exception just by looking at CR0.
+
+ We could inspect the instruction to see if the breakpoint bit is
+ set. Or we could check the resume type and assume that we set
+ things up correctly for single-stepping before we resumed. */
+ thread_info *tp = ze_find_thread (device, tid);
+ if (tp != nullptr)
+ {
+ const ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ if (zetp->resume_state == ZE_THREAD_RESUME_STEP)
+ return TARGET_STOPPED_BY_SINGLE_STEP;
+ }
+
+ return TARGET_STOPPED_BY_SW_BREAKPOINT;
+ }
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_ILLEGAL_OPCODE_STATUS)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_ILLEGAL_OPCODE_STATUS);
+ intelgt_write_cr0 (device, tid, cr0);
+
+ signal = GDB_SIGNAL_ILL;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ if ((cr0[1] & (1 << INTELGT_CR0_1_SOFTWARE_EXCEPTION_CONTROL)) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_SOFTWARE_EXCEPTION_CONTROL);
+ intelgt_write_cr0 (device, tid, cr0);
+
+ signal = GDB_EXC_SOFTWARE;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ if ((cr0[1] & ((1 << INTELGT_CR0_1_FORCE_EXCEPTION_STATUS)
+ | (1 << INTELGT_CR0_1_EXTERNAL_HALT_STATUS))) != 0)
+ {
+ cr0[1] &= ~(1 << INTELGT_CR0_1_FORCE_EXCEPTION_STATUS);
+ cr0[1] &= ~(1 << INTELGT_CR0_1_EXTERNAL_HALT_STATUS);
+ intelgt_write_cr0 (device, tid, cr0);
+
+ signal = GDB_SIGNAL_0;
+ return TARGET_STOPPED_BY_NO_REASON;
+ }
+
+ return TARGET_STOPPED_BY_NO_REASON;
+}
+
+int
+intelgt_ze_target::read_inst (const ze_device_info &device, CORE_ADDR pc,
+ gdb::array_view<gdb_byte> buffer)
+{
+ gdb_assert (buffer.size () >= intelgt::MAX_INST_LENGTH);
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ int status = read_memory (device, all, pc, buffer.data (),
+ intelgt::MAX_INST_LENGTH);
+ if (status == 0)
+ return intelgt::MAX_INST_LENGTH;
+
+ status = read_memory (device, all, pc, buffer.data (),
+ intelgt::COMPACT_INST_LENGTH);
+ if (status > 0)
+ return status;
+
+ if (intelgt::inst_length (buffer, device.properties.deviceId)
+ == intelgt::MAX_INST_LENGTH)
+ return -EIO;
+
+ memset (buffer.begin () + intelgt::COMPACT_INST_LENGTH, 0,
+ intelgt::MAX_INST_LENGTH - intelgt::COMPACT_INST_LENGTH);
+
+ return intelgt::COMPACT_INST_LENGTH;
+}
+
+bool
+intelgt_ze_target::is_at_breakpoint (thread_info *tp)
+{
+ regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+ CORE_ADDR pc = read_pc (regcache);
+
+ const ze_device_info *device = ze_thread_device (tp);
+ gdb_assert (device != nullptr);
+
+ gdb_byte inst[intelgt::MAX_INST_LENGTH];
+ int status = read_inst (*device, pc, inst);
+ if (status < 0)
+ return false;
+
+ return intelgt::has_breakpoint (inst, device->properties.deviceId);
+}
+
+bool
+intelgt_ze_target::is_at_eot (thread_info *tp)
+{
+ regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+ CORE_ADDR pc = read_pc (regcache);
+
+ const ze_device_info *device = ze_thread_device (tp);
+ gdb_assert (device != nullptr);
+
+ gdb_byte inst[intelgt::MAX_INST_LENGTH];
+ int status = read_inst (*device, pc, inst);
+ if (status < 0)
+ {
+ ze_device_thread_t zeid = ze_thread_id (tp);
+
+ warning (_("error reading memory for thread %s (%s) at 0x%"
+ PRIx64), tp->id.to_string ().c_str (),
+ ze_thread_id_str (zeid).c_str (), pc);
+ return false;
+ }
+
+ intelgt::xe_version device_version
+ = intelgt::get_xe_version (device->properties.deviceId);
+ switch (device_version)
+ {
+ case intelgt::xe_version::XE_HP:
+ case intelgt::xe_version::XE_HPG:
+ case intelgt::xe_version::XE_HPC:
+ case intelgt::xe_version::XE2:
+ case intelgt::xe_version::XE3:
+ {
+ /* The opcode mask for bits 6:0. */
+ constexpr uint8_t OPC_MASK = 0x7f;
+ switch (inst[0] & OPC_MASK)
+ {
+ case 0x31: /* send */
+ case 0x32: /* sendc */
+ {
+ /* The End Of Thread control. Only used for SEND and
+ SENDC. */
+ constexpr uint8_t CTRL_EOT_SEND = 34;
+ return intelgt::get_inst_bit (inst, CTRL_EOT_SEND);
+ }
+
+ default:
+ return false;
+ }
+ }
+
+ default:
+ error (_("Unsupported device id 0x%" PRIx32 "."),
+ device->properties.deviceId);
+ }
+}
+
+void
+intelgt_ze_target::prepare_thread_resume (thread_info *tp)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+ std::vector<uint32_t> cr0 = intelgt_read_cr0 (regcache);
+ gdb_assert (cr0.size () >= 2);
+
+ /* The thread is running. We may need to overwrite this below. */
+ zetp->exec_state = ZE_THREAD_STATE_RUNNING;
+
+ /* Clear any potential interrupt indication.
+
+ We leave other exception indications so the exception would be
+ reported again and can be handled by GDB. */
+ cr0[1] &= ~(1 << INTELGT_CR0_1_FORCE_EXCEPTION_STATUS);
+ cr0[1] &= ~(1 << INTELGT_CR0_1_EXTERNAL_HALT_STATUS);
+
+ /* Distinguish stepping and continuing. */
+ switch (zetp->resume_state)
+ {
+ case ZE_THREAD_RESUME_STEP:
+ /* We step by indicating a breakpoint exception, which will be
+ considered on the next instruction.
+
+ This does not work for EOT, though. */
+ if (!is_at_eot (tp))
+ {
+ cr0[0] |= (1 << INTELGT_CR0_0_BREAKPOINT_SUPPRESS);
+ cr0[1] |= (1 << INTELGT_CR0_1_BREAKPOINT_STATUS);
+ break;
+ }
+
+ /* At EOT, the thread dispatch ends and the h/w thread becomes idle.
+
+ There's no point in requesting a single-step exception, since
+ that will never come, but we need to inject an event to tell GDB
+ that the step completed.
+
+ An exception when executing the EOT instruction will overwrite
+ the thread exit as long as we have not reported the event to GDB.
+ If we have already reported the exit to GDB, this will re-create
+ the thread in order to report the exception. */
+ zetp->exec_state = ZE_THREAD_STATE_EXITED;
+ zetp->waitstatus.set_thread_exited (0);
+
+ [[fallthrough]];
+ case ZE_THREAD_RESUME_RUN:
+ cr0[1] &= ~(1 << INTELGT_CR0_1_BREAKPOINT_STATUS);
+ break;
+
+ default:
+ internal_error (_("bad resume kind: %d."), zetp->resume_state);
+ }
+
+ /* When stepping over a breakpoint, we need to suppress the breakpoint
+ exception we would otherwise get immediately.
+
+ This requires breakpoints to be already inserted when this function
+ is called. It also handles permanent breakpoints. */
+ if (is_at_breakpoint (tp))
+ cr0[0] |= (1 << INTELGT_CR0_0_BREAKPOINT_SUPPRESS);
+
+ intelgt_write_cr0 (regcache, cr0);
+
+ dprintf ("thread %s (%s) resumed, cr0.0=%" PRIx32 " .1=%" PRIx32
+ " .2=%" PRIx32 ".", tp->id.to_string ().c_str (),
+ ze_thread_id_str (zetp->id).c_str (), cr0[0], cr0[1], cr0[2]);
+}
+
+void
+intelgt_ze_target::add_regset (target_desc *tdesc, const ze_device_info &dinfo,
+ const zet_debug_regset_properties_t ®prop,
+ int ®num, ze_regset_info_t ®sets,
+ expedite_t &expedite)
+{
+ tdesc_feature *feature = nullptr;
+ const ze_device_properties_t &device = dinfo.properties;
+
+ ze_regset_info regset = {};
+ regset.type = (uint32_t) regprop.type;
+ regset.size = regprop.byteSize;
+ regset.begin = regnum;
+ regset.is_writeable
+ = ((regprop.generalFlags & ZET_DEBUG_REGSET_FLAG_WRITEABLE) != 0);
+
+ if (regprop.count < 1)
+ {
+ warning (_("Ignoring empty regset %u in %s."), regprop.type,
+ device.name);
+ return;
+ }
+
+ if ((regprop.generalFlags & ZET_DEBUG_REGSET_FLAG_READABLE) == 0)
+ {
+ warning (_("Ignoring non-readable regset %u in %s."), regprop.type,
+ device.name);
+ return;
+ }
+
+ switch (regprop.type)
+ {
+ case ZET_DEBUG_REGSET_TYPE_GRF_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_GRF);
+
+ intelgt_add_regset (feature, regnum, "r", regprop.count, "grf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_ADDR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_ADDR);
+
+ intelgt_add_regset (feature, regnum, "a", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 16u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_FLAG_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_FLAG);
+
+ intelgt_add_regset (feature, regnum, "f", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 16u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_CE_INTEL_GPU:
+ /* We expect a single 'ce' register. */
+ if (regprop.count != 1)
+ warning (_("Ignoring %u unexpected 'ce' registers in %s."),
+ regprop.count - 1, device.name);
+
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_CE);
+
+ tdesc_create_reg (feature, "ce", regnum++, 1, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u));
+
+ expedite.push_back ("ce");
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_SR);
+
+ intelgt_add_regset (feature, regnum, "sr", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_CR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_CR);
+
+ expedite.push_back ("cr0");
+ intelgt_add_regset (feature, regnum, "cr", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_TDR_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_TDR);
+
+ intelgt_add_regset (feature, regnum, "tdr", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 16u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_ACC_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_ACC);
+
+ intelgt_add_regset (feature, regnum, "acc", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_MME_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_MME);
+
+ intelgt_add_regset (feature, regnum, "mme", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SP_INTEL_GPU:
+ /* We expect a single 'sp' register. */
+ if (regprop.count != 1)
+ warning (_("Ignoring %u unexpected 'sp' registers in %s."),
+ regprop.count - 1, device.name);
+
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_SP);
+
+ tdesc_create_reg (feature, "sp", regnum++, 1, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ regprop.bitSize));
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_SBA_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_SBA);
+
+ switch (regprop.version)
+ {
+ case 0:
+ {
+ const char *regtype = intelgt_uint_reg_type (feature,
+ regprop.bitSize,
+ regprop.bitSize);
+ const char *sbaregs[] = {
+ "genstbase",
+ "sustbase",
+ "dynbase",
+ "iobase",
+ "isabase",
+ "blsustbase",
+ "blsastbase",
+ "btbase",
+ "scrbase0",
+ "scrbase1",
+ nullptr
+ };
+ int reg = 0;
+ for (; (reg < regprop.count) && (sbaregs[reg] != nullptr); ++reg)
+ {
+ if (strcmp (sbaregs[reg], "isabase") == 0)
+ expedite.push_back (sbaregs[reg]);
+
+ tdesc_create_reg (feature, sbaregs[reg], regnum++, 1,
+ "virtual", regprop.bitSize, regtype);
+ }
+ }
+ break;
+
+ default:
+ warning (_("Ignoring unknown SBA regset version %u in %s"),
+ regprop.version, device.name);
+ break;
+ }
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_DBG_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_DBG);
+
+ intelgt_add_regset (feature, regnum, "dbg", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_FC_INTEL_GPU:
+ feature = tdesc_create_feature (tdesc, intelgt::FEATURE_FC);
+
+ intelgt_add_regset (feature, regnum, "fc", regprop.count, "arf",
+ regprop.bitSize,
+ intelgt_uint_reg_type (feature, regprop.bitSize,
+ 32u),
+ expedite);
+ break;
+
+ case ZET_DEBUG_REGSET_TYPE_INVALID_INTEL_GPU:
+ case ZET_DEBUG_REGSET_TYPE_FORCE_UINT32:
+ break;
+ }
+
+ if (feature == nullptr)
+ {
+ warning (_("Ignoring unknown regset %u in %s."), regprop.type,
+ device.name);
+
+ return;
+ }
+
+ regset.end = regnum;
+ regsets.push_back (regset);
+}
+
+
+/* The Intel GT target ops object. */
+
+static intelgt_ze_target the_intelgt_ze_target;
+
+extern void initialize_low ();
+void
+initialize_low ()
+{
+ /* Delayed initialization of Level-Zero targets. See ze-low.h. */
+ the_intelgt_ze_target.init ();
+ set_target_ops (&the_intelgt_ze_target);
+}
diff --git a/gdbserver/ze-low.cc b/gdbserver/ze-low.cc
new file mode 100644
index 00000000000..c38c088e849
--- /dev/null
+++ b/gdbserver/ze-low.cc
@@ -0,0 +1,2846 @@
+/* Target interface for Level-Zero based targets for gdbserver.
+ See https://github.com/oneapi-src/level-zero.git.
+
+ Copyright (C) 2020-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "ze-low.h"
+#include "dll.h"
+
+#include <level_zero/zet_api.h>
+#include <exception>
+#include <sstream>
+#include <iomanip>
+#include <cstring>
+#include <thread>
+#include <utility>
+#include <algorithm>
+#include <set>
+
+#ifndef USE_WIN32API
+# include <signal.h>
+# include <fcntl.h>
+#endif
+
+
+/* Convenience macros. */
+
+#define dprintf(fmt, ...) \
+ debug_prefixed_printf_cond (debug_threads, "ze-low", fmt, ##__VA_ARGS__)
+
+#ifndef USE_WIN32API
+/* Async interaction stuff.
+
+ The read/write ends of the pipe registered as waitable file in the
+ event loop. */
+static int ze_event_pipe[2] = { -1, -1 };
+#endif
+
+/* Return whether we're in async mode. */
+
+static bool
+ze_is_async ()
+{
+#ifndef USE_WIN32API
+ return (ze_event_pipe[0] != -1);
+#else
+ return false;
+#endif
+}
+
+/* Get rid of any pending event in the pipe. */
+
+static void
+ze_async_flush ()
+{
+ if (!ze_is_async ())
+ return;
+
+#ifndef USE_WIN32API
+ int ret;
+ char buf;
+
+ errno = 0;
+ do
+ ret = read (ze_event_pipe[0], &buf, 1);
+ while (ret >= 0 || (ret == -1 && errno == EINTR));
+#else
+ error (_("%s: tbd"), __FUNCTION__);
+#endif
+}
+
+/* Put something in the pipe, so the event loop wakes up. */
+
+static void
+ze_async_mark ()
+{
+ if (!ze_is_async ())
+ return;
+
+#ifndef USE_WIN32API
+ int ret;
+
+ ze_async_flush ();
+
+ errno = 0;
+ do
+ ret = write (ze_event_pipe[1], "+", 1);
+ while (ret == 0 || (ret == -1 && errno == EINTR));
+
+ /* Ignore EAGAIN. If the pipe is full, the event loop will already
+ be awakened anyway. */
+#else
+ error (_("%s: tbd"), __FUNCTION__);
+#endif
+}
+
+/* Return a human-readable device thread id component string. */
+
+static std::string
+ze_thread_id_component_str (uint32_t component)
+{
+ if (component == UINT32_MAX)
+ return std::string ("all");
+
+ return std::to_string (component);
+}
+
+/* See ze-low.h. */
+
+std::string
+ze_thread_id_str (const ze_device_thread_t &thread)
+{
+ std::stringstream sstream;
+ sstream << ze_thread_id_component_str (thread.slice)
+ << "."
+ << ze_thread_id_component_str (thread.subslice)
+ << "."
+ << ze_thread_id_component_str (thread.eu)
+ << "."
+ << ze_thread_id_component_str (thread.thread);
+
+ return sstream.str ();
+}
+
+/* Return a human-readable UUID string. */
+
+static std::string
+uuid_str (const uint8_t uuid[], size_t size)
+{
+ std::stringstream sstream;
+ while (size--)
+ sstream << std::setw (2) << uuid[size];
+
+ return sstream.str ();
+}
+
+/* Return a human-readable device UUID string. */
+
+static std::string
+driver_uuid_str (const ze_driver_uuid_t &uuid)
+{
+ return uuid_str (uuid.id, sizeof (uuid.id));
+}
+
+/* Return the pid for DEVICE. */
+
+static int
+ze_device_pid (const ze_device_info &device)
+{
+ if (device.process != nullptr)
+ return device.process->pid;
+
+ return 0;
+}
+
+/* Return the device for PROCESS. */
+
+static ze_device_info *
+ze_process_device (process_info *process)
+{
+ if (process == nullptr)
+ return nullptr;
+
+ process_info_private *zeproc = process->priv;
+ if (zeproc == nullptr)
+ return nullptr;
+
+ return zeproc->device;
+}
+
+/* Return the device for THREAD. */
+
+ze_device_info *
+ze_thread_device (const thread_info *thread)
+{
+ if (thread == nullptr)
+ return nullptr;
+
+ return ze_process_device (thread->process ());
+}
+
+/* Return whether any interrupt on a device matching PTID is pending. */
+
+static bool
+ze_any_interrupt_pending (ptid_t ptid)
+{
+ process_info *found = find_process ([ptid] (process_info *process)
+ {
+ if ((ptid != minus_one_ptid) && (ptid.pid () != process->pid))
+ return false;
+
+ ze_device_info *device = ze_process_device (process);
+ if (device == nullptr)
+ return false;
+
+ return (device->ninterrupts > 0);
+ });
+
+ return (found != nullptr);
+}
+
+/* Returns whether ID is in SET. */
+
+static bool
+ze_device_thread_in (ze_device_thread_t id, ze_device_thread_t set)
+{
+ if ((set.slice != UINT32_MAX) && (set.slice != id.slice))
+ return false;
+
+ if ((set.subslice != UINT32_MAX) && (set.subslice != id.subslice))
+ return false;
+
+ if ((set.eu != UINT32_MAX) && (set.eu != id.eu))
+ return false;
+
+ if ((set.thread != UINT32_MAX) && (set.thread != id.thread))
+ return false;
+
+ return true;
+}
+
+/* See ze-low.h. */
+
+thread_info *
+ze_find_thread (const ze_device_info &device,
+ const ze_device_thread_t &tid)
+{
+ process_info *process = device.process;
+ if (process == nullptr)
+ return nullptr;
+
+ ptid_t ptid = ze_thread_ptid (device, tid);
+ return process->find_thread (ptid);
+}
+
+/* Returns a thread for TID on DEVICE.
+
+ Returns an existing thread, if it exists, or creates a new thread. */
+
+static thread_info *
+ze_notice_thread (const ze_device_info &device,
+ const ze_device_thread_t &tid)
+{
+ thread_info *tp = ze_find_thread (device, tid);
+ if (tp != nullptr)
+ return tp;
+
+ /* Pretend that newly dispatched threads have been resumed by GDB.
+
+ When we pause and unpause them, we want them to run. */
+ ze_thread_info *zetp = new ze_thread_info {};
+ zetp->resume_state = ZE_THREAD_RESUME_RUN;
+ zetp->exec_state = ZE_THREAD_STATE_RUNNING;
+ zetp->id = tid;
+
+ ptid_t ptid = ze_thread_ptid (device, tid);
+ process_info *process = device.process;
+ gdb_assert (process != nullptr);
+
+ dprintf ("Creating thread %s (%s) on device %lu (%s)",
+ ptid.to_string ().c_str (), ze_thread_id_str (tid).c_str (),
+ device.ordinal, device.properties.name);
+
+ return process->add_thread (ptid, zetp);
+}
+
+/* Create a thread and mark it exited. */
+
+static thread_info *
+ze_create_exited_thread (ptid_t ptid)
+{
+ gdb_assert (ptid != minus_one_ptid);
+ gdb_assert (ptid != null_ptid);
+ gdb_assert (!ptid.is_pid ());
+
+ process_info *process = find_process_pid (ptid.pid ());
+ if (process == nullptr)
+ return nullptr;
+
+ ze_device_info *device = ze_process_device (process);
+ if (device == nullptr)
+ return nullptr;
+
+ ze_thread_info *zetp = new ze_thread_info {};
+ zetp->exec_state = ZE_THREAD_STATE_EXITED;
+ zetp->waitstatus.set_thread_exited (0);
+
+ dprintf ("Creating exited thread %s on device %lu (%s)",
+ ptid.to_string ().c_str (), device->ordinal,
+ device->properties.name);
+
+ return process->add_thread (ptid, zetp);
+}
+
+/* Remove thread TP.
+
+ We do not support thread entry/exit events as they would be too
+ numerous. So we remove the thread right away. */
+
+static void
+ze_remove_thread (thread_info *tp)
+{
+ if (tp == nullptr)
+ return;
+
+ process_info *process = tp->process ();
+ gdb_assert (process != nullptr);
+
+ ze_device_info *device = ze_process_device (process);
+ gdb_assert (device != nullptr);
+
+ ze_device_thread_t tid = ze_thread_id (tp);
+
+ dprintf ("Removing thread %s (%s) on device %lu (%s)",
+ tp->id.to_string ().c_str (), ze_thread_id_str (tid).c_str (),
+ device->ordinal, device->properties.name);
+
+ delete (ze_thread_info *) tp->target_data ();
+ process->remove_thread (tp);
+}
+
+/* See ze-low.h. */
+
+ptid_t ze_thread_ptid (const ze_device_info &device,
+ const ze_device_thread_t &tid)
+{
+ ptid_t::lwp_type lwp = 1
+ + tid.thread
+ + (tid.eu
+ * device.properties.numThreadsPerEU)
+ + (tid.subslice
+ * device.properties.numEUsPerSubslice
+ * device.properties.numThreadsPerEU)
+ + (tid.slice
+ * device.properties.numSubslicesPerSlice
+ * device.properties.numEUsPerSubslice
+ * device.properties.numThreadsPerEU);
+
+ ptid_t ptid ((int) device.ordinal, lwp, 0);
+ return ptid;
+}
+
+/* Call FUNC for each thread on DEVICE matching ID. */
+
+template <typename Func>
+static void
+for_each_thread (const ze_device_info &device,
+ const ze_device_thread_t &filter,
+ Func func)
+{
+ const ze_device_properties_t &properties = device.properties;
+
+ uint32_t begin_slice = filter.slice, end_slice = filter.slice + 1;
+ if (begin_slice == UINT32_MAX)
+ {
+ begin_slice = 0;
+ end_slice = properties.numSlices;
+ }
+
+ uint32_t begin_subslice = filter.subslice,
+ end_subslice = filter.subslice + 1;
+ if (begin_subslice == UINT32_MAX)
+ {
+ begin_subslice = 0;
+ end_subslice = properties.numSubslicesPerSlice;
+ }
+
+ uint32_t begin_eu = filter.eu, end_eu = filter.eu + 1;
+ if (begin_eu == UINT32_MAX)
+ {
+ begin_eu = 0;
+ end_eu = properties.numEUsPerSubslice;
+ }
+
+ uint32_t begin_thread = filter.thread, end_thread = filter.thread + 1;
+ if (begin_thread == UINT32_MAX)
+ {
+ begin_thread = 0;
+ end_thread = properties.numThreadsPerEU;
+ }
+
+ for (uint32_t slice = begin_slice; slice < end_slice; ++slice)
+ for (uint32_t subslice = begin_subslice; subslice < end_subslice;
+ ++subslice)
+ for (uint32_t eu = begin_eu; eu < end_eu; ++eu)
+ for (uint32_t thread = begin_thread; thread < end_thread; ++thread)
+ {
+ ze_device_thread_t tid;
+ tid.slice = slice;
+ tid.subslice = subslice;
+ tid.eu = eu;
+ tid.thread = thread;
+
+ func (device, tid);
+ }
+}
+
+/* Add a process for DEVICE. */
+
+static process_info *
+ze_add_process (ze_device_info *device)
+{
+ gdb_assert (device != nullptr);
+
+ process_info *process = add_process (device->ordinal, 1);
+ process->priv = new process_info_private (device);
+ process->tdesc = device->tdesc.get ();
+ device->process = process;
+
+ return process;
+}
+
+/* Remove a Level-Zero PROCESS. */
+
+static void
+ze_remove_process (process_info *process)
+{
+ gdb_assert (process != nullptr);
+
+ process->for_each_thread ([=] (thread_info *thread)
+ {
+ delete (ze_thread_info *) thread->target_data ();
+ process->remove_thread (thread);
+ });
+
+ process_info_private *zeinfo = process->priv;
+ gdb_assert (zeinfo != nullptr);
+
+ /* We may or may not have a device.
+
+ When we got detached, we will remove the device first, and remove the
+ process when we select an event from one of its threads.
+
+ When we get a process exit event, the device will remain after the process
+ has been removed. */
+ ze_device_info *device = zeinfo->device;
+ if (device != nullptr)
+ {
+ gdb_assert (device->process == process);
+ device->process = nullptr;
+ }
+
+ process->priv = nullptr;
+ delete zeinfo;
+
+ remove_process (process);
+}
+
+/* Attach to DEVICE and create a hidden process for it.
+
+ Modifies DEVICE as a side-effect.
+ Returns the created process or nullptr if DEVICE does not support debug. */
+
+static process_info *
+ze_attach (ze_device_info *device)
+{
+ gdb_assert (device != nullptr);
+
+ const ze_device_properties_t &properties = device->properties;
+ if (device->session != nullptr)
+ error (_("Already attached to %s."), properties.name);
+
+ device->debug_attach_state = zetDebugAttach (device->handle, &device->config,
+ &device->session);
+ switch (device->debug_attach_state)
+ {
+ case ZE_RESULT_SUCCESS:
+ if (device->session == nullptr)
+ error (_("Bad handle returned by zetDebugAttach on %s."),
+ device->properties.name);
+
+ dprintf ("attached to device %lu (%s) with %ld threads.",
+ device->ordinal, properties.name, device->nthreads);
+
+ return ze_add_process (device);
+
+ case ZE_RESULT_NOT_READY:
+ /* We're too early. The Level-Zero user-mode driver has not been
+ initialized, yet. */
+ error (_("Attempting to attach too early to %s."),
+ device->properties.name);
+
+ case ZE_RESULT_ERROR_UNSUPPORTED_FEATURE:
+ /* Not all sub-devices support attaching to them. */
+ dprintf ("Attach not supported on %s", device->properties.name);
+ return nullptr;
+
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ /* Someone else is already attached. This could be us if we already
+ attached to some other sub-device in this device tree. */
+ error (_("Someone is already attached to %s."),
+ device->properties.name);
+
+ default:
+ error (_("Failed to attach to %s (%x)."), device->properties.name,
+ device->debug_attach_state);
+ }
+}
+
+/* Detach from DEVICE. */
+
+static void
+ze_detach (ze_device_info *device)
+{
+ gdb_assert (device != nullptr);
+
+ zet_debug_session_handle_t session = device->session;
+ if (session == nullptr)
+ error (_("Already detached from %s."), device->properties.name);
+
+ dprintf ("device %lu=%s", device->ordinal, device->properties.name);
+
+ ze_result_t status = zetDebugDetach (session);
+ switch (status)
+ {
+ case ZE_RESULT_ERROR_DEVICE_LOST:
+ case ZE_RESULT_SUCCESS:
+ device->session = nullptr;
+ break;
+
+ default:
+ error (_("Failed to detach from %s (%x)."), device->properties.name,
+ status);
+ }
+}
+
+/* Return a human-readable detach reason string. */
+
+static std::string
+ze_detach_reason_str (zet_debug_detach_reason_t reason)
+{
+ switch (reason)
+ {
+ case ZET_DEBUG_DETACH_REASON_INVALID:
+ return std::string (_("invalid"));
+
+ case ZET_DEBUG_DETACH_REASON_HOST_EXIT:
+ return std::string (_("the host process exited"));
+ }
+
+ return std::string (_("unknown"));
+}
+
+/* Return a human-readable module debug information format string. */
+
+static std::string
+ze_debug_info_format_str (zet_module_debug_info_format_t format)
+{
+ switch (format)
+ {
+ case ZET_MODULE_DEBUG_INFO_FORMAT_ELF_DWARF:
+ return std::string (_("DWARF"));
+ }
+
+ return std::string (_("unknown"));
+}
+
+/* Return a human-readable event string. */
+
+static std::string
+ze_event_str (const zet_debug_event_t &event)
+{
+ std::stringstream sstream;
+
+ switch (event.type)
+ {
+ case ZET_DEBUG_EVENT_TYPE_INVALID:
+ sstream << "invalid";
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_DETACHED:
+ sstream << "detached, reason="
+ << ze_detach_reason_str (event.info.detached.reason);
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY:
+ sstream << "process entry";
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT:
+ sstream << "process exit";
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_MODULE_LOAD:
+ sstream << "module load, format="
+ << ze_debug_info_format_str (event.info.module.format)
+ << ", module=[" << std::hex << event.info.module.moduleBegin
+ << "; " << std::hex << event.info.module.moduleEnd
+ << "), addr=" << std::hex << event.info.module.load;
+
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_MODULE_UNLOAD:
+ sstream << "module unload, format="
+ << ze_debug_info_format_str (event.info.module.format)
+ << ", module=[" << std::hex << event.info.module.moduleBegin
+ << "; " << std::hex << event.info.module.moduleEnd
+ << "), addr=" << std::hex << event.info.module.load;
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_THREAD_STOPPED:
+ sstream << "thread stopped, thread="
+ << ze_thread_id_str (event.info.thread.thread);
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_THREAD_UNAVAILABLE:
+ sstream << "thread unavailable, thread="
+ << ze_thread_id_str (event.info.thread.thread);
+ break;
+
+ default:
+ sstream << "unknown, code=" << event.type;
+ break;
+ }
+
+ if ((event.flags & ZET_DEBUG_EVENT_FLAG_NEED_ACK) != 0)
+ sstream << ", need-ack";
+
+ return sstream.str ();
+}
+
+/* Acknowledge an event, if necessary. */
+
+static void
+ze_ack_event (const ze_device_info &device, const zet_debug_event_t &event)
+{
+ /* There is nothing to do for events that do not need acknowledging. */
+ if ((event.flags & ZET_DEBUG_EVENT_FLAG_NEED_ACK) == 0)
+ return;
+
+ ze_result_t status = zetDebugAcknowledgeEvent (device.session, &event);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ break;
+
+ default:
+ error (_("error acknowledging event: %s: %x."),
+ ze_event_str (event).c_str (), status);
+ }
+}
+
+/* Clear TP's resume state. */
+
+static void
+ze_clear_resume_state (thread_info *tp)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ zetp->resume_state = ZE_THREAD_RESUME_NONE;
+}
+
+/* Set TP's resume state from RKIND. */
+
+static void
+ze_set_resume_state (thread_info *tp, resume_kind rkind)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ switch (rkind)
+ {
+ case resume_continue:
+ zetp->resume_state = ZE_THREAD_RESUME_RUN;
+ return;
+
+ case resume_step:
+ zetp->resume_state = ZE_THREAD_RESUME_STEP;
+ return;
+
+ case resume_stop:
+ zetp->resume_state = ZE_THREAD_RESUME_STOP;
+ return;
+ }
+
+ internal_error (_("bad resume kind: %d."), rkind);
+}
+
+/* Return TP's execution state. */
+
+static enum ze_thread_exec_state_t
+ze_exec_state (const thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return ZE_THREAD_STATE_UNKNOWN;
+
+ return zetp->exec_state;
+}
+
+/* Return whether TP has a stop execution state. */
+
+static bool
+ze_thread_stopped (const thread_info *tp)
+{
+ ze_thread_exec_state_t state = ze_exec_state (tp);
+
+ return ((state == ZE_THREAD_STATE_STOPPED)
+ || (state == ZE_THREAD_STATE_PAUSED));
+}
+
+/* Return whether TP has a pending event. */
+
+static bool
+ze_has_waitstatus (const thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return false;
+
+ return (zetp->waitstatus.kind () != TARGET_WAITKIND_IGNORE);
+}
+
+/* Return whether TP has a pending priority event. */
+
+static bool
+ze_has_priority_waitstatus (const thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return false;
+
+ switch (zetp->waitstatus.kind ())
+ {
+ case TARGET_WAITKIND_IGNORE:
+ return false;
+
+ case TARGET_WAITKIND_STOPPED:
+ /* If this thread was stopped via an interrupt, it is an interesting
+ case if GDB wanted it stopped with a stop resume request. */
+ if ((zetp->stop_reason == TARGET_STOPPED_BY_NO_REASON)
+ && (zetp->waitstatus.sig () == GDB_SIGNAL_0))
+ return (zetp->resume_state == ZE_THREAD_RESUME_STOP);
+
+ return true;
+
+ default:
+ return true;
+ }
+}
+
+/* Return TP's waitstatus and clear it in TP. */
+
+static target_waitstatus
+ze_move_waitstatus (thread_info *tp)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ if (zetp == nullptr)
+ return {};
+
+ target_waitstatus status = zetp->waitstatus;
+ zetp->waitstatus.set_ignore ();
+
+ return status;
+}
+
+/* Indicate that we have been detached from the device corresponding to
+ PROCESS. */
+
+static void
+ze_device_detached (const ze_device_info &device,
+ zet_debug_detach_reason_t reason)
+{
+ process_info *process = device.process;
+ if (process == nullptr)
+ return;
+
+ /* We model getting detached from a device as the corresponding device process
+ exiting with the detach reason as exit status.
+
+ In the first step, we remove all threads of that process. This
+ disards any potential event they were about to report.
+
+ In the second step, we create a new thread to report the exit. We
+ may not have any existing thread, so we anyway need to handle the
+ case of a process exiting without any threads. */
+
+ process->for_each_thread ([process] (thread_info *tp)
+ {
+ delete (ze_thread_info *) tp->target_data ();
+ process->remove_thread (tp);
+ });
+
+ /* FIXME: we should not have to create a thread to report a process exit
+ given that we do allow processes without threads. */
+ ze_thread_info *zetp = new ze_thread_info {};
+ zetp->exec_state = ZE_THREAD_STATE_EXITED;
+ zetp->waitstatus.set_exited ((int) reason);
+
+ ptid_t ptid { process->pid, 1, 0 };
+ process->add_thread (ptid, zetp);
+}
+
+/* Find the regset containing REGNO on DEVICE or throw if not found. */
+
+static ze_regset_info
+ze_find_regset (const ze_device_info &device, int regno)
+{
+ for (const ze_regset_info ®set : device.regsets)
+ {
+ if (regno < regset.begin)
+ continue;
+
+ if (regset.end <= regno)
+ continue;
+
+ return regset;
+ }
+
+ error (_("No register %d on device %lu (%s)."), regno, device.ordinal,
+ device.properties.name);
+}
+
+/* Fetch all registers for THREAD on DEVICE into REGCACHE. */
+
+static void
+ze_fetch_all_registers (const ze_device_info &device,
+ const ze_device_thread_t thread,
+ regcache *regcache)
+{
+ for (const ze_regset_info ®set : device.regsets)
+ {
+ gdb_assert (regset.begin <= regset.end);
+ int lnregs = regset.end - regset.begin;
+ uint32_t nregs = (uint32_t) lnregs;
+
+ std::vector<uint8_t> buffer (regset.size * nregs);
+ ze_result_t status
+ = zetDebugReadRegisters (device.session, thread, regset.type, 0,
+ nregs, buffer.data ());
+ switch (status)
+ {
+ default:
+ warning (_("Error %x reading regset %" PRIu32
+ " for %s on device %lu (%s)."),
+ status, regset.type, ze_thread_id_str (thread).c_str (),
+ device.ordinal, device.properties.name);
+
+ [[fallthrough]];
+ case ZE_RESULT_SUCCESS:
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ {
+ size_t offset = 0;
+ int reg = regset.begin;
+
+ for (; reg < regset.end; reg += 1, offset += regset.size)
+ {
+ if (status == ZE_RESULT_SUCCESS)
+ supply_register (regcache, reg, &buffer[offset]);
+ else
+ supply_register (regcache, reg, nullptr);
+ }
+ }
+ break;
+ }
+ }
+}
+
+/* Fetch register REGNO for THREAD on DEVICE into REGCACHE. */
+
+static void
+ze_fetch_register (const ze_device_info &device,
+ const ze_device_thread_t thread,
+ regcache *regcache, int regno)
+{
+ ze_regset_info regset = ze_find_regset (device, regno);
+
+ gdb_assert (regset.begin <= regno);
+ int lrsno = regno - regset.begin;
+ uint32_t rsno = (uint32_t) lrsno;
+
+ std::vector<uint8_t> buffer (regset.size);
+ ze_result_t status
+ = zetDebugReadRegisters (device.session, thread, regset.type, rsno, 1,
+ buffer.data ());
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ supply_register (regcache, regno, buffer.data ());
+ break;
+
+ default:
+ warning (_("Error %x reading register %d (regset %" PRIu32
+ ") for %s on device %lu (%s)."), status, regno,
+ regset.type, ze_thread_id_str (thread).c_str (),
+ device.ordinal, device.properties.name);
+
+ [[fallthrough]];
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ supply_register (regcache, regno, nullptr);
+ break;
+ }
+}
+
+/* Store all registers for THREAD on DEVICE from REGCACHE. */
+
+static void
+ze_store_all_registers (const ze_device_info &device,
+ const ze_device_thread_t thread,
+ regcache *regcache)
+{
+ for (const ze_regset_info ®set : device.regsets)
+ {
+ if (!regset.is_writeable)
+ continue;
+
+ gdb_assert (regset.begin <= regset.end);
+ int lnregs = regset.end - regset.begin;
+ uint32_t nregs = (uint32_t) lnregs;
+
+ std::vector<uint8_t> buffer (regset.size * nregs);
+ size_t offset = 0;
+ long reg = regset.begin;
+ for (; reg < regset.end; reg += 1, offset += regset.size)
+ collect_register (regcache, reg, &buffer[offset]);
+
+ ze_result_t status
+ = zetDebugWriteRegisters (device.session, thread, regset.type, 0,
+ nregs, buffer.data ());
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ break;
+
+ default:
+ error (_("Error %x writing regset %" PRIu32
+ " for %s on device %lu (%s)."),
+ status, regset.type, ze_thread_id_str (thread).c_str (),
+ device.ordinal, device.properties.name);
+ }
+ }
+}
+
+/* Store register REGNO for THREAD on DEVICE from REGCACHE. */
+
+static void
+ze_store_register (const ze_device_info &device,
+ const ze_device_thread_t thread,
+ regcache *regcache, int regno)
+{
+ ze_regset_info regset = ze_find_regset (device, regno);
+
+ if (!regset.is_writeable)
+ error (_("Writing read-only register %d (regset %" PRIu32
+ ") for %s on device %lu (%s)."), regno, regset.type,
+ ze_thread_id_str (thread).c_str (), device.ordinal,
+ device.properties.name);
+
+ gdb_assert (regset.begin <= regno);
+ int lrsno = regno - regset.begin;
+ uint32_t rsno = (uint32_t) lrsno;
+
+ std::vector<uint8_t> buffer (regset.size);
+ collect_register (regcache, regno, buffer.data ());
+
+ ze_result_t status
+ = zetDebugWriteRegisters (device.session, thread, regset.type, rsno, 1,
+ buffer.data ());
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ break;
+
+ default:
+ error (_("Error %x writing register %d (regset %" PRIu32
+ ") for %s on device %lu (%s)."), status, regno, regset.type,
+ ze_thread_id_str (thread).c_str (), device.ordinal,
+ device.properties.name);
+ }
+}
+
+/* Prepare for resuming TP. Return true if TP should be actually
+ resumed. */
+
+static bool
+ze_prepare_for_resuming (thread_info *tp)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ /* We should not call this function if there is a priority
+ waitstatus. */
+ gdb_assert (!ze_has_priority_waitstatus (tp));
+
+ ze_device_info *device = ze_thread_device (tp);
+ gdb_assert (device != nullptr);
+
+ ze_thread_exec_state_t state = zetp->exec_state;
+ switch (state)
+ {
+ case ZE_THREAD_STATE_PAUSED:
+ zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+
+ [[fallthrough]];
+ case ZE_THREAD_STATE_STOPPED:
+ return true;
+
+ case ZE_THREAD_STATE_RUNNING:
+ /* Ignore resuming already running threads. */
+ return false;
+
+ case ZE_THREAD_STATE_UNKNOWN:
+ warning (_("thread %s has unknown execution "
+ "state"), tp->id.to_string ().c_str ());
+ return false;
+ }
+
+ internal_error (_("bad execution state: %d."), state);
+}
+
+/* Prepare for stopping TP. Return true if TP should be
+ actually stopped by sending an interrupt to the target. */
+
+static bool
+ze_prepare_for_stopping (thread_info *tp)
+{
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ ze_device_info *device = ze_thread_device (tp);
+ gdb_assert (device != nullptr);
+
+ ze_thread_exec_state_t state = zetp->exec_state;
+ switch (state)
+ {
+ case ZE_THREAD_STATE_PAUSED:
+ /* A paused thread is already stopped. */
+ zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+
+ [[fallthrough]];
+ case ZE_THREAD_STATE_STOPPED:
+ /* We silently ignore already stopped threads. */
+ dprintf ("thread %s (%s) on device %lu (%s) is already stopped",
+ tp->id.to_string ().c_str (),
+ ze_thread_id_str (zetp->id).c_str (),
+ device->ordinal, device->properties.name);
+ return false;
+
+ case ZE_THREAD_STATE_RUNNING:
+ return true;
+
+ case ZE_THREAD_STATE_UNKNOWN:
+ warning (_("Thread %s (%s) on device %lu (%s) has unknown execution "
+ "state"),
+ tp->id.to_string ().c_str (),
+ ze_thread_id_str (zetp->id).c_str (),
+ device->ordinal, device->properties.name);
+ return false;
+ }
+
+ internal_error (_("bad execution state: %d."), state);
+}
+
+/* Resume THREAD on DEVICE. */
+
+static void
+ze_resume (ze_device_info &device, ze_device_thread_t thread)
+{
+ dprintf ("device %lu (%s), thread %s", device.ordinal,
+ device.properties.name, ze_thread_id_str (thread).c_str ());
+
+ ze_result_t status = zetDebugResume (device.session, thread);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ break;
+
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ /* The thread is already running or unavailable.
+
+ Assuming our thread state tracking is correct, the thread isn't
+ running, so we assume it became unavailable. That is strange,
+ too, as we had it stopped. */
+ warning (_("thread %s unexpectedly unavailable on device %lu (%s)."),
+ ze_thread_id_str (thread).c_str (), device.ordinal,
+ device.properties.name);
+
+ /* Update our thread state to reflect the target. */
+ for_each_thread (device, thread,
+ [&device] (const ze_device_info &dev,
+ const ze_device_thread_t &tid)
+ {
+ thread_info *tp = ze_find_thread (dev, tid);
+ ze_remove_thread (tp);
+ });
+ break;
+
+ default:
+ error (_("Failed to resume %s on device %lu (%s): %x."),
+ ze_thread_id_str (thread).c_str (), device.ordinal,
+ device.properties.name, status);
+ }
+}
+
+/* Interrupt THREAD on DEVICE. */
+
+static void
+ze_interrupt (ze_device_info &device, ze_device_thread_t thread)
+{
+ dprintf ("device %lu (%s), thread %s",
+ device.ordinal, device.properties.name,
+ ze_thread_id_str (thread).c_str ());
+
+ ze_result_t status = zetDebugInterrupt (device.session, thread);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ if (ze_is_thread_id_all (thread))
+ device.ninterrupts++;
+
+ break;
+
+ case ZE_RESULT_NOT_READY:
+ /* We already requested THREAD to be stopped. We do not track
+ requests so let's ignore this. */
+ break;
+
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ /* The thread is already stopped.
+
+ Assuming that our state tracking works, update non-stopped
+ threads to reflect that. */
+ for_each_thread (device, thread,
+ [] (const ze_device_info &dev,
+ const ze_device_thread_t &tid)
+ {
+ thread_info *tp = ze_find_thread (dev, tid);
+ if (ze_thread_stopped (tp))
+ return;
+
+ ze_remove_thread (tp);
+ });
+ break;
+
+ default:
+ error (_("Failed to interrupt %s on device %lu (%s): %x."),
+ ze_thread_id_str (thread).c_str (), device.ordinal,
+ device.properties.name, status);
+ }
+}
+
+bool
+ze_target::is_range_stepping (thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ if (ze_thread_stopped (tp)
+ && (zetp->resume_state == ZE_THREAD_RESUME_STEP)
+ && (zetp->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP))
+ {
+ regcache *regcache = get_thread_regcache (tp, /* fetch = */ true);
+ CORE_ADDR pc = read_pc (regcache);
+
+ return ((pc >= zetp->step_range_start)
+ && (pc < zetp->step_range_end));
+ }
+
+ return false;
+}
+
+int
+ze_target::attach_to_device (uint32_t pid, ze_device_handle_t device)
+{
+ ze_device_properties_t properties;
+
+ memset (&properties, 0, sizeof (properties));
+ properties.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
+ properties.pNext = nullptr;
+
+ ze_result_t status = zeDeviceGetProperties (device, &properties);
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to obtain device properties (%x)."),
+ status);
+ return 0;
+ }
+
+ /* We're a bit paranoid. */
+ properties.name[ZE_MAX_DEVICE_NAME-1] = 0;
+
+ int nattached = 0;
+ uint32_t nsub_devices = 0;
+ status = zeDeviceGetSubDevices (device, &nsub_devices, nullptr);
+ if (status != ZE_RESULT_SUCCESS)
+ warning (_("Failed to get number of sub-devices in %s (%x)."),
+ properties.name, status);
+ else if (nsub_devices > 0)
+ {
+ std::vector<ze_device_handle_t> sub_devices (nsub_devices);
+ status = zeDeviceGetSubDevices (device, &nsub_devices,
+ sub_devices.data ());
+ if (status != ZE_RESULT_SUCCESS)
+ warning (_("Failed to enumerate sub-devices in %s (%x)."),
+ properties.name, status);
+ else
+ for (ze_device_handle_t sub_device : sub_devices)
+ nattached += attach_to_device (pid, sub_device);
+ }
+
+ /* If we attached to a sub-device, we're done. We won't be able to attach to
+ a parent device, anymore. */
+ if (nattached > 0)
+ return nattached;
+
+ /* Allow affecting the normal attach behaviour via environment variables by
+ disallowing attaching to devices or sub-devices. */
+ if (properties.flags & ZE_DEVICE_PROPERTY_FLAG_SUBDEVICE)
+ {
+ const char *disallow_sub_dev
+ = std::getenv ("ZE_GDB_DO_NOT_ATTACH_TO_SUB_DEVICE");
+ if (disallow_sub_dev != nullptr && *disallow_sub_dev != 0)
+ return nattached;
+ }
+ else
+ {
+ const char *disallow_dev
+ = std::getenv ("ZE_GDB_DO_NOT_ATTACH_TO_DEVICE");
+ if (disallow_dev != nullptr && *disallow_dev != 0)
+ return nattached;
+ }
+
+ uint32_t nregsets = 0;
+ status = zetDebugGetRegisterSetProperties (device, &nregsets, nullptr);
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to obtain number of register sets in %s (%x)."),
+ properties.name, status);
+ return nattached;
+ }
+
+ std::vector<zet_debug_regset_properties_t> regsets (nregsets);
+ status = zetDebugGetRegisterSetProperties (device, &nregsets,
+ regsets.data ());
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to obtain register sets in %s (%x)."),
+ properties.name, status);
+ return nattached;
+ }
+
+ long nthreads = (long) properties.numThreadsPerEU;
+
+ nthreads *= properties.numEUsPerSubslice;
+ if (nthreads < 0)
+ {
+ warning (_("Too many threads on device %s."), properties.name);
+ return nattached;
+ }
+
+ nthreads *= properties.numSubslicesPerSlice;
+ if (nthreads < 0)
+ {
+ warning (_("Too many threads on device %s."), properties.name);
+ return nattached;
+ }
+
+ nthreads *= properties.numSlices;
+ if (nthreads < 0)
+ {
+ warning (_("Too many threads on device %s."), properties.name);
+ return nattached;
+ }
+
+ /* Check with the actual target implementation whether it supports this kind
+ of device. */
+ if (!is_device_supported (properties, regsets))
+ {
+ warning (_("Skipping unsupported device %s."), properties.name);
+ return nattached;
+ }
+
+ std::unique_ptr<ze_device_info> dinfo { new ze_device_info };
+ dinfo->config.pid = pid;
+ dinfo->handle = device;
+ dinfo->properties = properties;
+ dinfo->nthreads = nthreads;
+
+ ze_pci_ext_properties_t pci_properties {};
+ status = zeDevicePciGetPropertiesExt (device, &pci_properties);
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to obtain PCI properties in %s (%x)."),
+ properties.name, status);
+ pci_properties.address.domain = 0;
+ pci_properties.address.bus = 0;
+ pci_properties.address.device = 0;
+ pci_properties.address.function = 0;
+ }
+
+ target_desc *tdesc = create_tdesc (dinfo.get (), regsets,
+ pci_properties);
+ dinfo->tdesc.reset (tdesc);
+
+ unsigned long ordinal = this->ordinal + 1;
+ if (ordinal == 0)
+ internal_error (_("device ordinal overflow."));
+
+ dinfo->ordinal = ordinal;
+
+ try
+ {
+ process_info *process = ze_attach (dinfo.get ());
+ if (process == nullptr)
+ return nattached;
+ }
+ catch (const gdb_exception_error &except)
+ {
+ warning ("%s", except.what ());
+ }
+
+ /* Add the device even if we were not able to attach to allow attempting to
+ attach to it explicitly later on. */
+ devices.push_back (dinfo.release ());
+ this->ordinal = ordinal;
+
+ nattached += 1;
+ return nattached;
+}
+
+int
+ze_target::attach_to_devices (uint32_t pid)
+{
+ uint32_t ndrivers = 0;
+ ze_result_t status = zeDriverGet (&ndrivers, nullptr);
+ if (status != ZE_RESULT_SUCCESS)
+ error (_("Failed to get number of device drivers (%x)."), status);
+
+ std::vector<ze_driver_handle_t> drivers (ndrivers);
+ status = zeDriverGet (&ndrivers, drivers.data ());
+ if (status != ZE_RESULT_SUCCESS)
+ error (_("Failed to enumerate device drivers (%x)."), status);
+
+ int nattached = 0;
+ for (ze_driver_handle_t driver : drivers)
+ {
+ ze_driver_properties_t properties;
+
+ memset (&properties, 0, sizeof (properties));
+ properties.stype = ZE_STRUCTURE_TYPE_DRIVER_PROPERTIES;
+ properties.pNext = nullptr;
+
+ status = zeDriverGetProperties (driver, &properties);
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to obtain driver properties (%x)."),
+ status);
+ continue;
+ }
+
+ ze_api_version_t version;
+ status = zeDriverGetApiVersion (driver, &version);
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to obtain API version in %s (%x)."),
+ driver_uuid_str (properties.uuid).c_str (),
+ status);
+ continue;
+ }
+
+ switch (ZE_MAJOR_VERSION (version))
+ {
+ case 1:
+ /* We should be OK with all minor versions. */
+ break;
+
+ default:
+ warning (_("Unsupported API version in %s (%x)."),
+ driver_uuid_str (properties.uuid).c_str (),
+ ZE_MAJOR_VERSION (version));
+ continue;
+ }
+
+ uint32_t ndevices = 0;
+ status = zeDeviceGet (driver, &ndevices, nullptr);
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to get number of devices in %s (%x)."),
+ driver_uuid_str (properties.uuid).c_str (),
+ status);
+ continue;
+ }
+
+ std::vector<ze_device_handle_t> devices (ndevices);
+ status = zeDeviceGet (driver, &ndevices, devices.data ());
+ if (status != ZE_RESULT_SUCCESS)
+ {
+ warning (_("Failed to enumerate devices in %s (%x)."),
+ driver_uuid_str (properties.uuid).c_str (),
+ status);
+ continue;
+ }
+
+ dprintf ("scanning driver %s (%" PRIu32 " devices)",
+ driver_uuid_str (properties.uuid).c_str (), ndevices);
+
+ for (ze_device_handle_t device : devices)
+ nattached += attach_to_device (pid, device);
+ }
+
+ return nattached;
+}
+
+void
+ze_target::ze_notif_libraries ()
+{
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+
+ process_info *process = device->process;
+ if (process == nullptr)
+ continue;
+
+ if (process->dlls_changed)
+ push_notif_library (process);
+ }
+}
+
+uint64_t
+ze_target::fetch_events (ze_device_info &device)
+{
+ /* There are no events if we're not attached. */
+ if (device.session == nullptr)
+ return 0;
+
+ uint64_t nevents = 0;
+ for (;;)
+ {
+ zet_debug_event_t event = {};
+ ze_result_t status = zetDebugReadEvent (device.session, 0ull, &event);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ nevents += 1;
+ break;
+
+ case ZE_RESULT_NOT_READY:
+ return nevents;
+
+ default:
+ error (_("error fetching events from device %lu (%s): %x."),
+ device.ordinal, device.properties.name, status);
+ }
+
+ dprintf ("received event from device %lu (%s): %s", device.ordinal,
+ device.properties.name, ze_event_str (event).c_str ());
+
+ switch (event.type)
+ {
+ case ZET_DEBUG_EVENT_TYPE_INVALID:
+ break;
+
+ case ZET_DEBUG_EVENT_TYPE_DETACHED:
+ ze_device_detached (device, event.info.detached.reason);
+ device.session = nullptr;
+ return nevents;
+
+ case ZET_DEBUG_EVENT_TYPE_PROCESS_ENTRY:
+ ze_ack_event (device, event);
+ continue;
+
+ case ZET_DEBUG_EVENT_TYPE_PROCESS_EXIT:
+ ze_ack_event (device, event);
+ continue;
+
+ case ZET_DEBUG_EVENT_TYPE_MODULE_LOAD:
+ {
+ /* We would not remain attached without a process. */
+ process_info *process = device.process;
+ gdb_assert (process != nullptr);
+
+ bool need_ack
+ = ((event.flags & ZET_DEBUG_EVENT_FLAG_NEED_ACK) != 0);
+
+ loaded_dll (process, event.info.module.moduleBegin,
+ event.info.module.moduleEnd,
+ event.info.module.load);
+
+ /* If Level-Zero is not requesting the event to be
+ acknowledged, we're done.
+
+ This happens when attaching to an already running process,
+ for example. We will receive module load events for
+ modules that have already been loaded.
+
+ No need to inform GDB, either, as we expect GDB to query
+ shared libraries after attach. */
+ if (!need_ack)
+ continue;
+
+ device.ack_pending.emplace_back (event);
+ }
+ continue;
+
+ case ZET_DEBUG_EVENT_TYPE_MODULE_UNLOAD:
+ {
+ /* We would not remain attached without a process. */
+ process_info *process = device.process;
+ gdb_assert (process != nullptr);
+
+ unloaded_dll (process, event.info.module.moduleBegin,
+ event.info.module.moduleEnd,
+ event.info.module.load);
+
+ /* We don't need an ack, here, but maybe Level-Zero does. */
+ ze_ack_event (device, event);
+
+ /* We do not notify GDB immediately about the module unload.
+ This is harmless until we reclaim the memory for something
+ else. In our case, this can only be another module and we
+ will notify GDB in that case. */
+ }
+ continue;
+
+ case ZET_DEBUG_EVENT_TYPE_THREAD_STOPPED:
+ {
+ ze_device_thread_t filter = event.info.thread.thread;
+ ze_ack_event (device, event);
+
+ /* We would not remain attached without a process. */
+ process_info *process = device.process;
+ gdb_assert (process != nullptr);
+
+ uint32_t nstopped = 0;
+ for_each_thread (device, filter,
+ [&] (const ze_device_info &dev,
+ const ze_device_thread_t &tid)
+ {
+ /* Ignore threads we know to be stopped.
+
+ We already analyzed the stop reason and probably
+ destroyed it in the process. */
+ thread_info *tp = ze_find_thread (dev, tid);
+ if (ze_thread_stopped (tp))
+ return;
+
+ try
+ {
+ gdb_signal signal = GDB_SIGNAL_0;
+ target_stop_reason reason
+ = get_stop_reason (dev, tid, signal);
+
+ tp = ze_notice_thread (dev, tid);
+
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ zetp->exec_state = ZE_THREAD_STATE_STOPPED;
+ zetp->stop_reason = reason;
+ zetp->waitstatus.set_stopped (signal);
+
+ nstopped += 1;
+ }
+ /* FIXME: exceptions
+
+ We'd really like to catch some 'thread_unavailable'
+ exception rather than assuming that any exception is
+ due to thread availability. */
+ catch (...)
+ {
+ ze_remove_thread (tp);
+ }
+ });
+
+ /* This is the response to an interrupt if TID is "all". */
+ if (ze_is_thread_id_all (filter))
+ {
+ if (device.ninterrupts > 0)
+ device.ninterrupts--;
+ else
+ warning (_("ignoring spurious stop-all event on "
+ "device %lu"), device.ordinal);
+ }
+ }
+ continue;
+
+ case ZET_DEBUG_EVENT_TYPE_THREAD_UNAVAILABLE:
+ {
+ ze_device_thread_t filter = event.info.thread.thread;
+ ze_ack_event (device, event);
+
+ /* We would not remain attached without a process. */
+ process_info *process = device.process;
+ gdb_assert (process != nullptr);
+
+ process->for_each_thread ([&] (thread_info *tp)
+ {
+ ze_device_thread_t tid = ze_thread_id (tp);
+ if (!ze_device_thread_in (tid, filter))
+ return;
+
+ /* Ignore threads we know to be stopped.
+
+ They would not be considered in the response event for
+ an interrupt request. */
+ if (ze_thread_stopped (tp))
+ return;
+
+ dprintf ("Removing thread %s (%s) on device %lu (%s)",
+ tp->id.to_string ().c_str (),
+ ze_thread_id_str (tid).c_str (),
+ device.ordinal, device.properties.name);
+
+ delete (ze_thread_info *) tp->target_data ();
+ process->remove_thread (tp);
+ });
+
+ /* This is the response to an interrupt if TID is "all". */
+ if (ze_is_thread_id_all (filter))
+ {
+ if (device.ninterrupts > 0)
+ device.ninterrupts--;
+ else
+ warning (_("ignoring spurious unavailable-all event on "
+ "device %lu (%s)"), device.ordinal,
+ device.properties.name);
+ }
+ }
+ continue;
+ }
+
+ /* We only get here if we have not processed EVENT. */
+ warning (_("ignoring event '%s' on device %lu (%s)."),
+ ze_event_str (event).c_str (), device.ordinal,
+ device.properties.name);
+
+ /* Acknowledge the ignored event so we don't get stuck. */
+ ze_ack_event (device, event);
+ }
+}
+
+void
+ze_target::init ()
+{
+ ze_result_t status = zeInit (0);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ break;
+
+ default:
+ error (_("Failed to initialize Level-Zero: %x"), status);
+ }
+}
+
+bool
+ze_target::async (bool enable)
+{
+ bool previous = ze_is_async ();
+ if (previous != enable)
+ {
+#ifndef USE_WIN32API
+ if (enable)
+ {
+ try
+ {
+ errno = 0;
+ int status = pipe (ze_event_pipe);
+ if (status == -1)
+ error (_("Failed to create event pipe: %s."),
+ safe_strerror (errno));
+
+ status = fcntl (ze_event_pipe[0], F_SETFL, O_NONBLOCK);
+ if (status == -1)
+ error (_("Failed to set pipe[0] to non-blocking: %s."),
+ safe_strerror (errno));
+
+ status = fcntl (ze_event_pipe[1], F_SETFL, O_NONBLOCK);
+ if (status == -1)
+ error (_("Failed to set pipe[1] to non-blocking: %s."),
+ safe_strerror (errno));
+
+ /* Register the event loop handler. */
+ add_file_handler (ze_event_pipe[0],
+ handle_target_event, NULL,
+ "ze-low");
+
+ /* Always trigger a wait. */
+ ze_async_mark ();
+ }
+ catch (std::exception &ex)
+ {
+ warning ("%s", ex.what ());
+
+ if (ze_event_pipe[0] != -1)
+ {
+ close (ze_event_pipe[0]);
+ ze_event_pipe[0] = -1;
+ }
+
+ if (ze_event_pipe[1] != -1)
+ {
+ close (ze_event_pipe[1]);
+ ze_event_pipe[1] = -1;
+ }
+ }
+ }
+ else
+ {
+ delete_file_handler (ze_event_pipe[0]);
+
+ close (ze_event_pipe[0]);
+ close (ze_event_pipe[1]);
+ ze_event_pipe[0] = -1;
+ ze_event_pipe[1] = -1;
+ }
+#else
+ error (_("%s: tbd"), __FUNCTION__);
+#endif
+ }
+
+ return previous;
+}
+
+int
+ze_target::create_inferior (const char *program,
+ const std::string &args)
+{
+ /* Level-zero does not support creating inferiors. */
+ return -1;
+}
+
+int
+ze_target::attach (int pid)
+{
+ if (!devices.empty ())
+ error (_("Already attached."));
+
+ uint32_t hostpid = (uint32_t) pid;
+ if ((int) hostpid != pid)
+ error (_("Host process id is not supported."));
+
+ int ndevices = attach_to_devices (hostpid);
+ if (ndevices == 0)
+ error (_("No supported devices found."));
+
+ /* Let's check if we were able to attach to at least one device. */
+ int nattached = 0;
+ std::stringstream sstream;
+ sstream << "Failed to attach to any device.";
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+ switch (device->debug_attach_state)
+ {
+ case ZE_RESULT_SUCCESS:
+ if (device->session == nullptr)
+ {
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "failed to initialize debug session";
+ continue;
+ }
+
+ /* GDB (and higher layers of gdbserver) expects threads stopped on
+ attach in all-stop mode. In non-stop mode, GDB explicitly
+ sends a stop request. */
+ if (!non_stop)
+ {
+ device->process->for_each_thread ([this] (thread_info *tp)
+ {
+ ze_set_resume_state (tp, resume_stop);
+ bool should_stop = ze_prepare_for_stopping (tp);
+ gdb_assert (should_stop);
+ });
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ ze_interrupt (*device, all);
+ }
+
+ nattached += 1;
+ break;
+ case ZE_RESULT_NOT_READY:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "attempting to attach too early";
+ break;
+ case ZE_RESULT_ERROR_UNSUPPORTED_FEATURE:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "attaching is not supported";
+ break;
+ case ZE_RESULT_ERROR_INSUFFICIENT_PERMISSIONS:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "attaching is not permitted";
+ break;
+ case ZE_RESULT_ERROR_NOT_AVAILABLE:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "a debugger is already attached";
+ break;
+ default:
+ sstream << "\nDevice " << device->ordinal << " ["
+ << device->properties.name << "] : "
+ << "failed to attach with error code '"
+ << std::hex << device->debug_attach_state
+ << std::resetiosflags (std::ios::basefield)
+ << "'";
+ break;
+ }
+ }
+
+ if (nattached == 0)
+ error (_("%s"), sstream.str ().c_str ());
+
+ /* Return the ID of the last device we attached to. */
+ int device_pid = ze_device_pid (*(devices.back ()));
+ return device_pid;
+}
+
+int
+ze_target::detach (process_info *proc)
+{
+ gdb_assert (proc != nullptr);
+
+ process_info_private *priv = proc->priv;
+ gdb_assert (priv != nullptr);
+
+ ze_device_info *device = priv->device;
+ if (device != nullptr)
+ {
+ /* Resume all the threads on the device. GDB must have already
+ removed all the breakpoints. */
+ try
+ {
+ /* Clear all the pending events first. */
+ proc->for_each_thread ([] (thread_info *tp)
+ {
+ (void) ze_move_waitstatus (tp);
+ });
+
+ resume (*device);
+ }
+ catch (const gdb_exception_error &except)
+ {
+ /* Swallow the error. We are detaching anyway. */
+ dprintf ("%s", except.what ());
+ }
+
+ ze_detach (device);
+ }
+
+ mourn (proc);
+ return 0;
+}
+
+int
+ze_target::kill (process_info *proc)
+{
+ /* Level-zero does not support killing inferiors. */
+ return -1;
+}
+
+void
+ze_target::mourn (process_info *proc)
+{
+ ze_remove_process (proc);
+}
+
+void
+ze_target::join (int pid)
+{
+ /* Nothing to do for Level-Zero targets. */
+}
+
+void
+ze_target::resume (ze_device_info &device)
+{
+ process_info *process = device.process;
+ gdb_assert (process != nullptr);
+
+ bool has_thread_to_resume = false;
+ process->for_each_thread ([&] (thread_info *tp)
+ {
+ ze_set_resume_state (tp, resume_continue);
+ if (ze_prepare_for_resuming (tp))
+ {
+ prepare_thread_resume (tp);
+ regcache_invalidate_thread (tp);
+ has_thread_to_resume = true;
+ }
+ });
+
+ /* There is nothing to resume if nothing is stopped. */
+ if (!has_thread_to_resume)
+ return;
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ ze_resume (device, all);
+}
+
+void
+ze_target::resume_single_thread (thread_info *thread)
+{
+ ze_device_info *device = ze_thread_device (thread);
+ gdb_assert (device != nullptr);
+ ze_thread_info *zetp = ze_thread (thread);
+ gdb_assert (zetp != nullptr);
+
+ bool should_resume = ze_prepare_for_resuming (thread);
+ gdb_assert (should_resume);
+ prepare_thread_resume (thread);
+ regcache_invalidate_thread (thread);
+ ze_resume (*device, zetp->id);
+}
+
+size_t
+ze_target::mark_eventing_threads (ptid_t resume_ptid, resume_kind rkind)
+{
+ size_t num_eventing = 0;
+ for_each_thread ([&] (thread_info *tp)
+ {
+ if (!tp->id.matches (resume_ptid))
+ return;
+
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ /* TP may have stopped at a breakpoint that is already deleted
+ by GDB. Consider TP as an eventing thread only if the BP is
+ still there. Because we are inside the 'resume' request, if
+ the BP is valid, GDB must have already re-inserted it.
+
+ FIXME: Keep track of the stop_pc and compare it with the
+ current (i.e. to-be-resumed) pc. */
+ if ((zetp->exec_state == ZE_THREAD_STATE_STOPPED)
+ && (zetp->stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT)
+ && !is_at_breakpoint (tp))
+ {
+ /* The BP is gone. Clear the waitstatus, too. */
+ (void) ze_move_waitstatus (tp);
+ return;
+ }
+
+ /* TP may have stopped during range-stepping, but we reported
+ another thread to GDB. This means the range-stepping state
+ of TP is canceled.
+
+ The condition here is similar to, but not the same as
+ is_range_stepping. We do not check here if the thread's stop
+ pc is within the stepping range. We rather only check if there
+ was a range to step, because the thread may have stopped just
+ when it came out of the range. We should cancel the event in
+ that case, too. */
+ if (ze_thread_stopped (tp)
+ && (zetp->stop_reason == TARGET_STOPPED_BY_SINGLE_STEP)
+ && (zetp->step_range_end > zetp->step_range_start))
+ {
+ target_waitstatus waitstatus = ze_move_waitstatus (tp);
+ dprintf ("Thread %s (%s) was range-stepping, "
+ "canceling the pending event",
+ tp->id.to_string ().c_str (),
+ ze_thread_id_str (zetp->id).c_str ());
+ return;
+ }
+
+ /* Recover the resume state so that the thread can be picked up
+ by 'wait'. */
+ ze_set_resume_state (tp, rkind);
+ num_eventing++;
+ });
+
+ dprintf ("there are %zu eventing threads for ptid %s", num_eventing,
+ resume_ptid.to_string ().c_str ());
+
+ return num_eventing;
+}
+
+/* Display a resume request for logging purposes. */
+
+static void
+print_resume_info (const thread_resume &rinfo)
+{
+ ptid_t rptid = rinfo.thread;
+
+ switch (rinfo.kind)
+ {
+ case resume_continue:
+ dprintf ("received 'continue' resume request for (%s)",
+ rptid.to_string ().c_str ());
+ return;
+
+ case resume_step:
+ dprintf ("received 'step' resume request for (%s)"
+ " in range [0x%" PRIx64 ", 0x%" PRIx64 ")",
+ rptid.to_string ().c_str (),
+ rinfo.step_range_start, rinfo.step_range_end);
+ return;
+
+ case resume_stop:
+ dprintf ("received 'stop' resume request for (%s)",
+ rptid.to_string ().c_str ());
+ return;
+ }
+
+ internal_error (_("bad resume kind: %d."), rinfo.kind);
+}
+
+/* Normalize the resume requests for easier processing later on. */
+
+static void
+normalize_resume_infos (thread_resume *resume_info, size_t n)
+{
+ for (size_t i = 0; i < n; ++i)
+ {
+ thread_resume &rinfo = resume_info[i];
+ ptid_t rptid = rinfo.thread;
+
+ /* Log the original requests. */
+ print_resume_info (rinfo);
+
+ /* We convert ptids of the form (p, -1, 0) to (p, 0, 0) to make
+ 'ptid.matches' work. This transformation is safe because we
+ enumerate the threads starting at 1. */
+ if ((rptid.lwp () == -1) && (rptid.pid () > 0))
+ rinfo.thread = ptid_t (rptid.pid (), 0, 0);
+
+ if (rinfo.sig != 0)
+ {
+ /* Clear out the signal. Our target does not accept
+ signals. */
+ warning (_("Ignoring signal on resume request for %s"),
+ rinfo.thread.to_string ().c_str ());
+ rinfo.sig = 0;
+ }
+ }
+}
+
+/* Resuming threads of a device all at once with a single API call
+ is preferable to resuming threads individually. Therefore, we
+ want to combine individual resume requests with wildcard resumes,
+ if possible.
+
+ For instance, if we receive "vCont;s:1;s:2;c", we would like to
+ make a single ze_resume call with the 'all.all.all.all' thread id
+ after preparing threads 1 and 2 for stepping and the others for
+ continuing.
+
+ We preprocess the resume requests to find for which devices we
+ shall combine the requests. We attempt a merge in all-stop mode
+ when the requests contain continue/step requests only. */
+
+static std::set<ze_device_info *>
+find_wildcard_devices (thread_resume *resume_info, size_t n,
+ const std::list<ze_device_info *> &devices)
+{
+ std::set<ze_device_info *> wildcard_devices;
+
+ if (non_stop)
+ return wildcard_devices;
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ if (resume_info[i].kind == resume_stop)
+ {
+ wildcard_devices.clear ();
+ break;
+ }
+
+ ptid_t rptid = resume_info[i].thread;
+ if (rptid == minus_one_ptid)
+ {
+ for (ze_device_info *device : devices)
+ wildcard_devices.insert (device);
+ break;
+ }
+
+ if (rptid.is_pid ())
+ {
+ process_info *proc = find_process_pid (rptid.pid ());
+ ze_device_info *device = ze_process_device (proc);
+ if (device != nullptr)
+ wildcard_devices.insert (device);
+ }
+ }
+
+ return wildcard_devices;
+}
+
+void
+ze_target::resume (thread_resume *resume_info, size_t n)
+{
+ if (frozen)
+ return;
+
+ /* In all-stop mode, a new resume request overwrites any previous
+ request. We're going to set the request for affected threads below.
+ Clear it for all threads, here.
+
+ In the resume-all case, this will iterate over all threads twice to
+ first clear and then set the resume request. Not ideal, but if we
+ first iterated over all threads to set the resume state, we'd also
+ have to iterate over all threads again in order to actually resume
+ them.
+
+ And if we inverted the loops (i.e. iterate over threads, then over
+ resume requests), we'd miss out on the opportunity to resume all
+ threads at once. */
+ if (!non_stop)
+ for_each_thread ([] (thread_info *tp)
+ {
+ ze_clear_resume_state (tp);
+ });
+
+ normalize_resume_infos (resume_info, n);
+
+ /* Check if there is a thread with a pending event for any of the
+ resume requests. In all-stop mode, we would omit actually
+ resuming the target if there is such a thread. In non-stop mode,
+ we omit resuming the thread itself. */
+ size_t num_eventing = 0;
+ for (size_t i = 0; i < n; ++i)
+ {
+ const thread_resume &rinfo = resume_info[i];
+ resume_kind rkind = rinfo.kind;
+ ptid_t rptid = rinfo.thread;
+
+ if (rkind == resume_stop)
+ continue;
+
+ num_eventing += mark_eventing_threads (rptid, rkind);
+ }
+
+ if ((num_eventing > 0) && !non_stop)
+ return;
+
+ std::set<ze_device_info *> wildcard_devices
+ = find_wildcard_devices (resume_info, n, devices);
+
+ std::set<ze_device_info *> devices_to_resume;
+
+ /* Lambda for applying a resume info on a single thread. */
+ auto apply_resume_info = ([&] (const thread_resume &rinfo,
+ thread_info *tp)
+ {
+ if (ze_has_priority_waitstatus (tp))
+ return;
+
+ ze_set_resume_state (tp, rinfo.kind);
+ ze_device_info *device = ze_thread_device (tp);
+ ze_device_thread_t tid = ze_thread_id (tp);
+
+ switch (rinfo.kind)
+ {
+ case resume_stop:
+ if (ze_prepare_for_stopping (tp))
+ ze_interrupt (*device, tid);
+ break;
+
+ case resume_step:
+ {
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ regcache *regcache
+ = get_thread_regcache (tp, /* fetch = */ true);
+ CORE_ADDR pc = read_pc (regcache);
+
+ /* For single-stepping, start == end. Typically, both are 0.
+ For range-stepping, the PC must be within the range. */
+ CORE_ADDR start = rinfo.step_range_start;
+ CORE_ADDR end = rinfo.step_range_end;
+ gdb_assert ((start == end) || ((pc >= start) && (pc < end)));
+
+ zetp->step_range_start = start;
+ zetp->step_range_end = end;
+ }
+
+ [[fallthrough]];
+ case resume_continue:
+ if (ze_prepare_for_resuming (tp))
+ {
+ prepare_thread_resume (tp);
+ regcache_invalidate_thread (tp);
+
+ /* If the device can be resumed as a whole,
+ omit resuming the thread individually. */
+ if (wildcard_devices.count (device) == 0)
+ ze_resume (*device, tid);
+ else
+ devices_to_resume.insert (device);
+ }
+ break;
+ }
+ });
+
+ /* We may receive multiple requests that apply to a thread. E.g.
+ "vCont;r0xff10,0xffa0:p1.9;c" could be sent to make thread 1.9 do
+ range-stepping from 0xff10 to 0xffa0, while continuing others.
+ According to the Remote Protocol Section E.2 (Packets),
+ "For each inferior thread, the leftmost action with a matching
+ thread-id is applied." For this reason, we keep track of which
+ threads have been resumed individually so that we can skip them
+ when processing wildcard requests.
+
+ Alternatively, we could have the outer loop iterate over threads
+ and the inner loop iterate over resume infos to find the first
+ matching resume info for each thread. There may, however, be a
+ large number of threads and a handful of resume infos that apply
+ to a few threads only. For performance reasons, we prefer to
+ iterate over resume infos in the outer loop. */
+ std::set<thread_info *> individually_resumed_threads;
+ for (size_t i = 0; i < n; ++i)
+ {
+ const thread_resume &rinfo = resume_info[i];
+ gdb_assert (rinfo.sig == 0);
+ ptid_t rptid = rinfo.thread;
+ int rpid = rptid.pid ();
+ if ((rptid == minus_one_ptid)
+ || rptid.is_pid ()
+ || (rptid.lwp () == -1))
+ {
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+
+ int pid = ze_device_pid (*device);
+ if ((rpid != -1) && (rpid != pid))
+ continue;
+
+ device->process->for_each_thread ([&] (thread_info *tp)
+ {
+ /* We trust that GDB will not send us wildcard resume
+ requests with overlapping pids. Hence, we track
+ only individually-resumed threads. */
+ if (individually_resumed_threads.count (tp) == 0)
+ apply_resume_info (rinfo, tp);
+ });
+ }
+ }
+ else
+ {
+ thread_info *tp = find_thread_ptid (rptid);
+ if (tp == nullptr)
+ {
+ tp = ze_create_exited_thread (rptid);
+ if (tp == nullptr)
+ warning (_("No process with id %d."), rptid.pid ());
+
+ continue;
+ }
+
+ apply_resume_info (rinfo, tp);
+ individually_resumed_threads.insert (tp);
+ }
+ }
+
+ /* Finally, resume the whole devices. */
+ ze_device_thread_t all = ze_thread_id_all ();
+ for (ze_device_info *device : devices_to_resume)
+ ze_resume (*device, all);
+}
+
+/* Look for a thread preferably with a priority stop
+ event. If we cannot find such an event, we look for an
+ interrupt-related stop event, e.g. a stop because of an
+ external Ctrl-C or an internal pause_all request.
+
+ We first make an iteration over the threads to figure out
+ what kind of an event we can report. Once found, we select
+ a thread randomly. */
+
+static thread_info *
+ze_find_eventing_thread (ptid_t ptid)
+{
+ using thread_predicate = bool (*) (const thread_info *);
+ thread_predicate is_stopped = [] (const thread_info *tp)
+ {
+ return (ze_thread (tp)->waitstatus.kind () == TARGET_WAITKIND_STOPPED);
+ };
+
+ thread_predicate predicate = nullptr;
+ find_thread (ptid, [&] (thread_info *tp)
+ {
+ if (ze_has_priority_waitstatus (tp))
+ {
+ predicate = ze_has_priority_waitstatus;
+ return true;
+ }
+
+ if (is_stopped (tp))
+ predicate = is_stopped;
+ else if ((predicate == nullptr) && ze_has_waitstatus (tp))
+ predicate = ze_has_waitstatus;
+
+ return false;
+ });
+
+ thread_info *thread = nullptr;
+ if (predicate != nullptr)
+ thread = find_thread_in_random (ptid, [predicate] (thread_info *tp)
+ {
+ return predicate (tp);
+ });
+
+ return thread;
+}
+
+ptid_t
+ze_target::wait (ptid_t ptid, target_waitstatus *status,
+ target_wait_flags options)
+{
+ /* We need to wait for further events. */
+ ze_async_mark ();
+
+ do
+ {
+ /* We start by fetching all events.
+
+ This will mark threads stopped and also process solist updates.
+ We may get solist updates even if all device threads are running
+ or unavailable.
+
+ For all-stop, we anyway want to stop all threads and drain events
+ before reporting the stop to GDB. */
+ uint64_t nevents;
+ do
+ {
+ nevents = 0;
+
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+ /* Fetch from any device, regardless of PTID, so that we
+ drain the event queues as much as possible. We use
+ PTID down below to filter the events anyway. */
+ nevents += fetch_events (*device);
+ }
+ }
+ while (nevents > 0);
+
+ /* We may have accumulated new library events. */
+ ze_notif_libraries ();
+
+ /* Next, find a matching entity whose event we'll report. */
+ thread_info *thread = ze_find_eventing_thread (ptid);
+ if (thread != nullptr)
+ {
+ ze_thread_info *zetp = ze_thread (thread);
+ gdb_assert (zetp != nullptr);
+
+ if (is_range_stepping (thread))
+ {
+ /* We are inside the stepping range. Resume the thread
+ and go back to fetching events. */
+ dprintf ("thread %s is stepping in range "
+ "[0x%" PRIx64 ", 0x%" PRIx64 ")",
+ ze_thread_id_str (zetp->id).c_str (),
+ zetp->step_range_start, zetp->step_range_end);
+
+ zetp->waitstatus.set_ignore ();
+ gdb_assert (zetp->resume_state == ZE_THREAD_RESUME_STEP);
+
+ resume_single_thread (thread);
+ continue;
+ }
+
+ /* Stop all other threads.
+
+ Save the waitstatus before, because pause_all clears all
+ low-priority events. */
+ *status = zetp->waitstatus;
+
+ if (!non_stop)
+ pause_all (false);
+
+ /* We need to remove threads we report exited. */
+ ptid_t id = thread->id;
+ if (status->kind () == TARGET_WAITKIND_THREAD_EXITED)
+ {
+ ze_remove_thread (thread);
+ return id;
+ }
+
+ /* Now also clear the thread's event, regardless of its
+ priority. */
+ zetp->waitstatus.set_ignore ();
+ zetp->step_range_start = 0;
+ zetp->step_range_end = 0;
+
+ /* FIXME: switch_to_thread
+
+ Why isn't the caller switching based on the returned ptid? */
+ switch_to_thread (thread);
+ return id;
+ }
+
+ /* We only allow a blocking wait for interrupt responses.
+
+ We get neither thread entry nor exit events, so whether we
+ resumed anything or not doesn't give any indication as to whether
+ we can expect an event. */
+ if (!ze_any_interrupt_pending (ptid))
+ {
+ if (!interrupted)
+ break;
+
+ /* Break a blocking wait in GDB when we tried to interrupt and
+ nothing responded. */
+ interrupted = false;
+ status->set_no_resumed ();
+ return null_ptid;
+ }
+
+ std::this_thread::yield ();
+ }
+ while ((options & TARGET_WNOHANG) == 0);
+
+ /* We only get here if we did not find any event to report. */
+
+ status->set_ignore ();
+ return null_ptid;
+}
+
+void
+ze_target::fetch_registers (regcache *regcache, int regno)
+{
+ ze_device_thread_t tid = ze_thread_id (regcache->thread);
+ ze_device_info *device = ze_thread_device (regcache->thread);
+ gdb_assert (device != nullptr);
+
+ if (regno == -1)
+ ze_fetch_all_registers (*device, tid, regcache);
+ else
+ ze_fetch_register (*device, tid, regcache, regno);
+}
+
+void
+ze_target::store_registers (regcache *regcache, int regno)
+{
+ ze_device_thread_t tid = ze_thread_id (regcache->thread);
+ ze_device_info *device = ze_thread_device (regcache->thread);
+ gdb_assert (device != nullptr);
+
+ if (regno == -1)
+ ze_store_all_registers (*device, tid, regcache);
+ else
+ ze_store_register (*device, tid, regcache, regno);
+}
+
+int
+ze_target::read_memory (thread_info *tp, CORE_ADDR memaddr,
+ unsigned char *myaddr, int len)
+{
+ ze_device_thread_t tid = ze_thread_id (tp);
+ ze_device_info *device = ze_thread_device (tp);
+ if (device == nullptr)
+ return EIO;
+
+ return read_memory (*device, tid, memaddr, myaddr, len);
+}
+
+int
+ze_target::read_memory (const ze_device_info &device,
+ const ze_device_thread_t &tid, CORE_ADDR memaddr,
+ unsigned char *myaddr, int len)
+{
+ unsigned int addr_space = 0; /* Only the default space for now. */
+
+ zet_debug_memory_space_desc_t desc;
+ memset (&desc, 0, sizeof (desc));
+ desc.stype = ZET_STRUCTURE_TYPE_DEBUG_MEMORY_SPACE_DESC;
+ desc.pNext = nullptr;
+ desc.type = (zet_debug_memory_space_type_t) addr_space;
+ desc.address = (uint64_t) memaddr;
+
+ ze_result_t status
+ = zetDebugReadMemory (device.session, tid, &desc, len, myaddr);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ return 0;
+
+ default:
+ dprintf ("error reading %d bytes of memory from %s with %s: %x",
+ len, core_addr_to_string_nz (memaddr),
+ ze_thread_id_str (tid).c_str (), status);
+
+ return EIO;
+ }
+}
+
+int
+ze_target::read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ if (current_thread != nullptr)
+ return read_memory (current_thread, memaddr, myaddr, len);
+
+ process_info *process = current_process ();
+ if (process == nullptr)
+ return EIO;
+
+ ze_device_info *device = ze_process_device (process);
+ if (process == nullptr)
+ return EIO;
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ return read_memory (*device, all, memaddr, myaddr, len);
+}
+
+int
+ze_target::write_memory (thread_info *tp, CORE_ADDR memaddr,
+ const unsigned char *myaddr, int len)
+{
+ ze_device_thread_t tid = ze_thread_id (tp);
+ ze_device_info *device = ze_thread_device (tp);
+ if (device == nullptr)
+ return EIO;
+
+ return write_memory (*device, tid, memaddr, myaddr, len);
+}
+
+int
+ze_target::write_memory (const ze_device_info &device,
+ const ze_device_thread_t &tid, CORE_ADDR memaddr,
+ const unsigned char *myaddr, int len)
+{
+ unsigned int addr_space = 0; /* Only the default space for now. */
+
+ zet_debug_memory_space_desc_t desc;
+ memset (&desc, 0, sizeof (desc));
+ desc.stype = ZET_STRUCTURE_TYPE_DEBUG_MEMORY_SPACE_DESC;
+ desc.pNext = nullptr;
+ desc.type = (zet_debug_memory_space_type_t) addr_space;
+ desc.address = (uint64_t) memaddr;
+
+ dprintf ("writing %d bytes of memory to %s with %s",
+ len, core_addr_to_string_nz (memaddr),
+ ze_thread_id_str (tid).c_str ());
+
+ ze_result_t status
+ = zetDebugWriteMemory (device.session, tid, &desc, len, myaddr);
+ switch (status)
+ {
+ case ZE_RESULT_SUCCESS:
+ return 0;
+
+ default:
+ dprintf ("error writing %d bytes of memory to %s with %s: %x",
+ len, core_addr_to_string_nz (memaddr),
+ ze_thread_id_str (tid).c_str (), status);
+
+ return EIO;
+ }
+}
+
+int
+ze_target::write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len)
+{
+ if (current_thread != nullptr)
+ return write_memory (current_thread, memaddr, myaddr, len);
+
+ process_info *process = current_process ();
+ if (process == nullptr)
+ return EIO;
+
+ ze_device_info *device = ze_process_device (process);
+ if (process == nullptr)
+ return EIO;
+
+ ze_device_thread_t all = ze_thread_id_all ();
+ return write_memory (*device, all, memaddr, myaddr, len);
+}
+
+bool
+ze_target::thread_alive (ptid_t ptid)
+{
+ thread_info *tp = find_thread_ptid (ptid);
+ if (tp == nullptr)
+ return false;
+
+ const ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ return (zetp->exec_state != ZE_THREAD_STATE_EXITED);
+}
+
+bool
+ze_target::thread_stopped (struct thread_info *tp)
+{
+ const ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ return (zetp->exec_state == ZE_THREAD_STATE_STOPPED);
+}
+
+void
+ze_target::request_interrupt ()
+{
+ /* Interrupting all threads on all devices.
+
+ Threads that are already stopped will be ignored by the interrupt. */
+ ze_device_thread_t all = ze_thread_id_all ();
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+
+ /* Ignore devices we're not modelling as processes. */
+ if (device->process == nullptr)
+ continue;
+
+ /* There's no point in sending multiple interrupts to a device.
+
+ We are guaranteed to get a reply to an interrupt request, even if
+ no thread responded. */
+ if (device->ninterrupts != 0)
+ continue;
+
+ ze_interrupt (*device, all);
+ interrupted = true;
+ }
+}
+
+void
+ze_target::pause_all (bool freeze)
+{
+ dprintf ("freeze: %d", freeze);
+
+ if (freeze)
+ {
+ if (frozen == UINT32_MAX)
+ internal_error (_("freeze count overflow"));
+ frozen += 1;
+ }
+
+ /* Nothing to stop if we were frozen already. */
+ if (frozen > 1)
+ return;
+
+ request_interrupt ();
+
+ /* Fetch events until no device has any interrupt pending. */
+ uint64_t ninterrupts;
+ do {
+ ninterrupts = 0;
+ for (ze_device_info *device : devices)
+ {
+ gdb_assert (device != nullptr);
+
+ /* Ignore devices we're not modelling as processes. */
+ if (device->process == nullptr)
+ continue;
+
+ /* Event processing maintains the number of resumed threads. */
+ fetch_events (*device);
+ ninterrupts += device->ninterrupts;
+ }
+ }
+ while (ninterrupts != 0);
+
+ /* We may have accumulated new library events. */
+ ze_notif_libraries ();
+
+ /* Mark threads we interrupted paused so unpause_all can find then. */
+ for_each_thread ([] (thread_info *tp)
+ {
+ /* A thread without waitstatus has already been processed by a
+ previous pause_all or it has reported its event to higher layers
+ via wait.
+
+ Don't mark it paused. It either already is, if it was stopped by
+ a previous pause_all, or higher layers assume it to be stopped so
+ we don't want it so be resumed by unpause_all. */
+ if (!ze_has_waitstatus (tp))
+ return;
+
+ /* Do not mark threads that wait would pick, even if their event was
+ only just fetched. */
+ if (ze_has_priority_waitstatus (tp))
+ return;
+
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ zetp->exec_state = ZE_THREAD_STATE_PAUSED;
+ zetp->waitstatus.set_ignore ();
+ });
+
+ /* We already waited for threads and marked them paused. */
+ interrupted = false;
+}
+
+void
+ze_target::unpause_all (bool unfreeze)
+{
+ dprintf ("freeze: %d", unfreeze);
+
+ if (unfreeze)
+ {
+ if (frozen == 0)
+ internal_error (_("freeze count underflow"));
+ frozen -= 1;
+ }
+
+ /* Nothing to resume if we're still frozen. */
+ if (frozen > 1)
+ return;
+
+ /* Check which devices are safe to be resumed and which need to be
+ checked for individual threads to be resumed.
+
+ In all-stop mode, finding a single thread would already block the
+ unpause. We do not expect this to be performance critical (or used
+ at all), however, so let's unify all-stop and non-stop as much as
+ possible. */
+ std::set<ze_device_info *> devices_to_check;
+ std::set<ze_device_info *> devices_to_resume {devices.begin (),
+ devices.end ()};
+
+ for_each_thread ([&] (thread_info *tp)
+ {
+ ze_thread_exec_state_t state = ze_exec_state (tp);
+ switch (state)
+ {
+ case ZE_THREAD_STATE_PAUSED:
+ return;
+
+ case ZE_THREAD_STATE_STOPPED:
+ {
+ ze_device_info *device = ze_thread_device (tp);
+ if (device == nullptr)
+ return;
+
+ devices_to_check.insert (device);
+ devices_to_resume.erase (device);
+ }
+ return;
+
+ case ZE_THREAD_STATE_RUNNING:
+ warning (_("thread %d.%ld running in unpause"), tp->id.pid (),
+ tp->id.lwp ());
+ return;
+
+ case ZE_THREAD_STATE_UNKNOWN:
+ warning (_("thread %d.%ld has unknown execution "
+ "state"), tp->id.pid (), tp->id.lwp ());
+ return;
+ }
+
+ internal_error (_("bad execution state: %d."), state);
+ });
+
+ /* In all-stop mode, any device that cannot be resumed aborts unpause. */
+ if (!non_stop && !devices_to_check.empty ())
+ return;
+
+ /* Resume individual threads.
+
+ In all-stop mode, this will be empty. */
+ for (ze_device_info *device : devices_to_check)
+ {
+ gdb_assert (device != nullptr);
+
+ device->process->for_each_thread ([this] (thread_info *tp)
+ {
+ ze_thread_info *zetp = ze_thread (tp);
+ gdb_assert (zetp != nullptr);
+
+ /* We already diagnosed unexpected states above. */
+ ze_thread_exec_state_t state = zetp->exec_state;
+ if (state == ZE_THREAD_STATE_PAUSED)
+ resume_single_thread (tp);
+ });
+ }
+
+ /* Resume entire devices at once. */
+ for (ze_device_info *device : devices_to_resume)
+ {
+ gdb_assert (device != nullptr);
+
+ /* Skip devices we're not modeling as processes. */
+ if (device->process == nullptr)
+ continue;
+
+ resume (*device);
+ }
+}
+
+void
+ze_target::ack_library (process_info *process, const char *name)
+{
+ /* All libraries are in-memory. */
+ warning (_("unexpected acknowledgement requested for library %s."), name);
+}
+
+void
+ze_target::ack_in_memory_library (process_info *process,
+ CORE_ADDR begin, CORE_ADDR end)
+{
+ gdb_assert (process != nullptr);
+
+ process_info_private *zeproc = process->priv;
+ gdb_assert (zeproc != nullptr);
+
+ /* The only reason why we would not have a device is if we got detached.
+
+ There is nothing to acknowledge in that case. */
+ ze_device_info *device = zeproc->device;
+ if (device == nullptr)
+ {
+ dprintf ("[%s;%s) device not found.", core_addr_to_string_nz (begin),
+ core_addr_to_string_nz (end));
+ return;
+ }
+
+ events_t &events = device->ack_pending;
+ events_t::iterator it
+ = std::find_if (events.begin (), events.end (),
+ [begin, end] (const zet_debug_event_t &ev)
+ {
+ return ((ev.type == ZET_DEBUG_EVENT_TYPE_MODULE_LOAD)
+ && (ev.info.module.moduleBegin == begin)
+ && (ev.info.module.moduleEnd == end));
+ });
+
+ if (it == events.end ())
+ {
+ dprintf ("[%s;%s) not found.", core_addr_to_string_nz (begin),
+ core_addr_to_string_nz (end));
+ return;
+ }
+
+ ze_ack_event (*device, *it);
+ events.erase (it);
+
+ dprintf ("[%s;%s) acknowledged.", core_addr_to_string_nz (begin),
+ core_addr_to_string_nz (end));
+}
diff --git a/gdbserver/ze-low.h b/gdbserver/ze-low.h
new file mode 100644
index 00000000000..9b1ce3c1a69
--- /dev/null
+++ b/gdbserver/ze-low.h
@@ -0,0 +1,465 @@
+/* Target interface for Level-Zero based targets for gdbserver.
+ See https://github.com/oneapi-src/level-zero.git.
+
+ Copyright (C) 2020-2026 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDBSERVER_ZE_LOW_H
+#define GDBSERVER_ZE_LOW_H
+
+#include "target.h"
+#include "tdesc.h"
+
+#include <level_zero/zet_api.h>
+#include <string>
+#include <vector>
+#include <list>
+
+
+/* Information about register sets reported in target descriptions.
+
+ The main use of this is to find the information relevant for fetching
+ and storing registers via Level-Zero based on register numbers. */
+struct ze_regset_info
+{
+ /* The device-specific Level-Zero register set type. */
+ uint32_t type;
+
+ /* The register size in bytes for reading/writing. */
+ uint32_t size;
+
+ /* The begin (inclusive) and end (exclusive) register numbers for this
+ regset.
+
+ This is used to map register numbers to regset types. */
+ int begin, end;
+
+ /* Whether the regset is writable. We assume all are readable. */
+ bool is_writeable;
+};
+
+/* A vector of regset infos. */
+typedef std::vector<ze_regset_info> ze_regset_info_t;
+
+/* A vector of expedite register names.
+
+ The names are expected to be string literals. The vector must be
+ terminated with a single nullptr entry. */
+typedef std::vector<const char *> expedite_t;
+
+/* A list of debug events. */
+
+typedef std::list<zet_debug_event_t> events_t;
+
+/* Information about devices we're attached to.
+
+ This is pretty similar to process_info. The difference is that we only
+ want to tell GDB about devices that the host application actually uses.
+ To know that, however, we need to attach to all available devices. */
+
+struct ze_device_info
+{
+ /* The debug session configuration. */
+ zet_debug_config_t config = {};
+
+ /* The device handle. This must not be nullptr. */
+ ze_device_handle_t handle = nullptr;
+
+ /* The device's properties. */
+ ze_device_properties_t properties = {};
+
+ /* The debug session handle.
+
+ This is nullptr if we are not currently attached. */
+ zet_debug_session_handle_t session = nullptr;
+
+ /* The state for debug attach attempt.
+
+ This is complementary information for debug session handle. The
+ debug session handle is null, when debug attach attempt fails.
+ In this case, debug attach state contains more information on
+ the last error. */
+ ze_result_t debug_attach_state;
+
+ /* The target description for this device. */
+ target_desc_up tdesc;
+
+ /* The register sets reported in the device's target description. */
+ ze_regset_info_t regsets;
+
+ /* The expedite registers used for this device's target description. */
+ expedite_t expedite;
+
+ /* The device enumeration ordinal number. */
+ unsigned long ordinal = 0;
+
+ /* The process for this device.
+
+ We model devices we're attached to as inferior process. In GDB, we
+ hide inferiors representing devices that are not currently used and
+ only show inferiors for devices that are in use.
+
+ If we are not attached to this device, PROCESS will be nullptr. */
+ process_info *process = nullptr;
+
+ /* A list of to-be-acknowledged events. */
+ events_t ack_pending;
+
+ /* Total number of threads on this device. */
+ unsigned long nthreads = 0;
+
+ /* Number of interrupts sent to this target. */
+ unsigned long ninterrupts = 0;
+};
+
+/* A thread's resume state.
+
+ This is very similar to enum resume_kind except that we need an
+ additional none case to model the thread not being mentioned in any
+ resume request. */
+
+enum ze_thread_resume_state_t
+{
+ /* Gdbserver did not ask anything of this thread. */
+ ZE_THREAD_RESUME_NONE,
+
+ /* The thread shall stop. */
+ ZE_THREAD_RESUME_STOP,
+
+ /* The thread shall run. */
+ ZE_THREAD_RESUME_RUN,
+
+ /* The thread shall step. */
+ ZE_THREAD_RESUME_STEP
+};
+
+/* A thread's execution state. */
+
+enum ze_thread_exec_state_t
+{
+ /* We do not know the thread state. This is likely an error condition. */
+ ZE_THREAD_STATE_UNKNOWN,
+
+ /* The thread is stopped and is expected to remain stopped until we
+ resume it. */
+ ZE_THREAD_STATE_STOPPED,
+
+ /* The thread is stopped by pause_all (). In unpause_all (), we need to
+ resume just the paused threads.
+
+ In particular, we need to distinguish threads that reported their
+ event to higher layers in gdbserver and hence have their waitstatus
+ clear (set to ignore) from threads that were paused and had their
+ waitstatus cleared by pause_all (). */
+ ZE_THREAD_STATE_PAUSED,
+
+ /* The thread is running.
+
+ We do not get exit events, so we will only learn about the thread
+ becoming idle/unavailable if we try to stop it. */
+ ZE_THREAD_STATE_RUNNING,
+
+ /* The thread exited. */
+ ZE_THREAD_STATE_EXITED,
+};
+
+/* Thread private data for Level-Zero targets. */
+
+struct ze_thread_info
+{
+ /* The thread identifier. */
+ ze_device_thread_t id;
+
+ /* The thread's resume state.
+
+ What does gdbserver want this thread to do. */
+ enum ze_thread_resume_state_t resume_state = ZE_THREAD_RESUME_NONE;
+
+ /* The start/end addresses for range-stepping. */
+ CORE_ADDR step_range_start = 0;
+ CORE_ADDR step_range_end = 0;
+
+ /* The thread's execution state.
+
+ What is this thread actually doing. */
+ enum ze_thread_exec_state_t exec_state = ZE_THREAD_STATE_UNKNOWN;
+
+ /* The thread's stop reason.
+
+ This is only valid if EXEC_STATE == ZE_THREAD_STATE_STOPPED. */
+ target_stop_reason stop_reason = TARGET_STOPPED_BY_NO_REASON;
+
+ /* The waitstatus for this thread's last event.
+
+ TARGET_WAITKIND_IGNORE means that there is no last event. */
+ target_waitstatus waitstatus {};
+};
+
+/* Return the ZE thread info for TP. */
+
+static inline ze_thread_info *
+ze_thread (thread_info *tp)
+{
+ if (tp == nullptr)
+ return nullptr;
+
+ return (ze_thread_info *) tp->target_data ();
+}
+
+/* Return the ZE thread info for const TP. */
+
+static inline const ze_thread_info *
+ze_thread (const thread_info *tp)
+{
+ if (tp == nullptr)
+ return nullptr;
+
+ return (const ze_thread_info *) tp->target_data ();
+}
+
+/* Return the Level-Zero thread id for all threads. */
+
+static inline ze_device_thread_t
+ze_thread_id_all ()
+{
+ ze_device_thread_t all;
+ all.slice = UINT32_MAX;
+ all.subslice = UINT32_MAX;
+ all.eu = UINT32_MAX;
+ all.thread = UINT32_MAX;
+
+ return all;
+}
+
+/* Return true if TID is the all thread id. */
+
+static inline bool
+ze_is_thread_id_all (ze_device_thread_t tid)
+{
+ return (tid.slice == UINT32_MAX
+ && tid.subslice == UINT32_MAX
+ && tid.eu == UINT32_MAX
+ && tid.thread == UINT32_MAX);
+}
+
+/* Return the Level-Zero thread id for THREAD. */
+
+static inline ze_device_thread_t
+ze_thread_id (const thread_info *thread)
+{
+ const ze_thread_info *zetp = ze_thread (thread);
+ if (zetp == nullptr)
+ error (_("No thread."));
+
+ return zetp->id;
+}
+
+/* Return a human-readable device thread id string. */
+
+std::string ze_thread_id_str (const ze_device_thread_t &thread);
+
+/* Return the device for THREAD. */
+
+ze_device_info *ze_thread_device (const thread_info *thread);
+
+/* Return the thread for TID on DEVICE if it exists; nullptr otherwise. */
+
+thread_info *ze_find_thread (const ze_device_info &device,
+ const ze_device_thread_t &tid);
+
+/* Return the PTID for TID on DEVICE. */
+
+ptid_t ze_thread_ptid (const ze_device_info &device,
+ const ze_device_thread_t &tid);
+
+/* Process info private data for Level-Zero targets. */
+
+struct process_info_private
+{
+ /* The device we're modelling as process.
+
+ In case we get forcefully detached from the device this process
+ represents, DEVICE will be nullptr. The process will remain until
+ the detach event can be reported to GDB. */
+ ze_device_info *device;
+
+ process_info_private (ze_device_info *dev)
+ : device (dev)
+ {}
+};
+
+/* Target op definitions for Level-Zero based targets. */
+
+class ze_target : public process_stratum_target
+{
+public:
+ /* Initialize the Level-Zero target.
+
+ We cannot do this inside the ctor since zeInit() would generate a
+ worker thread that would inherit the uninitialized async I/O
+ state.
+
+ Postpone initialization until after async I/O has been
+ initialized. */
+ void init ();
+
+ bool supports_hardware_single_step () override { return true; }
+ bool supports_range_stepping () override { return true; }
+ bool supports_multi_process () override { return true; }
+ bool supports_non_stop () override { return true; }
+ int start_non_stop (bool enable) override { async (enable); return 0; }
+
+ bool async (bool enable) override;
+
+ int create_inferior (const char *program,
+ const std::string &args) override;
+
+ int attach (int pid) override;
+ int detach (process_info *proc) override;
+
+ int kill (process_info *proc) override;
+ void mourn (process_info *proc) override;
+ void join (int pid) override;
+
+ void resume (thread_resume *resume_info, size_t n) override;
+ ptid_t wait (ptid_t ptid, target_waitstatus *status,
+ target_wait_flags options) override;
+
+ void fetch_registers (regcache *regcache, int regno) override;
+ void store_registers (regcache *regcache, int regno) override;
+
+ int read_memory (CORE_ADDR memaddr, unsigned char *myaddr,
+ int len) override;
+
+ int write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+ int len) override;
+
+ bool thread_alive (ptid_t ptid) override;
+ bool supports_thread_stopped () override { return true; }
+ bool thread_stopped (struct thread_info *tp) override;
+
+ void request_interrupt () override;
+
+ void pause_all (bool freeze) override;
+ void unpause_all (bool unfreeze) override;
+
+ bool supports_pid_to_exec_file () override { return true; }
+ const char *pid_to_exec_file (int pid) override { return ""; }
+
+ bool uses_library_notifications () override { return true; }
+ void ack_library (process_info *process, const char *name) override;
+ void ack_in_memory_library (process_info *process, CORE_ADDR begin,
+ CORE_ADDR end) override;
+
+private:
+ typedef std::list<ze_device_info *> devices_t;
+
+ /* The devices we care about. */
+ devices_t devices;
+
+ /* The current device ordinal number used for enumerating devices. */
+ unsigned long ordinal = 0;
+
+ /* The freeze count for pause_all (). */
+ uint32_t frozen = 0;
+
+ /* Whether a global interrupt was requested.
+
+ This is used to reply NO_RESUMED if we requested an interrupt and no
+ thread responded to break a hang if the last dispatch exited. */
+ bool interrupted = false;
+
+ /* Attach to PID on devices in the device tree rooted at DEVICE.
+ Returns the number of devices we attached to. */
+ int attach_to_device (uint32_t pid, ze_device_handle_t device);
+
+ /* Attach to all available devices for process PID and store them in
+ this object. Returns the number of devices we attached to. */
+ int attach_to_devices (uint32_t pid);
+
+ /* Fetch and process events from DEVICE. Return number of events. */
+ uint64_t fetch_events (ze_device_info &device);
+
+ /* Push library notifications.
+
+ Call this every time events have been fetched. */
+ void ze_notif_libraries ();
+
+ /* Return the number of threads that match the RESUME_PTID and have
+ new events to report. Also recover these threads' resume state
+ to RKIND. */
+ size_t mark_eventing_threads (ptid_t resume_ptid, enum resume_kind rkind);
+
+ /* Resume all threads on DEVICE. */
+ void resume (ze_device_info &device);
+
+ /* Resume a single thread. This is a helper method that prepares
+ the thread for resuming, invalidates its regcache, and then
+ resumes. The method should be called only when we are sure the
+ thread should be resumed. */
+ void resume_single_thread (thread_info *thread);
+
+ /* Return true if TP has single-stepped within its stepping range. */
+ bool is_range_stepping (thread_info *tp);
+
+protected:
+ /* Check whether a device is supported by this target. */
+ virtual bool is_device_supported
+ (const ze_device_properties_t &,
+ const std::vector<zet_debug_regset_properties_t> &) = 0;
+
+ /* Create a target description for a device and populate the
+ corresponding regset information. */
+ virtual target_desc *create_tdesc
+ (ze_device_info *dinfo,
+ const std::vector<zet_debug_regset_properties_t> &,
+ const ze_pci_ext_properties_t &) = 0;
+
+ /* Return whether TP is at a breakpoint. */
+ virtual bool is_at_breakpoint (thread_info *tp) = 0;
+
+ /* TID stopped on DEVICE. Find out why and return the stop reason.
+ Optionally fill in SIGNAL. */
+ virtual target_stop_reason get_stop_reason (const ze_device_info &device,
+ const ze_device_thread_t &tid,
+ gdb_signal &signal) = 0;
+
+ /* Prepare TP for resuming using TP's RESUME_STATE.
+
+ This sets the ze execution state, typically to running. */
+ virtual void prepare_thread_resume (thread_info *tp) = 0;
+
+ /* Read the memory in the context of thread TP. */
+ int read_memory (thread_info *tp, CORE_ADDR memaddr,
+ unsigned char *myaddr, int len);
+
+ /* Read the memory in the context of TID on DEVICE. */
+ int read_memory (const ze_device_info &device,
+ const ze_device_thread_t &tid, CORE_ADDR memaddr,
+ unsigned char *myaddr, int len);
+
+ /* Write the memory in the context of thread TP. */
+ int write_memory (thread_info *tp, CORE_ADDR memaddr,
+ const unsigned char *myaddr, int len);
+
+ /* Write the memory in the context of TID on DEVICE. */
+ int write_memory (const ze_device_info &device,
+ const ze_device_thread_t &tid, CORE_ADDR memaddr,
+ const unsigned char *myaddr, int len);
+};
+
+#endif /* GDBSERVER_ZE_LOW_H */
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 36/44] testsuite, sycl: add SYCL support
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (34 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 35/44] gdbserver, ze, intelgt: introduce ze-low and intelgt-ze-low targets Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 37/44] testsuite, sycl: add test for backtracing inside a kernel Markus Metzger
` (7 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur, Abdul Basit Ijaz, Stephan Rohr
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Add lib files, board file, and basic test scenarios for SYCL. The
scenarios test the ability to define and hit breakpoints inside and
outside a SYCL kernel. The test cases are skipped if it is found out
that the compiler does not support SYCL.
Here is a sample command to run all SYCL tests on the CPU target
device using the Intel® oneAPI DPC++/C++ Compiler:
$ make check TESTS="gdb.sycl/*.exp" \
RUNTESTFLAGS="OFFLOAD_DEVICE_GROUP=cpu CXX_FOR_TARGET=icpx"
Alternatively, the intel-offload board file can be used. This can be
more practical to pick the right compilers.
$ make check TESTS="gdb.sycl/*.exp" \
RUNTESTFLAGS="OFFLOAD_DEVICE_GROUP=cpu --target_board='intel-offload'"
Running the GPU tests requires that a gdbserver executable built for
the intelgt target exists in the path and its name is
'gdbserver-intelgt'.
Co-authored-by: Abdul Basit Ijaz <abdul.b.ijaz@intel.com>
Co-authored-by: Stephan Rohr <stephan.rohr@intel.com>
Co-authored-by: Markus Metzger <markus.t.metzger@intel.com>
---
gdb/testsuite/README | 9 +
gdb/testsuite/boards/intel-offload.exp | 36 +++
gdb/testsuite/gdb.sycl/break.cpp | 127 ++++++++
gdb/testsuite/gdb.sycl/break.exp | 246 +++++++++++++++
gdb/testsuite/lib/gdb.exp | 17 +-
gdb/testsuite/lib/intelgt-utils.exp | 43 +++
gdb/testsuite/lib/sycl-devices.cpp | 106 +++++++
gdb/testsuite/lib/sycl-hello.cpp | 33 ++
gdb/testsuite/lib/sycl-util.cpp | 135 +++++++++
gdb/testsuite/lib/sycl.exp | 399 +++++++++++++++++++++++++
10 files changed, 1150 insertions(+), 1 deletion(-)
create mode 100644 gdb/testsuite/boards/intel-offload.exp
create mode 100644 gdb/testsuite/gdb.sycl/break.cpp
create mode 100644 gdb/testsuite/gdb.sycl/break.exp
create mode 100644 gdb/testsuite/lib/intelgt-utils.exp
create mode 100644 gdb/testsuite/lib/sycl-devices.cpp
create mode 100644 gdb/testsuite/lib/sycl-hello.cpp
create mode 100644 gdb/testsuite/lib/sycl-util.cpp
create mode 100644 gdb/testsuite/lib/sycl.exp
diff --git a/gdb/testsuite/README b/gdb/testsuite/README
index f354144b872..9a962db2835 100644
--- a/gdb/testsuite/README
+++ b/gdb/testsuite/README
@@ -354,6 +354,15 @@ by switching to a different user on the same machine. These users
will have random files copied into their $HOME directories, so it is a
good idea to setup new users just for this purpose.
+OFFLOAD_DEVICE_GROUP
+
+This option can be used to restrict the offloading tests to run only
+on a specific group/family of devices. Multiple target device groups
+can be selected by passing a comma separated list. By default, it is
+set to "cpu,gpu,accelerator". Example use:
+
+ make check RUNTESTFLAGS='OFFLOAD_DEVICE_GROUP="gpu,cpu"'
+
Testing All Simple Boards
*************************
diff --git a/gdb/testsuite/boards/intel-offload.exp b/gdb/testsuite/boards/intel-offload.exp
new file mode 100644
index 00000000000..b5d92ce2f31
--- /dev/null
+++ b/gdb/testsuite/boards/intel-offload.exp
@@ -0,0 +1,36 @@
+# Copyright 2022-2026 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# This file is a dejagnu "board file" and is used for running the
+# SYCL testsuite.
+#
+# Example usage:
+# bash$ make check TESTS="gdb.sycl/*.exp" RUNTESTFLAGS='--target_board=intel-offload'
+
+load_generic_config "unix"
+process_multilib_options ""
+load_board_description "local-board"
+
+set gdb_test_timeout 100
+
+unset_board_info isremote
+set_board_info isremote 0
+
+set_board_info compiler "icx"
+set_board_info c++compiler "icpx"
+set_board_info f90compiler "ifx"
+
+puts "Info: Using timeout value $gdb_test_timeout"
+puts "Info: Using C++ compiler icpx and Fortran compiler ifx"
diff --git a/gdb/testsuite/gdb.sycl/break.cpp b/gdb/testsuite/gdb.sycl/break.cpp
new file mode 100644
index 00000000000..e7be926180d
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break.cpp
@@ -0,0 +1,127 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2026 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include "sycl-util.cpp"
+
+static int
+foo ()
+{ /* foo.entry */
+ int a = 0; /* foo.entry */
+ int b = 1; /* foo.1 */
+ return a + b;
+}
+
+static int
+bar ()
+{ /* bar.entry */
+ int a = 0; /* bar.entry */
+ int b = 1; /* bar.1 */
+ return a + b;
+}
+
+struct SingleTask
+{
+ /* Test that we're not leaking host breakpoints into the kernel. */
+ SingleTask ()
+ {
+ int a = 0; /* host.1 */
+ }
+
+ void operator () () const /* kernel.single_task */
+ { /* kernel.single_task */
+ int a = 0; /* kernel.single_task */
+ int b = 0; /* kernel.1 */
+ int c = foo ();
+ int d = 0; /* kernel.2 */
+ int e = bar ();
+ }
+
+ /* Test that we're not leaking kernel breakpoints into the host. */
+ ~SingleTask ()
+ {
+ }
+};
+
+struct ParallelFor
+{
+ /* Test that we're not leaking host breakpoints into the kernel. */
+ ParallelFor ()
+ {
+ int a = 0; /* host.2 */
+ }
+
+ void operator () (sycl::nd_item<1> item) const /* kernel.parallel_for */
+ { /* kernel.parallel_for */
+ sycl::id<1> id = item.get_global_id (); /* kernel.parallel_for */
+ int gid = id.get (0);
+
+ int a = 0; /* kernel.3 */
+ int b = 0; /* kernel.4 */
+ int c = 0; /* kernel.5 */
+ int d = 0; /* kernel.6 */
+ }
+
+ /* Test that we're not leaking kernel breakpoints into the host. */
+ ~ParallelFor ()
+ {
+ }
+};
+
+int
+main (int argc, char *argv[])
+{
+ sycl::queue queue {get_sycl_queue (argc, argv)};
+
+ /* Submit a dummy kernel to trigger attaching.
+
+ This is a bit awkward. To start a test, sycl_start continues to to
+ zeModuleCreate inside the user-mode driver to attach to the graphics
+ device. To cover leaking breakpoints into and out of kernels, we
+ need to be attached before we hit the breakpoint at the SingleTask
+ ctor.
+
+ This will go away with support for automatically attaching. */
+ queue.single_task ([] () {});
+
+ SingleTask single_task;
+ for (int i = 0; i < 2; i++)
+ {
+ queue.single_task (single_task);
+ queue.wait ();
+ }
+
+ /* Test that breakpoints do not leak into or out of lambda kernels. */
+ int a = 0; /* host.3 */
+ queue.single_task ([] ()
+ {
+ int a = 0;
+ int b = 0; /* lambda.1 */
+ });
+ queue.wait ();
+
+ ParallelFor parallel_for;
+ sycl::nd_range<1> range {2, 1};
+ for (int i = 0; i < 2; i++)
+ {
+ queue.parallel_for (range, parallel_for);
+ queue.wait ();
+ int a = 0; /* host.4 */
+ }
+
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.sycl/break.exp b/gdb/testsuite/gdb.sycl/break.exp
new file mode 100644
index 00000000000..f471fa62642
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/break.exp
@@ -0,0 +1,246 @@
+# Copyright 2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test defining and hitting breakpoints.
+
+load_lib sycl.exp
+
+standard_testfile .cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+# Returns the symbol of the current kernel or "".
+proc find_kernel_symbol_gpu {} {
+ set symbol ""
+
+ # The kernel is at the top of the stack - at least on GPUs.
+ gdb_test_multiple "up" "find top frame" {
+ -re -wrap "Initial frame selected; you cannot go up." {
+ gdb_test_multiple "with print demangle off -- info symbol \$pc" \
+ "find kernel symbol" {
+ -re -wrap "(\[A-Za-z0-9_\]+) \[^\r\n\]*in section .*" {
+ set symbol $expect_out(1,string)
+ pass $gdb_test_name
+ }
+ -re -wrap "" {
+ fail $gdb_test_name
+ }
+ }
+ }
+ -re -wrap "#\[0-9\]+ .*" {
+ send_gdb "up\n"
+ exp_continue
+ }
+ }
+
+ return $symbol
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set foo_1 [gdb_get_line_number "foo.1"]
+ set bar_1 [gdb_get_line_number "bar.1"]
+ set host_1 [gdb_get_line_number "host.1"]
+ set host_2 [gdb_get_line_number "host.2"]
+ set host_3 [gdb_get_line_number "host.3"]
+ set host_4 [gdb_get_line_number "host.4"]
+ set lambda_1 [gdb_get_line_number "lambda.1"]
+ set kernel_1 [gdb_get_line_number "kernel.1"]
+ set kernel_2 [gdb_get_line_number "kernel.2"]
+ set kernel_3 [gdb_get_line_number "kernel.3"]
+ set kernel_4 [gdb_get_line_number "kernel.4"]
+ set kernel_5 [gdb_get_line_number "kernel.5"]
+ set kernel_6 [gdb_get_line_number "kernel.6"]
+
+ # The user-defined kernel function is wrapped by an actual kernel
+ # provided by the compiler when offloading to a device, so the
+ # user-defined kernel is actually a device function that is called
+ # from or inlined into the actual kernel.
+ #
+ # To test breaking on the kernel and sliding the breakpoint across
+ # the kernel prologue with its potentially multiple entries, we
+ # run each kernel twice.
+ #
+ # - in the first run, we break at the user-defined kernel
+ # function symbol and determine the actual kernel symbol.
+ #
+ # - in the second run, we break at the actual kernel symbol and
+ # run the rest of the breakpoint tests for this kernel.
+ #
+ # When running on CPU, the kernel function is not actually
+ # offloaded but called by the runtime. We simply use the same
+ # user kernel function symbol again in this case.
+ #
+ # We're going to set breakpoints
+ #
+ # - from the host process into kernels
+ # - from kernels into the host process
+ # - from kernels into other kernels
+ gdb_breakpoint "SingleTask::operator ()" -message -temporary \
+ -allow-pending
+ gdb_breakpoint "ParallelFor::operator ()" -message -temporary \
+ -allow-pending
+
+ with_test_prefix "single_task" {
+ # We also cover breakpoints leaking into and out of kernels,
+ # so we start with a host breakpoint located above the kernel.
+ gdb_breakpoint $host_1 -message
+
+ gdb_continue_to_breakpoint "host.1" ".*host.1.*"
+ gdb_continue_to_breakpoint "function.single_task" \
+ ".*kernel.single_task.*"
+
+ # This part only works for kernels offloaded to a device.
+ set symbol ""
+ if {[require_sycl_device "$device" "gpu" "*"]} {
+ set symbol [find_kernel_symbol_gpu]
+ }
+ # Fall back to the original user kernel function symbol.
+ if {$symbol eq ""} {
+ set symbol "SingleTask::operator ()"
+ }
+
+ # We do not know where the breakpoint ends up.
+ gdb_breakpoint $symbol -message -allow-pending
+ gdb_continue_to_breakpoint "kernel.single_task"
+
+ # We're at the start of the second run of the first kernel.
+ #
+ # Set breakpoints for the rest of the tests for this kernel.
+ # We stopped in the actual kernel, which may be located in a
+ # different source file.
+ gdb_breakpoint $srcfile:$kernel_1 -message
+ gdb_breakpoint $srcfile:$kernel_2 -message
+ gdb_breakpoint $srcfile:foo -message
+ gdb_breakpoint $srcfile:bar -message
+ gdb_breakpoint $srcfile:$foo_1 -message
+ gdb_breakpoint $srcfile:$bar_1 -message
+
+ gdb_continue_to_breakpoint "kernel.1" ".*kernel.1.*"
+ gdb_continue_to_breakpoint "foo.entry" ".*foo.entry.*"
+ gdb_continue_to_breakpoint "foo.1" ".*foo.1.*"
+ gdb_continue_to_breakpoint "kernel.2" ".*kernel.2.*"
+ gdb_continue_to_breakpoint "bar.entry" ".*bar.entry.*"
+ gdb_continue_to_breakpoint "bar.1" ".*bar.1.*"
+ }
+
+ with_test_prefix "lambda" {
+ # Test that we are not leaking breakpoints into or out of
+ # kernels defined as lambdas.
+ #
+ # We need a breakpoint before
+ #
+ # queue.single_task ([] ()
+ #
+ # because that line also contains the lambda definition.
+ gdb_breakpoint $host_3 -message
+ gdb_breakpoint $lambda_1 -message
+
+ gdb_continue_to_breakpoint "host.3" ".*host.3.*"
+ gdb_continue_to_breakpoint "lambda.1" ".*lambda.1.*"
+ }
+
+ with_test_prefix "parallel_for" {
+ # Now move on to the next kernel, again with an intermediate
+ # stop in the host process.
+ gdb_breakpoint $srcfile:$host_2 -message
+
+ gdb_continue_to_breakpoint "host.2" ".*host.2.*"
+ gdb_continue_to_breakpoint "function.parallel_for" \
+ ".*kernel.parallel_for.*"
+
+ # This part only works for kernels offloaded to a device.
+ set symbol ""
+ if {[require_sycl_device "$device" "gpu" "*"]} {
+ set symbol [find_kernel_symbol_gpu]
+ }
+ # Fall back to the original user kernel function symbol.
+ if {$symbol eq ""} {
+ set symbol "ParallelFor::operator ()"
+ }
+
+ # We only want to hit this once to not complicate the test.
+ #
+ # Until kernel.6, threads race to the next breakpoint, but we
+ # only get one stop from the winner; the loser will silently
+ # skip this breakpoint.
+ #
+ # This allows the test to be serial even though we're testing
+ # a parallel_for kernel. We may switch threads at every stop,
+ # but we don't really care which thread stops.
+ #
+ # But first, we need to get back to the host process so the
+ # other thread does not stop on this new breakpoint in this
+ # kernel instance.
+ gdb_breakpoint $srcfile:$host_4 -message -temporary
+ gdb_continue_to_breakpoint "host.4" ".*host.4.*"
+ #
+ # Now we can add the kernel breakpoint for the second run.
+ #
+ # We do not know where the breakpoint ends up.
+ gdb_breakpoint $symbol -message -temporary -allow-pending
+ gdb_continue_to_breakpoint "kernel.parallel_for"
+
+ # We're at the start of the second run.
+ #
+ # We're now running a kernel with two workgroups and hence two
+ # threads and we're going to cover
+ #
+ # - temporary breakpoints
+ # - disable breakpoints
+ # - delete breakpoints
+ #
+ # and we're making sure that only one thread stops.
+ #
+ # Set breakpoints for the rest of the tests. We stopped in
+ # the actual kernel, which may be located in a different
+ # source file.
+ gdb_breakpoint $srcfile:$kernel_3 -message -temporary
+ gdb_breakpoint $srcfile:$kernel_4 -message
+ set bp_dis [get_integer_valueof "\$bpnum" invalid "disable bp"]
+ gdb_breakpoint $srcfile:$kernel_5 -message
+ set bp_del [get_integer_valueof "\$bpnum" invalid "delete bp"]
+ gdb_breakpoint $srcfile:$kernel_6 -message
+
+ gdb_continue_to_breakpoint "kernel.3" ".*kernel.3.*"
+ gdb_continue_to_breakpoint "kernel.4" ".*kernel.4.*"
+ gdb_test_no_output "disable $bp_dis"
+
+ gdb_continue_to_breakpoint "kernel.5" ".*kernel.5.*"
+ gdb_test_no_output "delete $bp_del"
+
+ # We hit kernel.6 once for each workgroup.
+ gdb_continue_to_breakpoint "kernel.6.1" ".*kernel.6.*"
+ gdb_continue_to_breakpoint "kernel.6.2" ".*kernel.6.*"
+
+ gdb_test "continue" "$inferior_exited_re normally.*"
+ }
+ }
+}
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 6fb04869605..41f8c36d36b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -7462,6 +7462,21 @@ proc gdb_compile_win32 {source dest type options} {
}
}
+# Build a SYCL program from SOURCE. See prefatory comment for
+# gdb_compile, above, for discussion of the parameters to this proc.
+
+proc gdb_compile_sycl {source dest type options} {
+ set new_options {additional_flags=-fsycl}
+ lappend new_options {additional_flags=-fsycl-unnamed-lambda}
+ lappend new_options {c++}
+ lappend new_options {optimize=-O0}
+
+ # If the optimize option is given multiple times, only the last use is
+ # significant in DEJAGNU. So, to make optimize option set in the test
+ # significant, input 'options' are appended to the end here.
+ return [gdb_compile $source $dest $type [concat $new_options $options]]
+}
+
# Send a command to GDB.
# For options for TYPE see gdb_stdin_log_write
@@ -9737,7 +9752,7 @@ proc build_executable_from_specs {testname executable options args} {
set binfile [standard_output_file $executable]
set func gdb_compile
- set func_index [lsearch -regexp $options {^(pthreads|shlib|shlib_pthreads|openmp)$}]
+ set func_index [lsearch -regexp $options {^(pthreads|shlib|shlib_pthreads|openmp|sycl)$}]
if {$func_index != -1} {
set func "${func}_[lindex $options $func_index]"
}
diff --git a/gdb/testsuite/lib/intelgt-utils.exp b/gdb/testsuite/lib/intelgt-utils.exp
new file mode 100644
index 00000000000..952a3f55d10
--- /dev/null
+++ b/gdb/testsuite/lib/intelgt-utils.exp
@@ -0,0 +1,43 @@
+# Copyright 2020-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Intelgt utility procedures.
+
+# The lock file used to ensure that only one GDB has access to the GPU
+# at a time.
+
+set intelgt_lock_filename intelgt-parallel.lock
+
+# Return non-zero if the instruction at ADDRESS is compact, 0 otherwise.
+
+proc is_compact_insn {address} {
+ # Check the CmptCtrl flag (bit 29).
+ set test "is compact insn"
+ set is_compact [get_integer_valueof \
+ "((unsigned char *)$address)\[3\] & 0x20" 0 $test]
+ return $is_compact
+}
+
+# Set the breakpoint bit of the instruction at ADDRESS.
+
+proc_with_prefix set_breakpoint_bit {address} {
+ # Set Bit 7 on a compacted instruction, Bit 30 on a full instruction.
+ set test "set bp bit"
+ if {[is_compact_insn $address]} {
+ gdb_test "print/x ((unsigned char *)$address)\[0\] |= 0x80" "" $test
+ } else {
+ gdb_test "print/x ((unsigned char *)$address)\[3\] |= 0x40" "" $test
+ }
+}
diff --git a/gdb/testsuite/lib/sycl-devices.cpp b/gdb/testsuite/lib/sycl-devices.cpp
new file mode 100644
index 00000000000..d6f0784d1a5
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-devices.cpp
@@ -0,0 +1,106 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2022-2025 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Utility file for SYCL test programs to get list of available devices. */
+
+#include <sycl/sycl.hpp>
+#include <map>
+
+static std::string
+get_backend_name (sycl::backend backend_arg)
+{
+ std::string backend_name;
+
+ if (backend_arg == sycl::backend::opencl)
+ backend_name = "opencl";
+ else if (backend_arg == sycl::backend::ext_oneapi_level_zero)
+ backend_name = "ext_oneapi_level_zero";
+ else
+ {
+ std::cout << "SYCL: Unrecognized backend." << std::endl;
+ exit (1);
+ }
+
+ return backend_name;
+}
+
+static std::string
+get_device_type (sycl::info::device_type type)
+{
+ std::string type_name;
+
+ if (type == sycl::info::device_type::cpu)
+ type_name = "cpu";
+ else if (type == sycl::info::device_type::gpu)
+ type_name = "gpu";
+ else if (type == sycl::info::device_type::accelerator)
+ type_name = "accelerator";
+ else
+ {
+ std::cout << "SYCL: Unrecognized device type." << std::endl;
+ exit (1);
+ }
+
+ return type_name;
+}
+
+int
+main ()
+{
+ const std::vector<sycl::device> devices
+ = sycl::device::get_devices (sycl::info::device_type::all);
+
+ if (devices.empty ())
+ {
+ std::cout << "SYCL: Could not find any device" << std::endl;
+ exit (1);
+ }
+
+ std::map<std::string, int> device_types;
+
+ for (const sycl::device &device : devices)
+ {
+ const std::string backend_name
+ = get_backend_name (device.get_backend ());
+ if (backend_name == "")
+ continue;
+
+ const std::string dev_name
+ = device.get_info<sycl::info::device::name> ();
+ const std::string device_version
+ = device.get_info<sycl::info::device::version> ();
+ const std::string type
+ = get_device_type (device.get_info<sycl::info::device::device_type> ());
+
+ std::string dev_key {dev_name + ";" + backend_name + ";" + type + ";"
+ + device_version};
+ device_types[dev_key]++;
+ }
+
+ std::cout << "SYCL: List of Target devices: [";
+ int index = 0;
+ for (const auto& [dev_key, count] : device_types)
+ {
+ index++;
+ std::cout << dev_key << ";" << count;
+ if (index < device_types.size ())
+ std::cout << ",";
+ }
+ std::cout << "]" << std::endl;
+
+ return 0;
+}
diff --git a/gdb/testsuite/lib/sycl-hello.cpp b/gdb/testsuite/lib/sycl-hello.cpp
new file mode 100644
index 00000000000..3d2b8de407d
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-hello.cpp
@@ -0,0 +1,33 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2026 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include "sycl-util.cpp"
+
+int
+main (int argc, char *argv[])
+{
+ sycl::queue queue {get_sycl_queue (argc, argv)};
+ queue.single_task ([] ()
+ {
+ int a = 0; /* inside-kernel */
+ });
+
+ queue.wait ();
+
+ return 0;
+}
diff --git a/gdb/testsuite/lib/sycl-util.cpp b/gdb/testsuite/lib/sycl-util.cpp
new file mode 100644
index 00000000000..97c85c85c18
--- /dev/null
+++ b/gdb/testsuite/lib/sycl-util.cpp
@@ -0,0 +1,135 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2026 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Utility file for SYCL test programs to enable explicit selection of
+ a SYCL device. Include this file in each SYCL test program. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include <vector>
+
+static sycl::info::device_type
+get_device_type (const std::string &type_arg)
+{
+ sycl::info::device_type type;
+
+ if (type_arg.compare ("cpu") == 0)
+ type = sycl::info::device_type::cpu;
+ else if (type_arg.compare ("gpu") == 0)
+ type = sycl::info::device_type::gpu;
+ else if (type_arg.compare ("accelerator") == 0)
+ type = sycl::info::device_type::accelerator;
+ else
+ {
+ std::cout << "SYCL: Unrecognized device type '"
+ << type_arg << "'" << std::endl;
+ exit (1);
+ }
+
+ return type;
+}
+
+static sycl::backend
+get_backend_type (const std::string &backend_arg)
+{
+ sycl::backend backend;
+
+ if (backend_arg.compare ("opencl") == 0)
+ backend = sycl::backend::opencl;
+ else if (backend_arg.compare ("ext_oneapi_level_zero") == 0
+ || backend_arg.compare ("level_zero") == 0)
+ backend = sycl::backend::ext_oneapi_level_zero;
+ else
+ {
+ std::cout << "SYCL: Unrecognized backend '"
+ << backend_arg << "'" << std::endl;
+ exit (1);
+ }
+
+ return backend;
+}
+
+static std::vector<sycl::device>
+get_sycl_devices (int argc, char *argv[])
+{
+ if (argc <= 3)
+ {
+ std::cout << "Usage: " << argv[0]
+ << " <cpu|gpu|accelerator>"
+ << " <device name substring>"
+ << " <backend name opencl|level_zero>" << std::endl;
+ exit (1);
+ }
+
+ std::string type_arg {argv[1]};
+ std::string name_arg {argv[2]};
+ std::string backend_arg {argv[3]};
+
+ sycl::info::device_type type = get_device_type (type_arg);
+ sycl::backend backend_type = get_backend_type (backend_arg);
+
+ std::vector<sycl::device> devices = sycl::device::get_devices (type);
+
+ std::vector<sycl::device> filtered_devices;
+ for (const sycl::device &device : devices)
+ {
+ std::string dev_name = device.get_info<sycl::info::device::name> ();
+ std::string platform_name
+ = device.get_platform ().get_info<sycl::info::platform::name> ();
+ std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+ sycl::backend backend = device.get_backend ();
+
+ if (dev_name.find (name_arg) != std::string::npos
+ && backend == backend_type)
+ filtered_devices.push_back (device);
+ }
+
+ if (filtered_devices.empty ())
+ {
+ std::cout << "SYCL: Could not select a device" << std::endl;
+ exit (1);
+ }
+
+ return filtered_devices;
+}
+
+static void
+print_device (const sycl::device &device)
+{
+ std::string dev_name
+ = device.get_info<sycl::info::device::name> ();
+ std::string platform_name
+ = device.get_platform ().get_info<sycl::info::platform::name> ();
+ std::string version
+ = device.get_info<sycl::info::device::driver_version> ();
+
+ std::cout << "[" << dev_name << "]"
+ << " from [" << platform_name << "]"
+ << " version [" << version << "]";
+}
+
+static sycl::queue
+get_sycl_queue (int argc, char *argv[])
+{
+ sycl::device device = get_sycl_devices (argc, argv)[0];
+ std::cout << "SYCL: Using device: ";
+ print_device (device);
+ std::cout << std::endl;
+
+ return sycl::queue {device}; /* return-sycl-queue */
+}
diff --git a/gdb/testsuite/lib/sycl.exp b/gdb/testsuite/lib/sycl.exp
new file mode 100644
index 00000000000..3f1878b54c0
--- /dev/null
+++ b/gdb/testsuite/lib/sycl.exp
@@ -0,0 +1,399 @@
+# Copyright 2019-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Support library for testing SYCL GDB features
+#
+# A particular SYCL device can be selected by passing the SYCL program
+# three command-line arguments:
+# 1. the device type, whose value is in {cpu, gpu, accelerator}.
+# 2. a substring of the device name.
+# 3. backend name.
+#
+# To set these arguments properly, use a SYCL board file, and
+# make your test program select a queue via the get_sycl_queue
+# function in gdb.sycl/sycl-util.cpp. See gdb.sycl/sycl-hello.cpp
+# for a sample SYCL program setup.
+
+load_lib intelgt-utils.exp
+
+if {![info exists OFFLOAD_DEVICE_GROUP]} {
+ set OFFLOAD_DEVICE_GROUP "cpu,gpu,accelerator"
+}
+
+verbose -log "OFFLOAD_DEVICE_GROUP is '$OFFLOAD_DEVICE_GROUP'"
+
+# Return true if the SYCL device selected via the board file
+# matches the arguments. Otherwise return false.
+
+proc require_sycl_device {device type name} {
+ set args_list [sycl_get_device_args $device]
+ if {[llength $args_list] <= 2} {
+ return 0
+ }
+
+ set type_match [expr {$type eq [lindex $args_list 0]}]
+ set name_match [string match $name [lindex $args_list 1]]
+
+ return [expr {$type_match && $name_match}]
+}
+
+# Run a test on the target to check if it recognizes SYCL.
+# Remove device from the available devices list if SYCL is not supported
+# and return the updated list.
+
+proc get_sycl_supported_devices {sycl_device_list} {
+ global srcdir inferior_exited_re
+
+ set supported_sycl_device_list {}
+
+ # Set up, compile, and execute a simple SYCL program.
+ set exe [standard_output_file sycl-hello]
+ set src "$srcdir/lib/sycl-hello.cpp"
+
+ if {[build_executable "failed to compile $src" $exe $src {sycl debug}]} {
+ verbose "SYCL: Compilation failed" 0
+ return $supported_sycl_device_list
+ }
+ verbose -log "\nSYCL: Compilation succeeded"
+
+ foreach device $sycl_device_list {
+ if {![is_sycl_device_filtered $device]} {
+ continue
+ }
+
+ sycl_with_intelgt_lock $device {
+ clean_restart sycl-hello
+
+ if {![sycl_start $device]} {
+ verbose "SYCL: Support not detected for $device" 0
+ continue
+ }
+
+ set inside_kernel [gdb_get_line_number "inside-kernel" $src]
+ gdb_breakpoint "sycl-hello.cpp:$inside_kernel"
+
+ set result 1
+ gdb_test_multiple "continue" "continue" {
+ -re -wrap "$inferior_exited_re normally].*" {
+ set result 1
+ }
+ -re -wrap "$inferior_exited_re with code.*" {
+ set result 1
+ }
+ -re -wrap "(?:Breakpoint) .* (at|in).*sycl-hello.cpp:$inside_kernel.*" {
+ set result 0
+ }
+ -re -wrap "received signal SIGABRT, Aborted.*" {
+ set result 1
+ }
+ }
+
+ if {$result == 0} {
+ verbose "SYCL: Support detected for $device" 0
+ lappend supported_sycl_device_list "$device"
+ } else {
+ verbose "SYCL: Support not detected for $device" 0
+ }
+ }
+ }
+
+ return $supported_sycl_device_list
+}
+
+# Run the program under debug by passing DEVICE as the command line
+# argument. If the device is not Intel GT, stop at main. If the
+# device is Intel GT, continue until the zeContextCreate API call and
+# attempt to create another inferior connected to an Intel GT
+# gdbserver target.
+#
+# Return 1 on success, 0 on failure.
+
+proc sycl_start {device} {
+ if {[require_sycl_device $device "gpu" "Intel*"]} {
+ # To debug an Intel GT device, we create an additional
+ # inferior. For the multi-target setting to work, we
+ # need to operate in target-non-stop mode.
+ gdb_test_no_output "maint set target-non-stop on"
+ }
+
+ set args ""
+ foreach arg [sycl_get_device_args $device] {
+ append args "'$arg' "
+ }
+ gdb_test_no_output "set args $args"
+
+ if {![runto_main]} {
+ return 0
+ }
+
+ if {[require_sycl_device $device "gpu" "Intel*"]} {
+ # For the Intel GT target, define a "hook" BP at a
+ # Level-Zero API function at which the Level-Zero backend
+ # would have been already initialized, attaching to the
+ # device would be expected to succeed. Once we hit the
+ # hook BP, we create the additional inferior.
+ gdb_breakpoint "zeModuleCreate" {*}"-allow-pending -temporary"
+ set hook_bp_hit [gdb_continue_to_breakpoint "hook bp" \
+ "zeModuleCreate\[^\r\n\]*"]
+ if {$hook_bp_hit != 0} {
+ return 0
+ }
+
+ with_test_prefix "$device" {
+ set inf_pid [get_inferior_pid]
+ gdb_test "add-inferior -no-connection"
+ gdb_test "inferior 2"
+ set cmd "gdbserver-intelgt --once --multi -"
+ gdb_test "target extended-remote | $cmd" \
+ "Remote debugging.*" "connect the remote target"
+ gdb_test "attach $inf_pid"
+ gdb_test "inferior 1"
+ gdb_test "set schedule-multi on"
+ gdb_test "info inferior" ".*" "inferiors for logging"
+ }
+ }
+
+ return 1
+}
+
+# Get list of devices and return 0 if device list is non-empty else
+# return 1. Each device entry of this list contains ";" separated
+# following information:
+# device name;backend name;device type;device version;device count.
+
+gdb_caching_proc init_sycl_devices_list {} {
+ global srcdir
+ global inferior_exited_re
+ global sycl_device_list
+
+ set sycl_device_list {}
+ set supported_sycl_device_list {}
+
+ # Set up, compile, and execute a simple SYCL program.
+ set exe [standard_output_file sycl-devices]
+ set src "$srcdir/lib/sycl-devices.cpp"
+
+ if {![test_compiler_info {icx-*} c++]} {
+ unsupported "SYCL tests supported only for dpcpp and icpx compilers"
+ return $sycl_device_list
+ }
+
+ if {[build_executable "failed to compile $src" $exe $src {sycl debug}]} {
+ verbose "SYCL: Compilation failed" 0
+ return $sycl_device_list
+ }
+ verbose -log "\nSYCL: Compilation succeeded"
+
+ clean_restart sycl-devices
+
+ if {![runto_main]} {
+ untested "failed to run sycl-devices to main"
+ return $sycl_device_list
+ }
+
+ set result 1
+ gdb_test_multiple "continue" "continue" {
+ -re "SYCL: List of Target devices: \\\[(\[^\r\n\]+)\\\]" {
+ set sycl_device_list [split $expect_out(1,string) ","]
+ exp_continue
+ }
+ -re -wrap "$inferior_exited_re normally].*" {
+ set result 0
+ }
+ -re -wrap "$inferior_exited_re with code.*" {
+ set result 1
+ }
+ }
+
+ set supported_sycl_device_list [get_sycl_supported_devices $sycl_device_list]
+ if {($result == 0) && ([llength $supported_sycl_device_list] > 0)} {
+ verbose "SYCL: Devices found: $supported_sycl_device_list" 0
+ } else {
+ set result 1
+ verbose "SYCL: No device found" 0
+ }
+
+ gdb_exit
+
+ return $supported_sycl_device_list
+}
+
+# Return the ID of the current thread (<inferior number>.<thread
+# number>). This procedure can be more practical than using the
+# $_thread and $_inferior convenience variables, because if the SYCL
+# kernel is offloaded to a CPU target, the current thread would be a
+# single integer, but if offloaded to a GPU, it may be an
+# inferior-qualified number like N.M.
+proc get_current_thread {location} {
+ global decimal
+
+ gdb_test_multiple "thread" "get current thread at $location" {
+ -re -wrap "Current thread is ($decimal|$decimal\.$decimal).*" {
+ pass $gdb_test_name
+ return $expect_out(1,string)
+ }
+ -re -wrap "" {
+ fail $gdb_test_name
+ }
+ }
+ return 0
+}
+
+# Returns 1 if the target device is selected via OFFLOAD_DEVICE_GROUP
+# and 0 otherwise.
+# DEVICE contains ";" separated following information:
+# device name;backend name;device type;device version;device count.
+
+proc is_sycl_device_filtered {device} {
+ global OFFLOAD_DEVICE_GROUP
+
+ # Filter according to OFFLOAD_DEVICE_GROUP.
+ set device_info [split "$device" ";"]
+ set backend [lindex $device_info 1]
+ set device_type [lindex $device_info 2]
+
+ if {[lsearch -nocase [split $OFFLOAD_DEVICE_GROUP ","] $device_type] < 0} {
+ verbose -log "SYCL: device type $device_type is unwanted, skipping '$device'"
+ return 0
+ }
+
+ if {$device_type == "gpu"
+ && [string match -nocase "*opencl*" $backend]} {
+ verbose -log "SYCL: unsupported combination: $device_type & $backend"
+ return 0
+ }
+
+ return 1
+}
+
+# Returns number of devices found in device string.
+# DEVICE contains ";" separated following information:
+# device name;backend name;device type;device version;device count.
+
+proc sycl_get_device_count {device} {
+ set device_info [split "$device" ";"]
+ set device_count [lindex $device_info 4]
+ return $device_count
+}
+
+# Gets the list of args required for running the SYCL tests, where input device
+# contains ";" separated following information:
+# device name;backend name;device type;device version;device count.
+#
+# Returns [device type, device name, backend name, device version]
+proc sycl_get_device_args {device} {
+ global hex
+
+ set device_info [split "$device" ";"]
+ set backend_name [lindex $device_info 1]
+ set device_type [lindex $device_info 2]
+ set device_version [lindex $device_info 3]
+ set device_name ""
+ set args_list {}
+
+ if {$device_type eq "gpu"} {
+ lappend args_list "gpu"
+ lappend args_list [lindex $device_info 0]
+ } elseif {$device_type eq "cpu"} {
+ lappend args_list "cpu"
+ if {[string match "*Intel*" $device]} {
+ lappend args_list "Intel"
+ }
+ } elseif {$device_type eq "accelerator"} {
+ lappend args_list "accelerator"
+ if {[string match "*Intel*" $device]} {
+ lappend args_list "Intel"
+ }
+ } else {
+ verbose "SYCL: Unexpected device type: $device_type" 0
+ }
+ lappend args_list $backend_name
+ lappend args_list $device_version
+ return $args_list
+}
+
+# Gets the prefix string required for the SYCL tests.
+#
+# Function returns ":" separated test prefix which has following info:
+# In case of non GPU device: Device type:Backend type:cpp
+# and in case of GPU: Device type GPU: Backend type: Graphics device ID
+# e.g. gpu:opencl:{0x1234}
+
+proc sycl_get_device_prefix {device} {
+ global hex
+ set args_list [sycl_get_device_args $device]
+
+ if {[string match -nocase "*Graphics*" $device]
+ || [string match -nocase "*GPU*" $device]} {
+ # In case of GPU device, add device ID to the prefix to get a unique
+ # test name for multi GPU test machines.
+ return "[lindex $args_list 0]:[lindex $args_list 2]:\
+ {[regexp -all -inline $hex [lindex $args_list 1]]}"
+ }
+ return "[lindex $args_list 0]:[lindex $args_list 2]:cpp"
+}
+
+# Run BODY under the lock, if DEVICE is an Intel GPU. Also calls
+# gdb_exit before releasing the GPU lock.
+#
+# See the similar 'with_gpu_lock' in rocm.exp.
+
+proc sycl_with_intelgt_lock {device body} {
+ with_test_prefix [sycl_get_device_prefix $device] {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ set code [catch {uplevel 1 $body} result]
+ } else {
+ with_lock $::intelgt_lock_filename {
+ set code [catch {uplevel 1 $body} result]
+ }
+ }
+
+ # In case BODY returned early due to some testcase failing.
+ gdb_exit
+ }
+
+ if {$code == 1} {
+ return -code $code -errorinfo $::errorInfo \
+ -errorcode $::errorCode $result
+ } else {
+ return -code $code $result
+ }
+}
+
+# Get the namespace version for the SYCL header corresponding to the compiler
+# used. Return 0 for older compilers using SYCL without namespace versioning.
+
+proc get_sycl_header_version {} {
+ if {[test_compiler_info {icx-202[3-9]-*} c++]} {
+ return 1
+ }
+
+ return 0
+}
+
+# Spawn a SYCL program targeting DEVICE.
+
+proc spawn_sycl_proc {executable device} {
+ # We directly use 'remote_spawn' to be able to pass
+ # the program arguments.
+ set command [list $executable]
+ foreach arg [sycl_get_device_args $device] {
+ lappend command $arg
+ }
+ verbose -log "command: $command"
+
+ set spawn_id [remote_spawn target $command]
+ return $spawn_id
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 37/44] testsuite, sycl: add test for backtracing inside a kernel
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (35 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 36/44] testsuite, sycl: add SYCL support Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 38/44] testsuite, sycl: add test for 'info locals' and 'info args' Markus Metzger
` (6 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur, Natalia Saiapova
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Add SYCL test for checking the call stack inside a kernel, including
inlined functions.
Co-authored-by: Natalia Saiapova <natalia.saiapova@intel.com>
---
gdb/testsuite/gdb.sycl/call-stack.cpp | 92 ++++++++++++++
gdb/testsuite/gdb.sycl/call-stack.exp | 176 ++++++++++++++++++++++++++
2 files changed, 268 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.cpp
create mode 100644 gdb/testsuite/gdb.sycl/call-stack.exp
diff --git a/gdb/testsuite/gdb.sycl/call-stack.cpp b/gdb/testsuite/gdb.sycl/call-stack.cpp
new file mode 100644
index 00000000000..516dd3e3324
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/call-stack.cpp
@@ -0,0 +1,92 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2026 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include "sycl-util.cpp"
+
+int
+fourth (int x4, int y4)
+{
+ return x4 * y4; /* ordinary-fourth-loc */
+}
+
+int
+third (int x3, int y3)
+{
+ return fourth (x3 + 5, y3 * 3) + 30; /* ordinary-third-loc */
+}
+
+int
+second (int x2, int y2)
+{
+ return third (x2 + 5, y2 * 3) + 30; /* ordinary-second-loc */
+}
+
+int
+first (int x1, int y1)
+{
+ int result = second (x1 + 5, y1 * 3); /* ordinary-first-loc */
+ return result + 30; /* kernel-function-return */
+}
+
+__attribute__((always_inline))
+int
+inlined_second (int x, int y)
+{
+ return x * y; /* inlined-inner-loc */
+}
+
+__attribute__((always_inline))
+int
+inlined_first (int num1, int num2)
+{
+ int result = inlined_second (num1 + 5, num2 * 3); /* inlined-middle-loc */
+ return result + 30;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int data[3] = {7, 8, 9};
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::buffer<int, 1> buf {data, sycl::range<1> {3}};
+
+ deviceQueue.submit ([&] (sycl::handler& cgh)
+ {
+ auto numbers = buf.get_access<sycl::access::mode::read_write> (cgh);
+
+ cgh.single_task ([=] ()
+ {
+ int ten = numbers[1] + 2;
+ int four = numbers[2] - 5;
+ int fourteen = ten + four;
+ numbers[0] = first (fourteen + 1, 3); /* ordinary-outer-loc */
+ numbers[1] = inlined_first (10, 2); /* inlined-outer-loc */
+ numbers[2] = first (3, 4); /* another-call */
+ });
+ });
+ }
+
+ std::cout << "Result is " << data[0] << " "
+ << data[1] << " " << data[2] << std::endl;
+ /* Expected: 210 120 126 */
+
+ return 0; /* end-of-program */
+}
diff --git a/gdb/testsuite/gdb.sycl/call-stack.exp b/gdb/testsuite/gdb.sycl/call-stack.exp
new file mode 100644
index 00000000000..73e59dfeab7
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/call-stack.exp
@@ -0,0 +1,176 @@
+# Copyright 2019-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL when there are function calls inside
+# the kernel.
+
+load_lib sycl.exp
+
+standard_testfile .cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+# Return the current line number.
+proc get_current_line {} {
+ global decimal
+ gdb_test_multiple "info line" "get current line" {
+ -re -wrap "Line ($decimal).*" {
+ pass $gdb_test_name
+ return $expect_out(1,string)
+ }
+ -re -wrap "" {
+ fail $gdb_test_name
+ return 0
+ }
+ }
+}
+
+proc test_call_stack {device} {
+ global srcfile valnum_re decimal inferior_exited_re
+
+ set fourth_loc [gdb_get_line_number "ordinary-fourth-loc"]
+ set third_loc [gdb_get_line_number "ordinary-third-loc"]
+ set second_loc [gdb_get_line_number "ordinary-second-loc"]
+ set first_loc [gdb_get_line_number "ordinary-first-loc"]
+ set outer_loc [gdb_get_line_number "ordinary-outer-loc"]
+ set inlined_inner_loc [gdb_get_line_number "inlined-inner-loc"]
+ set inlined_middle_loc [gdb_get_line_number "inlined-middle-loc"]
+ set inlined_outer_loc [gdb_get_line_number "inlined-outer-loc"]
+
+ set fill "\[^\r\n\]*"
+
+ set fourth_desc "fourth \\(x4=$fill, y4=$fill\\) at ${fill}$srcfile:$fourth_loc"
+ set third_desc "third \\(x3=$fill, y3=$fill\\) at ${fill}$srcfile:$third_loc"
+ set second_desc "second \\(x2=$fill, y2=$fill\\) at ${fill}$srcfile:$second_loc"
+ set first_desc "first \\(x1=$fill, y1=$fill\\) at ${fill}$srcfile:$first_loc"
+ set outer_desc "${fill}operator\\(\\)${fill} at ${fill}$srcfile:$outer_loc"
+ set inlined_inner_desc \
+ "inlined_second ${fill} at ${fill}$srcfile:$inlined_inner_loc"
+ set inlined_middle_desc \
+ "inlined_first ${fill} at ${fill}$srcfile:$inlined_middle_loc"
+ set inlined_outer_desc \
+ "${fill}operator\\(\\)${fill} at ${fill}$srcfile:$inlined_outer_loc"
+
+ gdb_breakpoint "$srcfile:$fourth_loc"
+ gdb_continue_to_breakpoint "innermost-body" ".*$srcfile:$fourth_loc.*"
+
+ # Limit the backtrace to 5 frames because frame #5
+ # and beyond are implementation-specific to the SYCL runtime.
+ gdb_test "backtrace 5" [multi_line \
+ "#0${fill} $fourth_desc" \
+ "#1${fill} $third_desc" \
+ "#2${fill} $second_desc" \
+ "#3${fill} $first_desc" \
+ "#4${fill} $outer_desc.*"] \
+ "first backtrace"
+
+ # Test inlined function calls.
+ gdb_breakpoint $inlined_inner_loc
+
+ gdb_continue_to_breakpoint "inlined-body" ".*$srcfile:$inlined_inner_loc.*"
+
+ gdb_test "backtrace 3" [multi_line \
+ "#0${fill} $inlined_inner_desc" \
+ "#1${fill} $inlined_middle_desc" \
+ "#2${fill} $inlined_outer_desc.*"] \
+ "backtrace for inlined calls"
+
+ delete_breakpoints
+
+ # Now we will stop at the beginning of prologue of the fourth function
+ # and instruction step through the function until it returns back
+ # to the third.
+ gdb_breakpoint "*fourth"
+
+ gdb_test "continue" "fourth.*$srcfile.*" "continue to fourth prologue"
+ set i 0
+ set current_line [get_current_line]
+ set fourth_prologue_line $current_line
+
+ # Update description to not include arguments.
+ set third_desc "third ${fill} at ${fill}$srcfile:$third_loc"
+ set second_desc "second ${fill} at ${fill}$srcfile:$second_loc"
+ set first_desc "first ${fill} at ${fill}$srcfile:$first_loc"
+
+ # Print the current instruction and framedesc for logging purposes.
+ gdb_test "display/i \$pc"
+ gdb_test "display/x \$framedesc"
+
+ # Check the backtrace at each instruction until the return. We do not
+ # check the args here, as they might be invalid at prologue and epilogue.
+ # Also check that there are no additional messages after
+ # the backtrace except "(More stack frames follow...)".
+ #
+ # Restrict the number of iterations to avoid an infinite loop in case
+ # of problems.
+ while {($current_line == $fourth_prologue_line
+ || $current_line == $fourth_loc)
+ && $i < 100} {
+ with_test_prefix "iteration $i" {
+ if {[require_sycl_device "$device" "gpu" "Intel*"]} {
+ set fourth_desc "fourth ${fill} at ${fill}$srcfile:$current_line"
+ gdb_test "backtrace 6" [multi_line \
+ "#0${fill} $fourth_desc" \
+ "#1${fill} $third_desc" \
+ "#2${fill} $second_desc" \
+ "#3${fill} $first_desc" \
+ "#4${fill} main${fill}operator${fill}lambda${fill} at .*" \
+ "#5${fill}(\r\n\\(More stack frames follow\\.\\.\\.\\))?"] \
+ "backtrace in fourth"
+ } else {
+ # On CPU the backtrace in prologue might include
+ # additional RT specific frames. Do not assume any
+ # frame numbers and do a deeper backtrace. Do not
+ # expect the line number at fourth. We expect to see
+ # our frames somewhere in the middle.
+ set fourth_desc "fourth ${fill} at ${fill}$srcfile:$decimal"
+ gdb_test "backtrace 10" [multi_line \
+ "${fill} $fourth_desc" \
+ "${fill} $third_desc" \
+ "${fill} $second_desc" \
+ "${fill} $first_desc" \
+ "${fill} main${fill}operator${fill}lambda${fill} at .*"] \
+ "backtrace in fourth"
+ }
+ gdb_test "with scheduler-locking on -- stepi"
+ incr i
+ set current_line [get_current_line]
+ }
+ }
+
+ # Disable printing of PC and FRAMEDESC.
+ gdb_test "undisplay 1-2"
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ test_call_stack "$device"
+ }
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 38/44] testsuite, sycl: add test for 'info locals' and 'info args'
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (36 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 37/44] testsuite, sycl: add test for backtracing inside a kernel Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:27 ` [PATCH v4 39/44] testsuite, sycl: add tests for stepping Markus Metzger
` (5 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Test that local variables and arguments inside a SYCL kernel can be
displayed correctly.
---
.../gdb.sycl/info-locals-and-args.exp | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/info-locals-and-args.exp
diff --git a/gdb/testsuite/gdb.sycl/info-locals-and-args.exp b/gdb/testsuite/gdb.sycl/info-locals-and-args.exp
new file mode 100644
index 00000000000..f2b446daef8
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/info-locals-and-args.exp
@@ -0,0 +1,78 @@
+# Copyright 2019-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL for the "info locals" and "info args"
+# commands inside a kernel.
+
+load_lib sycl.exp
+
+standard_testfile call-stack.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set outer_loc [gdb_get_line_number "ordinary-outer-loc"]
+ set inner_loc [gdb_get_line_number "kernel-function-return"]
+ gdb_breakpoint "$outer_loc"
+ gdb_breakpoint "$inner_loc"
+
+ gdb_continue_to_breakpoint "bp 1" ".*$srcfile:$outer_loc.*"
+
+ set fill "\[^\r\n\]+"
+ set ten 0
+ set four 0
+ set fourteen 0
+ gdb_test_multiple "info locals" "info locals 1" -lbl {
+ -re "\r\nten = $fill" {
+ set ten 1
+ exp_continue
+ }
+ -re "\r\nfour = $fill" {
+ set four 1
+ exp_continue
+ }
+ -re "\r\nfourteen = $fill" {
+ set fourteen 1
+ exp_continue
+ }
+ -re -wrap "" {
+ gdb_assert { $ten == 1 && $four == 1 && $fourteen == 1 } \
+ $gdb_test_name
+ }
+ }
+
+ gdb_continue_to_breakpoint "bp 2" ".*$srcfile:$inner_loc.*"
+ gdb_test "info locals" "result = $fill.*" "info locals 2"
+ gdb_test "info args" [multi_line \
+ "x1 = $fill" \
+ "y1 = $fill"]
+ }
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 39/44] testsuite, sycl: add tests for stepping
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (37 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 38/44] testsuite, sycl: add test for 'info locals' and 'info args' Markus Metzger
@ 2026-08-12 13:27 ` Markus Metzger
2026-08-12 13:28 ` [PATCH v4 40/44] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Markus Metzger
` (4 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:27 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Co-authored-by: Markus Metzger <markus.t.metzger@intel.com>
---
gdb/testsuite/gdb.sycl/step.exp | 48 +++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/step.exp
diff --git a/gdb/testsuite/gdb.sycl/step.exp b/gdb/testsuite/gdb.sycl/step.exp
new file mode 100644
index 00000000000..278a40cc73e
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/step.exp
@@ -0,0 +1,48 @@
+# Copyright 2019-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test stepping inside kernels.
+
+load_lib sycl.exp
+
+standard_testfile call-stack.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set outer_loc [gdb_get_line_number "ordinary-outer-loc"]
+ gdb_breakpoint $outer_loc
+ gdb_continue_to_breakpoint "inside" ".*$srcfile:$outer_loc.*"
+
+ gdb_test "step" "first .* at .*" "step into function"
+ gdb_test "next" "kernel-function-return.*"
+ }
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 40/44] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (38 preceding siblings ...)
2026-08-12 13:27 ` [PATCH v4 39/44] testsuite, sycl: add tests for stepping Markus Metzger
@ 2026-08-12 13:28 ` Markus Metzger
2026-08-12 13:28 ` [PATCH v4 41/44] testsuite, sycl: add test for scheduler-locking Markus Metzger
` (3 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Add SYCL test cases for a parallel_for kernel on a 1-dimensional and
2-dimensional index space.
---
gdb/testsuite/gdb.sycl/parallel-for-1D.cpp | 72 +++++++++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-1D.exp | 57 +++++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-2D.cpp | 73 ++++++++++++++++++++++
gdb/testsuite/gdb.sycl/parallel-for-2D.exp | 57 +++++++++++++++++
4 files changed, 259 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-1D.exp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
create mode 100644 gdb/testsuite/gdb.sycl/parallel-for-2D.exp
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-1D.cpp b/gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
new file mode 100644
index 00000000000..fa9f01c04aa
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-1D.cpp
@@ -0,0 +1,72 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2026 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include "sycl-util.cpp"
+
+static int
+get_dim (sycl::id<1> wi, int index)
+{
+ return wi[index]; /* inside-kernel-callee */
+}
+
+int
+main (int argc, char *argv[])
+{
+ constexpr size_t DIM0 = 1024;
+
+ int in[DIM0];
+ int out[DIM0];
+
+ /* Initialize the input. */
+ for (unsigned int i = 0; i < DIM0; i++)
+ in[i] = i + 123;
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::range<1> dataRange {DIM0};
+ sycl::buffer<int, 1> inBuffer {&in[0], dataRange};
+ sycl::buffer<int, 1> outBuffer {&out[0], dataRange};
+
+ deviceQueue.submit ([&] (sycl::handler& cgh) /* line-before-kernel */
+ {
+ auto accessorIn = inBuffer.get_access<sycl::access::mode::read> (cgh);
+ auto accessorOut
+ = outBuffer.get_access<sycl::access::mode::write> (cgh);
+
+ cgh.parallel_for<class kernel> (dataRange, [=] (sycl::id<1> wiID)
+ {
+ int dim0 = get_dim (wiID, 0); /* kernel-first-line */
+ int in_elem = accessorIn[wiID];
+ int in_elem2 = accessorIn[dim0];
+ accessorOut[wiID] = in_elem + 100; /* kernel-last-line */
+ });
+ });
+ }
+
+ /* Verify the output. */
+ for (unsigned int i = 0; i < DIM0; i++)
+ if (out[i] != in[i] + 100)
+ {
+ std::cout << "Element " << i << " is " << out[i] << std::endl;
+ return 1;
+ }
+
+ std::cout << "Correct" << std::endl; /* end-marker */
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-1D.exp b/gdb/testsuite/gdb.sycl/parallel-for-1D.exp
new file mode 100644
index 00000000000..2fd99df3ca0
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-1D.exp
@@ -0,0 +1,57 @@
+# Copyright 2019-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL, for a parallel_for kernel on a 1D range.
+
+load_lib sycl.exp
+
+standard_testfile .cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set last_line [gdb_get_line_number "kernel-last-line"]
+ gdb_breakpoint $last_line
+
+ # Check that we hit the BP for a number of elements. We do not check
+ # for each element because the number of hits received may depend on
+ # whether the kernel was vectorized, and if so, the width of vectors.
+ # Since the data array in the test program is large, having a small
+ # number of trips here should be safe.
+
+ for {set i 1} {$i <= 5} {incr i} {
+ with_test_prefix "trip $i" {
+ gdb_continue_to_breakpoint "hit the last line" \
+ ".*$srcfile:$last_line.*"
+ }
+ }
+ }
+}
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-2D.cpp b/gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
new file mode 100644
index 00000000000..7811e57faf1
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-2D.cpp
@@ -0,0 +1,73 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2019-2026 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include <iostream>
+#include "sycl-util.cpp"
+
+int
+main (int argc, char *argv[])
+{
+ constexpr size_t DIM0 = 128;
+ constexpr size_t DIM1 = 64;
+
+ int in[DIM0][DIM1];
+ int out[DIM1][DIM0]; /* will transpose the input. */
+
+ /* Initialize the input. */
+ int val = 123;
+ for (unsigned int i = 0; i < DIM0; i++)
+ for (unsigned int j = 0; j < DIM1; j++)
+ in[i][j] = val++;
+
+ { /* Extra scope enforces waiting on the kernel. */
+ sycl::queue deviceQueue {get_sycl_queue (argc, argv)};
+ sycl::range<2> dataRangeIn {DIM0, DIM1};
+ sycl::range<2> dataRangeOut {DIM1, DIM0};
+ sycl::buffer<int, 2> inBuffer {&in[0][0], dataRangeIn};
+ sycl::buffer<int, 2> outBuffer {&out[0][0], dataRangeOut};
+
+ deviceQueue.submit ([&] (sycl::handler& cgh)
+ {
+ auto accessorIn = inBuffer.get_access<sycl::access::mode::read> (cgh);
+ auto accessorOut
+ = outBuffer.get_access<sycl::access::mode::write> (cgh);
+
+ cgh.parallel_for<class kernel> (dataRangeIn, [=] (sycl::id<2> wiID)
+ {
+ int dim0 = wiID[0]; /* kernel-first-line */
+ int dim1 = wiID[1];
+ int in_elem = accessorIn[wiID];
+ /* Negate the value, write into the transpositional location. */
+ accessorOut[dim1][dim0] = -1 * in_elem; /* kernel-last-line */
+ });
+ });
+ }
+
+ /* Verify the output. */
+ for (unsigned int i = 0; i < DIM0; i++)
+ for (unsigned int j = 0; j < DIM1; j++)
+ if (in[i][j] != -out[j][i])
+ {
+ std::cout << "Element " << j << "," << i
+ << " is " << out[j][i] << std::endl;
+ return 1;
+ }
+
+ std::cout << "Correct" << std::endl;
+ return 0;
+}
diff --git a/gdb/testsuite/gdb.sycl/parallel-for-2D.exp b/gdb/testsuite/gdb.sycl/parallel-for-2D.exp
new file mode 100644
index 00000000000..0e1a68bda67
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/parallel-for-2D.exp
@@ -0,0 +1,57 @@
+# Copyright 2019-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests GDBs support for SYCL, for a parallel_for kernel on a 2D range.
+
+load_lib sycl.exp
+
+standard_testfile .cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set last_line [gdb_get_line_number "kernel-last-line"]
+ gdb_breakpoint $last_line
+
+ # Check that we hit the BP for a number of elements. We do not check
+ # for each element because the number of hits received may depend on
+ # whether the kernel was vectorized, and if so, the width of vectors.
+ # Since the data array in the test program is large, having a small
+ # number of trips here should be safe.
+
+ for {set i 1} {$i <= 5} {incr i} {
+ with_test_prefix "trip $i" {
+ gdb_continue_to_breakpoint "hit the last line" \
+ ".*$srcfile:$last_line.*"
+ }
+ }
+ }
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 41/44] testsuite, sycl: add test for scheduler-locking
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (39 preceding siblings ...)
2026-08-12 13:28 ` [PATCH v4 40/44] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Markus Metzger
@ 2026-08-12 13:28 ` Markus Metzger
2026-08-12 13:28 ` [PATCH v4 42/44] testsuite, arch, intelgt: add a disassembly test Markus Metzger
` (2 subsequent siblings)
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Add a new test to check that scheduler-locking when inside a SYCL
kernel works fine.
---
gdb/testsuite/gdb.sycl/scheduler-locking.exp | 63 ++++++++++++++++++++
1 file changed, 63 insertions(+)
create mode 100644 gdb/testsuite/gdb.sycl/scheduler-locking.exp
diff --git a/gdb/testsuite/gdb.sycl/scheduler-locking.exp b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
new file mode 100644
index 00000000000..96f7d58b8bc
--- /dev/null
+++ b/gdb/testsuite/gdb.sycl/scheduler-locking.exp
@@ -0,0 +1,63 @@
+# Copyright 2020-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test the scheduler-locker for resuming inside a SYCL kernel.
+
+load_lib sycl.exp
+
+standard_testfile parallel-for-1D.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set first_line [gdb_get_line_number "kernel-first-line"]
+ set last_line [gdb_get_line_number "kernel-last-line"]
+ gdb_breakpoint $first_line
+ gdb_breakpoint $last_line
+
+ for {set i 1} {$i <= 5} {incr i} {
+ with_test_prefix "trip $i" {
+ gdb_test_no_output "set scheduler-locking off"
+ gdb_continue_to_breakpoint ".*kernel-first-line.*"
+
+ # Resuming should make only the current thread move.
+ set current_thread [get_current_thread "kernel-first-line"]
+
+ gdb_test_no_output "set scheduler-locking step"
+ gdb_test "next" "int in_elem = .*"
+
+ gdb_test_no_output "set scheduler-locking on"
+ gdb_test "continue" \
+ "Thread $current_thread \[^\r\n\]+$srcfile:$last_line.*"
+ }
+ }
+ }
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 42/44] testsuite, arch, intelgt: add a disassembly test
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (40 preceding siblings ...)
2026-08-12 13:28 ` [PATCH v4 41/44] testsuite, sycl: add test for scheduler-locking Markus Metzger
@ 2026-08-12 13:28 ` Markus Metzger
2026-08-12 13:28 ` [PATCH v4 43/44] testsuite, arch, intelgt: add intelgt-program-bp.exp Markus Metzger
2026-08-12 13:28 ` [PATCH v4 44/44] testsuite, intelgt: add a test for interrupting an exited thread Markus Metzger
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Albertano Caruso
From: Albertano Caruso <albertano.caruso@intel.com>
Test the disassemble command on the Intel GT target.
---
.../gdb.arch/intelgt-disassemble.exp | 82 +++++++++++++++++++
gdb/testsuite/gdb.arch/sycl-hello.cpp | 33 ++++++++
2 files changed, 115 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/intelgt-disassemble.exp
create mode 100644 gdb/testsuite/gdb.arch/sycl-hello.cpp
diff --git a/gdb/testsuite/gdb.arch/intelgt-disassemble.exp b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
new file mode 100644
index 00000000000..f3c3dc8b600
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-disassemble.exp
@@ -0,0 +1,82 @@
+# Copyright 2019-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Tests disassembly support for Intel(R) Graphics Technology.
+
+load_lib sycl.exp
+
+standard_testfile sycl-hello.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ unsupported "test is aimed at Intel GPUs only"
+ continue
+ }
+
+ sycl_with_intelgt_lock $device {
+ clean_restart ${testfile}
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ # Set breakpoint inside the kernel.
+ set bp_location [gdb_get_line_number "inside-kernel"]
+ gdb_breakpoint "$srcfile:$bp_location"
+
+ # Hit the breakpoint.
+ gdb_continue_to_breakpoint "kernel" ".*$srcfile:$bp_location.*"
+
+ # Check if IGA is supposed to exist.
+ set has_iga 0
+ gdb_test_multiple "show configuration" "check iga" {
+ -re -wrap "with-libiga64.*" {
+ set has_iga 1
+ }
+ -re -wrap "without-libiga64.*" {
+ set has_iga 0
+ }
+ }
+
+ if {$has_iga} {
+ gdb_test "disassemble \$pc, \$pc+32" \
+ [multi_line "Dump of assembler code from $hex to $hex:" \
+ "=> $hex .*" \
+ "End of assembler dump."] \
+ "disassemble"
+ } else {
+ unsupported "disassemble"
+
+ gdb_test "disassemble \$pc, \$pc+32" \
+ [multi_line "Dump of assembler code from $hex to $hex:" \
+ "=> $hex \[^\r\n\]+" \
+ "Disassemble feature not available: libiga64 is missing." \
+ "" \
+ "unknown disassembler error.*"] \
+ "disassembly is unavailable without iga"
+ }
+ }
+}
diff --git a/gdb/testsuite/gdb.arch/sycl-hello.cpp b/gdb/testsuite/gdb.arch/sycl-hello.cpp
new file mode 100644
index 00000000000..3d2b8de407d
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/sycl-hello.cpp
@@ -0,0 +1,33 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+ Copyright 2026 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <sycl/sycl.hpp>
+#include "sycl-util.cpp"
+
+int
+main (int argc, char *argv[])
+{
+ sycl::queue queue {get_sycl_queue (argc, argv)};
+ queue.single_task ([] ()
+ {
+ int a = 0; /* inside-kernel */
+ });
+
+ queue.wait ();
+
+ return 0;
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 43/44] testsuite, arch, intelgt: add intelgt-program-bp.exp
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (41 preceding siblings ...)
2026-08-12 13:28 ` [PATCH v4 42/44] testsuite, arch, intelgt: add a disassembly test Markus Metzger
@ 2026-08-12 13:28 ` Markus Metzger
2026-08-12 13:28 ` [PATCH v4 44/44] testsuite, intelgt: add a test for interrupting an exited thread Markus Metzger
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:28 UTC (permalink / raw)
To: gdb-patches; +Cc: Tankut Baris Aktemur
From: Tankut Baris Aktemur <tankut.baris.aktemur@intel.com>
Introduce a new test to check if GDB can resume from a
manually-inserted program breakpoint on the Intel GT device without
repeatedly hitting the breakpoint.
---
gdb/testsuite/gdb.arch/intelgt-program-bp.exp | 83 +++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/intelgt-program-bp.exp
diff --git a/gdb/testsuite/gdb.arch/intelgt-program-bp.exp b/gdb/testsuite/gdb.arch/intelgt-program-bp.exp
new file mode 100644
index 00000000000..7e86592baa1
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-program-bp.exp
@@ -0,0 +1,83 @@
+# Copyright 2020-2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test that GDB steps over program breakpoints (i.e. breakpoints
+# manually inserted by the user by modifying an instruction) on an
+# Intel GT target.
+
+load_lib sycl.exp
+
+standard_testfile sycl-hello.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ unsupported "test is aimed at Intel GPUs only"
+ continue
+ }
+
+ sycl_with_intelgt_lock $device {
+ clean_restart $testfile
+
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set kernel_line [gdb_get_line_number "inside-kernel"]
+ # Make the BP temporary, so that GDB will remove it. We will
+ # then set it manually.
+ gdb_breakpoint $kernel_line -temporary
+ gdb_continue_to_breakpoint "inside kernel" ".*$srcfile:$kernel_line.*"
+
+ # Manually set the BP bit on the current PC and the next one.
+ with_test_prefix "current pc" {
+ set first_pc [get_hexadecimal_valueof "\$pc" 0 "first stop pc"]
+ set_breakpoint_bit $first_pc
+ }
+
+ with_test_prefix "next pc" {
+ if {[is_compact_insn $first_pc]} {
+ # A compacted instruction is 8 bytes.
+ set next_pc [format 0x%x [expr $first_pc + 0x08]]
+ } else {
+ # A full instruction is 16 bytes.
+ set next_pc [format 0x%x [expr $first_pc + 0x10]]
+ }
+ set_breakpoint_bit $next_pc
+ }
+
+ # We should now step-over the current PC and hit the manually-inserted
+ # BP on the next PC.
+ gdb_test "continue" \
+ "Thread \[^\r\n\]+ received signal SIGTRAP.*" \
+ "continue to the manual BP"
+ set second_pc [get_hexadecimal_valueof "\$pc" 0 "second stop pc"]
+ gdb_assert {$second_pc == $next_pc}
+
+ # Resuming should not hit the manual BP again. Expect to terminate.
+ gdb_test "continue" "$inferior_exited_re normally].*" \
+ "continue to end"
+ }
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 44/44] testsuite, intelgt: add a test for interrupting an exited thread
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
` (42 preceding siblings ...)
2026-08-12 13:28 ` [PATCH v4 43/44] testsuite, arch, intelgt: add intelgt-program-bp.exp Markus Metzger
@ 2026-08-12 13:28 ` Markus Metzger
43 siblings, 0 replies; 45+ messages in thread
From: Markus Metzger @ 2026-08-12 13:28 UTC (permalink / raw)
To: gdb-patches
On Intel GPUs we do not get exit events when a thread dispatch ends. The
gdbserver-intelgt target synthesizes an exit event during single-stepping,
but otherwise just removes threads from the thread list.
Test that we can still interrupt an Intel GPU inferior after all resumed
threads exited.
---
.../intelgt-interrupt-exited-thread.exp | 85 +++++++++++++++++++
1 file changed, 85 insertions(+)
create mode 100644 gdb/testsuite/gdb.arch/intelgt-interrupt-exited-thread.exp
diff --git a/gdb/testsuite/gdb.arch/intelgt-interrupt-exited-thread.exp b/gdb/testsuite/gdb.arch/intelgt-interrupt-exited-thread.exp
new file mode 100644
index 00000000000..3730cc8cda7
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/intelgt-interrupt-exited-thread.exp
@@ -0,0 +1,85 @@
+# Copyright 2026 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# Test scenarios where we try to interrupt threads that exited.
+
+load_lib sycl.exp
+
+standard_testfile sycl-hello.cpp
+
+set sycl_device_list [init_sycl_devices_list]
+if {[llength $sycl_device_list] == 0} {
+ unsupported "target does not support SYCL"
+ return
+}
+
+if {[build_executable "failed to compile $srcfile" "$binfile" "$srcfile" \
+ {sycl debug}]} {
+ return
+}
+
+foreach device $sycl_device_list {
+ sycl_with_intelgt_lock $device {
+ if {![require_sycl_device "$device" "gpu" "Intel*"]} {
+ unsupported "test is aimed at Intel GPUs only"
+ continue
+ }
+
+ clean_restart $testfile
+ if {![sycl_start $device]} {
+ continue
+ }
+
+ set line [gdb_get_line_number "inside-kernel"]
+ gdb_breakpoint $line
+ gdb_continue_to_breakpoint ".*inside-kernel.*"
+
+ gdb_test_no_output "set scheduler-locking on"
+ gdb_test_multiple "continue" "continue thread" {
+ -re "Continuing" {
+ pass $gdb_test_name
+ }
+ }
+
+ after 500 {send_gdb "\003"}
+ gdb_test "" "Thread .* exited.*" "thread exited"
+ gdb_test "thread" ".*exited.*"
+
+ gdb_test_no_output "set scheduler-locking off"
+ gdb_test_no_output "set schedule-multiple off"
+
+ with_test_prefix "thread exited" {
+ gdb_test_multiple "continue" "continue inferior" {
+ -re "Continuing" {
+ pass $gdb_test_name
+ }
+ }
+
+ after 500 {send_gdb "\003"}
+ gdb_test "" "No unwaited-for children left." "no resumed"
+ }
+
+ with_test_prefix "no thread" {
+ gdb_test_multiple "continue" "continue inferior" {
+ -re "Continuing" {
+ pass $gdb_test_name
+ }
+ }
+
+ after 500 {send_gdb "\003"}
+ gdb_test "" "No unwaited-for children left." "no resumed"
+ }
+ }
+}
--
2.43.0
________________________________________
Intel Deutschland GmbH
Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany
Tel: +49 (89) 99143-0
www.intel.de
Managing Directors: Candice Moore, Jeffrey Schneiderman, Ramachandran Sitaraman
Chairperson of the Supervisory Board: Sonja Pierer
Registered Seat: Munich Commercial Register B: Amtsgericht Munich HRB 186928
This e-mail and any attachments may contain confidential material for
the sole use of the intended recipient(s). Any review or distribution
by others is strictly prohibited. If you are not the intended
recipient, please contact the sender and delete all copies.
^ permalink raw reply [flat|nested] 45+ messages in thread
end of thread, other threads:[~2026-08-12 13:41 UTC | newest]
Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-12 13:27 [PATCH v4 00/44] A new target to debug Intel GPUs Markus Metzger
2026-08-12 13:27 ` [PATCH v4 01/44] bfd: add intelgt target to BFD Markus Metzger
2026-08-12 13:27 ` [PATCH v4 02/44] opcodes: add intelgt as a configuration Markus Metzger
2026-08-12 13:27 ` [PATCH v4 03/44] gdbserver: allow configuring for a heterogeneous target Markus Metzger
2026-08-12 13:27 ` [PATCH v4 04/44] config.sub: recognize level-zero as "ze" Markus Metzger
2026-08-12 13:27 ` [PATCH v4 05/44] gdbserver: import AC_LIB_HAVE_LINKFLAGS macro into the autoconf script Markus Metzger
2026-08-12 13:27 ` [PATCH v4 06/44] gdb, gdbserver, gdbsupport: add 'device' tag to XML target description Markus Metzger
2026-08-12 13:27 ` [PATCH v4 07/44] gdb, arch, intelgt: add intelgt arch definitions Markus Metzger
2026-08-12 13:27 ` [PATCH v4 08/44] gdb: add a new extract_integer variant that takes two array_views Markus Metzger
2026-08-12 13:27 ` [PATCH v4 09/44] gdb, intelgt: add the target-dependent definitions for the Intel GT architecture Markus Metzger
2026-08-12 13:27 ` [PATCH v4 10/44] gdb, intelgt: add disassemble feature " Markus Metzger
2026-08-12 13:27 ` [PATCH v4 11/44] gdb: revise the pid_to_exec_file target op Markus Metzger
2026-08-12 13:27 ` [PATCH v4 12/44] gdb, remote: do 'remote_add_inferior' in 'remote_notice_new_inferior' earlier Markus Metzger
2026-08-12 13:27 ` [PATCH v4 13/44] gdbserver: move dlls_changed initialization on attach into targets Markus Metzger
2026-08-12 13:27 ` [PATCH v4 14/44] gdbserver: adjust pid after the target attaches Markus Metzger
2026-08-12 13:27 ` [PATCH v4 15/44] gdbserver: check process when we need a process Markus Metzger
2026-08-12 13:27 ` [PATCH v4 16/44] gdb: use process in xfer_partial if inferior_ptid is null_ptid Markus Metzger
2026-08-12 13:27 ` [PATCH v4 17/44] gdb: allow inferiors without threads in all_matching_threads_iterator Markus Metzger
2026-08-12 13:27 ` [PATCH v4 18/44] gdbserver: improve threads debug output for process general thread Markus Metzger
2026-08-12 13:27 ` [PATCH v4 19/44] gdb, gdbserver: allow setting null_ptid as " Markus Metzger
2026-08-12 13:27 ` [PATCH v4 20/44] gdbserver: allow inferiors without threads Markus Metzger
2026-08-12 13:27 ` [PATCH v4 21/44] gdb: allow creating and attaching to " Markus Metzger
2026-08-12 13:27 ` [PATCH v4 22/44] gdb, remote: don't create an inferior on attach Markus Metzger
2026-08-12 13:27 ` [PATCH v4 23/44] gdb: allow switching to an inferior without threads Markus Metzger
2026-08-12 13:27 ` [PATCH v4 24/44] gdb: allow resuming an inferior with no threads Markus Metzger
2026-08-12 13:27 ` [PATCH v4 25/44] gdb: allow continuing an inferior without threads Markus Metzger
2026-08-12 13:27 ` [PATCH v4 26/44] gdb: partially fix C-c not working Markus Metzger
2026-08-12 13:27 ` [PATCH v4 27/44] gdb, linux-nat: use current_inferior()->pid in mourn_inferior() Markus Metzger
2026-08-12 13:27 ` [PATCH v4 28/44] gdb: inferior events Markus Metzger
2026-08-12 13:27 ` [PATCH v4 29/44] gdb, remote: allow deleting the last thread in inferior in update_thread_list() Markus Metzger
2026-08-12 13:27 ` [PATCH v4 30/44] gdb: keep target registered in inferior_event_handler() Markus Metzger
2026-08-12 13:27 ` [PATCH v4 31/44] gdb, dwarf, ze: add DW_OP_INTEL_regval_bits Markus Metzger
2026-08-12 13:27 ` [PATCH v4 32/44] gdbserver: add a pointer to the owner thread in regcache Markus Metzger
2026-08-12 13:27 ` [PATCH v4 33/44] gdb, gdbserver, ze: in-memory libraries Markus Metzger
2026-08-12 13:27 ` [PATCH v4 34/44] gdb, gdbserver: library notifications Markus Metzger
2026-08-12 13:27 ` [PATCH v4 35/44] gdbserver, ze, intelgt: introduce ze-low and intelgt-ze-low targets Markus Metzger
2026-08-12 13:27 ` [PATCH v4 36/44] testsuite, sycl: add SYCL support Markus Metzger
2026-08-12 13:27 ` [PATCH v4 37/44] testsuite, sycl: add test for backtracing inside a kernel Markus Metzger
2026-08-12 13:27 ` [PATCH v4 38/44] testsuite, sycl: add test for 'info locals' and 'info args' Markus Metzger
2026-08-12 13:27 ` [PATCH v4 39/44] testsuite, sycl: add tests for stepping Markus Metzger
2026-08-12 13:28 ` [PATCH v4 40/44] testsuite, sycl: add test for 1-D and 2-D parallel_for kernels Markus Metzger
2026-08-12 13:28 ` [PATCH v4 41/44] testsuite, sycl: add test for scheduler-locking Markus Metzger
2026-08-12 13:28 ` [PATCH v4 42/44] testsuite, arch, intelgt: add a disassembly test Markus Metzger
2026-08-12 13:28 ` [PATCH v4 43/44] testsuite, arch, intelgt: add intelgt-program-bp.exp Markus Metzger
2026-08-12 13:28 ` [PATCH v4 44/44] testsuite, intelgt: add a test for interrupting an exited thread Markus Metzger
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox